Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit/./../../../../js/src/jit/CodeGenerator.cpp
Warning:line 13523, 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-10-17-231001-351276-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/FloatingPoint.h"
14#include "mozilla/IntegerTypeTraits.h"
15#include "mozilla/Latin1.h"
16#include "mozilla/MathAlgorithms.h"
17#include "mozilla/ScopeExit.h"
18#include "mozilla/SIMD.h"
19
20#include <algorithm>
21#include <bit>
22#include <cmath>
23#include <limits>
24#include <type_traits>
25#include <utility>
26
27#include "builtin/MapObject.h"
28#include "builtin/Math.h"
29#include "builtin/Number.h"
30#include "builtin/RegExp.h"
31#include "builtin/String.h"
32#include "irregexp/RegExpTypes.h"
33#include "jit/ABIArgGenerator.h"
34#include "jit/CompileInfo.h"
35#include "jit/InlineScriptTree.h"
36#include "jit/Invalidation.h"
37#include "jit/IonGenericCallStub.h"
38#include "jit/IonIC.h"
39#include "jit/IonScript.h"
40#include "jit/JitcodeMap.h"
41#include "jit/JitFrames.h"
42#include "jit/JitRuntime.h"
43#include "jit/JitSpewer.h"
44#include "jit/JitZone.h"
45#include "jit/Linker.h"
46#include "jit/MIRGenerator.h"
47#include "jit/MoveEmitter.h"
48#include "jit/RangeAnalysis.h"
49#include "jit/RegExpStubConstants.h"
50#include "jit/SafepointIndex.h"
51#include "jit/SharedICHelpers.h"
52#include "jit/SharedICRegisters.h"
53#include "jit/VMFunctions.h"
54#include "jit/WarpSnapshot.h"
55#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
56#include "js/experimental/JitInfo.h" // JSJit{Getter,Setter}CallArgs, JSJitMethodCallArgsTraits, JSJitInfo
57#include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
58#include "js/RegExpFlags.h" // JS::RegExpFlag
59#include "js/ScalarType.h" // js::Scalar::Type
60#include "proxy/DOMProxy.h"
61#include "proxy/ScriptedProxyHandler.h"
62#include "util/DifferentialTesting.h"
63#include "util/PortableMath.h"
64#include "util/Unicode.h"
65#include "vm/ArrayBufferViewObject.h"
66#include "vm/AsyncFunction.h"
67#include "vm/AsyncIteration.h"
68#include "vm/BuiltinObjectKind.h"
69#include "vm/DateObject.h"
70#include "vm/FunctionFlags.h" // js::FunctionFlags
71#include "vm/Interpreter.h"
72#include "vm/JSAtomUtils.h" // AtomizeString
73#include "vm/MatchPairs.h"
74#include "vm/RegExpObject.h"
75#include "vm/RegExpStatics.h"
76#include "vm/RuntimeFuses.h"
77#include "vm/StaticStrings.h"
78#include "vm/StringObject.h"
79#include "vm/StringType.h"
80#include "vm/TypedArrayObject.h"
81#include "wasm/WasmCodegenConstants.h"
82#include "wasm/WasmPI.h"
83#include "wasm/WasmStacks.h"
84#include "wasm/WasmValType.h"
85#ifdef MOZ_VTUNE1
86# include "vtune/VTuneWrapper.h"
87#endif
88#include "wasm/WasmBinary.h"
89#include "wasm/WasmGC.h"
90#include "wasm/WasmGcObject.h"
91#include "wasm/WasmStubs.h"
92
93#include "builtin/Boolean-inl.h"
94#include "jit/MacroAssembler-inl.h"
95#include "jit/shared/CodeGenerator-shared-inl.h"
96#include "jit/TemplateObject-inl.h"
97#include "jit/VMFunctionList-inl.h"
98#include "vm/BytecodeUtil-inl.h"
99#include "vm/JSScript-inl.h"
100#include "wasm/WasmInstance-inl.h"
101
102using namespace js;
103using namespace js::jit;
104
105using mozilla::CheckedUint32;
106using mozilla::DebugOnly;
107using mozilla::FloatingPoint;
108using mozilla::NegativeInfinity;
109using mozilla::PositiveInfinity;
110
111using JS::ExpandoAndGeneration;
112
113namespace js {
114namespace jit {
115
116#ifdef CHECK_OSIPOINT_REGISTERS1
117template <class Op>
118static void HandleRegisterDump(Op op, MacroAssembler& masm,
119 LiveRegisterSet liveRegs, Register activation,
120 Register scratch) {
121 const size_t baseOffset = JitActivation::offsetOfRegs();
122
123 // Handle live GPRs.
124 for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) {
125 Register reg = *iter;
126 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
127
128 if (reg == activation) {
129 // To use the original value of the activation register (that's
130 // now on top of the stack), we need the scratch register.
131 masm.push(scratch);
132 masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch);
133 op(scratch, dump);
134 masm.pop(scratch);
135 } else {
136 op(reg, dump);
137 }
138 }
139
140 // Handle live FPRs.
141 for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) {
142 FloatRegister reg = *iter;
143 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
144 op(reg, dump);
145 }
146}
147
148class StoreOp {
149 MacroAssembler& masm;
150
151 public:
152 explicit StoreOp(MacroAssembler& masm) : masm(masm) {}
153
154 void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); }
155 void operator()(FloatRegister reg, Address dump) {
156 if (reg.isDouble()) {
157 masm.storeDouble(reg, dump);
158 } else if (reg.isSingle()) {
159 masm.storeFloat32(reg, dump);
160 } else if (reg.isSimd128()) {
161 MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD"
, "./../../../../js/src/jit/CodeGenerator.cpp", 161); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { MOZ_CrashSequence
(__null, 161); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
162 } else {
163 MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type."
, "./../../../../js/src/jit/CodeGenerator.cpp", 163); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected register type." ")"); do { MOZ_CrashSequence
(__null, 163); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
164 }
165 }
166};
167
168class VerifyOp {
169 MacroAssembler& masm;
170 Label* failure_;
171
172 public:
173 VerifyOp(MacroAssembler& masm, Label* failure)
174 : masm(masm), failure_(failure) {}
175
176 void operator()(Register reg, Address dump) {
177 masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
178 }
179 void operator()(FloatRegister reg, Address dump) {
180 if (reg.isDouble()) {
181 ScratchDoubleScope scratch(masm);
182 masm.loadDouble(dump, scratch);
183 masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_);
184 } else if (reg.isSingle()) {
185 ScratchFloat32Scope scratch(masm);
186 masm.loadFloat32(dump, scratch);
187 masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_);
188 } else if (reg.isSimd128()) {
189 MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD"
, "./../../../../js/src/jit/CodeGenerator.cpp", 189); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { MOZ_CrashSequence
(__null, 189); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
190 } else {
191 MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type."
, "./../../../../js/src/jit/CodeGenerator.cpp", 191); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected register type." ")"); do { MOZ_CrashSequence
(__null, 191); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
192 }
193 }
194};
195
196void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) {
197 // Ensure the live registers stored by callVM did not change between
198 // the call and this OsiPoint. Try-catch relies on this invariant.
199
200 // Load pointer to the JitActivation in a scratch register.
201 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
202 Register scratch = allRegs.takeAny();
203 masm.push(scratch);
204 masm.loadJitActivation(scratch);
205
206 // If we should not check registers (because the instruction did not call
207 // into the VM, or a GC happened), we're done.
208 Label failure, done;
209 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
210 masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
211
212 // Having more than one VM function call made in one visit function at
213 // runtime is a sec-ciritcal error, because if we conservatively assume that
214 // one of the function call can re-enter Ion, then the invalidation process
215 // will potentially add a call at a random location, by patching the code
216 // before the return address.
217 masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
218
219 // Set checkRegs to 0, so that we don't try to verify registers after we
220 // return from this script to the caller.
221 masm.store32(Imm32(0), checkRegs);
222
223 // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
224 // temps after calling into the VM. This is fine because no other
225 // instructions (including this OsiPoint) will depend on them. Also
226 // backtracking can also use the same register for an input and an output.
227 // These are marked as clobbered and shouldn't get checked.
228 LiveRegisterSet liveRegs;
229 liveRegs.set() = RegisterSet::Intersect(
230 safepoint->liveRegs().set(),
231 RegisterSet::Not(safepoint->clobberedRegs().set()));
232
233 VerifyOp op(masm, &failure);
234 HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
235
236 masm.jump(&done);
237
238 // Do not profile the callWithABI that occurs below. This is to avoid a
239 // rare corner case that occurs when profiling interacts with itself:
240 //
241 // When slow profiling assertions are turned on, FunctionBoundary ops
242 // (which update the profiler pseudo-stack) may emit a callVM, which
243 // forces them to have an osi point associated with them. The
244 // FunctionBoundary for inline function entry is added to the caller's
245 // graph with a PC from the caller's code, but during codegen it modifies
246 // Gecko Profiler instrumentation to add the callee as the current top-most
247 // script. When codegen gets to the OSIPoint, and the callWithABI below is
248 // emitted, the codegen thinks that the current frame is the callee, but
249 // the PC it's using from the OSIPoint refers to the caller. This causes
250 // the profiler instrumentation of the callWithABI below to ASSERT, since
251 // the script and pc are mismatched. To avoid this, we simply omit
252 // instrumentation for these callWithABIs.
253
254 // Any live register captured by a safepoint (other than temp registers)
255 // must remain unchanged between the call and the OsiPoint instruction.
256 masm.bind(&failure);
257 masm.assumeUnreachable("Modified registers between VM call and OsiPoint");
258
259 masm.bind(&done);
260 masm.pop(scratch);
261}
262
263bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) {
264 if (!checkOsiPointRegisters) {
265 return false;
266 }
267
268 if (safepoint->liveRegs().emptyGeneral() &&
269 safepoint->liveRegs().emptyFloat()) {
270 return false; // No registers to check.
271 }
272
273 return true;
274}
275
276void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) {
277 if (!shouldVerifyOsiPointRegs(safepoint)) {
278 return;
279 }
280
281 // Set checkRegs to 0. If we perform a VM call, the instruction
282 // will set it to 1.
283 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
284 Register scratch = allRegs.takeAny();
285 masm.push(scratch);
286 masm.loadJitActivation(scratch);
287 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
288 masm.store32(Imm32(0), checkRegs);
289 masm.pop(scratch);
290}
291
292static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) {
293 // Store a copy of all live registers before performing the call.
294 // When we reach the OsiPoint, we can use this to check nothing
295 // modified them in the meantime.
296
297 // Load pointer to the JitActivation in a scratch register.
298 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
299 Register scratch = allRegs.takeAny();
300 masm.push(scratch);
301 masm.loadJitActivation(scratch);
302
303 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
304 masm.add32(Imm32(1), checkRegs);
305
306 StoreOp op(masm);
307 HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
308
309 masm.pop(scratch);
310}
311#endif // CHECK_OSIPOINT_REGISTERS
312
313// Before doing any call to Cpp, you should ensure that volatile
314// registers are evicted by the register allocator.
315void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) {
316 TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id);
317 const VMFunctionData& fun = GetVMFunction(id);
318
319 // Stack is:
320 // ... frame ...
321 // [args]
322#ifdef DEBUG1
323 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"
, 323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs"
")"); do { MOZ_CrashSequence(__null, 323); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
324 pushedArgs_ = 0;
325#endif
326
327#ifdef CHECK_OSIPOINT_REGISTERS1
328 if (shouldVerifyOsiPointRegs(ins->safepoint())) {
329 StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
330 }
331#endif
332
333#ifdef DEBUG1
334 if (ins->mirRaw()) {
335 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"
, 335); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()"
")"); do { MOZ_CrashSequence(__null, 335); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
336 MInstruction* mir = ins->mirRaw()->toInstruction();
337 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"
, 337); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()"
")"); do { MOZ_CrashSequence(__null, 337); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
338
339 // If this MIR instruction has an overridden AliasSet, set the JitRuntime's
340 // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call
341 // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because
342 // interrupt callbacks can call JS (chrome JS or shell testing functions).
343 // MCheckOverRecursed for a generator resume doesn't check for interrupts.
344 bool isWhitelisted = mir->isInterruptCheck() ||
345 (mir->isCheckOverRecursed() &&
346 !mir->toCheckOverRecursed()->isResumingGenerator());
347 if (!mir->hasDefaultAliasSet() && !isWhitelisted) {
348 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
349 masm.move32(Imm32(1), ReturnReg);
350 masm.store32(ReturnReg, AbsoluteAddress(addr));
351 }
352 }
353#endif
354
355 // Push an exit frame descriptor.
356 masm.Push(FrameDescriptor(FrameType::IonJS));
357
358 // Call the wrapper function. The wrapper is in charge to unwind the stack
359 // when returning from the call. Failures are handled with exceptions based
360 // on the return value of the C functions. To guard the outcome of the
361 // returned value, use another LIR instruction.
362 ensureOsiSpace();
363 uint32_t callOffset = masm.callJit(code);
364 markSafepointAt(callOffset, ins);
365
366#ifdef DEBUG1
367 // Reset the disallowArbitraryCode flag after the call.
368 {
369 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
370 masm.push(ReturnReg);
371 masm.move32(Imm32(0), ReturnReg);
372 masm.store32(ReturnReg, AbsoluteAddress(addr));
373 masm.pop(ReturnReg);
374 }
375#endif
376
377 // Pop rest of the exit frame and the arguments left on the stack.
378 int framePop =
379 sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall();
380 masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop);
381
382 // Stack is:
383 // ... frame ...
384}
385
386template <typename Fn, Fn fn>
387void CodeGenerator::callVM(LInstruction* ins) {
388 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
389 callVMInternal(id, ins);
390}
391
392// ArgSeq store arguments for OutOfLineCallVM.
393//
394// OutOfLineCallVM are created with "oolCallVM" function. The third argument of
395// this function is an instance of a class which provides a "generate" in charge
396// of pushing the argument, with "pushArg", for a VMFunction.
397//
398// Such list of arguments can be created by using the "ArgList" function which
399// creates one instance of "ArgSeq", where the type of the arguments are
400// inferred from the type of the arguments.
401//
402// The list of arguments must be written in the same order as if you were
403// calling the function in C++.
404//
405// Example:
406// ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs()))
407
408template <typename... ArgTypes>
409class ArgSeq {
410 std::tuple<std::remove_reference_t<ArgTypes>...> args_;
411
412 template <std::size_t... ISeq>
413 inline void generate(CodeGenerator* codegen,
414 std::index_sequence<ISeq...>) const {
415 // Arguments are pushed in reverse order, from last argument to first
416 // argument.
417 (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...);
418 }
419
420 public:
421 explicit ArgSeq(ArgTypes&&... args)
422 : args_(std::forward<ArgTypes>(args)...) {}
423
424 inline void generate(CodeGenerator* codegen) const {
425 generate(codegen, std::index_sequence_for<ArgTypes...>{});
426 }
427
428#ifdef DEBUG1
429 static constexpr size_t numArgs = sizeof...(ArgTypes);
430#endif
431};
432
433template <typename... ArgTypes>
434inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) {
435 return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...);
436}
437
438// Store wrappers, to generate the right move of data after the VM call.
439
440struct StoreNothing {
441 inline void generate(CodeGenerator* codegen) const {}
442 inline LiveRegisterSet clobbered() const {
443 return LiveRegisterSet(); // No register gets clobbered
444 }
445};
446
447class StoreRegisterTo {
448 private:
449 Register out_;
450
451 public:
452 explicit StoreRegisterTo(Register out) : out_(out) {}
453
454 inline void generate(CodeGenerator* codegen) const {
455 // It's okay to use storePointerResultTo here - the VMFunction wrapper
456 // ensures the upper bytes are zero for bool/int32 return values.
457 codegen->storePointerResultTo(out_);
458 }
459 inline LiveRegisterSet clobbered() const {
460 LiveRegisterSet set;
461 set.add(out_);
462 return set;
463 }
464};
465
466class StoreFloatRegisterTo {
467 private:
468 FloatRegister out_;
469
470 public:
471 explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {}
472
473 inline void generate(CodeGenerator* codegen) const {
474 codegen->storeFloatResultTo(out_);
475 }
476 inline LiveRegisterSet clobbered() const {
477 LiveRegisterSet set;
478 set.add(out_);
479 return set;
480 }
481};
482
483template <typename Output>
484class StoreValueTo_ {
485 private:
486 Output out_;
487
488 public:
489 explicit StoreValueTo_(const Output& out) : out_(out) {}
490
491 inline void generate(CodeGenerator* codegen) const {
492 codegen->storeResultValueTo(out_);
493 }
494 inline LiveRegisterSet clobbered() const {
495 LiveRegisterSet set;
496 set.add(out_);
497 return set;
498 }
499};
500
501template <typename Output>
502StoreValueTo_<Output> StoreValueTo(const Output& out) {
503 return StoreValueTo_<Output>(out);
504}
505
506template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
507class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> {
508 private:
509 LInstruction* lir_;
510 ArgSeq args_;
511 StoreOutputTo out_;
512
513 public:
514 OutOfLineCallVM(LInstruction* lir, const ArgSeq& args,
515 const StoreOutputTo& out)
516 : lir_(lir), args_(args), out_(out) {}
517
518 void accept(CodeGenerator* codegen) override {
519 codegen->visitOutOfLineCallVM(this);
520 }
521
522 LInstruction* lir() const { return lir_; }
523 const ArgSeq& args() const { return args_; }
524 const StoreOutputTo& out() const { return out_; }
525};
526
527template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
528OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args,
529 const StoreOutputTo& out) {
530 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", 530); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mirRaw()" ")"); do { MOZ_CrashSequence
(__null, 530); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
531 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"
, 531); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()"
")"); do { MOZ_CrashSequence(__null, 531); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
532
533#ifdef DEBUG1
534 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
535 const VMFunctionData& fun = GetVMFunction(id);
536 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"
, 536); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs"
")"); do { MOZ_CrashSequence(__null, 536); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
537 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", 538); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { MOZ_CrashSequence(__null, 538); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
538 (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", 538); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { MOZ_CrashSequence(__null, 538); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
539#endif
540
541 OutOfLineCode* ool = new (alloc())
542 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out);
543 addOutOfLineCode(ool, lir->mirRaw()->toInstruction());
544 return ool;
545}
546
547template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
548void CodeGenerator::visitOutOfLineCallVM(
549 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) {
550 LInstruction* lir = ool->lir();
551
552#ifdef JS_JITSPEW1
553 {
554 AutoJitSpewMessage msg(JitSpew_Codegen,
555 " # LIR=%s",
556 lir->opName());
557 if (const char* extra = lir->getExtraName()) {
558 msg.append(":%s", extra);
559 }
560 }
561#endif
562 perfSpewer().recordInstruction(masm, lir);
563 if (!lir->isCall()) {
564 saveLive(lir);
565 }
566 ool->args().generate(this);
567 callVM<Fn, fn>(lir);
568 ool->out().generate(this);
569 if (!lir->isCall()) {
570 restoreLiveIgnore(lir, ool->out().clobbered());
571 }
572 masm.jump(ool->rejoin());
573}
574
575class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> {
576 private:
577 LInstruction* lir_;
578 size_t cacheIndex_;
579 size_t cacheInfoIndex_;
580
581 public:
582 OutOfLineICFallback(LInstruction* lir, size_t cacheIndex,
583 size_t cacheInfoIndex)
584 : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {}
585
586 void bind(MacroAssembler* masm) override {
587 // The binding of the initial jump is done in
588 // CodeGenerator::visitOutOfLineICFallback.
589 }
590
591 size_t cacheIndex() const { return cacheIndex_; }
592 size_t cacheInfoIndex() const { return cacheInfoIndex_; }
593 LInstruction* lir() const { return lir_; }
594
595 void accept(CodeGenerator* codegen) override {
596 codegen->visitOutOfLineICFallback(this);
597 }
598};
599
600void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) {
601 if (cacheIndex == SIZE_MAX(18446744073709551615UL)) {
602 masm.setOOM();
603 return;
604 }
605
606 DataPtr<IonIC> cache(this, cacheIndex);
607 MInstruction* mir = lir->mirRaw()->toInstruction();
608 cache->setScriptedLocation(mir->block()->info().script(),
609 mir->resumePoint()->pc());
610
611 Register temp = cache->scratchRegisterForEntryJump();
612 icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp);
613 masm.jump(Address(temp, 0));
614
615 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", 615); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!icInfo_.empty()" ")"); do { MOZ_CrashSequence
(__null, 615); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
616
617 OutOfLineICFallback* ool =
618 new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1);
619 addOutOfLineCode(ool, mir);
620
621 masm.bind(ool->rejoin());
622 cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset()));
623}
624
625void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) {
626 LInstruction* lir = ool->lir();
627 size_t cacheIndex = ool->cacheIndex();
628 size_t cacheInfoIndex = ool->cacheInfoIndex();
629
630 DataPtr<IonIC> ic(this, cacheIndex);
631
632 // Register the location of the OOL path in the IC.
633 ic->setFallbackOffset(CodeOffset(masm.currentOffset()));
634
635 switch (ic->kind()) {
636 case CacheKind::GetProp:
637 case CacheKind::GetElem: {
638 IonGetPropertyIC* getPropIC = ic->asGetPropertyIC();
639
640 saveLive(lir);
641
642 pushArg(getPropIC->id());
643 pushArg(getPropIC->value());
644 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
645 pushArg(ImmGCPtr(gen->outerInfo().script()));
646
647 using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*,
648 HandleValue, HandleValue, MutableHandleValue);
649 callVM<Fn, IonGetPropertyIC::update>(lir);
650
651 StoreValueTo(getPropIC->output()).generate(this);
652 restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
653
654 masm.jump(ool->rejoin());
655 return;
656 }
657 case CacheKind::GetPropSuper:
658 case CacheKind::GetElemSuper: {
659 IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC();
660
661 saveLive(lir);
662
663 pushArg(getPropSuperIC->id());
664 pushArg(getPropSuperIC->receiver());
665 pushArg(getPropSuperIC->object());
666 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
667 pushArg(ImmGCPtr(gen->outerInfo().script()));
668
669 using Fn =
670 bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject,
671 HandleValue, HandleValue, MutableHandleValue);
672 callVM<Fn, IonGetPropSuperIC::update>(lir);
673
674 StoreValueTo(getPropSuperIC->output()).generate(this);
675 restoreLiveIgnore(lir,
676 StoreValueTo(getPropSuperIC->output()).clobbered());
677
678 masm.jump(ool->rejoin());
679 return;
680 }
681 case CacheKind::SetProp:
682 case CacheKind::SetElem: {
683 IonSetPropertyIC* setPropIC = ic->asSetPropertyIC();
684
685 saveLive(lir);
686
687 pushArg(setPropIC->rhs());
688 pushArg(setPropIC->id());
689 pushArg(setPropIC->object());
690 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
691 pushArg(ImmGCPtr(gen->outerInfo().script()));
692
693 using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*,
694 HandleObject, HandleValue, HandleValue);
695 callVM<Fn, IonSetPropertyIC::update>(lir);
696
697 restoreLive(lir);
698
699 masm.jump(ool->rejoin());
700 return;
701 }
702 case CacheKind::GetName: {
703 IonGetNameIC* getNameIC = ic->asGetNameIC();
704
705 saveLive(lir);
706
707 pushArg(getNameIC->environment());
708 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
709 pushArg(ImmGCPtr(gen->outerInfo().script()));
710
711 using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject,
712 MutableHandleValue);
713 callVM<Fn, IonGetNameIC::update>(lir);
714
715 StoreValueTo(getNameIC->output()).generate(this);
716 restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered());
717
718 masm.jump(ool->rejoin());
719 return;
720 }
721 case CacheKind::BindName: {
722 IonBindNameIC* bindNameIC = ic->asBindNameIC();
723
724 saveLive(lir);
725
726 pushArg(bindNameIC->environment());
727 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
728 pushArg(ImmGCPtr(gen->outerInfo().script()));
729
730 using Fn =
731 JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject);
732 callVM<Fn, IonBindNameIC::update>(lir);
733
734 StoreRegisterTo(bindNameIC->output()).generate(this);
735 restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered());
736
737 masm.jump(ool->rejoin());
738 return;
739 }
740 case CacheKind::GetIterator: {
741 IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC();
742
743 saveLive(lir);
744
745 pushArg(getIteratorIC->value());
746 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
747 pushArg(ImmGCPtr(gen->outerInfo().script()));
748
749 using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*,
750 HandleValue);
751 callVM<Fn, IonGetIteratorIC::update>(lir);
752
753 StoreRegisterTo(getIteratorIC->output()).generate(this);
754 restoreLiveIgnore(lir,
755 StoreRegisterTo(getIteratorIC->output()).clobbered());
756
757 masm.jump(ool->rejoin());
758 return;
759 }
760 case CacheKind::OptimizeSpreadCall: {
761 auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC();
762
763 saveLive(lir);
764
765 pushArg(optimizeSpreadCallIC->value());
766 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
767 pushArg(ImmGCPtr(gen->outerInfo().script()));
768
769 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*,
770 HandleValue, MutableHandleValue);
771 callVM<Fn, IonOptimizeSpreadCallIC::update>(lir);
772
773 StoreValueTo(optimizeSpreadCallIC->output()).generate(this);
774 restoreLiveIgnore(
775 lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered());
776
777 masm.jump(ool->rejoin());
778 return;
779 }
780 case CacheKind::In: {
781 IonInIC* inIC = ic->asInIC();
782
783 saveLive(lir);
784
785 pushArg(inIC->object());
786 pushArg(inIC->key());
787 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
788 pushArg(ImmGCPtr(gen->outerInfo().script()));
789
790 using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue,
791 HandleObject, bool*);
792 callVM<Fn, IonInIC::update>(lir);
793
794 StoreRegisterTo(inIC->output()).generate(this);
795 restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered());
796
797 masm.jump(ool->rejoin());
798 return;
799 }
800 case CacheKind::HasOwn: {
801 IonHasOwnIC* hasOwnIC = ic->asHasOwnIC();
802
803 saveLive(lir);
804
805 pushArg(hasOwnIC->id());
806 pushArg(hasOwnIC->value());
807 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
808 pushArg(ImmGCPtr(gen->outerInfo().script()));
809
810 using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue,
811 HandleValue, int32_t*);
812 callVM<Fn, IonHasOwnIC::update>(lir);
813
814 StoreRegisterTo(hasOwnIC->output()).generate(this);
815 restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered());
816
817 masm.jump(ool->rejoin());
818 return;
819 }
820 case CacheKind::CheckPrivateField: {
821 IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC();
822
823 saveLive(lir);
824
825 pushArg(checkPrivateFieldIC->id());
826 pushArg(checkPrivateFieldIC->value());
827
828 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
829 pushArg(ImmGCPtr(gen->outerInfo().script()));
830
831 using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*,
832 HandleValue, HandleValue, bool*);
833 callVM<Fn, IonCheckPrivateFieldIC::update>(lir);
834
835 StoreRegisterTo(checkPrivateFieldIC->output()).generate(this);
836 restoreLiveIgnore(
837 lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered());
838
839 masm.jump(ool->rejoin());
840 return;
841 }
842 case CacheKind::InstanceOf: {
843 IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC();
844
845 saveLive(lir);
846
847 pushArg(hasInstanceOfIC->rhs());
848 pushArg(hasInstanceOfIC->lhs());
849 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
850 pushArg(ImmGCPtr(gen->outerInfo().script()));
851
852 using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*,
853 HandleValue lhs, HandleObject rhs, bool* res);
854 callVM<Fn, IonInstanceOfIC::update>(lir);
855
856 StoreRegisterTo(hasInstanceOfIC->output()).generate(this);
857 restoreLiveIgnore(lir,
858 StoreRegisterTo(hasInstanceOfIC->output()).clobbered());
859
860 masm.jump(ool->rejoin());
861 return;
862 }
863 case CacheKind::UnaryArith: {
864 IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC();
865
866 saveLive(lir);
867
868 pushArg(unaryArithIC->input());
869 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
870 pushArg(ImmGCPtr(gen->outerInfo().script()));
871
872 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
873 IonUnaryArithIC* stub, HandleValue val,
874 MutableHandleValue res);
875 callVM<Fn, IonUnaryArithIC::update>(lir);
876
877 StoreValueTo(unaryArithIC->output()).generate(this);
878 restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered());
879
880 masm.jump(ool->rejoin());
881 return;
882 }
883 case CacheKind::ToPropertyKey: {
884 IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC();
885
886 saveLive(lir);
887
888 pushArg(toPropertyKeyIC->input());
889 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
890 pushArg(ImmGCPtr(gen->outerInfo().script()));
891
892 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
893 IonToPropertyKeyIC* ic, HandleValue val,
894 MutableHandleValue res);
895 callVM<Fn, IonToPropertyKeyIC::update>(lir);
896
897 StoreValueTo(toPropertyKeyIC->output()).generate(this);
898 restoreLiveIgnore(lir,
899 StoreValueTo(toPropertyKeyIC->output()).clobbered());
900
901 masm.jump(ool->rejoin());
902 return;
903 }
904 case CacheKind::BinaryArith: {
905 IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC();
906
907 saveLive(lir);
908
909 pushArg(binaryArithIC->rhs());
910 pushArg(binaryArithIC->lhs());
911 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
912 pushArg(ImmGCPtr(gen->outerInfo().script()));
913
914 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
915 IonBinaryArithIC* stub, HandleValue lhs,
916 HandleValue rhs, MutableHandleValue res);
917 callVM<Fn, IonBinaryArithIC::update>(lir);
918
919 StoreValueTo(binaryArithIC->output()).generate(this);
920 restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered());
921
922 masm.jump(ool->rejoin());
923 return;
924 }
925 case CacheKind::Compare: {
926 IonCompareIC* compareIC = ic->asCompareIC();
927
928 saveLive(lir);
929
930 pushArg(compareIC->rhs());
931 pushArg(compareIC->lhs());
932 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
933 pushArg(ImmGCPtr(gen->outerInfo().script()));
934
935 using Fn =
936 bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
937 HandleValue lhs, HandleValue rhs, bool* res);
938 callVM<Fn, IonCompareIC::update>(lir);
939
940 StoreRegisterTo(compareIC->output()).generate(this);
941 restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered());
942
943 masm.jump(ool->rejoin());
944 return;
945 }
946 case CacheKind::CloseIter: {
947 IonCloseIterIC* closeIterIC = ic->asCloseIterIC();
948
949 saveLive(lir);
950
951 pushArg(closeIterIC->iter());
952 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
953 pushArg(ImmGCPtr(gen->outerInfo().script()));
954
955 using Fn =
956 bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject);
957 callVM<Fn, IonCloseIterIC::update>(lir);
958
959 restoreLive(lir);
960
961 masm.jump(ool->rejoin());
962 return;
963 }
964 case CacheKind::OptimizeGetIterator: {
965 auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC();
966
967 saveLive(lir);
968
969 pushArg(optimizeGetIteratorIC->value());
970 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
971 pushArg(ImmGCPtr(gen->outerInfo().script()));
972
973 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*,
974 HandleValue, bool* res);
975 callVM<Fn, IonOptimizeGetIteratorIC::update>(lir);
976
977 StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this);
978 restoreLiveIgnore(
979 lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered());
980
981 masm.jump(ool->rejoin());
982 return;
983 }
984 case CacheKind::Call:
985 case CacheKind::TypeOf:
986 case CacheKind::TypeOfEq:
987 case CacheKind::ToBool:
988 case CacheKind::LazyConstant:
989 case CacheKind::NewArray:
990 case CacheKind::NewObject:
991 case CacheKind::Lambda:
992 case CacheKind::GetImport:
993 MOZ_CRASH("Unsupported IC")do { do { } while (false); MOZ_ReportCrash("" "Unsupported IC"
, "./../../../../js/src/jit/CodeGenerator.cpp", 993); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported IC" ")"); do { MOZ_CrashSequence(__null
, 993); __attribute__((nomerge)) ::abort(); } while (false); }
while (false)
;
994 }
995 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp"
, 995); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 995); __attribute__((nomerge)) ::abort(); } while (false
); } while (false)
;
996}
997
998StringObject* MNewStringObject::templateObj() const {
999 return &templateObj_->as<StringObject>();
1000}
1001
1002CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph,
1003 MacroAssembler* masm,
1004 const wasm::CodeMetadata* wasmCodeMeta)
1005 : CodeGeneratorSpecific(gen, graph, masm, wasmCodeMeta),
1006 ionScriptLabels_(gen->alloc()),
1007 nurseryObjectLabels_(gen->alloc()),
1008 nurseryValueLabels_(gen->alloc()),
1009 scriptCounts_(nullptr) {}
1010
1011CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); }
1012
1013void CodeGenerator::visitValueToNumberInt32(LValueToNumberInt32* lir) {
1014 ValueOperand operand = ToValue(lir->input());
1015 Register output = ToRegister(lir->output());
1016 FloatRegister temp = ToFloatRegister(lir->temp0());
1017
1018 Label fails;
1019 masm.convertValueToInt32(operand, temp, output, &fails,
1020 lir->mir()->needsNegativeZeroCheck(),
1021 lir->mir()->conversion());
1022
1023 bailoutFrom(&fails, lir->snapshot());
1024}
1025
1026void CodeGenerator::visitValueTruncateToInt32(LValueTruncateToInt32* lir) {
1027 ValueOperand operand = ToValue(lir->input());
1028 Register output = ToRegister(lir->output());
1029 FloatRegister temp = ToFloatRegister(lir->temp0());
1030 Register stringReg = ToRegister(lir->temp1());
1031
1032 auto* oolDouble = oolTruncateDouble(temp, output, lir->mir());
1033
1034 using Fn = bool (*)(JSContext*, JSString*, double*);
1035 auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg),
1036 StoreFloatRegisterTo(temp));
1037 Label* stringEntry = oolString->entry();
1038 Label* stringRejoin = oolString->rejoin();
1039
1040 Label fails;
1041 masm.truncateValueToInt32(operand, stringEntry, stringRejoin,
1042 oolDouble->entry(), stringReg, temp, output,
1043 &fails);
1044 masm.bind(oolDouble->rejoin());
1045
1046 bailoutFrom(&fails, lir->snapshot());
1047}
1048
1049void CodeGenerator::visitValueToDouble(LValueToDouble* lir) {
1050 ValueOperand operand = ToValue(lir->input());
1051 FloatRegister output = ToFloatRegister(lir->output());
1052
1053 Label fail;
1054 masm.convertValueToDouble(operand, output, &fail);
1055 bailoutFrom(&fail, lir->snapshot());
1056}
1057
1058void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) {
1059 ValueOperand operand = ToValue(lir->input());
1060 FloatRegister output = ToFloatRegister(lir->output());
1061
1062 Label fail;
1063 masm.convertValueToFloat32(operand, output, &fail);
1064 bailoutFrom(&fail, lir->snapshot());
1065}
1066
1067void CodeGenerator::visitValueToFloat16(LValueToFloat16* lir) {
1068 ValueOperand operand = ToValue(lir->input());
1069 Register temp = ToTempRegisterOrInvalid(lir->temp0());
1070 FloatRegister output = ToFloatRegister(lir->output());
1071
1072 LiveRegisterSet volatileRegs;
1073 if (!MacroAssembler::SupportsFloat64To16()) {
1074 volatileRegs = liveVolatileRegs(lir);
1075 }
1076
1077 Label fail;
1078 masm.convertValueToFloat16(operand, output, temp, volatileRegs, &fail);
1079 bailoutFrom(&fail, lir->snapshot());
1080}
1081
1082void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) {
1083 ValueOperand operand = ToValue(lir->input());
1084 Register output = ToRegister(lir->output());
1085
1086 using Fn = BigInt* (*)(JSContext*, HandleValue);
1087 auto* ool =
1088 oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output));
1089
1090 Register tag = masm.extractTag(operand, output);
1091
1092 Label notBigInt, done;
1093 masm.branchTestBigInt(Assembler::NotEqual, tag, &notBigInt);
1094 masm.unboxBigInt(operand, output);
1095 masm.jump(&done);
1096 masm.bind(&notBigInt);
1097
1098 masm.branchTestBoolean(Assembler::Equal, tag, ool->entry());
1099 masm.branchTestString(Assembler::Equal, tag, ool->entry());
1100
1101 // ToBigInt(object) can have side-effects; all other types throw a TypeError.
1102 bailout(lir->snapshot());
1103
1104 masm.bind(ool->rejoin());
1105 masm.bind(&done);
1106}
1107
1108void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) {
1109 masm.convertInt32ToDouble(ToRegister(lir->input()),
1110 ToFloatRegister(lir->output()));
1111}
1112
1113void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) {
1114 masm.convertFloat32ToDouble(ToFloatRegister(lir->input()),
1115 ToFloatRegister(lir->output()));
1116}
1117
1118void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) {
1119 masm.convertDoubleToFloat32(ToFloatRegister(lir->input()),
1120 ToFloatRegister(lir->output()));
1121}
1122
1123void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) {
1124 masm.convertInt32ToFloat32(ToRegister(lir->input()),
1125 ToFloatRegister(lir->output()));
1126}
1127
1128void CodeGenerator::visitDoubleToFloat16(LDoubleToFloat16* lir) {
1129 LiveRegisterSet volatileRegs;
1130 if (!MacroAssembler::SupportsFloat64To16()) {
1131 volatileRegs = liveVolatileRegs(lir);
1132 }
1133 masm.convertDoubleToFloat16(
1134 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1135 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1136}
1137
1138void CodeGenerator::visitDoubleToFloat32ToFloat16(
1139 LDoubleToFloat32ToFloat16* lir) {
1140 masm.convertDoubleToFloat16(
1141 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1142 ToRegister(lir->temp0()), ToRegister(lir->temp1()));
1143}
1144
1145void CodeGenerator::visitFloat32ToFloat16(LFloat32ToFloat16* lir) {
1146 LiveRegisterSet volatileRegs;
1147 if (!MacroAssembler::SupportsFloat32To16()) {
1148 volatileRegs = liveVolatileRegs(lir);
1149 }
1150 masm.convertFloat32ToFloat16(
1151 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1152 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1153}
1154
1155void CodeGenerator::visitInt32ToFloat16(LInt32ToFloat16* lir) {
1156 LiveRegisterSet volatileRegs;
1157 if (!MacroAssembler::SupportsFloat32To16()) {
1158 volatileRegs = liveVolatileRegs(lir);
1159 }
1160 masm.convertInt32ToFloat16(
1161 ToRegister(lir->input()), ToFloatRegister(lir->output()),
1162 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1163}
1164
1165void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) {
1166 Label fail;
1167 FloatRegister input = ToFloatRegister(lir->input());
1168 Register output = ToRegister(lir->output());
1169 masm.convertDoubleToInt32(input, output, &fail,
1170 lir->mir()->needsNegativeZeroCheck());
1171 bailoutFrom(&fail, lir->snapshot());
1172}
1173
1174void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) {
1175 Label fail;
1176 FloatRegister input = ToFloatRegister(lir->input());
1177 Register output = ToRegister(lir->output());
1178 masm.convertFloat32ToInt32(input, output, &fail,
1179 lir->mir()->needsNegativeZeroCheck());
1180 bailoutFrom(&fail, lir->snapshot());
1181}
1182
1183void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) {
1184#ifdef JS_64BIT1
1185 Register output = ToRegister(lir->output());
1186 const LAllocation* input = lir->input();
1187 if (input->isGeneralReg()) {
1188 masm.move32SignExtendToPtr(ToRegister(input), output);
1189 } else {
1190 masm.load32SignExtendToPtr(ToAddress(input), output);
1191 }
1192#else
1193 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", 1193); AnnotateMozCrashReason
("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { MOZ_CrashSequence
(__null, 1193); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
1194#endif
1195}
1196
1197void CodeGenerator::visitNonNegativeIntPtrToInt32(
1198 LNonNegativeIntPtrToInt32* lir) {
1199#ifdef JS_64BIT1
1200 Register output = ToRegister(lir->output());
1201 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", 1201); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"
); do { MOZ_CrashSequence(__null, 1201); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1202
1203 Label bail;
1204 masm.guardNonNegativeIntPtrToInt32(output, &bail);
1205 bailoutFrom(&bail, lir->snapshot());
1206#else
1207 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", 1207); AnnotateMozCrashReason
("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { MOZ_CrashSequence
(__null, 1207); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
1208#endif
1209}
1210
1211void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) {
1212 Register input = ToRegister(lir->input());
1213 FloatRegister output = ToFloatRegister(lir->output());
1214 masm.convertIntPtrToDouble(input, output);
1215}
1216
1217void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) {
1218 Register output = ToRegister(lir->output());
1219 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", 1219); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"
); do { MOZ_CrashSequence(__null, 1219); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1220
1221 uint32_t byteSize = lir->mir()->byteSize();
1222
1223#ifdef DEBUG1
1224 Label ok;
1225 masm.branchTestPtr(Assembler::NotSigned, output, output, &ok);
1226 masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength");
1227 masm.bind(&ok);
1228#endif
1229
1230 Label bail;
1231 masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail);
1232 bailoutFrom(&bail, lir->snapshot());
1233}
1234
1235void CodeGenerator::emitOOLTestObject(Register objreg,
1236 Label* ifEmulatesUndefined,
1237 Label* ifDoesntEmulateUndefined,
1238 Register scratch) {
1239 saveVolatile(scratch);
1240#if defined(DEBUG1) || defined(FUZZING)
1241 masm.loadRuntimeFuse(
1242 RuntimeFuses::FuseIndex::HasSeenObjectEmulateUndefinedFuse, scratch);
1243 using Fn = bool (*)(JSObject* obj, size_t fuseValue);
1244 masm.setupAlignedABICall();
1245 masm.passABIArg(objreg);
1246 masm.passABIArg(scratch);
1247 masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>();
1248#else
1249 using Fn = bool (*)(JSObject* obj);
1250 masm.setupAlignedABICall();
1251 masm.passABIArg(objreg);
1252 masm.callWithABI<Fn, js::EmulatesUndefined>();
1253#endif
1254 masm.storeCallPointerResult(scratch);
1255 restoreVolatile(scratch);
1256
1257 masm.branchIfTrueBool(scratch, ifEmulatesUndefined);
1258 masm.jump(ifDoesntEmulateUndefined);
1259}
1260
1261// Base out-of-line code generator for all tests of the truthiness of an
1262// object, where the object might not be truthy. (Recall that per spec all
1263// objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class
1264// flag to permit objects to look like |undefined| in certain contexts,
1265// including in object truthiness testing.) We check truthiness inline except
1266// when we're testing it on a proxy, in which case out-of-line code will call
1267// EmulatesUndefined for a conclusive answer.
1268class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> {
1269 Register objreg_;
1270 Register scratch_;
1271
1272 Label* ifEmulatesUndefined_;
1273 Label* ifDoesntEmulateUndefined_;
1274
1275#ifdef DEBUG1
1276 bool initialized() { return ifEmulatesUndefined_ != nullptr; }
1277#endif
1278
1279 public:
1280 OutOfLineTestObject()
1281 : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {}
1282
1283 void accept(CodeGenerator* codegen) final {
1284 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", 1284); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "initialized()" ")"); do { MOZ_CrashSequence
(__null, 1284); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1285 codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_,
1286 ifDoesntEmulateUndefined_, scratch_);
1287 }
1288
1289 // Specify the register where the object to be tested is found, labels to
1290 // jump to if the object is truthy or falsy, and a scratch register for
1291 // use in the out-of-line path.
1292 void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined,
1293 Label* ifDoesntEmulateUndefined, Register scratch) {
1294 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", 1294); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!initialized()" ")"); do { MOZ_CrashSequence
(__null, 1294); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1295 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", 1295); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ifEmulatesUndefined" ")"); do { MOZ_CrashSequence
(__null, 1295); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1296 objreg_ = objreg;
1297 scratch_ = scratch;
1298 ifEmulatesUndefined_ = ifEmulatesUndefined;
1299 ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined;
1300 }
1301};
1302
1303// A subclass of OutOfLineTestObject containing two extra labels, for use when
1304// the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line
1305// code. The user should bind these labels in inline code, and specify them as
1306// targets via setInputAndTargets, as appropriate.
1307class OutOfLineTestObjectWithLabels : public OutOfLineTestObject {
1308 Label label1_;
1309 Label label2_;
1310
1311 public:
1312 OutOfLineTestObjectWithLabels() = default;
1313
1314 Label* label1() { return &label1_; }
1315 Label* label2() { return &label2_; }
1316};
1317
1318void CodeGenerator::testObjectEmulatesUndefinedKernel(
1319 Register objreg, Label* ifEmulatesUndefined,
1320 Label* ifDoesntEmulateUndefined, Register scratch,
1321 OutOfLineTestObject* ool) {
1322 ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
1323 scratch);
1324
1325 // Perform a fast-path check of the object's class flags if the object's
1326 // not a proxy. Let out-of-line code handle the slow cases that require
1327 // saving registers, making a function call, and restoring registers.
1328 masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(),
1329 ifEmulatesUndefined);
1330}
1331
1332void CodeGenerator::branchTestObjectEmulatesUndefined(
1333 Register objreg, Label* ifEmulatesUndefined,
1334 Label* ifDoesntEmulateUndefined, Register scratch,
1335 OutOfLineTestObject* ool) {
1336 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", 1337); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") ("
"ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { MOZ_CrashSequence(__null, 1337); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1337 "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", 1337); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") ("
"ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { MOZ_CrashSequence(__null, 1337); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1338
1339 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1340 ifDoesntEmulateUndefined, scratch, ool);
1341 masm.bind(ifDoesntEmulateUndefined);
1342}
1343
1344void CodeGenerator::testObjectEmulatesUndefined(Register objreg,
1345 Label* ifEmulatesUndefined,
1346 Label* ifDoesntEmulateUndefined,
1347 Register scratch,
1348 OutOfLineTestObject* ool) {
1349 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1350 ifDoesntEmulateUndefined, scratch, ool);
1351 masm.jump(ifDoesntEmulateUndefined);
1352}
1353
1354void CodeGenerator::testValueTruthyForType(
1355 JSValueType type, ScratchTagScope& tag, const ValueOperand& value,
1356 Register tempToUnbox, Register temp, FloatRegister floatTemp,
1357 Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool,
1358 bool skipTypeTest) {
1359#ifdef DEBUG1
1360 if (skipTypeTest) {
1361 Label expected;
1362 masm.branchTestType(Assembler::Equal, tag, type, &expected);
1363 masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType");
1364 masm.bind(&expected);
1365 }
1366#endif
1367
1368 // Handle irregular types first.
1369 switch (type) {
1370 case JSVAL_TYPE_UNDEFINED:
1371 case JSVAL_TYPE_NULL:
1372 // Undefined and null are falsy.
1373 if (!skipTypeTest) {
1374 masm.branchTestType(Assembler::Equal, tag, type, ifFalsy);
1375 } else {
1376 masm.jump(ifFalsy);
1377 }
1378 return;
1379 case JSVAL_TYPE_SYMBOL:
1380 // Symbols are truthy.
1381 if (!skipTypeTest) {
1382 masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy);
1383 } else {
1384 masm.jump(ifTruthy);
1385 }
1386 return;
1387 case JSVAL_TYPE_OBJECT: {
1388 if (!ool) {
1389 // If we have no ool path, then the hasSeenObjectEmulateUndefined fuse
1390 // is intact, and all objects are truthy.
1391 if (!skipTypeTest) {
1392 masm.branchTestObject(Assembler::Equal, tag, ifTruthy);
1393 } else {
1394 masm.jump(ifTruthy);
1395 }
1396 return;
1397 }
1398 Label notObject;
1399 if (!skipTypeTest) {
1400 masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
1401 }
1402 ScratchTagScopeRelease _(&tag);
1403 Register objreg = masm.extractObject(value, tempToUnbox);
1404 testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool);
1405 masm.bind(&notObject);
1406 return;
1407 }
1408 default:
1409 break;
1410 }
1411
1412 // Check the type of the value (unless this is the last possible type).
1413 Label differentType;
1414 if (!skipTypeTest) {
1415 masm.branchTestType(Assembler::NotEqual, tag, type, &differentType);
1416 }
1417
1418 // Branch if the value is falsy.
1419 ScratchTagScopeRelease _(&tag);
1420 switch (type) {
1421 case JSVAL_TYPE_BOOLEAN: {
1422 masm.branchTestBooleanTruthy(false, value, ifFalsy);
1423 break;
1424 }
1425 case JSVAL_TYPE_INT32: {
1426 masm.branchTestInt32Truthy(false, value, ifFalsy);
1427 break;
1428 }
1429 case JSVAL_TYPE_STRING: {
1430 masm.branchTestStringTruthy(false, value, ifFalsy);
1431 break;
1432 }
1433 case JSVAL_TYPE_BIGINT: {
1434 masm.branchTestBigIntTruthy(false, value, ifFalsy);
1435 break;
1436 }
1437 case JSVAL_TYPE_DOUBLE: {
1438 masm.unboxDouble(value, floatTemp);
1439 masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy);
1440 break;
1441 }
1442 default:
1443 MOZ_CRASH("Unexpected value type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected value type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1443); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected value type" ")"); do { MOZ_CrashSequence
(__null, 1443); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
1444 }
1445
1446 // If we reach this point, the value is truthy. We fall through for
1447 // truthy on the last test; otherwise, branch.
1448 if (!skipTypeTest) {
1449 masm.jump(ifTruthy);
1450 }
1451
1452 masm.bind(&differentType);
1453}
1454
1455void CodeGenerator::testValueTruthy(const ValueOperand& value,
1456 Register tempToUnbox, Register temp,
1457 FloatRegister floatTemp,
1458 const TypeDataList& observedTypes,
1459 Label* ifTruthy, Label* ifFalsy,
1460 OutOfLineTestObject* ool) {
1461 ScratchTagScope tag(masm, value);
1462 masm.splitTagForTest(value, tag);
1463
1464 const std::initializer_list<JSValueType> defaultOrder = {
1465 JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN,
1466 JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING,
1467 JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
1468
1469 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
1470
1471 // Generate tests for previously observed types first.
1472 // The TypeDataList is sorted by descending frequency.
1473 for (auto& observed : observedTypes) {
1474 JSValueType type = observed.type();
1475 remaining -= type;
1476
1477 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1478 ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false);
1479 }
1480
1481 // Generate tests for remaining types.
1482 for (auto type : defaultOrder) {
1483 if (!remaining.contains(type)) {
1484 continue;
1485 }
1486 remaining -= type;
1487
1488 // We don't need a type test for the last possible type.
1489 bool skipTypeTest = remaining.isEmpty();
1490 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1491 ifTruthy, ifFalsy, ool, skipTypeTest);
1492 }
1493 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", 1493); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { MOZ_CrashSequence
(__null, 1493); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1494
1495 // We fall through if the final test is truthy.
1496}
1497
1498void CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test) {
1499 const LAllocation* input = test->input();
1500 MBasicBlock* ifTrue = test->ifTrue();
1501 MBasicBlock* ifFalse = test->ifFalse();
1502
1503 bool fallThroughFalse = isNextBlock(ifFalse->lir());
1504 Label* label = fallThroughFalse ? getJumpLabelForBranch(ifTrue)
1505 : getJumpLabelForBranch(ifFalse);
1506 if (input->isGeneralReg()) {
1507 Register reg = ToRegister(input);
1508 masm.branchTest32(fallThroughFalse ? Assembler::NonZero : Assembler::Zero,
1509 reg, reg, label);
1510 } else {
1511 masm.branch32(fallThroughFalse ? Assembler::NotEqual : Assembler::Equal,
1512 ToAddress(input), Imm32(0), label);
1513 }
1514 if (!fallThroughFalse) {
1515 jumpToBlock(ifTrue);
1516 }
1517}
1518
1519void CodeGenerator::visitTestIPtrAndBranch(LTestIPtrAndBranch* test) {
1520 Register input = ToRegister(test->input());
1521 MBasicBlock* ifTrue = test->ifTrue();
1522 MBasicBlock* ifFalse = test->ifFalse();
1523
1524 if (isNextBlock(ifFalse->lir())) {
1525 masm.branchTestPtr(Assembler::NonZero, input, input,
1526 getJumpLabelForBranch(ifTrue));
1527 } else {
1528 masm.branchTestPtr(Assembler::Zero, input, input,
1529 getJumpLabelForBranch(ifFalse));
1530 jumpToBlock(ifTrue);
1531 }
1532}
1533
1534void CodeGenerator::visitTestI64AndBranch(LTestI64AndBranch* test) {
1535 Register64 input = ToRegister64(test->input());
1536 MBasicBlock* ifTrue = test->ifTrue();
1537 MBasicBlock* ifFalse = test->ifFalse();
1538
1539 if (isNextBlock(ifFalse->lir())) {
1540 masm.branchTest64(Assembler::NonZero, input, input,
1541 getJumpLabelForBranch(ifTrue));
1542 } else if (isNextBlock(ifTrue->lir())) {
1543 masm.branchTest64(Assembler::Zero, input, input,
1544 getJumpLabelForBranch(ifFalse));
1545 } else {
1546 masm.branchTest64(Assembler::NonZero, input, input,
1547 getJumpLabelForBranch(ifTrue),
1548 getJumpLabelForBranch(ifFalse));
1549 }
1550}
1551
1552void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) {
1553 Register input = ToRegister(lir->input());
1554 MBasicBlock* ifTrue = lir->ifTrue();
1555 MBasicBlock* ifFalse = lir->ifFalse();
1556
1557 if (isNextBlock(ifFalse->lir())) {
1558 masm.branchIfBigIntIsNonZero(input, getJumpLabelForBranch(ifTrue));
1559 } else {
1560 masm.branchIfBigIntIsZero(input, getJumpLabelForBranch(ifFalse));
1561 jumpToBlock(ifTrue);
1562 }
1563}
1564
1565static Assembler::Condition ReverseCondition(Assembler::Condition condition) {
1566 switch (condition) {
1567 case Assembler::Equal:
1568 case Assembler::NotEqual:
1569 return condition;
1570 case Assembler::Above:
1571 return Assembler::Below;
1572 case Assembler::AboveOrEqual:
1573 return Assembler::BelowOrEqual;
1574 case Assembler::Below:
1575 return Assembler::Above;
1576 case Assembler::BelowOrEqual:
1577 return Assembler::AboveOrEqual;
1578 case Assembler::GreaterThan:
1579 return Assembler::LessThan;
1580 case Assembler::GreaterThanOrEqual:
1581 return Assembler::LessThanOrEqual;
1582 case Assembler::LessThan:
1583 return Assembler::GreaterThan;
1584 case Assembler::LessThanOrEqual:
1585 return Assembler::GreaterThanOrEqual;
1586 default:
1587 break;
1588 }
1589 MOZ_CRASH("unhandled condition")do { do { } while (false); MOZ_ReportCrash("" "unhandled condition"
, "./../../../../js/src/jit/CodeGenerator.cpp", 1589); AnnotateMozCrashReason
("MOZ_CRASH(" "unhandled condition" ")"); do { MOZ_CrashSequence
(__null, 1589); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
1590}
1591
1592void CodeGenerator::visitCompare(LCompare* comp) {
1593 MCompare::CompareType compareType = comp->mir()->compareType();
1594 Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop());
1595 const LAllocation* left = comp->left();
1596 const LAllocation* right = comp->right();
1597 Register output = ToRegister(comp->output());
1598
1599 if (compareType == MCompare::Compare_Object ||
1600 compareType == MCompare::Compare_Symbol ||
1601 compareType == MCompare::Compare_IntPtr ||
1602 compareType == MCompare::Compare_UIntPtr ||
1603 compareType == MCompare::Compare_WasmAnyRef) {
1604 if (right->isConstant()) {
1605 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", 1606); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { MOZ_CrashSequence(__null, 1606); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1606 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", 1606); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { MOZ_CrashSequence(__null, 1606); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1607 masm.cmpPtrSet(cond, ToRegister(left), ImmWord(ToInt32(right)), output);
1608 } else if (right->isGeneralReg()) {
1609 masm.cmpPtrSet(cond, ToRegister(left), ToRegister(right), output);
1610 } else {
1611 masm.cmpPtrSet(ReverseCondition(cond), ToAddress(right), ToRegister(left),
1612 output);
1613 }
1614 return;
1615 }
1616
1617 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", 1618); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { MOZ_CrashSequence(__null, 1618); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1618 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", 1618); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { MOZ_CrashSequence(__null, 1618); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1619
1620 if (right->isConstant()) {
1621 if (left->isGeneralReg()) {
1622 masm.cmp32Set(cond, ToRegister(left), Imm32(ToInt32(right)), output);
1623 } else {
1624 masm.cmp32Set(cond, ToAddress(left), Imm32(ToInt32(right)), output);
1625 }
1626 } else if (right->isGeneralReg()) {
1627 masm.cmp32Set(cond, ToRegister(left), ToRegister(right), output);
1628 } else {
1629 masm.cmp32Set(ReverseCondition(cond), ToAddress(right), ToRegister(left),
1630 output);
1631 }
1632}
1633
1634void CodeGenerator::visitStrictConstantCompareInt32(
1635 LStrictConstantCompareInt32* comp) {
1636 ValueOperand value = ToValue(comp->value());
1637 Register temp = ToRegister(comp->temp0());
1638 Register output = ToRegister(comp->output());
1639
1640 int32_t constantVal = comp->mir()->constant();
1641 JSOp op = comp->mir()->jsop();
1642 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", 1642); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 1642); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1643
1644 masm.testValueSet(JSOpToCondition(op, false), value, Int32Value(constantVal),
1645 output);
1646 masm.testValueSet(JSOpToCondition(op, false), value, DoubleValue(constantVal),
1647 temp);
1648
1649 if (op == JSOp::StrictEq) {
1650 masm.or32(temp, output);
1651 } else {
1652 masm.and32(temp, output);
1653 }
1654
1655 if (constantVal == 0) {
1656 masm.testValueSet(JSOpToCondition(op, false), value, DoubleValue(-0.0),
1657 temp);
1658
1659 if (op == JSOp::StrictEq) {
1660 masm.or32(temp, output);
1661 } else {
1662 masm.and32(temp, output);
1663 }
1664 }
1665}
1666
1667void CodeGenerator::visitStrictConstantCompareInt32AndBranch(
1668 LStrictConstantCompareInt32AndBranch* comp) {
1669 ValueOperand value = ToValue(comp->value());
1670
1671 int32_t constantVal = comp->cmpMir()->constant();
1672 JSOp op = comp->cmpMir()->jsop();
1673 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", 1673); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 1673); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1674 Assembler::Condition cond = JSOpToCondition(op, false);
1675
1676 MBasicBlock* ifTrue = comp->ifTrue();
1677 MBasicBlock* ifFalse = comp->ifFalse();
1678
1679 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1680 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1681
1682 Label* onEqual = op == JSOp::StrictEq ? trueLabel : falseLabel;
1683
1684 // If the next block is the true case, invert the condition to fall through.
1685 if (isNextBlock(ifTrue->lir())) {
1686 cond = Assembler::InvertCondition(cond);
1687 trueLabel = falseLabel;
1688 falseLabel = nullptr;
1689 } else if (isNextBlock(ifFalse->lir())) {
1690 falseLabel = nullptr;
1691 }
1692
1693 masm.branch64(Assembler::Equal, value.toRegister64(),
1694 Imm64(Int32Value(constantVal).asRawBits()), onEqual);
1695 if (constantVal == 0) {
1696 // +0.0 and -0.0 are the only Values whose bits are zero outside the sign
1697 // bit.
1698 masm.branchTest64(
1699 cond == Assembler::Equal ? Assembler::Zero : Assembler::NonZero,
1700 value.toRegister64(),
1701 Imm64(~mozilla::SpecificFloatingPointBits<double, 1, 0, 0>::value),
1702 trueLabel, falseLabel);
1703 } else {
1704 masm.branch64(cond, value.toRegister64(),
1705 Imm64(DoubleValue(constantVal).asRawBits()), trueLabel,
1706 falseLabel);
1707 }
1708}
1709
1710void CodeGenerator::visitStrictConstantCompareBoolean(
1711 LStrictConstantCompareBoolean* comp) {
1712 ValueOperand value = ToValue(comp->value());
1713 Register output = ToRegister(comp->output());
1714
1715 bool constantVal = comp->mir()->constant();
1716 JSOp op = comp->mir()->jsop();
1717 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", 1717); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 1717); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1718
1719 masm.testValueSet(JSOpToCondition(op, false), value,
1720 BooleanValue(constantVal), output);
1721}
1722
1723void CodeGenerator::visitStrictConstantCompareBooleanAndBranch(
1724 LStrictConstantCompareBooleanAndBranch* comp) {
1725 ValueOperand value = ToValue(comp->value());
1726
1727 bool constantVal = comp->cmpMir()->constant();
1728 JSOp op = comp->cmpMir()->jsop();
1729 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", 1729); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 1729); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1730 Assembler::Condition cond = JSOpToCondition(op, false);
1731
1732 MBasicBlock* ifTrue = comp->ifTrue();
1733 MBasicBlock* ifFalse = comp->ifFalse();
1734
1735 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1736 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1737
1738 // If the next block is the true case, invert the condition to fall through.
1739 if (isNextBlock(ifTrue->lir())) {
1740 cond = Assembler::InvertCondition(cond);
1741 trueLabel = falseLabel;
1742 falseLabel = nullptr;
1743 } else if (isNextBlock(ifFalse->lir())) {
1744 falseLabel = nullptr;
1745 }
1746
1747 masm.branch64(cond, value.toRegister64(),
1748 Imm64(BooleanValue(constantVal).asRawBits()), trueLabel,
1749 falseLabel);
1750}
1751
1752void CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp) {
1753 MCompare::CompareType compareType = comp->cmpMir()->compareType();
1754 Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop());
1755 const LAllocation* left = comp->left();
1756 const LAllocation* right = comp->right();
1757
1758 MBasicBlock* ifTrue = comp->ifTrue();
1759 MBasicBlock* ifFalse = comp->ifFalse();
1760
1761 // If the next block is the true case, invert the condition to fall through.
1762 Label* label;
1763 if (isNextBlock(ifTrue->lir())) {
1764 cond = Assembler::InvertCondition(cond);
1765 label = getJumpLabelForBranch(ifFalse);
1766 } else {
1767 label = getJumpLabelForBranch(ifTrue);
1768 }
1769
1770 if (compareType == MCompare::Compare_Object ||
1771 compareType == MCompare::Compare_Symbol ||
1772 compareType == MCompare::Compare_IntPtr ||
1773 compareType == MCompare::Compare_UIntPtr ||
1774 compareType == MCompare::Compare_WasmAnyRef) {
1775 if (right->isConstant()) {
1776 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", 1777); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { MOZ_CrashSequence(__null, 1777); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1777 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", 1777); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { MOZ_CrashSequence(__null, 1777); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1778 masm.branchPtr(cond, ToRegister(left), ImmWord(ToInt32(right)), label);
1779 } else if (right->isGeneralReg()) {
1780 masm.branchPtr(cond, ToRegister(left), ToRegister(right), label);
1781 } else {
1782 masm.branchPtr(ReverseCondition(cond), ToAddress(right), ToRegister(left),
1783 label);
1784 }
1785 } else {
1786 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", 1787); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { MOZ_CrashSequence(__null, 1787); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1787 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", 1787); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { MOZ_CrashSequence(__null, 1787); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1788
1789 if (right->isConstant()) {
1790 if (left->isGeneralReg()) {
1791 masm.branch32(cond, ToRegister(left), Imm32(ToInt32(right)), label);
1792 } else {
1793 masm.branch32(cond, ToAddress(left), Imm32(ToInt32(right)), label);
1794 }
1795 } else if (right->isGeneralReg()) {
1796 masm.branch32(cond, ToRegister(left), ToRegister(right), label);
1797 } else {
1798 masm.branch32(ReverseCondition(cond), ToAddress(right), ToRegister(left),
1799 label);
1800 }
1801 }
1802
1803 if (!isNextBlock(ifTrue->lir())) {
1804 jumpToBlock(ifFalse);
1805 }
1806}
1807
1808void CodeGenerator::visitCompareI64(LCompareI64* lir) {
1809 MCompare::CompareType compareType = lir->mir()->compareType();
1810 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", 1811); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { MOZ_CrashSequence(__null, 1811); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1811 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", 1811); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { MOZ_CrashSequence(__null, 1811); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1812 bool isSigned = compareType == MCompare::Compare_Int64;
1813 Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
1814 Register64 left = ToRegister64(lir->left());
1815 LInt64Allocation right = lir->right();
1816 Register output = ToRegister(lir->output());
1817
1818 if (IsConstant(right)) {
1819 masm.cmp64Set(cond, left, Imm64(ToInt64(right)), output);
1820 } else if (IsRegister64(right)) {
1821 masm.cmp64Set(cond, left, ToRegister64(right), output);
1822 } else {
1823 masm.cmp64Set(ReverseCondition(cond), ToAddress(right), left, output);
1824 }
1825}
1826
1827void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) {
1828 MCompare::CompareType compareType = lir->cmpMir()->compareType();
1829 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", 1830); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { MOZ_CrashSequence(__null, 1830); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1830 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", 1830); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { MOZ_CrashSequence(__null, 1830); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1831 bool isSigned = compareType == MCompare::Compare_Int64;
1832 Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
1833 Register64 left = ToRegister64(lir->left());
1834 LInt64Allocation right = lir->right();
1835
1836 MBasicBlock* ifTrue = lir->ifTrue();
1837 MBasicBlock* ifFalse = lir->ifFalse();
1838
1839 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1840 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1841
1842 // If the next block is the true case, invert the condition to fall through.
1843 if (isNextBlock(ifTrue->lir())) {
1844 cond = Assembler::InvertCondition(cond);
1845 trueLabel = falseLabel;
1846 falseLabel = nullptr;
1847 } else if (isNextBlock(ifFalse->lir())) {
1848 falseLabel = nullptr;
1849 }
1850
1851 if (IsConstant(right)) {
1852 masm.branch64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel);
1853 } else if (IsRegister64(right)) {
1854 masm.branch64(cond, left, ToRegister64(right), trueLabel, falseLabel);
1855 } else {
1856 masm.branch64(ReverseCondition(cond), ToAddress(right), left, trueLabel,
1857 falseLabel);
1858 }
1859}
1860
1861void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) {
1862 Assembler::Condition cond = baab->cond();
1863 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"
, 1863); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cond == Assembler::Zero || cond == Assembler::NonZero"
")"); do { MOZ_CrashSequence(__null, 1863); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1864
1865 Register left = ToRegister(baab->left());
1866 const LAllocation* right = baab->right();
1867
1868 MBasicBlock* ifTrue = baab->ifTrue();
1869 MBasicBlock* ifFalse = baab->ifFalse();
1870
1871 // If the next block is the true case, invert the condition to fall through.
1872 Label* label;
1873 if (isNextBlock(ifTrue->lir())) {
1874 cond = Assembler::InvertCondition(cond);
1875 label = getJumpLabelForBranch(ifFalse);
1876 } else {
1877 label = getJumpLabelForBranch(ifTrue);
1878 }
1879
1880 if (right->isConstant()) {
1881 masm.branchTest32(cond, left, Imm32(ToInt32(right)), label);
1882 } else {
1883 masm.branchTest32(cond, left, ToRegister(right), label);
1884 }
1885
1886 if (!isNextBlock(ifTrue->lir())) {
1887 jumpToBlock(ifFalse);
1888 }
1889}
1890
1891void CodeGenerator::visitBitAnd64AndBranch(LBitAnd64AndBranch* baab) {
1892 Assembler::Condition cond = baab->cond();
1893 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"
, 1893); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cond == Assembler::Zero || cond == Assembler::NonZero"
")"); do { MOZ_CrashSequence(__null, 1893); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1894
1895 Register64 left = ToRegister64(baab->left());
1896 LInt64Allocation right = baab->right();
1897
1898 MBasicBlock* ifTrue = baab->ifTrue();
1899 MBasicBlock* ifFalse = baab->ifFalse();
1900
1901 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1902 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1903
1904 // If the next block is the true case, invert the condition to fall through.
1905 if (isNextBlock(ifTrue->lir())) {
1906 cond = Assembler::InvertCondition(cond);
1907 trueLabel = falseLabel;
1908 falseLabel = nullptr;
1909 } else if (isNextBlock(ifFalse->lir())) {
1910 falseLabel = nullptr;
1911 }
1912
1913 if (IsConstant(right)) {
1914 masm.branchTest64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel);
1915 } else {
1916 masm.branchTest64(cond, left, ToRegister64(right), trueLabel, falseLabel);
1917 }
1918}
1919
1920void CodeGenerator::assertObjectDoesNotEmulateUndefined(
1921 Register input, Register temp, const MInstruction* mir) {
1922#if defined(DEBUG1) || defined(FUZZING)
1923 // Validate that the object indeed doesn't have the emulates undefined flag.
1924 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
1925 addOutOfLineCode(ool, mir);
1926
1927 Label* doesNotEmulateUndefined = ool->label1();
1928 Label* emulatesUndefined = ool->label2();
1929
1930 testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined,
1931 temp, ool);
1932 masm.bind(emulatesUndefined);
1933 masm.assumeUnreachable(
1934 "Found an object emulating undefined while the fuse is intact");
1935 masm.bind(doesNotEmulateUndefined);
1936#endif
1937}
1938
1939void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) {
1940 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1941 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1942 Register input = ToRegister(lir->input());
1943 Register temp = ToRegister(lir->temp0());
1944
1945 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
1946 if (intact) {
1947 assertObjectDoesNotEmulateUndefined(input, temp, lir->mir());
1948 // Bug 1874905: It would be fantastic if this could be optimized out
1949 masm.jump(truthy);
1950 } else {
1951 auto* ool = new (alloc()) OutOfLineTestObject();
1952 addOutOfLineCode(ool, lir->mir());
1953
1954 testObjectEmulatesUndefined(input, falsy, truthy, temp, ool);
1955 }
1956}
1957
1958void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) {
1959 OutOfLineTestObject* ool = nullptr;
1960 if (!hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted()) {
1961 ool = new (alloc()) OutOfLineTestObject();
1962 addOutOfLineCode(ool, lir->mir());
1963 }
1964
1965 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1966 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1967
1968 ValueOperand input = ToValue(lir->input());
1969 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
1970 Register temp = ToRegister(lir->temp2());
1971 FloatRegister floatTemp = ToFloatRegister(lir->temp0());
1972 const TypeDataList& observedTypes = lir->mir()->observedTypes();
1973
1974 testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy,
1975 falsy, ool);
1976 masm.jump(truthy);
1977}
1978
1979void CodeGenerator::visitBooleanToString(LBooleanToString* lir) {
1980 Register input = ToRegister(lir->input());
1981 Register output = ToRegister(lir->output());
1982 const JSAtomState& names = gen->runtime->names();
1983 Label true_, done;
1984
1985 masm.branchTest32(Assembler::NonZero, input, input, &true_);
1986 masm.movePtr(ImmGCPtr(names.false_), output);
1987 masm.jump(&done);
1988
1989 masm.bind(&true_);
1990 masm.movePtr(ImmGCPtr(names.true_), output);
1991
1992 masm.bind(&done);
1993}
1994
1995void CodeGenerator::visitIntToString(LIntToString* lir) {
1996 Register input = ToRegister(lir->input());
1997 Register output = ToRegister(lir->output());
1998
1999 using Fn = JSLinearString* (*)(JSContext*, int);
2000 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
2001 lir, ArgList(input), StoreRegisterTo(output));
2002
2003 masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(),
2004 ool->entry());
2005
2006 masm.bind(ool->rejoin());
2007}
2008
2009void CodeGenerator::visitDoubleToString(LDoubleToString* lir) {
2010 FloatRegister input = ToFloatRegister(lir->input());
2011 Register temp = ToRegister(lir->temp0());
2012 Register output = ToRegister(lir->output());
2013
2014 using Fn = JSString* (*)(JSContext*, double);
2015 OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>(
2016 lir, ArgList(input), StoreRegisterTo(output));
2017
2018 // Try double to integer conversion and run integer to string code.
2019 masm.convertDoubleToInt32(input, temp, ool->entry(), false);
2020 masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(),
2021 ool->entry());
2022
2023 masm.bind(ool->rejoin());
2024}
2025
2026void CodeGenerator::visitValueToString(LValueToString* lir) {
2027 ValueOperand input = ToValue(lir->input());
2028 Register output = ToRegister(lir->output());
2029
2030 using Fn = JSString* (*)(JSContext*, HandleValue);
2031 OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>(
2032 lir, ArgList(input), StoreRegisterTo(output));
2033
2034 Label done;
2035 Register tag = masm.extractTag(input, output);
2036 const JSAtomState& names = gen->runtime->names();
2037
2038 // String
2039 {
2040 Label notString;
2041 masm.branchTestString(Assembler::NotEqual, tag, &notString);
2042 masm.unboxString(input, output);
2043 masm.jump(&done);
2044 masm.bind(&notString);
2045 }
2046
2047 // Integer
2048 {
2049 Label notInteger;
2050 masm.branchTestInt32(Assembler::NotEqual, tag, &notInteger);
2051 Register unboxed = ToTempUnboxRegister(lir->temp0());
2052 unboxed = masm.extractInt32(input, unboxed);
2053 masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(),
2054 ool->entry());
2055 masm.jump(&done);
2056 masm.bind(&notInteger);
2057 }
2058
2059 // Double
2060 {
2061 // Note: no fastpath. Need two extra registers and can only convert doubles
2062 // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT.
2063 masm.branchTestDouble(Assembler::Equal, tag, ool->entry());
2064 }
2065
2066 // Undefined
2067 {
2068 Label notUndefined;
2069 masm.branchTestUndefined(Assembler::NotEqual, tag, &notUndefined);
2070 masm.movePtr(ImmGCPtr(names.undefined), output);
2071 masm.jump(&done);
2072 masm.bind(&notUndefined);
2073 }
2074
2075 // Null
2076 {
2077 Label notNull;
2078 masm.branchTestNull(Assembler::NotEqual, tag, &notNull);
2079 masm.movePtr(ImmGCPtr(names.null), output);
2080 masm.jump(&done);
2081 masm.bind(&notNull);
2082 }
2083
2084 // Boolean
2085 {
2086 Label notBoolean, true_;
2087 masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
2088 masm.branchTestBooleanTruthy(true, input, &true_);
2089 masm.movePtr(ImmGCPtr(names.false_), output);
2090 masm.jump(&done);
2091 masm.bind(&true_);
2092 masm.movePtr(ImmGCPtr(names.true_), output);
2093 masm.jump(&done);
2094 masm.bind(&notBoolean);
2095 }
2096
2097 // Objects/symbols are only possible when |mir->mightHaveSideEffects()|.
2098 if (lir->mir()->mightHaveSideEffects()) {
2099 // Object
2100 if (lir->mir()->supportSideEffects()) {
2101 masm.branchTestObject(Assembler::Equal, tag, ool->entry());
2102 } else {
2103 // Bail.
2104 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"
, 2104); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { MOZ_CrashSequence(__null, 2104); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2105 Label bail;
2106 masm.branchTestObject(Assembler::Equal, tag, &bail);
2107 bailoutFrom(&bail, lir->snapshot());
2108 }
2109
2110 // Symbol
2111 if (lir->mir()->supportSideEffects()) {
2112 masm.branchTestSymbol(Assembler::Equal, tag, ool->entry());
2113 } else {
2114 // Bail.
2115 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"
, 2115); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { MOZ_CrashSequence(__null, 2115); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2116 Label bail;
2117 masm.branchTestSymbol(Assembler::Equal, tag, &bail);
2118 bailoutFrom(&bail, lir->snapshot());
2119 }
2120 }
2121
2122 // BigInt
2123 {
2124 // No fastpath currently implemented.
2125 masm.branchTestBigInt(Assembler::Equal, tag, ool->entry());
2126 }
2127
2128 masm.assumeUnreachable("Unexpected type for LValueToString.");
2129
2130 masm.bind(&done);
2131 masm.bind(ool->rejoin());
2132}
2133
2134using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**);
2135
2136static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder,
2137 size_t offset, Register buffer,
2138 LiveGeneralRegisterSet& liveVolatiles,
2139 StoreBufferMutationFn fun) {
2140 Label callVM;
2141 Label exit;
2142
2143 // Call into the VM to barrier the write. The only registers that need to
2144 // be preserved are those in liveVolatiles, so once they are saved on the
2145 // stack all volatile registers are available for use.
2146 masm.bind(&callVM);
2147 masm.PushRegsInMask(liveVolatiles);
2148
2149 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
2150 regs.takeUnchecked(buffer);
2151 regs.takeUnchecked(holder);
2152 Register addrReg = regs.takeAny();
2153
2154 masm.computeEffectiveAddress(Address(holder, offset), addrReg);
2155
2156 bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>();
2157 if (needExtraReg) {
2158 masm.push(holder);
2159 masm.setupUnalignedABICall(holder);
2160 } else {
2161 masm.setupUnalignedABICall(regs.takeAny());
2162 }
2163 masm.passABIArg(buffer);
2164 masm.passABIArg(addrReg);
2165 masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun),
2166 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
2167
2168 if (needExtraReg) {
2169 masm.pop(holder);
2170 }
2171 masm.PopRegsInMask(liveVolatiles);
2172 masm.bind(&exit);
2173}
2174
2175// Warning: this function modifies prev and next.
2176static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder,
2177 size_t offset, Register prev, Register next,
2178 LiveGeneralRegisterSet& liveVolatiles) {
2179 Label exit;
2180 Label checkRemove, putCell;
2181
2182 // if (next && (buffer = next->storeBuffer()))
2183 // but we never pass in nullptr for next.
2184 Register storebuffer = next;
2185 masm.loadStoreBuffer(next, storebuffer);
2186 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove);
2187
2188 // if (prev && prev->storeBuffer())
2189 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell);
2190 masm.loadStoreBuffer(prev, prev);
2191 masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit);
2192
2193 // buffer->putCell(cellp)
2194 masm.bind(&putCell);
2195 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
2196 JSString::addCellAddressToStoreBuffer);
2197 masm.jump(&exit);
2198
2199 // if (prev && (buffer = prev->storeBuffer()))
2200 masm.bind(&checkRemove);
2201 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit);
2202 masm.loadStoreBuffer(prev, storebuffer);
2203 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit);
2204 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
2205 JSString::removeCellAddressFromStoreBuffer);
2206
2207 masm.bind(&exit);
2208}
2209
2210void CodeGenerator::visitRegExp(LRegExp* lir) {
2211 Register output = ToRegister(lir->output());
2212 Register temp = ToRegister(lir->temp0());
2213 JSObject* source = lir->mir()->source();
2214
2215 using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>);
2216 OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>(
2217 lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output));
2218 if (lir->mir()->hasShared()) {
2219 TemplateObject templateObject(source);
2220 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
2221 ool->entry());
2222 } else {
2223 masm.jump(ool->entry());
2224 }
2225 masm.bind(ool->rejoin());
2226}
2227
2228/*
2229 * [SMDOC] RegExp stubs
2230 *
2231 * The RegExp stubs are a set of lazily generated per-zone stubs
2232 * providing fast paths for regexp execution in baseline and Ion.
2233 * In general, they are invoked from self-hosted code.
2234 *
2235 * There are four stubs:
2236 * - RegExpMatcher: Given a regular expression, an input string,
2237 * and the current lastIndex, return the match result object.
2238 * - RegExpExecMatch: The same as RegExpMatcher, but lastIndex is
2239 * not an argument. Instead, for sticky/global regexps, it is
2240 * loaded from the regexp, and the new value is stored back to
2241 * the regexp after execution. Otherwise, it is hardcoded to 0.
2242 * - RegExpSearcher: Given a regular expression, an input string,
2243 * and the current lastIndex, return the index of the next match.
2244 * - RegExpExecTest: Given a regular expression and an input string,
2245 * return a boolean indicating whether a match was found. This
2246 * stub has the same behaviour as RegExpExecMatch with respect to
2247 * lastIndex.
2248 */
2249
2250// Offset of the InputOutputData relative to the frame pointer in regexp stubs.
2251// The InputOutputData is allocated by the caller, so it is placed above the
2252// frame pointer and return address on the stack.
2253static constexpr size_t RegExpInputOutputDataOffset = 2 * sizeof(void*);
2254
2255static constexpr size_t RegExpPairsVectorStartOffset =
2256 RegExpInputOutputDataOffset + InputOutputDataSize + sizeof(MatchPairs);
2257
2258static Address RegExpPairCountAddress() {
2259 return Address(FramePointer, RegExpInputOutputDataOffset +
2260 int32_t(InputOutputDataSize) +
2261 MatchPairs::offsetOfPairCount());
2262}
2263
2264static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp,
2265 Register input, Register lastIndex,
2266 Register staticsReg, Register temp1,
2267 Register temp2, gc::Heap initialStringHeap,
2268 LiveGeneralRegisterSet& volatileRegs) {
2269 Address pendingInputAddress(staticsReg,
2270 RegExpStatics::offsetOfPendingInput());
2271 Address matchesInputAddress(staticsReg,
2272 RegExpStatics::offsetOfMatchesInput());
2273 Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource());
2274 Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex());
2275 Label legacyFeaturesEnabled, done;
2276 if (JS::Prefs::experimental_legacy_regexp()) {
2277 Address invalidatedAddress(staticsReg,
2278 RegExpStatics::offsetOfInvalidated());
2279
2280 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset(
2281 RegExpObject::flagsSlot())),
2282 temp1, JSVAL_TYPE_INT32);
2283 masm.branchTest32(Assembler::NonZero, temp1,
2284 Imm32(RegExpObject::LegacyFeaturesEnabledBit),
2285 &legacyFeaturesEnabled);
2286 masm.store8(Imm32(1), invalidatedAddress);
2287 masm.jump(&done);
2288 masm.bind(&legacyFeaturesEnabled);
2289 masm.store8(Imm32(0), invalidatedAddress);
2290 }
2291
2292 masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String);
2293 masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String);
2294 masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String);
2295
2296 if (initialStringHeap == gc::Heap::Default) {
2297 // Writing into RegExpStatics tenured memory; must post-barrier.
2298 if (staticsReg.volatile_()) {
2299 volatileRegs.add(staticsReg);
2300 }
2301
2302 masm.loadPtr(pendingInputAddress, temp1);
2303 masm.storePtr(input, pendingInputAddress);
2304 masm.movePtr(input, temp2);
2305 EmitPostWriteBarrierS(masm, staticsReg,
2306 RegExpStatics::offsetOfPendingInput(),
2307 temp1 /* prev */, temp2 /* next */, volatileRegs);
2308
2309 masm.loadPtr(matchesInputAddress, temp1);
2310 masm.storePtr(input, matchesInputAddress);
2311 masm.movePtr(input, temp2);
2312 EmitPostWriteBarrierS(masm, staticsReg,
2313 RegExpStatics::offsetOfMatchesInput(),
2314 temp1 /* prev */, temp2 /* next */, volatileRegs);
2315 } else {
2316 masm.debugAssertGCThingIsTenured(input, temp1);
2317 masm.storePtr(input, pendingInputAddress);
2318 masm.storePtr(input, matchesInputAddress);
2319 }
2320
2321 masm.storePtr(lastIndex,
2322 Address(staticsReg, RegExpStatics::offsetOfLazyIndex()));
2323 masm.store32(
2324 Imm32(1),
2325 Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation()));
2326
2327 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffsetTyped(
2328 RegExpObject::SHARED_SLOT)),
2329 temp1, JSVAL_TYPE_PRIVATE_GCTHING);
2330 masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2);
2331 masm.storePtr(temp2, lazySourceAddress);
2332 static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size");
2333 masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2);
2334 masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags()));
2335 masm.bind(&done);
2336}
2337
2338// Prepare an InputOutputData and optional MatchPairs which space has been
2339// allocated for on the stack, and try to execute a RegExp on a string input.
2340// If the RegExp was successfully executed and matched the input, fallthrough.
2341// Otherwise, jump to notFound or failure.
2342static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp,
2343 Register input, Register lastIndex,
2344 Register temp1, Register temp2,
2345 Register temp3, gc::Heap initialStringHeap,
2346 Label* notFound, Label* failure,
2347 JitZone::StubKind kind) {
2348 JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp");
2349
2350 using irregexp::InputOutputData;
2351
2352 /*
2353 * [SMDOC] Stack layout for PrepareAndExecuteRegExp
2354 *
2355 * Before this function is called, the caller is responsible for
2356 * allocating enough stack space for the result data. This code
2357 * will fill in that data. This means that the match pairs will
2358 * not be freed when we return from a match stub, which allows us
2359 * to reuse them if we have to call into the VM to allocate results,
2360 * instead of executing the regexp from scratch. For consistency,
2361 * we use the same approach for stubs that don't use match pairs.
2362 *
2363 * +---------------+
2364 * | Saved frameptr|
2365 * | Return address|
2366 * Current frame +---------------+
2367 *------------------------------------------------------------
2368 * Caller's frame +---------------+
2369 * |InputOutputData|
2370 * inputAddress +----------> input|
2371 * startIndexAddress +----------> startIndex|
2372 * matchesAddress +----------> matches|-----+
2373 * canResumeAddress +----------> false| |
2374 * +---------------+ |
2375 * matchPairs(Address|Offset) +-----> +---------------+ <--+
2376 * | MatchPairs |
2377 * pairCountAddress +----------> count |
2378 * pairsPointerAddress +----------> pairs |-----+
2379 * +---------------+ |
2380 * pairsArray(Address|Offset) +-----> +---------------+ <--+
2381 * | MatchPair |
2382 * firstMatchStartAddress +----------> start | <--+
2383 * | limit | |
2384 * +---------------+ |
2385 * . |
2386 * . Reserved space for
2387 * . RegExpObject::MaxPairCount
2388 * . MatchPair objects
2389 * . |
2390 * +---------------+ |
2391 * | MatchPair | |
2392 * | start | |
2393 * | limit | <--+
2394 * +---------------+
2395 */
2396
2397 int32_t ioOffset = RegExpInputOutputDataOffset;
2398 int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData));
2399 int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs));
2400
2401 Address inputAddress(FramePointer,
2402 ioOffset + InputOutputData::offsetOfInput());
2403 Address startIndexAddress(FramePointer,
2404 ioOffset + InputOutputData::offsetOfStartIndex());
2405 Address matchesAddress(FramePointer,
2406 ioOffset + InputOutputData::offsetOfMatches());
2407 Address canResumeAddress(FramePointer,
2408 ioOffset + InputOutputData::offsetOfCanResume());
2409
2410 Address matchPairsAddress(FramePointer, matchPairsOffset);
2411 Address pairCountAddress(FramePointer,
2412 matchPairsOffset + MatchPairs::offsetOfPairCount());
2413 Address pairsPointerAddress(FramePointer,
2414 matchPairsOffset + MatchPairs::offsetOfPairs());
2415
2416 Address pairsArrayAddress(FramePointer, pairsArrayOffset);
2417 Address firstMatchStartAddress(FramePointer,
2418 pairsArrayOffset + MatchPair::offsetOfStart());
2419
2420 // First, fill in a skeletal MatchPairs instance on the stack. This will be
2421 // passed to the OOL stub in the caller if we aren't able to execute the
2422 // RegExp inline, and that stub needs to be able to determine whether the
2423 // execution finished successfully.
2424
2425 // Initialize MatchPairs::pairCount to 1. The correct value can only
2426 // be determined after loading the RegExpShared. If the RegExpShared
2427 // has Kind::Atom, this is the correct pairCount.
2428 masm.store32(Imm32(1), pairCountAddress);
2429
2430 // Initialize MatchPairs::pairs pointer
2431 masm.computeEffectiveAddress(pairsArrayAddress, temp1);
2432 masm.storePtr(temp1, pairsPointerAddress);
2433
2434 // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch
2435 masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress);
2436
2437 // Determine the set of volatile inputs to save when calling into C++ or
2438 // regexp code.
2439 LiveGeneralRegisterSet volatileRegs;
2440 if (lastIndex.volatile_()) {
2441 volatileRegs.add(lastIndex);
2442 }
2443 if (input.volatile_()) {
2444 volatileRegs.add(input);
2445 }
2446 if (regexp.volatile_()) {
2447 volatileRegs.add(regexp);
2448 }
2449
2450 // Ensure the input string is not a rope.
2451 Label isLinear;
2452 masm.branchIfNotRope(input, &isLinear);
2453 {
2454 masm.PushRegsInMask(volatileRegs);
2455
2456 using Fn = JSLinearString* (*)(JSString*);
2457 masm.setupUnalignedABICall(temp1);
2458 masm.passABIArg(input);
2459 masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>();
2460
2461 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", 2461); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { MOZ_CrashSequence
(__null, 2461); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2462 masm.storeCallPointerResult(temp1);
2463 masm.PopRegsInMask(volatileRegs);
2464
2465 masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure);
2466 }
2467 masm.bind(&isLinear);
2468
2469 // Load the RegExpShared.
2470 Register regexpReg = temp1;
2471 Address sharedSlot = Address(
2472 regexp, NativeObject::getFixedSlotOffsetTyped(RegExpObject::SHARED_SLOT));
2473 masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure);
2474 masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING);
2475
2476 // Handle Atom matches
2477 Label notAtom, checkSuccess;
2478 masm.branchPtr(Assembler::Equal,
2479 Address(regexpReg, RegExpShared::offsetOfPatternAtom()),
2480 ImmWord(0), &notAtom);
2481 {
2482 masm.computeEffectiveAddress(matchPairsAddress, temp3);
2483
2484 masm.PushRegsInMask(volatileRegs);
2485 using Fn =
2486 RegExpRunStatus (*)(RegExpShared* re, const JSLinearString* input,
2487 size_t start, MatchPairs* matchPairs);
2488 masm.setupUnalignedABICall(temp2);
2489 masm.passABIArg(regexpReg);
2490 masm.passABIArg(input);
2491 masm.passABIArg(lastIndex);
2492 masm.passABIArg(temp3);
2493 masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>();
2494
2495 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", 2495); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { MOZ_CrashSequence
(__null, 2495); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2496 masm.storeCallInt32Result(temp1);
2497 masm.PopRegsInMask(volatileRegs);
2498
2499 masm.jump(&checkSuccess);
2500 }
2501 masm.bind(&notAtom);
2502
2503 // Try fast rejection using quickcheck data.
2504 // This should be kept in sync with RegExpShared::quickCheckRejects.
2505 Label doneQuickCheck;
2506 masm.load8ZeroExtend(
2507 Address(regexpReg, RegExpShared::offsetOfInternalFlags()), temp2);
2508 masm.branchTest32(Assembler::Zero, temp2,
2509 Imm32(uint32_t(RegExpShared::InternalFlag::HasQuickCheck)),
2510 &doneQuickCheck);
2511 masm.branchTwoByteString(input, &doneQuickCheck);
2512
2513 // if (index >= length) return false
2514 // Load string chars pointer into temp3.
2515 masm.loadStringLength(input, temp2);
2516 masm.branch32(Assembler::GreaterThanOrEqual, lastIndex, temp2,
2517 &doneQuickCheck);
2518 masm.loadStringChars(input, temp3, CharEncoding::Latin1);
2519
2520 // if (index + sizeof(uint32_t) <= length) {
2521 Label skipMask;
2522 masm.sub32(Imm32(4), temp2);
2523 masm.branch32(Assembler::GreaterThan, lastIndex, temp2, &skipMask);
2524
2525 // if ((word & quickCheckMask_) != quickCheckValue_) { return true; }
2526 masm.load32(BaseIndex(temp3, lastIndex, TimesOne), temp2);
2527 masm.and32(Address(regexpReg, RegExpShared::offsetOfQuickCheckMask()), temp2);
2528 masm.branch32(Assembler::NotEqual,
2529 Address(regexpReg, RegExpShared::offsetOfQuickCheckValue()),
2530 temp2, notFound);
2531 masm.bind(&skipMask);
2532
2533 // Check the first character against the reject bitset.
2534 // Load chars[index] into temp2.
2535 masm.load8ZeroExtend(BaseIndex(temp3, lastIndex, TimesOne), temp2);
2536
2537 // [word, bit] = quickCheckBitsetBit(chars[index])
2538 static_assert(RegExpShared::QuickCheckBitsetBitsPerWord == 32);
2539 masm.rshift32(Imm32(5), temp2, temp3); // word in temp3
2540 masm.and32(Imm32(0x1f), temp2); // bit in temp2
2541
2542 // if ((quickCheckRejectBitset_[word] & bit) != 0) return true;
2543 // (implemented as `(bitset[word] >> bit) & 1 == 1`)
2544 masm.load32(BaseIndex(regexpReg, temp3, TimesFour,
2545 RegExpShared::offsetOfQuickCheckRejectBitset()),
2546 temp3);
2547 masm.flexibleRshift32(temp2, temp3);
2548 masm.branchTest32(Assembler::NonZero, temp3, Imm32(1), notFound);
2549 masm.bind(&doneQuickCheck);
2550
2551 // If we don't need to look at the capture groups, we can leave pairCount at 1
2552 // (set above). The regexp code is special-cased to skip copying capture
2553 // groups if the pair count is 1, which also lets us avoid having to allocate
2554 // memory to store them.
2555 bool skipMatchPairs = kind == JitZone::StubKind::RegExpSearcher ||
2556 kind == JitZone::StubKind::RegExpExecTest;
2557 if (!skipMatchPairs) {
2558 // Don't handle regexps with too many capture pairs.
2559 masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2);
2560 masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount),
2561 failure);
2562
2563 // Fill in the pair count in the MatchPairs on the stack.
2564 masm.store32(temp2, pairCountAddress);
2565 }
2566
2567 // Load the code pointer for the input's encoding.
2568 Register codePointer = temp1; // Note: temp1 was previously regexpReg.
2569 {
2570 Label isLatin1, done;
2571 masm.branchLatin1String(input, &isLatin1);
2572
2573 // Two-byte input
2574 masm.loadPtr(
2575 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)),
2576 codePointer);
2577 masm.jump(&done);
2578
2579 // Latin1 input
2580 masm.bind(&isLatin1);
2581 masm.loadPtr(
2582 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)),
2583 codePointer);
2584
2585 masm.bind(&done);
2586 }
2587
2588 // Guard that the RegExpShared has been compiled for this type of input.
2589 // If it has not been compiled, we fall back to the OOL case, which will
2590 // do a VM call into the interpreter.
2591 // TODO: add an interpreter trampoline?
2592 masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure);
2593 masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
2594
2595 // Finish filling in the InputOutputData instance on the stack
2596 masm.store32(Imm32(0), canResumeAddress);
2597 masm.computeEffectiveAddress(matchPairsAddress, temp2);
2598 masm.storePtr(temp2, matchesAddress);
2599 masm.storePtr(lastIndex, startIndexAddress);
2600 masm.storePtr(input, inputAddress);
2601
2602 // Execute the RegExp.
2603 masm.computeEffectiveAddress(Address(FramePointer, ioOffset), temp2);
2604 masm.PushRegsInMask(volatileRegs);
2605 masm.setupUnalignedABICall(temp3);
2606 masm.passABIArg(temp2);
2607 masm.callWithABI(codePointer);
2608 masm.storeCallInt32Result(temp1);
2609 masm.PopRegsInMask(volatileRegs);
2610
2611 masm.bind(&checkSuccess);
2612 masm.branch32(Assembler::Equal, temp1,
2613 Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound);
2614 masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)),
2615 failure);
2616
2617 // Lazily update the RegExpStatics.
2618 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2619 RegExpRealm::offsetOfRegExpStatics();
2620 masm.loadGlobalObjectData(temp1);
2621 masm.loadPtr(Address(temp1, offset), temp1);
2622 UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3,
2623 initialStringHeap, volatileRegs);
2624
2625 return true;
2626}
2627
2628static void EmitInitDependentStringBase(MacroAssembler& masm,
2629 Register dependent, Register base,
2630 Register temp1, Register temp2,
2631 bool needsPostBarrier) {
2632 // Determine the base string to use and store it in temp2.
2633 Label notDependent, markedDependedOn;
2634 masm.load32(Address(base, JSString::offsetOfFlags()), temp1);
2635 masm.branchTest32(Assembler::Zero, temp1, Imm32(StringFlags::DEPENDENT_BIT),
2636 &notDependent);
2637 {
2638 // The base is also a dependent string. Load its base to prevent chains of
2639 // dependent strings in most cases. This must either be an atom or already
2640 // have the DEPENDED_ON_BIT set.
2641 masm.loadDependentStringBase(base, temp2);
2642 masm.jump(&markedDependedOn);
2643 }
2644 masm.bind(&notDependent);
2645 {
2646 // The base is not a dependent string. Set the DEPENDED_ON_BIT if it's not
2647 // an atom (ATOM_BIT is not set). Roughly:
2648 //
2649 // flags |= ((~flags) & ATOM_BIT) << (DEPENDED_ON_BIT - ATOM_BIT))
2650 //
2651 // but further modified to combine the initial move with an OR:
2652 //
2653 // flags |= ~(flags | ~ATOM_BIT) << (DEPENDED_ON_BIT - ATOM_BIT)
2654 //
2655 masm.nor32(Imm32(~StringFlags::ATOM_BIT), temp1, temp2);
2656 constexpr uint32_t AtomIndex = std::countr_zero(StringFlags::ATOM_BIT);
2657 constexpr uint32_t DependedOnIndex =
2658 std::countr_zero(StringFlags::DEPENDED_ON_BIT);
2659 static_assert(AtomIndex < DependedOnIndex);
2660 constexpr uint32_t ShiftAmount = DependedOnIndex - AtomIndex;
2661 masm.lshift32ThenOr(Imm32(ShiftAmount), temp1, temp2);
2662 masm.store32(temp2, Address(base, JSString::offsetOfFlags()));
2663 masm.movePtr(base, temp2);
2664 }
2665 masm.bind(&markedDependedOn);
2666
2667#ifdef DEBUG1
2668 // Assert the base has the DEPENDED_ON_BIT set or is an atom.
2669 Label isAppropriatelyMarked;
2670 masm.branchTest32(Assembler::NonZero,
2671 Address(temp2, JSString::offsetOfFlags()),
2672 Imm32(StringFlags::ATOM_BIT | StringFlags::DEPENDED_ON_BIT),
2673 &isAppropriatelyMarked);
2674 masm.assumeUnreachable("Base string is missing DEPENDED_ON_BIT");
2675 masm.bind(&isAppropriatelyMarked);
2676#endif
2677 masm.storeDependentStringBase(temp2, dependent);
2678
2679 // Post-barrier the base store. The base is still in temp2.
2680 if (needsPostBarrier) {
2681 Label done;
2682 masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done);
2683 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done);
2684
2685 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2686 regsToSave.takeUnchecked(temp1);
2687 regsToSave.takeUnchecked(temp2);
2688
2689 masm.PushRegsInMask(regsToSave);
2690
2691 masm.mov(ImmPtr(masm.runtime()), temp1);
2692
2693 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell);
2694 masm.setupUnalignedABICall(temp2);
2695 masm.passABIArg(temp1);
2696 masm.passABIArg(dependent);
2697 masm.callWithABI<Fn, PostWriteBarrier>();
2698
2699 masm.PopRegsInMask(regsToSave);
2700
2701 masm.bind(&done);
2702 } else {
2703#ifdef DEBUG1
2704 Label done;
2705 masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done);
2706 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done);
2707 masm.assumeUnreachable("Missing post barrier for dependent string base");
2708 masm.bind(&done);
2709#endif
2710 }
2711}
2712
2713static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
2714 Register len, Register byteOpScratch,
2715 CharEncoding encoding,
2716 size_t maximumLength = SIZE_MAX(18446744073709551615UL));
2717
2718class CreateDependentString {
2719 CharEncoding encoding_;
2720 Register string_;
2721 Register temp1_;
2722 Register temp2_;
2723 Label* failure_;
2724
2725 enum class FallbackKind : uint8_t {
2726 InlineString,
2727 FatInlineString,
2728 NotInlineString,
2729 Count
2730 };
2731 mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)>
2732 fallbacks_, joins_;
2733
2734 public:
2735 CreateDependentString(CharEncoding encoding, Register string, Register temp1,
2736 Register temp2, Label* failure)
2737 : encoding_(encoding),
2738 string_(string),
2739 temp1_(temp1),
2740 temp2_(temp2),
2741 failure_(failure) {}
2742
2743 Register string() const { return string_; }
2744 CharEncoding encoding() const { return encoding_; }
2745
2746 // Generate code that creates DependentString.
2747 // Caller should call generateFallback after masm.ret(), to generate
2748 // fallback path.
2749 void generate(MacroAssembler& masm, const JSAtomState& names,
2750 CompileRuntime* runtime, Register base,
2751 BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
2752 gc::Heap initialStringHeap);
2753
2754 // Generate fallback path for creating DependentString.
2755 void generateFallback(MacroAssembler& masm);
2756};
2757
2758void CreateDependentString::generate(MacroAssembler& masm,
2759 const JSAtomState& names,
2760 CompileRuntime* runtime, Register base,
2761 BaseIndex startIndexAddress,
2762 BaseIndex limitIndexAddress,
2763 gc::Heap initialStringHeap) {
2764 JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)",
2765 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2766
2767 auto newGCString = [&](FallbackKind kind) {
2768 uint32_t flags;
2769 switch (kind) {
2770 case FallbackKind::InlineString:
2771 flags = StringFlags::thinInlineStringFlags(encoding_);
2772 break;
2773 case FallbackKind::FatInlineString:
2774 flags = StringFlags::fatInlineStringFlags(encoding_);
2775 break;
2776 case FallbackKind::NotInlineString:
2777 flags = StringFlags::dependentStringFlags(encoding_);
2778 break;
2779 default:
2780 MOZ_CRASH("Unexpected FallbackKind")do { do { } while (false); MOZ_ReportCrash("" "Unexpected FallbackKind"
, "./../../../../js/src/jit/CodeGenerator.cpp", 2780); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected FallbackKind" ")"); do { MOZ_CrashSequence
(__null, 2780); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
2781 }
2782
2783 if (kind != FallbackKind::FatInlineString) {
2784 masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]);
2785 } else {
2786 masm.newGCFatInlineString(string_, temp2_, initialStringHeap,
2787 &fallbacks_[kind]);
2788 }
2789 masm.bind(&joins_[kind]);
2790 masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags()));
2791 };
2792
2793 // Compute the string length.
2794 masm.load32(startIndexAddress, temp2_);
2795 masm.load32(limitIndexAddress, temp1_);
2796 masm.sub32(temp2_, temp1_);
2797
2798 Label done, nonEmpty;
2799
2800 // Zero length matches use the empty string.
2801 masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty);
2802 masm.movePtr(ImmGCPtr(names.empty_), string_);
2803 masm.jump(&done);
2804
2805 masm.bind(&nonEmpty);
2806
2807 // Complete matches use the base string.
2808 Label nonBaseStringMatch;
2809 masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch);
2810 masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()),
2811 temp1_, &nonBaseStringMatch);
2812 masm.movePtr(base, string_);
2813 masm.jump(&done);
2814
2815 masm.bind(&nonBaseStringMatch);
2816
2817 Label notInline;
2818
2819 int32_t maxInlineLength = encoding_ == CharEncoding::Latin1
2820 ? JSFatInlineString::MAX_LENGTH_LATIN1
2821 : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
2822 masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), &notInline);
2823 {
2824 // Make a thin or fat inline string.
2825 Label stringAllocated, fatInline;
2826
2827 int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1
2828 ? JSThinInlineString::MAX_LENGTH_LATIN1
2829 : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
2830 masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength),
2831 &fatInline);
2832 if (encoding_ == CharEncoding::Latin1) {
2833 // One character Latin-1 strings can be loaded directly from the
2834 // static strings table.
2835 Label thinInline;
2836 masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline);
2837 {
2838 static_assert(
2839 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
2840 "Latin-1 strings can be loaded from static strings");
2841
2842 masm.loadStringChars(base, temp1_, encoding_);
2843 masm.loadChar(temp1_, temp2_, temp1_, encoding_);
2844
2845 masm.lookupStaticString(temp1_, string_, runtime->staticStrings());
2846
2847 masm.jump(&done);
2848 }
2849 masm.bind(&thinInline);
2850 }
2851 {
2852 newGCString(FallbackKind::InlineString);
2853 masm.jump(&stringAllocated);
2854 }
2855 masm.bind(&fatInline);
2856 {
2857 newGCString(FallbackKind::FatInlineString);
2858 }
2859 masm.bind(&stringAllocated);
2860
2861 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2862
2863 masm.pushRegs(string_, base);
2864
2865 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", 2866); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") ("
"startIndexAddress is still valid after stack pushes" ")"); do
{ MOZ_CrashSequence(__null, 2866); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
2866 "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", 2866); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") ("
"startIndexAddress is still valid after stack pushes" ")"); do
{ MOZ_CrashSequence(__null, 2866); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2867
2868 // Load chars pointer for the new string.
2869 masm.loadInlineStringCharsForStore(string_, string_);
2870
2871 // Load the source characters pointer.
2872 masm.loadStringChars(base, temp2_, encoding_);
2873 masm.load32(startIndexAddress, base);
2874 masm.addToCharPtr(temp2_, base, encoding_);
2875
2876 CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_);
2877
2878 masm.popRegs(base, string_);
2879
2880 masm.jump(&done);
2881 }
2882
2883 masm.bind(&notInline);
2884
2885 {
2886 // Make a dependent string.
2887 // Warning: string may be tenured (if the fallback case is hit), so
2888 // stores into it must be post barriered.
2889 newGCString(FallbackKind::NotInlineString);
2890
2891 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2892
2893 masm.loadNonInlineStringChars(base, temp1_, encoding_);
2894 masm.load32(startIndexAddress, temp2_);
2895 masm.addToCharPtr(temp1_, temp2_, encoding_);
2896 masm.storeNonInlineStringChars(temp1_, string_);
2897
2898 EmitInitDependentStringBase(masm, string_, base, temp1_, temp2_,
2899 /* needsPostBarrier = */ true);
2900 }
2901
2902 masm.bind(&done);
2903}
2904
2905void CreateDependentString::generateFallback(MacroAssembler& masm) {
2906 JitSpew(JitSpew_Codegen,
2907 "# Emitting CreateDependentString fallback (encoding=%s)",
2908 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2909
2910 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2911 regsToSave.takeUnchecked(string_);
2912 regsToSave.takeUnchecked(temp2_);
2913
2914 for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) {
2915 masm.bind(&fallbacks_[kind]);
2916
2917 masm.PushRegsInMask(regsToSave);
2918
2919 using Fn = void* (*)(JSContext * cx);
2920 masm.setupUnalignedABICall(string_);
2921 masm.loadJSContext(string_);
2922 masm.passABIArg(string_);
2923 if (kind == FallbackKind::FatInlineString) {
2924 masm.callWithABI<Fn, AllocateFatInlineString>();
2925 } else {
2926 masm.callWithABI<Fn, AllocateDependentString>();
2927 }
2928 masm.storeCallPointerResult(string_);
2929
2930 masm.PopRegsInMask(regsToSave);
2931
2932 masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_);
2933
2934 masm.jump(&joins_[kind]);
2935 }
2936}
2937
2938// Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar,
2939// but RegExpExecMatch also has to load and update .lastIndex for global/sticky
2940// regular expressions.
2941static JitCode* GenerateRegExpMatchStubShared(JSContext* cx,
2942 gc::Heap initialStringHeap,
2943 JitZone::StubKind kind) {
2944 bool isExecMatch = kind == JitZone::StubKind::RegExpExecMatch;
2945 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"
, 2945); AnnotateMozCrashReason("MOZ_ASSERT" "(" "kind == JitZone::StubKind::RegExpMatcher"
")"); do { MOZ_CrashSequence(__null, 2945); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2946
2947 if (isExecMatch) {
2948 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub");
2949 } else {
2950 JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub");
2951 }
2952
2953 // |initialStringHeap| could be stale after a GC.
2954 JS::AutoCheckCannotGC nogc(cx);
2955
2956 Register regexp = RegExpMatcherRegExpReg;
2957 Register input = RegExpMatcherStringReg;
2958 Register lastIndex = RegExpMatcherLastIndexReg;
2959 ValueOperand result = JSReturnOperand;
2960
2961 // We are free to clobber all registers, as LRegExpMatcher is a call
2962 // instruction.
2963 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2964 regs.take(input);
2965 regs.take(regexp);
2966 regs.take(lastIndex);
2967
2968 Register temp1 = regs.takeAny();
2969 Register temp2 = regs.takeAny();
2970 Register temp3 = regs.takeAny();
2971 Register maybeTemp4 = InvalidReg;
2972 if (!regs.empty()) {
2973 // There are not enough registers on x86.
2974 maybeTemp4 = regs.takeAny();
2975 }
2976 Register maybeTemp5 = InvalidReg;
2977 if (!regs.empty()) {
2978 // There are not enough registers on x86.
2979 maybeTemp5 = regs.takeAny();
2980 }
2981
2982 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
2983 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
2984
2985 TempAllocator temp(&cx->tempLifoAlloc());
2986 JitContext jcx(cx);
2987 StackMacroAssembler masm(cx, temp);
2988 AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared");
2989
2990#ifdef JS_USE_LINK_REGISTER
2991 masm.pushRegs(LinkRegister, FramePointer);
2992#else
2993 masm.push(FramePointer);
2994#endif
2995 masm.moveStackPtrTo(FramePointer);
2996
2997 Label notFoundZeroLastIndex;
2998 if (isExecMatch) {
2999 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
3000 }
3001
3002 Label notFound, oolEntry;
3003 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3004 temp3, initialStringHeap, &notFound, &oolEntry,
3005 kind)) {
3006 return nullptr;
3007 }
3008
3009 // If a regexp has named captures, fall back to the OOL stub, which
3010 // will end up calling CreateRegExpMatchResults.
3011 Register shared = temp2;
3012 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffsetTyped(
3013 RegExpObject::SHARED_SLOT)),
3014 shared, JSVAL_TYPE_PRIVATE_GCTHING);
3015 masm.branchPtr(Assembler::NotEqual,
3016 Address(shared, RegExpShared::offsetOfGroupsTemplate()),
3017 ImmWord(0), &oolEntry);
3018
3019 // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub.
3020 masm.branchTest32(Assembler::NonZero,
3021 Address(shared, RegExpShared::offsetOfFlags()),
3022 Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry);
3023
3024 Address pairCountAddress = RegExpPairCountAddress();
3025
3026 // Construct the result.
3027 Register object = temp1;
3028 {
3029 // In most cases, the array will have just 1-2 elements, so we optimize for
3030 // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots
3031 // because two slots are used for the elements header).
3032
3033 // Load the array length in temp2 and the shape in temp3.
3034 Label allocated;
3035 masm.load32(pairCountAddress, temp2);
3036 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
3037 RegExpRealm::offsetOfNormalMatchResultShape();
3038 masm.loadGlobalObjectData(temp3);
3039 masm.loadPtr(Address(temp3, offset), temp3);
3040
3041 auto emitAllocObject = [&](size_t elementCapacity) {
3042 gc::AllocKind kind = GuessArrayGCKind(elementCapacity);
3043 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", 3044); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 3044); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3044 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", 3044); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 3044); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3045 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", 3045); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFinalizedKind(kind)" ")"); do { MOZ_CrashSequence
(__null, 3045); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3046
3047#ifdef DEBUG1
3048 // Assert all of the available slots are used for |elementCapacity|
3049 // elements.
3050 size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity;
3051 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"
, 3051); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)"
")"); do { MOZ_CrashSequence(__null, 3051); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3052#endif
3053
3054 constexpr size_t numUsedDynamicSlots =
3055 RegExpRealm::MatchResultObjectSlotSpan;
3056 constexpr size_t numDynamicSlots =
3057 RegExpRealm::MatchResultObjectNumDynamicSlots;
3058 constexpr size_t arrayLength = 1;
3059 masm.createArrayWithFixedElements(object, temp3, temp2, temp3,
3060 arrayLength, elementCapacity,
3061 numUsedDynamicSlots, numDynamicSlots,
3062 kind, gc::Heap::Default, &oolEntry);
3063 };
3064
3065 Label moreThan2;
3066 masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2);
3067 emitAllocObject(2);
3068 masm.jump(&allocated);
3069
3070 Label moreThan6;
3071 masm.bind(&moreThan2);
3072 masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6);
3073 emitAllocObject(6);
3074 masm.jump(&allocated);
3075
3076 masm.bind(&moreThan6);
3077 static_assert(RegExpObject::MaxPairCount == 14);
3078 emitAllocObject(RegExpObject::MaxPairCount);
3079
3080 masm.bind(&allocated);
3081 }
3082
3083 static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t),
3084 "MatchPair consists of two int32 values representing the start"
3085 "and the end offset of the match");
3086
3087 int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset;
3088
3089 // Incremented by one below for each match pair.
3090 Register matchIndex = temp2;
3091 masm.move32(Imm32(0), matchIndex);
3092
3093 // The element in which to store the result of the current match.
3094 size_t elementsOffset = NativeObject::offsetOfFixedElements();
3095 BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset);
3096
3097 // The current match pair's "start" and "limit" member.
3098 BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight,
3099 pairsVectorStartOffset + MatchPair::offsetOfStart());
3100 BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight,
3101 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3102
3103 Label* depStrFailure = &oolEntry;
3104 Label restoreRegExpAndLastIndex;
3105
3106 Register temp4;
3107 if (maybeTemp4 == InvalidReg) {
3108 depStrFailure = &restoreRegExpAndLastIndex;
3109
3110 // We don't have enough registers for a fourth temporary. Reuse |regexp|
3111 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
3112 masm.push(regexp);
3113 temp4 = regexp;
3114 } else {
3115 temp4 = maybeTemp4;
3116 }
3117
3118 Register temp5;
3119 if (maybeTemp5 == InvalidReg) {
3120 depStrFailure = &restoreRegExpAndLastIndex;
3121
3122 // We don't have enough registers for a fifth temporary. Reuse |lastIndex|
3123 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
3124 masm.push(lastIndex);
3125 temp5 = lastIndex;
3126 } else {
3127 temp5 = maybeTemp5;
3128 }
3129
3130 auto maybeRestoreRegExpAndLastIndex = [&]() {
3131 if (maybeTemp5 == InvalidReg) {
3132 masm.pop(lastIndex);
3133 }
3134 if (maybeTemp4 == InvalidReg) {
3135 masm.pop(regexp);
3136 }
3137 };
3138
3139 // Loop to construct the match strings. There are two different loops,
3140 // depending on whether the input is a Two-Byte or a Latin-1 string.
3141 CreateDependentString depStrs[]{
3142 {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure},
3143 {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure},
3144 };
3145
3146 {
3147 Label isLatin1, done;
3148 masm.branchLatin1String(input, &isLatin1);
3149
3150 for (auto& depStr : depStrs) {
3151 if (depStr.encoding() == CharEncoding::Latin1) {
3152 masm.bind(&isLatin1);
3153 }
3154
3155 Label matchLoop;
3156 masm.bind(&matchLoop);
3157
3158 static_assert(MatchPair::NoMatch == -1,
3159 "MatchPair::start is negative if no match was found");
3160
3161 Label isUndefined, storeDone;
3162 masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0),
3163 &isUndefined);
3164 {
3165 depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()),
3166 input, matchPairStart, matchPairLimit,
3167 initialStringHeap);
3168
3169 // Storing into nursery-allocated results object's elements; no post
3170 // barrier.
3171 masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement);
3172 masm.jump(&storeDone);
3173 }
3174 masm.bind(&isUndefined);
3175 {
3176 masm.storeValue(UndefinedValue(), objectMatchElement);
3177 }
3178 masm.bind(&storeDone);
3179
3180 masm.add32(Imm32(1), matchIndex);
3181 masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex,
3182 &done);
3183 masm.jump(&matchLoop);
3184 }
3185
3186#ifdef DEBUG1
3187 masm.assumeUnreachable("The match string loop doesn't fall through.");
3188#endif
3189
3190 masm.bind(&done);
3191 }
3192
3193 maybeRestoreRegExpAndLastIndex();
3194
3195 // Fill in the rest of the output object.
3196 masm.store32(
3197 matchIndex,
3198 Address(object,
3199 elementsOffset + ObjectElements::offsetOfInitializedLength()));
3200 masm.store32(
3201 matchIndex,
3202 Address(object, elementsOffset + ObjectElements::offsetOfLength()));
3203
3204 Address firstMatchPairStartAddress(
3205 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart());
3206 Address firstMatchPairLimitAddress(
3207 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit());
3208
3209 static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0,
3210 "First slot holds the 'index' property");
3211 static_assert(RegExpRealm::MatchResultObjectInputSlot == 1,
3212 "Second slot holds the 'input' property");
3213
3214 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
3215
3216 masm.load32(firstMatchPairStartAddress, temp3);
3217 masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
3218
3219 // No post barrier needed (address is within nursery object.)
3220 masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
3221
3222 // For the ExecMatch stub, if the regular expression is global or sticky, we
3223 // have to update its .lastIndex slot.
3224 if (isExecMatch) {
3225 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", 3225); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "object != lastIndex" ")"); do { MOZ_CrashSequence
(__null, 3225); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3226 Label notGlobalOrSticky;
3227 masm.branchTest32(Assembler::Zero, flagsSlot,
3228 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3229 &notGlobalOrSticky);
3230 masm.load32(firstMatchPairLimitAddress, lastIndex);
3231 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
3232 masm.bind(&notGlobalOrSticky);
3233 }
3234
3235 // All done!
3236 masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
3237 masm.pop(FramePointer);
3238 masm.ret();
3239
3240 masm.bind(&notFound);
3241 if (isExecMatch) {
3242 Label notGlobalOrSticky;
3243 masm.branchTest32(Assembler::Zero, flagsSlot,
3244 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3245 &notGlobalOrSticky);
3246 masm.bind(&notFoundZeroLastIndex);
3247 masm.storeValue(Int32Value(0), lastIndexSlot);
3248 masm.bind(&notGlobalOrSticky);
3249 }
3250 masm.moveValue(NullValue(), result);
3251 masm.pop(FramePointer);
3252 masm.ret();
3253
3254 // Fallback paths for CreateDependentString.
3255 for (auto& depStr : depStrs) {
3256 depStr.generateFallback(masm);
3257 }
3258
3259 // Fall-through to the ool entry after restoring the registers.
3260 masm.bind(&restoreRegExpAndLastIndex);
3261 maybeRestoreRegExpAndLastIndex();
3262
3263 // Use an undefined value to signal to the caller that the OOL stub needs to
3264 // be called.
3265 masm.bind(&oolEntry);
3266 masm.moveValue(UndefinedValue(), result);
3267 masm.pop(FramePointer);
3268 masm.ret();
3269
3270 Linker linker(masm);
3271 JitCode* code = linker.newCode(cx, CodeKind::Other);
3272 if (!code) {
3273 return nullptr;
3274 }
3275
3276 const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub";
3277 CollectPerfSpewerJitCodeProfile(code, name);
3278#ifdef MOZ_VTUNE1
3279 vtune::MarkStub(code, name);
3280#endif
3281
3282 return code;
3283}
3284
3285JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) {
3286 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
3287 JitZone::StubKind::RegExpMatcher);
3288}
3289
3290JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) {
3291 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
3292 JitZone::StubKind::RegExpExecMatch);
3293}
3294
3295void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) {
3296 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"
, 3296); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { MOZ_CrashSequence(__null, 3296); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3297 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"
, 3297); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { MOZ_CrashSequence(__null, 3297); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3298 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", 3298); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg"
")"); do { MOZ_CrashSequence(__null, 3298); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3299 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"
, 3299); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { MOZ_CrashSequence(__null, 3299); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3300
3301#if defined(JS_NUNBOX32)
3302 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
3303 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
3304 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
3305 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
3306 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type);
3307 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data);
3308#elif defined(JS_PUNBOX641)
3309 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
3310 static_assert(RegExpMatcherStringReg != JSReturnReg);
3311 static_assert(RegExpMatcherLastIndexReg != JSReturnReg);
3312#endif
3313
3314 masm.reserveStack(RegExpReservedStack);
3315
3316 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
3317 Register lastIndex = ToRegister(lir->lastIndex());
3318 Register input = ToRegister(lir->string());
3319 Register regexp = ToRegister(lir->regexp());
3320
3321 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3322 regs.take(lastIndex);
3323 regs.take(input);
3324 regs.take(regexp);
3325 Register temp = regs.takeAny();
3326
3327 masm.computeEffectiveAddress(
3328 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3329
3330 pushArg(temp);
3331 pushArg(lastIndex);
3332 pushArg(input);
3333 pushArg(regexp);
3334
3335 // We are not using oolCallVM because we are in a Call, and that live
3336 // registers are already saved by the the register allocator.
3337 using Fn = bool (*)(JSContext*, HandleObject regexp, HandleString input,
3338 int32_t lastIndex, MatchPairs* pairs,
3339 MutableHandleValue output);
3340 callVM<Fn, RegExpMatcherRaw>(lir);
3341
3342 masm.jump(ool.rejoin());
3343 });
3344 addOutOfLineCode(ool, lir->mir());
3345
3346 JitCode* regExpMatcherStub =
3347 snapshot_->getZoneStub(JitZone::StubKind::RegExpMatcher);
3348 masm.call(regExpMatcherStub);
3349 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
3350 masm.bind(ool->rejoin());
3351
3352 masm.freeStack(RegExpReservedStack);
3353}
3354
3355void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) {
3356 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"
, 3356); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { MOZ_CrashSequence(__null, 3356); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3357 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"
, 3357); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { MOZ_CrashSequence(__null, 3357); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3358 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"
, 3358); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { MOZ_CrashSequence(__null, 3358); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3359
3360#if defined(JS_NUNBOX32)
3361 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
3362 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
3363 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
3364 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
3365#elif defined(JS_PUNBOX641)
3366 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
3367 static_assert(RegExpMatcherStringReg != JSReturnReg);
3368#endif
3369
3370 masm.reserveStack(RegExpReservedStack);
3371
3372 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
3373 Register input = ToRegister(lir->string());
3374 Register regexp = ToRegister(lir->regexp());
3375
3376 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3377 regs.take(input);
3378 regs.take(regexp);
3379 Register temp = regs.takeAny();
3380
3381 masm.computeEffectiveAddress(
3382 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3383
3384 pushArg(temp);
3385 pushArg(input);
3386 pushArg(regexp);
3387
3388 // We are not using oolCallVM because we are in a Call and live registers
3389 // have already been saved by the register allocator.
3390 using Fn =
3391 bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input,
3392 MatchPairs* pairs, MutableHandleValue output);
3393 callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir);
3394 masm.jump(ool.rejoin());
3395 });
3396 addOutOfLineCode(ool, lir->mir());
3397
3398 JitCode* regExpExecMatchStub =
3399 snapshot_->getZoneStub(JitZone::StubKind::RegExpExecMatch);
3400 masm.call(regExpExecMatchStub);
3401 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
3402
3403 masm.bind(ool->rejoin());
3404 masm.freeStack(RegExpReservedStack);
3405}
3406
3407JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) {
3408 JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub");
3409
3410 Register regexp = RegExpSearcherRegExpReg;
3411 Register input = RegExpSearcherStringReg;
3412 Register lastIndex = RegExpSearcherLastIndexReg;
3413 Register result = ReturnReg;
3414
3415 // We are free to clobber all registers, as LRegExpSearcher is a call
3416 // instruction.
3417 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3418 regs.take(input);
3419 regs.take(regexp);
3420 regs.take(lastIndex);
3421
3422 Register temp1 = regs.takeAny();
3423 Register temp2 = regs.takeAny();
3424 Register temp3 = regs.takeAny();
3425
3426 TempAllocator temp(&cx->tempLifoAlloc());
3427 JitContext jcx(cx);
3428 StackMacroAssembler masm(cx, temp);
3429 AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub");
3430
3431#ifdef JS_USE_LINK_REGISTER
3432 masm.pushRegs(LinkRegister, FramePointer);
3433#else
3434 masm.push(FramePointer);
3435#endif
3436 masm.moveStackPtrTo(FramePointer);
3437
3438#ifdef DEBUG1
3439 // Store sentinel value to cx->regExpSearcherLastLimit.
3440 // See comment in RegExpSearcherImpl.
3441 masm.loadJSContext(temp1);
3442 masm.store32(Imm32(RegExpSearcherLastLimitSentinel),
3443 Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit()));
3444#endif
3445
3446 Label notFound, oolEntry;
3447 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3448 temp3, initialStringHeap, &notFound, &oolEntry,
3449 JitZone::StubKind::RegExpSearcher)) {
3450 return nullptr;
3451 }
3452
3453 int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset;
3454 Address matchPairStart(FramePointer,
3455 pairsVectorStartOffset + MatchPair::offsetOfStart());
3456 Address matchPairLimit(FramePointer,
3457 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3458
3459 // Store match limit to cx->regExpSearcherLastLimit and return the index.
3460 masm.load32(matchPairLimit, result);
3461 masm.loadJSContext(input);
3462 masm.store32(result,
3463 Address(input, JSContext::offsetOfRegExpSearcherLastLimit()));
3464 masm.load32(matchPairStart, result);
3465 masm.pop(FramePointer);
3466 masm.ret();
3467
3468 masm.bind(&notFound);
3469 masm.move32(Imm32(RegExpSearcherResultNotFound), result);
3470 masm.pop(FramePointer);
3471 masm.ret();
3472
3473 masm.bind(&oolEntry);
3474 masm.move32(Imm32(RegExpSearcherResultFailed), result);
3475 masm.pop(FramePointer);
3476 masm.ret();
3477
3478 Linker linker(masm);
3479 JitCode* code = linker.newCode(cx, CodeKind::Other);
3480 if (!code) {
3481 return nullptr;
3482 }
3483
3484 CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub");
3485#ifdef MOZ_VTUNE1
3486 vtune::MarkStub(code, "RegExpSearcherStub");
3487#endif
3488
3489 return code;
3490}
3491
3492void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) {
3493 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"
, 3493); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg"
")"); do { MOZ_CrashSequence(__null, 3493); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3494 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"
, 3494); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg"
")"); do { MOZ_CrashSequence(__null, 3494); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3495 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", 3495); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg"
")"); do { MOZ_CrashSequence(__null, 3495); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3496 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", 3496); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { MOZ_CrashSequence(__null, 3496); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3497
3498 static_assert(RegExpSearcherRegExpReg != ReturnReg);
3499 static_assert(RegExpSearcherStringReg != ReturnReg);
3500 static_assert(RegExpSearcherLastIndexReg != ReturnReg);
3501
3502 masm.reserveStack(RegExpReservedStack);
3503
3504 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
3505 Register lastIndex = ToRegister(lir->lastIndex());
3506 Register input = ToRegister(lir->string());
3507 Register regexp = ToRegister(lir->regexp());
3508
3509 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3510 regs.take(lastIndex);
3511 regs.take(input);
3512 regs.take(regexp);
3513 Register temp = regs.takeAny();
3514
3515 masm.computeEffectiveAddress(
3516 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3517
3518 pushArg(temp);
3519 pushArg(lastIndex);
3520 pushArg(input);
3521 pushArg(regexp);
3522
3523 // We are not using oolCallVM because we are in a Call, and that live
3524 // registers are already saved by the the register allocator.
3525 using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input,
3526 int32_t lastIndex, MatchPairs* pairs, int32_t* result);
3527 callVM<Fn, RegExpSearcherRaw>(lir);
3528
3529 masm.jump(ool.rejoin());
3530 });
3531 addOutOfLineCode(ool, lir->mir());
3532
3533 JitCode* regExpSearcherStub =
3534 snapshot_->getZoneStub(JitZone::StubKind::RegExpSearcher);
3535 masm.call(regExpSearcherStub);
3536 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed),
3537 ool->entry());
3538 masm.bind(ool->rejoin());
3539
3540 masm.freeStack(RegExpReservedStack);
3541}
3542
3543void CodeGenerator::visitRegExpSearcherLastLimit(
3544 LRegExpSearcherLastLimit* lir) {
3545 Register result = ToRegister(lir->output());
3546 Register scratch = ToRegister(lir->temp0());
3547
3548 masm.loadAndClearRegExpSearcherLastLimit(result, scratch);
3549}
3550
3551JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) {
3552 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub");
3553
3554 Register regexp = RegExpExecTestRegExpReg;
3555 Register input = RegExpExecTestStringReg;
3556 Register result = ReturnReg;
3557
3558 TempAllocator temp(&cx->tempLifoAlloc());
3559 JitContext jcx(cx);
3560 StackMacroAssembler masm(cx, temp);
3561 AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub");
3562
3563#ifdef JS_USE_LINK_REGISTER
3564 masm.pushRegs(LinkRegister, FramePointer);
3565#else
3566 masm.push(FramePointer);
3567#endif
3568 masm.moveStackPtrTo(FramePointer);
3569
3570 // We are free to clobber all registers, as LRegExpExecTest is a call
3571 // instruction.
3572 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3573 regs.take(input);
3574 regs.take(regexp);
3575
3576 // Ensure lastIndex != result.
3577 regs.take(result);
3578 Register lastIndex = regs.takeAny();
3579 regs.add(result);
3580 Register temp1 = regs.takeAny();
3581 Register temp2 = regs.takeAny();
3582 Register temp3 = regs.takeAny();
3583
3584 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
3585 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
3586
3587 // Load lastIndex and skip RegExp execution if needed.
3588 Label notFoundZeroLastIndex;
3589 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
3590
3591 Label notFound, oolEntry;
3592 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3593 temp3, initialStringHeap, &notFound, &oolEntry,
3594 JitZone::StubKind::RegExpExecTest)) {
3595 return nullptr;
3596 }
3597
3598 // Set `result` to true/false to indicate found/not-found, or to
3599 // RegExpExecTestResultFailed if we have to retry in C++. If the regular
3600 // expression is global or sticky, we also have to update its .lastIndex slot.
3601
3602 Label done;
3603 int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset;
3604 Address matchPairLimit(FramePointer,
3605 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3606
3607 masm.move32(Imm32(1), result);
3608 masm.branchTest32(Assembler::Zero, flagsSlot,
3609 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3610 &done);
3611 masm.load32(matchPairLimit, lastIndex);
3612 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
3613 masm.jump(&done);
3614
3615 masm.bind(&notFound);
3616 masm.move32(Imm32(0), result);
3617 masm.branchTest32(Assembler::Zero, flagsSlot,
3618 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3619 &done);
3620 masm.storeValue(Int32Value(0), lastIndexSlot);
3621 masm.jump(&done);
3622
3623 masm.bind(&notFoundZeroLastIndex);
3624 masm.move32(Imm32(0), result);
3625 masm.storeValue(Int32Value(0), lastIndexSlot);
3626 masm.jump(&done);
3627
3628 masm.bind(&oolEntry);
3629 masm.move32(Imm32(RegExpExecTestResultFailed), result);
3630
3631 masm.bind(&done);
3632 masm.pop(FramePointer);
3633 masm.ret();
3634
3635 Linker linker(masm);
3636 JitCode* code = linker.newCode(cx, CodeKind::Other);
3637 if (!code) {
3638 return nullptr;
3639 }
3640
3641 CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub");
3642#ifdef MOZ_VTUNE1
3643 vtune::MarkStub(code, "RegExpExecTestStub");
3644#endif
3645
3646 return code;
3647}
3648
3649void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) {
3650 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"
, 3650); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg"
")"); do { MOZ_CrashSequence(__null, 3650); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3651 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"
, 3651); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg"
")"); do { MOZ_CrashSequence(__null, 3651); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3652 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", 3652); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { MOZ_CrashSequence(__null, 3652); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3653
3654 static_assert(RegExpExecTestRegExpReg != ReturnReg);
3655 static_assert(RegExpExecTestStringReg != ReturnReg);
3656
3657 masm.reserveStack(RegExpReservedStack);
3658
3659 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
3660 Register input = ToRegister(lir->string());
3661 Register regexp = ToRegister(lir->regexp());
3662
3663 pushArg(input);
3664 pushArg(regexp);
3665
3666 // We are not using oolCallVM because we are in a Call and live registers
3667 // have already been saved by the register allocator.
3668 using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp,
3669 HandleString input, bool* result);
3670 callVM<Fn, RegExpBuiltinExecTestFromJit>(lir);
3671
3672 masm.jump(ool.rejoin());
3673 });
3674 addOutOfLineCode(ool, lir->mir());
3675
3676 JitCode* regExpExecTestStub =
3677 snapshot_->getZoneStub(JitZone::StubKind::RegExpExecTest);
3678 masm.call(regExpExecTestStub);
3679
3680 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed),
3681 ool->entry());
3682
3683 masm.bind(ool->rejoin());
3684
3685 masm.freeStack(RegExpReservedStack);
3686}
3687
3688void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) {
3689 Register regexp = ToRegister(ins->regexp());
3690 Register input = ToRegister(ins->input());
3691 Register output = ToRegister(ins->output());
3692
3693 using Fn =
3694 bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*);
3695 auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>(
3696 ins, ArgList(regexp, input), StoreRegisterTo(output));
3697
3698 // Load RegExpShared in |output|.
3699 Label vmCall;
3700 masm.loadParsedRegExpShared(regexp, output, ool->entry());
3701
3702 // Return true iff pairCount > 1.
3703 Label returnTrue;
3704 masm.branch32(Assembler::Above,
3705 Address(output, RegExpShared::offsetOfPairCount()), Imm32(1),
3706 &returnTrue);
3707 masm.move32(Imm32(0), output);
3708 masm.jump(ool->rejoin());
3709
3710 masm.bind(&returnTrue);
3711 masm.move32(Imm32(1), output);
3712
3713 masm.bind(ool->rejoin());
3714}
3715
3716static void FindFirstDollarIndex(MacroAssembler& masm, Register str,
3717 Register len, Register temp0, Register temp1,
3718 Register output, CharEncoding encoding) {
3719#ifdef DEBUG1
3720 Label ok;
3721 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
3722 masm.assumeUnreachable("Length should be greater than 0.");
3723 masm.bind(&ok);
3724#endif
3725
3726 Register chars = temp0;
3727 masm.loadStringChars(str, chars, encoding);
3728
3729 masm.move32(Imm32(0), output);
3730
3731 Label start, done;
3732 masm.bind(&start);
3733
3734 Register currentChar = temp1;
3735 masm.loadChar(chars, output, currentChar, encoding);
3736 masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done);
3737
3738 masm.add32(Imm32(1), output);
3739 masm.branch32(Assembler::NotEqual, output, len, &start);
3740
3741 masm.move32(Imm32(-1), output);
3742
3743 masm.bind(&done);
3744}
3745
3746void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) {
3747 Register str = ToRegister(ins->str());
3748 Register output = ToRegister(ins->output());
3749 Register temp0 = ToRegister(ins->temp0());
3750 Register temp1 = ToRegister(ins->temp1());
3751 Register len = ToRegister(ins->temp2());
3752
3753 using Fn = bool (*)(JSContext*, JSString*, int32_t*);
3754 OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>(
3755 ins, ArgList(str), StoreRegisterTo(output));
3756
3757 masm.branchIfRope(str, ool->entry());
3758 masm.loadStringLength(str, len);
3759
3760 Label isLatin1, done;
3761 masm.branchLatin1String(str, &isLatin1);
3762 {
3763 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3764 CharEncoding::TwoByte);
3765 masm.jump(&done);
3766 }
3767 masm.bind(&isLatin1);
3768 {
3769 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3770 CharEncoding::Latin1);
3771 }
3772 masm.bind(&done);
3773 masm.bind(ool->rejoin());
3774}
3775
3776void CodeGenerator::visitStringReplace(LStringReplace* lir) {
3777 if (lir->replacement()->isConstant()) {
3778 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
3779 } else {
3780 pushArg(ToRegister(lir->replacement()));
3781 }
3782
3783 if (lir->pattern()->isConstant()) {
3784 pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString()));
3785 } else {
3786 pushArg(ToRegister(lir->pattern()));
3787 }
3788
3789 if (lir->string()->isConstant()) {
3790 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
3791 } else {
3792 pushArg(ToRegister(lir->string()));
3793 }
3794
3795 using Fn =
3796 JSString* (*)(JSContext*, HandleString, HandleString, HandleString);
3797 if (lir->mir()->isFlatReplacement()) {
3798 callVM<Fn, StringFlatReplaceString>(lir);
3799 } else {
3800 callVM<Fn, StringReplace>(lir);
3801 }
3802}
3803
3804void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) {
3805 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3806 TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir->lhs()));
3807 TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir->rhs()));
3808 ValueOperand output = ToOutValue(lir);
3809
3810 JSOp jsop = lir->mir()->jsop();
3811
3812 switch (jsop) {
3813 case JSOp::Add:
3814 case JSOp::Sub:
3815 case JSOp::Mul:
3816 case JSOp::Div:
3817 case JSOp::Mod:
3818 case JSOp::Pow:
3819 case JSOp::BitAnd:
3820 case JSOp::BitOr:
3821 case JSOp::BitXor:
3822 case JSOp::Lsh:
3823 case JSOp::Rsh:
3824 case JSOp::Ursh: {
3825 IonBinaryArithIC ic(liveRegs, lhs, rhs, output);
3826 addIC(lir, allocateIC(ic));
3827 return;
3828 }
3829 default:
3830 MOZ_CRASH("Unsupported jsop in MBinaryValueCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryValueCache"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3830); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache" ")"); do
{ MOZ_CrashSequence(__null, 3830); __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
3831 }
3832}
3833
3834void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) {
3835 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3836 TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir->lhs()));
3837 TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir->rhs()));
3838 Register output = ToRegister(lir->output());
3839
3840 JSOp jsop = lir->mir()->jsop();
3841
3842 switch (jsop) {
3843 case JSOp::Lt:
3844 case JSOp::Le:
3845 case JSOp::Gt:
3846 case JSOp::Ge:
3847 case JSOp::Eq:
3848 case JSOp::Ne:
3849 case JSOp::StrictEq:
3850 case JSOp::StrictNe: {
3851 IonCompareIC ic(liveRegs, lhs, rhs, output);
3852 addIC(lir, allocateIC(ic));
3853 return;
3854 }
3855 default:
3856 MOZ_CRASH("Unsupported jsop in MBinaryBoolCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryBoolCache"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3856); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache" ")"); do
{ MOZ_CrashSequence(__null, 3856); __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
3857 }
3858}
3859
3860void CodeGenerator::visitUnaryCache(LUnaryCache* lir) {
3861 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3862 TypedOrValueRegister input = TypedOrValueRegister(ToValue(lir->input()));
3863 ValueOperand output = ToOutValue(lir);
3864
3865 IonUnaryArithIC ic(liveRegs, input, output);
3866 addIC(lir, allocateIC(ic));
3867}
3868
3869void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) {
3870 pushArg(ImmGCPtr(lir->mir()->module()));
3871
3872 using Fn = JSObject* (*)(JSContext*, HandleObject);
3873 callVM<Fn, js::GetOrCreateModuleMetaObject>(lir);
3874}
3875
3876void CodeGenerator::visitDynamicImport(LDynamicImport* lir) {
3877 pushArg(Imm32(uint8_t(lir->mir()->phase())));
3878 pushArg(ToValue(lir->options()));
3879 pushArg(ToValue(lir->specifier()));
3880 pushArg(ImmGCPtr(current->mir()->info().script()));
3881
3882 using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue,
3883 ImportPhase);
3884 callVM<Fn, js::StartDynamicModuleImport>(lir);
3885}
3886
3887void CodeGenerator::visitLambda(LLambda* lir) {
3888 Register envChain = ToRegister(lir->environmentChain());
3889 Register output = ToRegister(lir->output());
3890 Register tempReg = ToRegister(lir->temp0());
3891 gc::Heap heap = lir->mir()->initialHeap();
3892
3893 JSFunction* fun = lir->mir()->templateFunction();
3894 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", 3894); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun->isTenured()" ")"); do { MOZ_CrashSequence
(__null, 3894); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3895
3896 using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject, gc::Heap);
3897 OutOfLineCode* ool = oolCallVM<Fn, js::LambdaOptimizedFallback>(
3898 lir, ArgList(ImmGCPtr(fun), envChain, Imm32(uint32_t(heap))),
3899 StoreRegisterTo(output));
3900
3901 TemplateObject templateObject(fun);
3902 masm.createGCObject(output, tempReg, templateObject, heap, ool->entry(),
3903 /* initContents = */ true,
3904 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
3905
3906 masm.storeValue(JSVAL_TYPE_OBJECT, envChain,
3907 Address(output, JSFunction::offsetOfEnvironment()));
3908
3909 // If we specified the tenured heap then we need a post barrier. Otherwise no
3910 // post barrier needed as the output is guaranteed to be allocated in the
3911 // nursery.
3912 if (heap == gc::Heap::Tenured) {
3913 Label skipBarrier;
3914 masm.branchPtrInNurseryChunk(Assembler::NotEqual, envChain, tempReg,
3915 &skipBarrier);
3916 saveVolatile(tempReg);
3917 emitPostWriteBarrier(output);
3918 restoreVolatile(tempReg);
3919 masm.bind(&skipBarrier);
3920 }
3921
3922 masm.bind(ool->rejoin());
3923}
3924
3925void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) {
3926 Register envChain = ToRegister(lir->envChain());
3927 Register prototype = ToRegister(lir->prototype());
3928
3929 pushArg(prototype);
3930 pushArg(envChain);
3931 pushArg(ImmGCPtr(lir->mir()->function()));
3932
3933 using Fn =
3934 JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject);
3935 callVM<Fn, js::FunWithProtoOperation>(lir);
3936}
3937
3938void CodeGenerator::visitSetFunName(LSetFunName* lir) {
3939 pushArg(Imm32(lir->mir()->prefixKind()));
3940 pushArg(ToValue(lir->name()));
3941 pushArg(ToRegister(lir->fun()));
3942
3943 using Fn =
3944 bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
3945 callVM<Fn, js::SetFunctionName>(lir);
3946}
3947
3948void CodeGenerator::visitOsiPoint(LOsiPoint* lir) {
3949 // Note: markOsiPoint ensures enough space exists between the last
3950 // LOsiPoint and this one to patch adjacent call instructions.
3951
3952 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"
, 3952); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 3952); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3953
3954 uint32_t osiCallPointOffset = markOsiPoint(lir);
3955
3956 LSafepoint* safepoint = lir->associatedSafepoint();
3957 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"
, 3957); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()"
")"); do { MOZ_CrashSequence(__null, 3957); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3958 safepoint->setOsiCallPointOffset(osiCallPointOffset);
3959
3960#ifdef DEBUG1
3961 // There should be no movegroups or other instructions between
3962 // an instruction and its OsiPoint. This is necessary because
3963 // we use the OsiPoint's snapshot from within VM calls.
3964 for (LInstructionReverseIterator iter(current->rbegin(lir));
3965 iter != current->rend(); iter++) {
3966 if (*iter == lir) {
3967 continue;
3968 }
3969 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", 3969); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!iter->isMoveGroup()" ")"); do { MOZ_CrashSequence
(__null, 3969); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3970 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"
, 3970); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint"
")"); do { MOZ_CrashSequence(__null, 3970); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3971 break;
3972 }
3973#endif
3974
3975#ifdef CHECK_OSIPOINT_REGISTERS1
3976 if (shouldVerifyOsiPointRegs(safepoint)) {
3977 verifyOsiPointRegs(safepoint);
3978 }
3979#endif
3980}
3981
3982void CodeGenerator::visitPhi(LPhi* lir) {
3983 MOZ_CRASH("Unexpected LPhi in CodeGenerator")do { do { } while (false); MOZ_ReportCrash("" "Unexpected LPhi in CodeGenerator"
, "./../../../../js/src/jit/CodeGenerator.cpp", 3983); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator" ")"); do { MOZ_CrashSequence
(__null, 3983); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
3984}
3985
3986void CodeGenerator::visitGoto(LGoto* lir) {
3987 // It would be valid to do simply `jumpToBlock(lir->target()); return;`.
3988 // That shorts out chains of completely empty (apart from the final Goto)
3989 // blocks. However, we try to do a bit better by shorting out chains of
3990 // blocks which are either completely empty or contain only MoveGroups, by
3991 // emitting the MoveGroups at this point. Hence this is a very limited form
3992 // of tail duplication, in which the duplicated tail(s) consist entirely of
3993 // MoveGroups.
3994 //
3995 // Ideally this logic should be in CodeGeneratorShared::jumpToBlock as it
3996 // would cover more use cases. That unfortunately creates a circular
3997 // dependency between the classes CodeGeneratorShared, CodeGenerator{Arch}
3998 // and CodeGenerator, which is not easy to resolve; specifically,
3999 // CodeGeneratorShared would need to call CodeGenerator::visitMoveGroup, but
4000 // CodeGenerator is (indirectly) a child class of CodeGeneratorShared.
4001 //
4002 // See CodeGeneratorShared::jumpToBlock(MBasicBlock*) as reference.
4003
4004 // If we can fall through to the target, don't bother cloning MoveGroups
4005 // because this would turn the fallthrough into an explicit jump.
4006 MBasicBlock* target = lir->target();
4007 if (isNextBlock(target->lir())) {
4008 return;
4009 }
4010
4011 uint32_t numMoveGroupsCloned = 0;
4012 while (true) {
4013 LBlock* targetLBlock = target->lir();
4014 LBlock* nextLBlock = targetLBlock->isMoveGroupsThenGoto();
4015 if (!nextLBlock) {
4016 break;
4017 }
4018 // This block is merely zero-or-more MoveGroups followed by a Goto. Emit
4019 // the MoveGroups and keep following the chain.
4020 auto iter = targetLBlock->begin();
4021 while (true) {
4022 LInstruction* ins = *iter;
4023 if (!ins->isMoveGroup()) {
4024 break;
4025 }
4026 visitMoveGroup(ins->toMoveGroup());
4027 iter++;
4028 numMoveGroupsCloned++;
4029 }
4030 // Ensured by LBlock::isMoveGroupsThenGoto
4031 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", 4031); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(*iter)->isGoto()" ")"); do { MOZ_CrashSequence
(__null, 4031); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4032 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", 4032); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(*iter)->toGoto()->getSuccessor(0)->lir() == nextLBlock"
")"); do { MOZ_CrashSequence(__null, 4032); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4033 iter++;
4034 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"
, 4034); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "iter == targetLBlock->end()"
")"); do { MOZ_CrashSequence(__null, 4034); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4035 target = nextLBlock->mir();
4036 if (numMoveGroupsCloned >= 1) {
4037 // Be very conservative about cloning. Higher numbers give more
4038 // aggressive chasing but seem to sometimes cause a slight cycle count
4039 // regression. In practice, cloning one happens occasionally, cloning of
4040 // two groups happens very rarely, and cloning of more than 2 groups has
4041 // only been seen in artificially constructed test cases.
4042 break;
4043 }
4044 }
4045
4046 // If the above loop exited due to hitting the MoveGroup clone limit, we
4047 // still need to skip past any "trivial" blocks, to avoid asserting in
4048 // `target->lir()->label()` below.
4049 target = skipTrivialBlocks(target);
4050
4051 // No jump necessary if we can fall through to the next block.
4052 if (isNextBlock(target->lir())) {
4053 return;
4054 }
4055
4056 masm.jump(target->lir()->label());
4057}
4058
4059void CodeGenerator::visitTableSwitch(LTableSwitch* ins) {
4060 MTableSwitch* mir = ins->mir();
4061 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
4062
4063 Register intIndex;
4064 if (mir->getOperand(0)->type() != MIRType::Int32) {
4065 intIndex = ToRegister(ins->temp0());
4066
4067 // The input is a double, so try and convert it to an integer.
4068 // If it does not fit in an integer, take the default case.
4069 masm.convertDoubleToInt32(ToFloatRegister(ins->index()), intIndex,
4070 defaultcase, false);
4071 } else {
4072 intIndex = ToRegister(ins->index());
4073 }
4074
4075 emitTableSwitchDispatch(mir, intIndex, ToTempRegisterOrInvalid(ins->temp1()));
4076}
4077
4078void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) {
4079 MTableSwitch* mir = ins->mir();
4080 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
4081
4082 Register index = ToRegister(ins->temp0());
4083 ValueOperand value = ToValue(ins->input());
4084 Register tag = masm.extractTag(value, index);
4085 masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase);
4086
4087 Label unboxInt, isInt;
4088 masm.branchTestInt32(Assembler::Equal, tag, &unboxInt);
4089 {
4090 FloatRegister floatIndex = ToFloatRegister(ins->temp1());
4091 masm.unboxDouble(value, floatIndex);
4092 masm.convertDoubleToInt32(floatIndex, index, defaultcase, false);
4093 masm.jump(&isInt);
4094 }
4095
4096 masm.bind(&unboxInt);
4097 masm.unboxInt32(value, index);
4098
4099 masm.bind(&isInt);
4100
4101 emitTableSwitchDispatch(mir, index, ToTempRegisterOrInvalid(ins->temp2()));
4102}
4103
4104void CodeGenerator::visitParameter(LParameter* lir) {}
4105
4106void CodeGenerator::visitCallee(LCallee* lir) {
4107 Register callee = ToRegister(lir->output());
4108 Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken());
4109
4110 masm.loadFunctionFromCalleeToken(ptr, callee);
4111}
4112
4113void CodeGenerator::visitIsConstructing(LIsConstructing* lir) {
4114 Register output = ToRegister(lir->output());
4115 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
4116 masm.loadPtr(calleeToken, output);
4117
4118 // We must be inside a function.
4119 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"
, 4119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()"
")"); do { MOZ_CrashSequence(__null, 4119); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4120
4121 // The low bit indicates whether this call is constructing, just clear the
4122 // other bits.
4123 static_assert(CalleeToken_Function == 0x0,
4124 "CalleeTokenTag value should match");
4125 static_assert(CalleeToken_FunctionConstructing == 0x1,
4126 "CalleeTokenTag value should match");
4127 masm.andPtr(Imm32(0x1), output);
4128}
4129
4130void CodeGenerator::visitReturn(LReturn* lir) {
4131#if defined(JS_NUNBOX32)
4132 DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX);
4133 DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX);
4134 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"
, 4134); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 4134); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4135 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", 4135); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 4135); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4136#elif defined(JS_PUNBOX641)
4137 DebugOnly<LAllocation*> result = lir->getOperand(0);
4138 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"
, 4138); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 4138); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4139#endif
4140 // Don't emit a jump to the return label if this is the last block, as
4141 // it'll fall through to the epilogue.
4142 if (current->mir() != *gen->graph().poBegin()) {
4143 masm.jump(&returnLabel_);
4144 }
4145}
4146
4147void CodeGenerator::visitOsrEntry(LOsrEntry* lir) {
4148 Register temp = ToRegister(lir->temp());
4149
4150 // Remember the OSR entry offset into the code buffer.
4151 masm.flushBuffer();
4152 setOsrEntryOffset(masm.size());
4153
4154 // Allocate the full frame for this function
4155 // Note we have a new entry here. So we reset MacroAssembler::framePushed()
4156 // to 0, before reserving the stack.
4157 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"
, 4157); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 4157); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4158 masm.setFramePushed(0);
4159
4160 // The Baseline code ensured both the frame pointer and stack pointer point to
4161 // the JitFrameLayout on the stack.
4162
4163 // If profiling, save the current frame pointer to a per-thread global field.
4164 if (isProfilerInstrumentationEnabled()) {
4165 masm.profilerEnterFrame(FramePointer, temp);
4166 }
4167
4168 masm.reserveStack(frameSize());
4169 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"
, 4169); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 4169); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4170
4171 // Ensure that the Ion frames is properly aligned.
4172 masm.assertStackAlignment(JitStackAlignment, 0);
4173}
4174
4175void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) {
4176 const LAllocation* frame = lir->entry();
4177 const LDefinition* object = lir->output();
4178
4179 const ptrdiff_t frameOffset =
4180 BaselineFrame::reverseOffsetOfEnvironmentChain();
4181
4182 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
4183}
4184
4185void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) {
4186 const LAllocation* frame = lir->entry();
4187 const LDefinition* object = lir->output();
4188
4189 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj();
4190
4191 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
4192}
4193
4194void CodeGenerator::visitOsrValue(LOsrValue* value) {
4195 const LAllocation* frame = value->entry();
4196 const ValueOperand out = ToOutValue(value);
4197
4198 const ptrdiff_t frameOffset = value->mir()->frameOffset();
4199
4200 masm.loadValue(Address(ToRegister(frame), frameOffset), out);
4201}
4202
4203void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) {
4204 const LAllocation* frame = lir->entry();
4205 const ValueOperand out = ToOutValue(lir);
4206
4207 Address flags =
4208 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags());
4209 Address retval =
4210 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue());
4211
4212 masm.moveValue(UndefinedValue(), out);
4213
4214 Label done;
4215 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL),
4216 &done);
4217 masm.loadValue(retval, out);
4218 masm.bind(&done);
4219}
4220
4221void CodeGenerator::visitStackArgT(LStackArgT* lir) {
4222 const LAllocation* arg = lir->arg();
4223 MIRType argType = lir->type();
4224 uint32_t argslot = lir->argslot();
4225 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", 4225); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { MOZ_CrashSequence(__null, 4225); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4226
4227 Address dest = AddressOfPassedArg(argslot);
4228
4229 if (arg->isFloatReg()) {
4230 masm.boxDouble(ToFloatRegister(arg), dest);
4231 } else if (arg->isGeneralReg()) {
4232 masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest);
4233 } else {
4234 masm.storeValue(arg->toConstant()->toJSValue(), dest);
4235 }
4236}
4237
4238void CodeGenerator::visitStackArgV(LStackArgV* lir) {
4239 ValueOperand val = ToValue(lir->value());
4240 uint32_t argslot = lir->argslot();
4241 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", 4241); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { MOZ_CrashSequence(__null, 4241); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4242
4243 masm.storeValue(val, AddressOfPassedArg(argslot));
4244}
4245
4246void CodeGenerator::visitMoveGroup(LMoveGroup* group) {
4247 if (!group->numMoves()) {
4248 return;
4249 }
4250
4251 MoveResolver& resolver = masm.moveResolver();
4252
4253 for (size_t i = 0; i < group->numMoves(); i++) {
4254 const LMove& move = group->getMove(i);
4255
4256 LAllocation from = move.from();
4257 LAllocation to = move.to();
4258 LDefinition::Type type = move.type();
4259
4260 // No bogus moves.
4261 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"
, 4261); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to"
")"); do { MOZ_CrashSequence(__null, 4261); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4262 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", 4262); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!from.isConstant()" ")"); do { MOZ_CrashSequence
(__null, 4262); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4263 MoveOp::Type moveType;
4264 switch (type) {
4265 case LDefinition::OBJECT:
4266 case LDefinition::SLOTS:
4267 case LDefinition::WASM_ANYREF:
4268 case LDefinition::WASM_STRUCT_DATA:
4269 case LDefinition::WASM_ARRAY_DATA:
4270#ifdef JS_NUNBOX32
4271 case LDefinition::TYPE:
4272 case LDefinition::PAYLOAD:
4273#else
4274 case LDefinition::BOX:
4275#endif
4276 case LDefinition::GENERAL:
4277 case LDefinition::STACKRESULTS:
4278 moveType = MoveOp::GENERAL;
4279 break;
4280 case LDefinition::INT32:
4281 moveType = MoveOp::INT32;
4282 break;
4283 case LDefinition::FLOAT32:
4284 moveType = MoveOp::FLOAT32;
4285 break;
4286 case LDefinition::DOUBLE:
4287 moveType = MoveOp::DOUBLE;
4288 break;
4289 case LDefinition::SIMD128:
4290 moveType = MoveOp::SIMD128;
4291 break;
4292 default:
4293 MOZ_CRASH("Unexpected move type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected move type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 4293); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected move type" ")"); do { MOZ_CrashSequence
(__null, 4293); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
4294 }
4295
4296 masm.propagateOOM(
4297 resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType));
4298 }
4299
4300 masm.propagateOOM(resolver.resolve());
4301 if (masm.oom()) {
4302 return;
4303 }
4304
4305 MoveEmitter emitter(masm);
4306
4307#ifdef JS_CODEGEN_X86
4308 if (group->maybeScratchRegister().isGeneralReg()) {
4309 emitter.setScratchRegister(
4310 group->maybeScratchRegister().toGeneralReg()->reg());
4311 } else {
4312 resolver.sortMemoryToMemoryMoves();
4313 }
4314#endif
4315
4316 emitter.emit(resolver);
4317 emitter.finish();
4318}
4319
4320void CodeGenerator::visitInteger(LInteger* lir) {
4321 masm.move32(Imm32(lir->i32()), ToRegister(lir->output()));
4322}
4323
4324void CodeGenerator::visitInteger64(LInteger64* lir) {
4325 masm.move64(Imm64(lir->i64()), ToOutRegister64(lir));
4326}
4327
4328void CodeGenerator::visitPointer(LPointer* lir) {
4329 masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output()));
4330}
4331
4332void CodeGenerator::visitDouble(LDouble* ins) {
4333 masm.loadConstantDouble(ins->value(), ToFloatRegister(ins->output()));
4334}
4335
4336void CodeGenerator::visitFloat32(LFloat32* ins) {
4337 masm.loadConstantFloat32(ins->value(), ToFloatRegister(ins->output()));
4338}
4339
4340void CodeGenerator::visitValue(LValue* value) {
4341 ValueOperand result = ToOutValue(value);
4342 masm.moveValue(value->value(), result);
4343}
4344
4345void CodeGenerator::visitNurseryObject(LNurseryObject* lir) {
4346 Register output = ToRegister(lir->output());
4347 uint32_t nurseryIndex = lir->mir()->nurseryObjectIndex();
4348
4349 // Load a pointer to the entry in IonScript's nursery objects list.
4350 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output);
4351 masm.propagateOOM(nurseryObjectLabels_.emplaceBack(label, nurseryIndex));
4352
4353 // Load the JSObject*.
4354 masm.loadPtr(Address(output, 0), output);
4355}
4356
4357void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) {
4358 // No-op.
4359}
4360
4361void CodeGenerator::visitDebugEnterGCUnsafeRegion(
4362 LDebugEnterGCUnsafeRegion* lir) {
4363 Register temp = ToRegister(lir->temp0());
4364
4365 masm.loadJSContext(temp);
4366
4367 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4368 masm.add32(Imm32(1), inUnsafeRegion);
4369
4370 Label ok;
4371 masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok);
4372 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4373 masm.bind(&ok);
4374}
4375
4376void CodeGenerator::visitDebugLeaveGCUnsafeRegion(
4377 LDebugLeaveGCUnsafeRegion* lir) {
4378 Register temp = ToRegister(lir->temp0());
4379
4380 masm.loadJSContext(temp);
4381
4382 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4383 masm.add32(Imm32(-1), inUnsafeRegion);
4384
4385 Label ok;
4386 masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok);
4387 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4388 masm.bind(&ok);
4389}
4390
4391void CodeGenerator::visitSlots(LSlots* lir) {
4392 Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots());
4393 masm.loadPtr(slots, ToRegister(lir->output()));
4394}
4395
4396void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) {
4397 ValueOperand dest = ToOutValue(lir);
4398 Register base = ToRegister(lir->input());
4399 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4400
4401 masm.loadValue(Address(base, offset), dest);
4402}
4403
4404void CodeGenerator::visitLoadDynamicSlotFromOffset(
4405 LLoadDynamicSlotFromOffset* lir) {
4406 ValueOperand dest = ToOutValue(lir);
4407 Register slots = ToRegister(lir->slots());
4408 Register offset = ToRegister(lir->offset());
4409
4410 // slots[offset]
4411 masm.loadValue(BaseIndex(slots, offset, TimesOne), dest);
4412}
4413
4414static ConstantOrRegister ToConstantOrRegister(const LAllocation* value,
4415 MIRType valueType) {
4416 if (value->isConstant()) {
4417 return ConstantOrRegister(value->toConstant()->toJSValue());
4418 }
4419 return TypedOrValueRegister(valueType, ToAnyRegister(value));
4420}
4421
4422void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) {
4423 Register base = ToRegister(lir->slots());
4424 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4425 Address dest(base, offset);
4426
4427 if (lir->mir()->needsPreBarrier()) {
4428 emitPreBarrier(dest);
4429 }
4430
4431 MIRType valueType = lir->mir()->value()->type();
4432 ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType);
4433 masm.storeUnboxedValue(value, valueType, dest);
4434}
4435
4436void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) {
4437 Register base = ToRegister(lir->slots());
4438 int32_t offset = lir->mir()->slot() * sizeof(Value);
4439
4440 ValueOperand value = ToValue(lir->value());
4441
4442 if (lir->mir()->needsPreBarrier()) {
4443 emitPreBarrier(Address(base, offset));
4444 }
4445
4446 masm.storeValue(value, Address(base, offset));
4447}
4448
4449void CodeGenerator::visitStoreDynamicSlotFromOffsetV(
4450 LStoreDynamicSlotFromOffsetV* lir) {
4451 Register slots = ToRegister(lir->slots());
4452 Register offset = ToRegister(lir->offset());
4453 ValueOperand value = ToValue(lir->value());
4454 Register temp = ToRegister(lir->temp0());
4455
4456 BaseIndex baseIndex(slots, offset, TimesOne);
4457 masm.computeEffectiveAddress(baseIndex, temp);
4458
4459 Address address(temp, 0);
4460
4461 emitPreBarrier(address);
4462
4463 // obj->slots[offset]
4464 masm.storeValue(value, address);
4465}
4466
4467void CodeGenerator::visitStoreDynamicSlotFromOffsetT(
4468 LStoreDynamicSlotFromOffsetT* lir) {
4469 Register slots = ToRegister(lir->slots());
4470 Register offset = ToRegister(lir->offset());
4471 const LAllocation* value = lir->value();
4472 MIRType valueType = lir->mir()->value()->type();
4473 Register temp = ToRegister(lir->temp0());
4474
4475 BaseIndex baseIndex(slots, offset, TimesOne);
4476 masm.computeEffectiveAddress(baseIndex, temp);
4477
4478 Address address(temp, 0);
4479
4480 emitPreBarrier(address);
4481
4482 // obj->slots[offset]
4483 ConstantOrRegister nvalue =
4484 value->isConstant()
4485 ? ConstantOrRegister(value->toConstant()->toJSValue())
4486 : TypedOrValueRegister(valueType, ToAnyRegister(value));
4487 masm.storeConstantOrRegister(nvalue, address);
4488}
4489
4490void CodeGenerator::visitElements(LElements* lir) {
4491 Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
4492 masm.loadPtr(elements, ToRegister(lir->output()));
4493}
4494
4495void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) {
4496 Address environment(ToRegister(lir->function()),
4497 JSFunction::offsetOfEnvironment());
4498 masm.unboxObject(environment, ToRegister(lir->output()));
4499}
4500
4501void CodeGenerator::visitHomeObject(LHomeObject* lir) {
4502 Register func = ToRegister(lir->function());
4503 Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
4504
4505 masm.assertFunctionIsExtended(func);
4506#ifdef DEBUG1
4507 Label isObject;
4508 masm.branchTestObject(Assembler::Equal, homeObject, &isObject);
4509 masm.assumeUnreachable("[[HomeObject]] must be Object");
4510 masm.bind(&isObject);
4511#endif
4512
4513 masm.unboxObject(homeObject, ToRegister(lir->output()));
4514}
4515
4516void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) {
4517 Register homeObject = ToRegister(lir->homeObject());
4518 ValueOperand output = ToOutValue(lir);
4519 Register temp = output.scratchReg();
4520
4521 masm.loadObjProto(homeObject, temp);
4522
4523#ifdef DEBUG1
4524 // We won't encounter a lazy proto, because the prototype is guaranteed to
4525 // either be a JSFunction or a PlainObject, and only proxy objects can have a
4526 // lazy proto.
4527 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", 4527); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 4527); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4528
4529 Label proxyCheckDone;
4530 masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone);
4531 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase");
4532 masm.bind(&proxyCheckDone);
4533#endif
4534
4535 Label nullProto, done;
4536 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
4537
4538 // Box prototype and return
4539 masm.tagValue(JSVAL_TYPE_OBJECT, temp, output);
4540 masm.jump(&done);
4541
4542 masm.bind(&nullProto);
4543 masm.moveValue(NullValue(), output);
4544
4545 masm.bind(&done);
4546}
4547
4548template <class T>
4549static T* ToConstantObject(MDefinition* def) {
4550 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", 4550); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "def->isConstant()" ")"); do { MOZ_CrashSequence
(__null, 4550); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4551 return &def->toConstant()->toObject().as<T>();
4552}
4553
4554void CodeGenerator::visitNewLexicalEnvironmentObject(
4555 LNewLexicalEnvironmentObject* lir) {
4556 Register output = ToRegister(lir->output());
4557 Register temp = ToRegister(lir->temp0());
4558
4559 auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>(
4560 lir->mir()->templateObj());
4561 auto* scope = &templateObj->scope();
4562 gc::Heap initialHeap = gc::Heap::Default;
4563
4564 using Fn =
4565 BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>);
4566 auto* ool =
4567 oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>(
4568 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4569
4570 TemplateObject templateObject(templateObj);
4571 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4572
4573 masm.bind(ool->rejoin());
4574}
4575
4576void CodeGenerator::visitNewClassBodyEnvironmentObject(
4577 LNewClassBodyEnvironmentObject* lir) {
4578 Register output = ToRegister(lir->output());
4579 Register temp = ToRegister(lir->temp0());
4580
4581 auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>(
4582 lir->mir()->templateObj());
4583 auto* scope = &templateObj->scope();
4584 gc::Heap initialHeap = gc::Heap::Default;
4585
4586 using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*,
4587 Handle<ClassBodyScope*>);
4588 auto* ool =
4589 oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>(
4590 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4591
4592 TemplateObject templateObject(templateObj);
4593 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4594
4595 masm.bind(ool->rejoin());
4596}
4597
4598void CodeGenerator::visitNewVarEnvironmentObject(
4599 LNewVarEnvironmentObject* lir) {
4600 Register output = ToRegister(lir->output());
4601 Register temp = ToRegister(lir->temp0());
4602
4603 auto* templateObj =
4604 ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj());
4605 auto* scope = &templateObj->scope().as<VarScope>();
4606 gc::Heap initialHeap = gc::Heap::Default;
4607
4608 using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>);
4609 auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>(
4610 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4611
4612 TemplateObject templateObject(templateObj);
4613 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4614
4615 masm.bind(ool->rejoin());
4616}
4617
4618void CodeGenerator::visitGuardShape(LGuardShape* guard) {
4619 Register obj = ToRegister(guard->object());
4620 Register temp = ToTempRegisterOrInvalid(guard->temp0());
4621 Label bail;
4622 masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp,
4623 obj, &bail);
4624 bailoutFrom(&bail, guard->snapshot());
4625}
4626
4627void CodeGenerator::visitGuardFuse(LGuardFuse* guard) {
4628 auto fuseIndex = guard->mir()->fuseIndex();
4629
4630 Label bail;
4631
4632 // Bake specific fuse address for Ion code, because we won't share this code
4633 // across realms.
4634 GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex);
4635 masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(fuse->fuseRef()),
4636 ImmWord(0), &bail);
4637
4638 bailoutFrom(&bail, guard->snapshot());
4639}
4640
4641void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) {
4642 Register obj = ToRegister(guard->object());
4643 Register shapeList = ToRegister(guard->shapeList());
4644 Register temp = ToRegister(guard->temp0());
4645 Register temp2 = ToRegister(guard->temp1());
4646 Register temp3 = ToRegister(guard->temp2());
4647 Register spectre = ToTempRegisterOrInvalid(guard->temp3());
4648
4649 Label bail;
4650 masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp);
4651 masm.branchTestObjShapeList(obj, temp, temp2, temp3, spectre, &bail);
4652 bailoutFrom(&bail, guard->snapshot());
4653}
4654
4655void CodeGenerator::visitGuardShapeList(LGuardShapeList* guard) {
4656 Register obj = ToRegister(guard->object());
4657 Register temp = ToRegister(guard->temp0());
4658 Register spectre = ToTempRegisterOrInvalid(guard->temp1());
4659
4660 Label done, bail;
4661 masm.loadObjShapeUnsafe(obj, temp);
4662
4663 // Count the number of branches to emit.
4664 const auto& shapes = guard->mir()->shapeList()->shapes();
4665 size_t branchesLeft = std::count_if(shapes.begin(), shapes.end(),
4666 [](Shape* s) { return s != nullptr; });
4667 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", 4667); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "branchesLeft > 0" ")"); do { MOZ_CrashSequence
(__null, 4667); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4668
4669 for (Shape* shape : shapes) {
4670 if (!shape) {
4671 continue;
4672 }
4673 if (branchesLeft > 1) {
4674 masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(shape), &done);
4675 if (spectre != InvalidReg) {
4676 masm.spectreMovePtr(Assembler::Equal, spectre, obj);
4677 }
4678 } else {
4679 // This is the last branch so invert the condition and jump to |bail|.
4680 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &bail);
4681 if (spectre != InvalidReg) {
4682 masm.spectreMovePtr(Assembler::NotEqual, spectre, obj);
4683 }
4684 }
4685 branchesLeft--;
4686 }
4687 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", 4687); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "branchesLeft == 0" ")"); do { MOZ_CrashSequence
(__null, 4687); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4688
4689 masm.bind(&done);
4690 bailoutFrom(&bail, guard->snapshot());
4691}
4692
4693void CodeGenerator::visitGuardShapeListToOffset(
4694 LGuardShapeListToOffset* guard) {
4695 Register obj = ToRegister(guard->object());
4696 Register temp = ToRegister(guard->temp0());
4697 Register spectre = ToTempRegisterOrInvalid(guard->temp1());
4698 Register offset = ToRegister(guard->output());
4699
4700 Label done, bail;
4701 masm.loadObjShapeUnsafe(obj, temp);
4702
4703 // Count the number of branches to emit.
4704 const auto& shapes = guard->mir()->shapeList()->shapes();
4705 const auto& offsets = guard->mir()->shapeList()->offsets();
4706 size_t branchesLeft = std::count_if(shapes.begin(), shapes.end(),
4707 [](Shape* s) { return s != nullptr; });
4708 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", 4708); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "branchesLeft > 0" ")"); do { MOZ_CrashSequence
(__null, 4708); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4709
4710 size_t index = 0;
4711 for (Shape* shape : shapes) {
4712 if (!shape) {
4713 index++;
4714 continue;
4715 }
4716
4717 if (branchesLeft > 1) {
4718 Label next;
4719 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &next);
4720 if (spectre != InvalidReg) {
4721 masm.spectreMovePtr(Assembler::NotEqual, spectre, obj);
4722 }
4723 masm.move32(Imm32(offsets[index]), offset);
4724 masm.jump(&done);
4725 masm.bind(&next);
4726 } else {
4727 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &bail);
4728 if (spectre != InvalidReg) {
4729 masm.spectreMovePtr(Assembler::NotEqual, spectre, obj);
4730 }
4731 masm.move32(Imm32(offsets[index]), offset);
4732 }
4733
4734 branchesLeft--;
4735 index++;
4736 }
4737 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", 4737); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "branchesLeft == 0" ")"); do { MOZ_CrashSequence
(__null, 4737); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4738
4739 masm.bind(&done);
4740 bailoutFrom(&bail, guard->snapshot());
4741}
4742
4743void CodeGenerator::visitGuardMultipleShapesToOffset(
4744 LGuardMultipleShapesToOffset* guard) {
4745 Register obj = ToRegister(guard->object());
4746 Register shapeList = ToRegister(guard->shapeList());
4747 Register temp = ToRegister(guard->temp0());
4748 Register temp1 = ToRegister(guard->temp1());
4749 Register temp2 = ToRegister(guard->temp2());
4750 Register offset = ToRegister(guard->output());
4751 Register spectre = JitOptions.spectreObjectMitigations ? offset : InvalidReg;
4752
4753 Label bail;
4754 masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp);
4755 masm.branchTestObjShapeListSetOffset(obj, temp, offset, temp1, temp2, spectre,
4756 &bail);
4757 bailoutFrom(&bail, guard->snapshot());
4758}
4759
4760void CodeGenerator::visitGuardProto(LGuardProto* guard) {
4761 Register obj = ToRegister(guard->object());
4762 Register expected = ToRegister(guard->expected());
4763 Register temp = ToRegister(guard->temp0());
4764
4765 masm.loadObjProto(obj, temp);
4766
4767 Label bail;
4768 masm.branchPtr(Assembler::NotEqual, temp, expected, &bail);
4769 bailoutFrom(&bail, guard->snapshot());
4770}
4771
4772void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) {
4773 Register obj = ToRegister(guard->object());
4774 Register temp = ToRegister(guard->temp0());
4775
4776 masm.loadObjProto(obj, temp);
4777
4778 Label bail;
4779 masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail);
4780 bailoutFrom(&bail, guard->snapshot());
4781}
4782
4783void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) {
4784 Register obj = ToRegister(guard->object());
4785 Register temp = ToRegister(guard->temp0());
4786
4787 Label bail;
4788 masm.branchIfNonNativeObj(obj, temp, &bail);
4789 bailoutFrom(&bail, guard->snapshot());
4790}
4791
4792void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) {
4793 Register temp = ToRegister(guard->temp0());
4794 Label bail;
4795
4796 masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp);
4797 masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()),
4798 &bail);
4799 bailoutFrom(&bail, guard->snapshot());
4800}
4801
4802void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) {
4803 Register obj = ToRegister(guard->object());
4804 Register temp = ToRegister(guard->temp0());
4805
4806 Label bail;
4807 masm.branchTestObjectIsProxy(false, obj, temp, &bail);
4808 bailoutFrom(&bail, guard->snapshot());
4809}
4810
4811void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) {
4812 Register obj = ToRegister(guard->object());
4813 Register temp = ToRegister(guard->temp0());
4814
4815 Label bail;
4816 masm.branchTestObjectIsProxy(true, obj, temp, &bail);
4817 bailoutFrom(&bail, guard->snapshot());
4818}
4819
4820void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) {
4821 Register proxy = ToRegister(guard->proxy());
4822 Register temp = ToRegister(guard->temp0());
4823
4824 Label bail;
4825 masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp,
4826 GetDOMProxyHandlerFamily(), &bail);
4827 bailoutFrom(&bail, guard->snapshot());
4828}
4829
4830void CodeGenerator::visitProxyGet(LProxyGet* lir) {
4831 Register proxy = ToRegister(lir->proxy());
4832 Register temp = ToRegister(lir->temp0());
4833
4834 pushArg(lir->mir()->id(), temp);
4835 pushArg(proxy);
4836
4837 using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue);
4838 callVM<Fn, ProxyGetProperty>(lir);
4839}
4840
4841void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) {
4842 Register proxy = ToRegister(lir->proxy());
4843 ValueOperand idVal = ToValue(lir->idVal());
4844
4845 pushArg(idVal);
4846 pushArg(proxy);
4847
4848 using Fn =
4849 bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
4850 callVM<Fn, ProxyGetPropertyByValue>(lir);
4851}
4852
4853void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) {
4854 Register proxy = ToRegister(lir->proxy());
4855 ValueOperand idVal = ToValue(lir->id());
4856
4857 pushArg(idVal);
4858 pushArg(proxy);
4859
4860 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*);
4861 if (lir->mir()->hasOwn()) {
4862 callVM<Fn, ProxyHasOwn>(lir);
4863 } else {
4864 callVM<Fn, ProxyHas>(lir);
4865 }
4866}
4867
4868void CodeGenerator::visitProxySet(LProxySet* lir) {
4869 Register proxy = ToRegister(lir->proxy());
4870 ValueOperand rhs = ToValue(lir->rhs());
4871 Register temp = ToRegister(lir->temp0());
4872
4873 pushArg(Imm32(lir->mir()->strict()));
4874 pushArg(rhs);
4875 pushArg(lir->mir()->id(), temp);
4876 pushArg(proxy);
4877
4878 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
4879 callVM<Fn, ProxySetProperty>(lir);
4880}
4881
4882void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) {
4883 Register proxy = ToRegister(lir->proxy());
4884 ValueOperand idVal = ToValue(lir->idVal());
4885 ValueOperand rhs = ToValue(lir->rhs());
4886
4887 pushArg(Imm32(lir->mir()->strict()));
4888 pushArg(rhs);
4889 pushArg(idVal);
4890 pushArg(proxy);
4891
4892 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
4893 callVM<Fn, ProxySetPropertyByValue>(lir);
4894}
4895
4896void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) {
4897 Register obj = ToRegister(lir->obj());
4898 ValueOperand rhs = ToValue(lir->rhs());
4899
4900 pushArg(Imm32(lir->mir()->strict()));
4901 pushArg(rhs);
4902 pushArg(obj);
4903
4904 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool);
4905 callVM<Fn, jit::SetArrayLength>(lir);
4906}
4907
4908void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) {
4909 Register obj = ToRegister(lir->object());
4910 Register temp0 = ToRegister(lir->temp0());
4911 Register temp1 = ToRegister(lir->temp1());
4912 Register temp2 = ToRegister(lir->temp2());
4913 Register temp3 = ToRegister(lir->temp3());
4914 ValueOperand output = ToOutValue(lir);
4915
4916 Label done;
4917 PropertyKey id = lir->mir()->name();
4918 masm.movePropertyKey(id, temp0);
4919 masm.move32(Imm32(HashPropertyKeyThreadSafe(id)), temp1);
4920
4921 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", 4921); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 4921); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4922 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", 4922); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 4922); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4923 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", 4923); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 4923); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4924 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", 4924); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 4924); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4925#if defined(JS_NUNBOX32)
4926 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"
, 4926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 4926); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4927 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", 4927); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 4927); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4928#else
4929 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", 4929); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 4929); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4930#endif
4931 TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub();
4932 masm.call(megamorphicLoadStub);
4933 masm.branchPtr(Assembler::Equal, temp2,
4934 Imm32(JitRuntime::MegamorphicLoadStubCacheHit), &done);
4935
4936 Label bail;
4937 masm.branchIfNonNativeObj(obj, temp0, &bail);
4938
4939 masm.Push(UndefinedValue());
4940 masm.moveStackPtrTo(temp3);
4941
4942 using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id,
4943 MegamorphicCache::Entry* cacheEntry, Value* vp);
4944 masm.setupAlignedABICall();
4945 masm.loadJSContext(temp0);
4946 masm.passABIArg(temp0);
4947 masm.passABIArg(obj);
4948 masm.movePropertyKey(lir->mir()->name(), temp1);
4949 masm.passABIArg(temp1);
4950 masm.passABIArg(temp2);
4951 masm.passABIArg(temp3);
4952
4953 masm.callWithABI<Fn, GetNativeDataPropertyPure>();
4954
4955 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", 4955); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)" ")"); do { MOZ_CrashSequence
(__null, 4955); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4956 masm.Pop(output);
4957
4958 masm.branchIfFalseBool(ReturnReg, &bail);
4959 masm.bind(&done);
4960
4961 bailoutFrom(&bail, lir->snapshot());
4962}
4963
4964void CodeGenerator::visitMegamorphicLoadSlotPermissive(
4965 LMegamorphicLoadSlotPermissive* lir) {
4966 Register obj = ToRegister(lir->object());
4967 Register temp0 = ToRegister(lir->temp0());
4968 Register temp1 = ToRegister(lir->temp1());
4969 Register temp2 = ToRegister(lir->temp2());
4970 Register temp3 = ToRegister(lir->temp3());
4971 ValueOperand output = ToOutValue(lir);
4972
4973 masm.movePtr(obj, temp3);
4974
4975 Label done, getter, nullGetter;
4976 PropertyKey id = lir->mir()->name();
4977 masm.movePropertyKey(id, temp0);
4978 masm.move32(Imm32(HashPropertyKeyThreadSafe(id)), temp1);
4979
4980 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", 4980); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 4980); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4981 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", 4981); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 4981); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4982 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", 4982); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 4982); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4983 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", 4983); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 4983); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4984#if defined(JS_NUNBOX32)
4985 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"
, 4985); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 4985); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4986 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", 4986); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 4986); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4987#else
4988 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", 4988); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 4988); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4989#endif
4990 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", 4990); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!output.aliases(temp3)" ")"); do { MOZ_CrashSequence
(__null, 4990); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4991 TrampolinePtr megamorphicLoadStub =
4992 gen->jitRuntime()->megamorphicLoadStubPermissive();
4993 masm.call(megamorphicLoadStub);
4994 masm.branchPtr(Assembler::Equal, temp2,
4995 Imm32(JitRuntime::MegamorphicLoadStubCacheHit), &done);
4996 masm.branchPtr(Assembler::Equal, temp2,
4997 Imm32(JitRuntime::MegamorphicLoadStubCacheHitGetter), &getter);
4998
4999 masm.movePropertyKey(lir->mir()->name(), temp1);
5000 pushArg(temp2);
5001 pushArg(temp1);
5002 pushArg(obj);
5003
5004 using Fn = bool (*)(JSContext*, HandleObject, HandleId,
5005 MegamorphicCacheEntry*, MutableHandleValue);
5006 callVM<Fn, GetPropMaybeCached>(lir);
5007
5008 masm.jump(&done);
5009
5010 masm.bind(&getter);
5011
5012 emitCallMegamorphicGetter(lir, output, temp3, temp1, temp2, &nullGetter);
5013 masm.jump(&done);
5014
5015 masm.bind(&nullGetter);
5016 masm.moveValue(UndefinedValue(), output);
5017 masm.bind(&done);
5018}
5019
5020void CodeGenerator::visitMegamorphicLoadSlotByValue(
5021 LMegamorphicLoadSlotByValue* lir) {
5022 Register obj = ToRegister(lir->object());
5023 ValueOperand idVal = ToValue(lir->idVal());
5024 Register temp0 = ToRegister(lir->temp0());
5025 Register temp1 = ToRegister(lir->temp1());
5026 Register temp2 = ToRegister(lir->temp2());
5027 ValueOperand output = ToOutValue(lir);
5028
5029 Label done, bail, atomizeMiss;
5030 masm.xorPtr(temp2, temp2);
5031 masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss);
5032
5033 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", 5033); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 5033); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5034 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", 5034); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 5034); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5035 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", 5035); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 5035); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5036 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", 5036); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 5036); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5037#if defined(JS_NUNBOX32)
5038 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"
, 5038); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 5038); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5039 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", 5039); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 5039); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5040#else
5041 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", 5041); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 5041); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5042#endif
5043 TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub();
5044 masm.call(megamorphicLoadStub);
5045 masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done);
5046
5047 masm.bind(&atomizeMiss);
5048 masm.branchIfNonNativeObj(obj, temp0, &bail);
5049
5050 // idVal will be in vp[0], result will be stored in vp[1].
5051 masm.reserveStack(sizeof(Value));
5052 masm.Push(idVal);
5053 masm.moveStackPtrTo(temp0);
5054
5055 using Fn = bool (*)(JSContext* cx, JSObject* obj,
5056 MegamorphicCache::Entry* cacheEntry, Value* vp);
5057 masm.setupAlignedABICall();
5058 masm.loadJSContext(temp1);
5059 masm.passABIArg(temp1);
5060 masm.passABIArg(obj);
5061 masm.passABIArg(temp2);
5062 masm.passABIArg(temp0);
5063 masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>();
5064
5065 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", 5065); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { MOZ_CrashSequence
(__null, 5065); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5066 masm.storeCallPointerResult(temp0);
5067 masm.Pop(idVal);
5068
5069 uint32_t framePushed = masm.framePushed();
5070 Label ok;
5071 masm.branchIfTrueBool(temp0, &ok);
5072 masm.freeStack(sizeof(Value)); // Discard result Value.
5073 masm.jump(&bail);
5074
5075 masm.bind(&ok);
5076 masm.setFramePushed(framePushed);
5077 masm.Pop(output);
5078
5079 masm.bind(&done);
5080
5081 bailoutFrom(&bail, lir->snapshot());
5082}
5083
5084void CodeGenerator::visitMegamorphicLoadSlotByValuePermissive(
5085 LMegamorphicLoadSlotByValuePermissive* lir) {
5086 Register obj = ToRegister(lir->object());
5087 ValueOperand idVal = ToValue(lir->idVal());
5088 Register temp0 = ToRegister(lir->temp0());
5089 Register temp1 = ToRegister(lir->temp1());
5090 Register temp2 = ToRegister(lir->temp2());
5091
5092 Label done, atomizeMiss;
5093
5094 // If we have enough registers available, we can call getters directly from
5095 // jitcode. On x86, we have to call into the VM.
5096#ifndef JS_CODEGEN_X86
5097 ValueOperand output = ToOutValue(lir);
5098 Label getter, nullGetter;
5099 Register temp3 = ToRegister(lir->temp3());
5100 masm.movePtr(obj, temp3);
5101 masm.xorPtr(temp2, temp2);
5102 masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss);
5103
5104 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", 5104); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 5104); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5105 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", 5105); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 5105); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5106 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", 5106); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 5106); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5107 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", 5107); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 5107); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5108# if defined(JS_NUNBOX32)
5109 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"
, 5109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 5109); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5110 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", 5110); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")"
); do { MOZ_CrashSequence(__null, 5110); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5111# else
5112 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", 5112); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 5112); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5113# endif
5114 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", 5114); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!output.aliases(temp3)" ")"); do { MOZ_CrashSequence
(__null, 5114); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5115 TrampolinePtr megamorphicLoadStub =
5116 gen->jitRuntime()->megamorphicLoadStubPermissive();
5117 masm.call(megamorphicLoadStub);
5118 masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done);
5119 masm.branchTest32(Assembler::NonZero, temp2, Imm32(2), &getter);
5120#else
5121 masm.xorPtr(temp2, temp2);
5122 masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss);
5123
5124 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", 5124); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence
(__null, 5124); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5125 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", 5125); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 5125); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5126 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", 5126); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 5126); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5127 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", 5127); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence
(__null, 5127); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5128# if defined(JS_NUNBOX32)
5129 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", 5129); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToOutValue(lir).typeReg() == JSReturnReg_Type"
")"); do { MOZ_CrashSequence(__null, 5129); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5130 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", 5130); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToOutValue(lir).payloadReg() == JSReturnReg_Data"
")"); do { MOZ_CrashSequence(__null, 5130); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5131# else
5132 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", 5132); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToOutValue(lir).payloadOrValueReg() == JSReturnReg"
")"); do { MOZ_CrashSequence(__null, 5132); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5133# endif
5134 TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub();
5135 masm.call(megamorphicLoadStub);
5136 masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done);
5137#endif
5138
5139 masm.bind(&atomizeMiss);
5140
5141 pushArg(temp2);
5142 pushArg(idVal);
5143 pushArg(obj);
5144
5145 using Fn = bool (*)(JSContext*, HandleObject, HandleValue,
5146 MegamorphicCacheEntry*, MutableHandleValue);
5147 callVM<Fn, GetElemMaybeCached>(lir);
5148
5149#ifndef JS_CODEGEN_X86
5150 masm.jump(&done);
5151 masm.bind(&getter);
5152
5153 emitCallMegamorphicGetter(lir, output, temp3, temp1, temp2, &nullGetter);
5154 masm.jump(&done);
5155
5156 masm.bind(&nullGetter);
5157 masm.moveValue(UndefinedValue(), output);
5158#endif
5159
5160 masm.bind(&done);
5161}
5162
5163void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) {
5164 Register obj = ToRegister(lir->object());
5165 ValueOperand value = ToValue(lir->rhs());
5166
5167 Register temp0 = ToRegister(lir->temp0());
5168#ifndef JS_CODEGEN_X86
5169 Register temp1 = ToRegister(lir->temp1());
5170 Register temp2 = ToRegister(lir->temp2());
5171#endif
5172
5173 // The instruction is marked as call-instruction so only these registers are
5174 // live.
5175 LiveRegisterSet liveRegs;
5176 liveRegs.addUnchecked(obj);
5177 liveRegs.addUnchecked(value);
5178 liveRegs.addUnchecked(temp0);
5179#ifndef JS_CODEGEN_X86
5180 liveRegs.addUnchecked(temp1);
5181 liveRegs.addUnchecked(temp2);
5182#endif
5183
5184 Label cacheHit, done;
5185#ifdef JS_CODEGEN_X86
5186 masm.emitMegamorphicCachedSetSlot(
5187 lir->mir()->name(), obj, temp0, value, liveRegs, &cacheHit,
5188 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
5189 EmitPreBarrier(masm, addr, mirType);
5190 });
5191#else
5192 masm.emitMegamorphicCachedSetSlot(
5193 lir->mir()->name(), obj, temp0, temp1, temp2, value, liveRegs, &cacheHit,
5194 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
5195 EmitPreBarrier(masm, addr, mirType);
5196 });
5197#endif
5198
5199 pushArg(Imm32(lir->mir()->strict()));
5200 pushArg(value);
5201 pushArg(lir->mir()->name(), temp0);
5202 pushArg(obj);
5203
5204 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
5205 callVM<Fn, SetPropertyMegamorphic<true>>(lir);
5206
5207 masm.jump(&done);
5208 masm.bind(&cacheHit);
5209
5210 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
5211 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
5212
5213 // Note: because this is a call-instruction, no registers need to be saved.
5214 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", 5214); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { MOZ_CrashSequence
(__null, 5214); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5215 emitPostWriteBarrier(obj);
5216
5217 masm.bind(&done);
5218}
5219
5220void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) {
5221 Register obj = ToRegister(lir->object());
5222 ValueOperand idVal = ToValue(lir->idVal());
5223 Register temp0 = ToRegister(lir->temp0());
5224 Register temp1 = ToRegister(lir->temp1());
5225 Register temp2 = ToRegister(lir->temp2());
5226 Register output = ToRegister(lir->output());
5227
5228 Label bail, cacheHit, atomizeMiss;
5229 masm.xorPtr(temp2, temp2);
5230 masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss);
5231 masm.emitMegamorphicCacheLookupExists(obj, temp0, temp1, temp2, output,
5232 &cacheHit, lir->mir()->hasOwn());
5233
5234 masm.bind(&atomizeMiss);
5235 masm.branchIfNonNativeObj(obj, temp0, &bail);
5236
5237 // idVal will be in vp[0], result will be stored in vp[1].
5238 masm.reserveStack(sizeof(Value));
5239 masm.Push(idVal);
5240 masm.moveStackPtrTo(temp0);
5241
5242 using Fn = bool (*)(JSContext* cx, JSObject* obj,
5243 MegamorphicCache::Entry* cacheEntry, Value* vp);
5244 masm.setupAlignedABICall();
5245 masm.loadJSContext(temp1);
5246 masm.passABIArg(temp1);
5247 masm.passABIArg(obj);
5248 masm.passABIArg(temp2);
5249 masm.passABIArg(temp0);
5250 if (lir->mir()->hasOwn()) {
5251 masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>();
5252 } else {
5253 masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>();
5254 }
5255
5256 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", 5256); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { MOZ_CrashSequence
(__null, 5256); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5257 masm.storeCallPointerResult(temp0);
5258 masm.Pop(idVal);
5259
5260 uint32_t framePushed = masm.framePushed();
5261 Label ok;
5262 masm.branchIfTrueBool(temp0, &ok);
5263 masm.freeStack(sizeof(Value)); // Discard result Value.
5264 masm.jump(&bail);
5265
5266 masm.bind(&ok);
5267 masm.setFramePushed(framePushed);
5268 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
5269 masm.freeStack(sizeof(Value));
5270 masm.bind(&cacheHit);
5271
5272 bailoutFrom(&bail, lir->snapshot());
5273}
5274
5275void CodeGenerator::visitSmallObjectVariableKeyHasProp(
5276 LSmallObjectVariableKeyHasProp* lir) {
5277 Register id = ToRegister(lir->idStr());
5278 Register output = ToRegister(lir->output());
5279
5280#ifdef DEBUG1
5281 Label isAtom;
5282 masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()),
5283 Imm32(StringFlags::ATOM_BIT), &isAtom);
5284 masm.assumeUnreachable("Expected atom input");
5285 masm.bind(&isAtom);
5286#endif
5287
5288 SharedShape* shape = &lir->mir()->shape()->asShared();
5289
5290 Label done, success;
5291 for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) {
5292 masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()),
5293 &success);
5294 }
5295 masm.move32(Imm32(0), output);
5296 masm.jump(&done);
5297 masm.bind(&success);
5298 masm.move32(Imm32(1), output);
5299 masm.bind(&done);
5300}
5301
5302void CodeGenerator::visitGuardToArrayBuffer(LGuardToArrayBuffer* guard) {
5303 Register obj = ToRegister(guard->object());
5304 Register temp = ToRegister(guard->temp0());
5305
5306 // branchIfIsNotArrayBuffer may zero the object register on speculative paths
5307 // (we should have a defineReuseInput allocation in this case).
5308
5309 Label bail;
5310 masm.branchIfIsNotArrayBuffer(obj, temp, &bail);
5311 bailoutFrom(&bail, guard->snapshot());
5312}
5313
5314void CodeGenerator::visitGuardToSharedArrayBuffer(
5315 LGuardToSharedArrayBuffer* guard) {
5316 Register obj = ToRegister(guard->object());
5317 Register temp = ToRegister(guard->temp0());
5318
5319 // branchIfIsNotSharedArrayBuffer may zero the object register on speculative
5320 // paths (we should have a defineReuseInput allocation in this case).
5321
5322 Label bail;
5323 masm.branchIfIsNotSharedArrayBuffer(obj, temp, &bail);
5324 bailoutFrom(&bail, guard->snapshot());
5325}
5326
5327void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared(
5328 LGuardIsNotArrayBufferMaybeShared* guard) {
5329 Register obj = ToRegister(guard->object());
5330 Register temp = ToRegister(guard->temp0());
5331
5332 Label bail;
5333 masm.branchIfIsArrayBufferMaybeShared(obj, temp, &bail);
5334 bailoutFrom(&bail, guard->snapshot());
5335}
5336
5337void CodeGenerator::visitGuardIsNonResizableTypedArray(
5338 LGuardIsNonResizableTypedArray* guard) {
5339 Register obj = ToRegister(guard->object());
5340 Register temp = ToRegister(guard->temp0());
5341
5342 Label bail;
5343 masm.loadObjClassUnsafe(obj, temp);
5344 masm.branchIfClassIsNotNonResizableTypedArray(temp, &bail);
5345 bailoutFrom(&bail, guard->snapshot());
5346}
5347
5348void CodeGenerator::visitGuardIsResizableTypedArray(
5349 LGuardIsResizableTypedArray* guard) {
5350 Register obj = ToRegister(guard->object());
5351 Register temp = ToRegister(guard->temp0());
5352
5353 Label bail;
5354 masm.loadObjClassUnsafe(obj, temp);
5355 masm.branchIfClassIsNotResizableTypedArray(temp, &bail);
5356 bailoutFrom(&bail, guard->snapshot());
5357}
5358
5359void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) {
5360 Register obj = ToRegister(guard->object());
5361
5362 Label bail;
5363
5364 Address handlerAddr(obj, ProxyObject::offsetOfHandler());
5365 masm.branchPtr(Assembler::NotEqual, handlerAddr,
5366 ImmPtr(guard->mir()->handler()), &bail);
5367
5368 bailoutFrom(&bail, guard->snapshot());
5369}
5370
5371void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) {
5372 Register input = ToRegister(guard->input());
5373 Register expected = ToRegister(guard->expected());
5374
5375 Assembler::Condition cond =
5376 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
5377 bailoutCmpPtr(cond, input, expected, guard->snapshot());
5378}
5379
5380void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) {
5381 Register input = ToRegister(guard->input());
5382 Register expected = ToRegister(guard->expected());
5383
5384 bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot());
5385}
5386
5387void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) {
5388 Register str = ToRegister(guard->str());
5389 Register scratch = ToRegister(guard->temp0());
5390
5391 LiveRegisterSet volatileRegs = liveVolatileRegs(guard);
5392 volatileRegs.takeUnchecked(scratch);
5393
5394 Label bail;
5395 masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs,
5396 &bail);
5397 bailoutFrom(&bail, guard->snapshot());
5398}
5399
5400void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) {
5401 Register symbol = ToRegister(guard->symbol());
5402
5403 bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()),
5404 guard->snapshot());
5405}
5406
5407void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) {
5408 Register num = ToRegister(guard->num());
5409
5410 bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()),
5411 guard->snapshot());
5412}
5413
5414void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) {
5415 Register str = ToRegister(lir->string());
5416 Register output = ToRegister(lir->output());
5417
5418 Label vmCall, done;
5419 masm.loadStringIndexValue(str, output, &vmCall);
5420 masm.jump(&done);
5421
5422 {
5423 masm.bind(&vmCall);
5424
5425 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5426 volatileRegs.takeUnchecked(output);
5427 masm.PushRegsInMask(volatileRegs);
5428
5429 using Fn = int32_t (*)(JSString* str);
5430 masm.setupAlignedABICall();
5431 masm.passABIArg(str);
5432 masm.callWithABI<Fn, GetIndexFromString>();
5433 masm.storeCallInt32Result(output);
5434
5435 masm.PopRegsInMask(volatileRegs);
5436
5437 // GetIndexFromString returns a negative value on failure.
5438 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
5439 }
5440
5441 masm.bind(&done);
5442}
5443
5444void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) {
5445 Register str = ToRegister(lir->string());
5446 Register output = ToRegister(lir->output());
5447 Register temp = ToRegister(lir->temp0());
5448
5449 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5450
5451 Label bail;
5452 masm.guardStringToInt32(str, output, temp, volatileRegs, &bail);
5453 bailoutFrom(&bail, lir->snapshot());
5454}
5455
5456void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) {
5457 Register str = ToRegister(lir->string());
5458 FloatRegister output = ToFloatRegister(lir->output());
5459 Register temp0 = ToRegister(lir->temp0());
5460 Register temp1 = ToRegister(lir->temp1());
5461
5462 Label vmCall, done;
5463 // Use indexed value as fast path if possible.
5464 masm.loadStringIndexValue(str, temp0, &vmCall);
5465 masm.convertInt32ToDouble(temp0, output);
5466 masm.jump(&done);
5467 {
5468 masm.bind(&vmCall);
5469
5470 // Reserve stack for holding the result value of the call.
5471 masm.reserveStack(sizeof(double));
5472 masm.moveStackPtrTo(temp0);
5473
5474 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5475 volatileRegs.takeUnchecked(temp0);
5476 volatileRegs.takeUnchecked(temp1);
5477 masm.PushRegsInMask(volatileRegs);
5478
5479 using Fn = bool (*)(JSContext* cx, JSString* str, double* result);
5480 masm.setupAlignedABICall();
5481 masm.loadJSContext(temp1);
5482 masm.passABIArg(temp1);
5483 masm.passABIArg(str);
5484 masm.passABIArg(temp0);
5485 masm.callWithABI<Fn, StringToNumberPure>();
5486 masm.storeCallPointerResult(temp0);
5487
5488 masm.PopRegsInMask(volatileRegs);
5489
5490 Label ok;
5491 masm.branchIfTrueBool(temp0, &ok);
5492 {
5493 // OOM path, recovered by StringToNumberPure.
5494 //
5495 // Use addToStackPtr instead of freeStack as freeStack tracks stack height
5496 // flow-insensitively, and using it here would confuse the stack height
5497 // tracking.
5498 masm.addToStackPtr(Imm32(sizeof(double)));
5499 bailout(lir->snapshot());
5500 }
5501 masm.bind(&ok);
5502 masm.Pop(output);
5503 }
5504 masm.bind(&done);
5505}
5506
5507void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) {
5508 Register obj = ToRegister(guard->object());
5509 Register temp = ToRegister(guard->temp0());
5510
5511 // Load obj->elements.
5512 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp);
5513
5514 // Make sure there are no dense elements.
5515 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
5516 bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot());
5517}
5518
5519void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) {
5520 Register input = ToRegister(lir->input());
5521 Register64 output = ToOutRegister64(lir);
5522
5523 masm.move32To64ZeroExtend(input, output);
5524}
5525
5526void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input,
5527 Register64 output) {
5528 Register temp = output.scratchReg();
5529
5530 saveLive(lir);
5531
5532 masm.reserveStack(sizeof(uint64_t));
5533 masm.moveStackPtrTo(temp);
5534 pushArg(temp);
5535 pushArg(input);
5536
5537 using Fn = bool (*)(JSContext*, HandleString, uint64_t*);
5538 callVM<Fn, DoStringToInt64>(lir);
5539
5540 masm.load64(Address(masm.getStackPointer(), 0), output);
5541 masm.freeStack(sizeof(uint64_t));
5542
5543 restoreLiveIgnore(lir, StoreValueTo(output).clobbered());
5544}
5545
5546void CodeGenerator::visitStringToInt64(LStringToInt64* lir) {
5547 Register input = ToRegister(lir->input());
5548 Register64 output = ToOutRegister64(lir);
5549
5550 emitStringToInt64(lir, input, output);
5551}
5552
5553void CodeGenerator::visitValueToInt64(LValueToInt64* lir) {
5554 ValueOperand input = ToValue(lir->input());
5555 Register temp = ToRegister(lir->temp0());
5556 Register64 output = ToOutRegister64(lir);
5557
5558 int checks = 3;
5559
5560 Label fail, done;
5561 // Jump to fail if this is the last check and we fail it,
5562 // otherwise to the next test.
5563 auto emitTestAndUnbox = [&](auto testAndUnbox) {
5564 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", 5564); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "checks > 0" ")"); do { MOZ_CrashSequence
(__null, 5564); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5565
5566 checks--;
5567 Label notType;
5568 Label* target = checks ? &notType : &fail;
5569
5570 testAndUnbox(target);
5571
5572 if (checks) {
5573 masm.jump(&done);
5574 masm.bind(&notType);
5575 }
5576 };
5577
5578 Register tag = masm.extractTag(input, temp);
5579
5580 // BigInt.
5581 emitTestAndUnbox([&](Label* target) {
5582 masm.branchTestBigInt(Assembler::NotEqual, tag, target);
5583 masm.unboxBigInt(input, temp);
5584 masm.loadBigInt64(temp, output);
5585 });
5586
5587 // Boolean
5588 emitTestAndUnbox([&](Label* target) {
5589 masm.branchTestBoolean(Assembler::NotEqual, tag, target);
5590 masm.unboxBoolean(input, temp);
5591 masm.move32To64ZeroExtend(temp, output);
5592 });
5593
5594 // String
5595 emitTestAndUnbox([&](Label* target) {
5596 masm.branchTestString(Assembler::NotEqual, tag, target);
5597 masm.unboxString(input, temp);
5598 emitStringToInt64(lir, temp, output);
5599 });
5600
5601 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"
, 5601); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0"
")"); do { MOZ_CrashSequence(__null, 5601); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5602
5603 bailoutFrom(&fail, lir->snapshot());
5604 masm.bind(&done);
5605}
5606
5607void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) {
5608 Register operand = ToRegister(lir->input());
5609 Register64 output = ToOutRegister64(lir);
5610
5611 masm.loadBigInt64(operand, output);
5612}
5613
5614OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir,
5615 Scalar::Type type,
5616 Register64 input,
5617 Register output) {
5618#if JS_BITS_PER_WORD64 == 32
5619 using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t);
5620 auto args = ArgList(input.low, input.high);
5621#else
5622 using Fn = BigInt* (*)(JSContext*, uint64_t);
5623 auto args = ArgList(input);
5624#endif
5625
5626 if (type == Scalar::BigInt64) {
5627 return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args,
5628 StoreRegisterTo(output));
5629 }
5630 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", 5630); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == Scalar::BigUint64" ")"); do { MOZ_CrashSequence
(__null, 5630); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5631 return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args,
5632 StoreRegisterTo(output));
5633}
5634
5635void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type,
5636 Register64 input, Register output,
5637 Register maybeTemp,
5638 Register64 maybeTemp64) {
5639 OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output);
5640
5641 if (maybeTemp != InvalidReg) {
5642 masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry());
5643 } else {
5644 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
5645 regs.take(input);
5646 regs.take(output);
5647
5648 Register temp = regs.takeAny();
5649
5650 masm.push(temp);
5651
5652 Label fail, ok;
5653 masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail);
5654 masm.pop(temp);
5655 masm.jump(&ok);
5656 masm.bind(&fail);
5657 masm.pop(temp);
5658 masm.jump(ool->entry());
5659 masm.bind(&ok);
5660 }
5661 masm.initializeBigInt64(type, output, input, maybeTemp64);
5662 masm.bind(ool->rejoin());
5663}
5664
5665void CodeGenerator::emitCallMegamorphicGetter(
5666 LInstruction* lir, ValueOperand accessorAndOutput, Register obj,
5667 Register calleeScratch, Register argcScratch, Label* nullGetter) {
5668 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", 5668); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "calleeScratch == IonGenericCallCalleeReg" ")"
); do { MOZ_CrashSequence(__null, 5668); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5669 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"
, 5669); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argcScratch == IonGenericCallArgcReg"
")"); do { MOZ_CrashSequence(__null, 5669); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5670
5671 masm.unboxNonDouble(accessorAndOutput, calleeScratch,
5672 JSVAL_TYPE_PRIVATE_GCTHING);
5673
5674 masm.loadPtr(Address(calleeScratch, GetterSetter::offsetOfGetter()),
5675 calleeScratch);
5676 masm.branchTestPtr(Assembler::Zero, calleeScratch, calleeScratch, nullGetter);
5677
5678 if (JitStackValueAlignment > 1) {
5679 masm.reserveStack(sizeof(Value) * (JitStackValueAlignment - 1));
5680 }
5681 masm.pushValue(JSVAL_TYPE_OBJECT, obj);
5682
5683 masm.checkStackAlignment();
5684
5685 masm.move32(Imm32(0), argcScratch);
5686 ensureOsiSpace();
5687
5688 TrampolinePtr genericCallStub =
5689 gen->jitRuntime()->getIonGenericCallStub(IonGenericCallKind::Call);
5690 uint32_t callOffset = masm.callJit(genericCallStub);
5691 markSafepointAt(callOffset, lir);
5692
5693 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5694
5695 masm.moveValue(JSReturnOperand, accessorAndOutput);
5696
5697 masm.setFramePushed(frameSize());
5698 emitRestoreStackPointerFromFP();
5699}
5700
5701void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) {
5702 Register64 input = ToRegister64(lir->input());
5703 Register64 temp = ToRegister64(lir->temp0());
5704 Register output = ToRegister(lir->output());
5705
5706 emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp.scratchReg(),
5707 temp);
5708}
5709
5710void CodeGenerator::visitUint64ToBigInt(LUint64ToBigInt* lir) {
5711 Register64 input = ToRegister64(lir->input());
5712 Register temp = ToRegister(lir->temp0());
5713 Register output = ToRegister(lir->output());
5714
5715 emitCreateBigInt(lir, Scalar::BigUint64, input, output, temp);
5716}
5717
5718void CodeGenerator::visitInt64ToIntPtr(LInt64ToIntPtr* lir) {
5719 Register64 input = ToRegister64(lir->input());
5720#ifdef JS_64BIT1
5721 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", 5721); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "input.reg == ToRegister(lir->output())"
")"); do { MOZ_CrashSequence(__null, 5721); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5722#else
5723 Register output = ToRegister(lir->output());
5724#endif
5725
5726 Label bail;
5727 if (lir->mir()->isSigned()) {
5728 masm.branchInt64NotInPtrRange(input, &bail);
5729 } else {
5730 masm.branchUInt64NotInPtrRange(input, &bail);
5731 }
5732 bailoutFrom(&bail, lir->snapshot());
5733
5734#ifndef JS_64BIT1
5735 masm.move64To32(input, output);
5736#endif
5737}
5738
5739void CodeGenerator::visitIntPtrToInt64(LIntPtrToInt64* lir) {
5740#ifdef JS_64BIT1
5741 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", 5741); AnnotateMozCrashReason
("MOZ_CRASH(" "Not used on 64-bit platforms" ")"); do { MOZ_CrashSequence
(__null, 5741); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
5742#else
5743 Register input = ToRegister(lir->input());
5744 Register64 output = ToOutRegister64(lir);
5745
5746 masm.move32To64SignExtend(input, output);
5747#endif
5748}
5749
5750Address CodeGenerator::getNurseryValueAddress(ValueOrNurseryValueIndex val,
5751 Register reg) {
5752 // Move the address of the Value stored in the IonScript into |reg|.
5753 uint32_t nurseryIndex = val.toNurseryValueIndex();
5754 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), reg);
5755 masm.propagateOOM(nurseryValueLabels_.emplaceBack(label, nurseryIndex));
5756 return Address(reg, 0);
5757}
5758
5759void CodeGenerator::visitGuardValue(LGuardValue* lir) {
5760 ValueOperand input = ToValue(lir->input());
5761 Register temp = ToTempRegisterOrInvalid(lir->temp0());
5762 ValueOrNurseryValueIndex expected = lir->mir()->expected();
5763
5764 Label bail;
5765 if (expected.isValue()) {
5766 Value expectedVal = expected.toValue();
5767 if (expectedVal.isNaN()) {
5768 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", 5768); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 5768); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5769 masm.branchTestNaNValue(Assembler::NotEqual, input, temp, &bail);
5770 } else {
5771 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", 5771); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp == InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 5771); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5772 masm.branchTestValue(Assembler::NotEqual, input, expectedVal, &bail);
5773 }
5774 } else {
5775 // Compare to the Value stored in IonScript's nursery values list.
5776 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", 5776); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 5776); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5777 Address valueAddr = getNurseryValueAddress(expected, temp);
5778 masm.branchTestValue(Assembler::NotEqual, valueAddr, input, &bail);
5779 }
5780
5781 bailoutFrom(&bail, lir->snapshot());
5782}
5783
5784void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) {
5785 ValueOperand input = ToValue(lir->value());
5786
5787 ScratchTagScope tag(masm, input);
5788 masm.splitTagForTest(input, tag);
5789
5790 Label done;
5791 masm.branchTestNull(Assembler::Equal, tag, &done);
5792
5793 Label bail;
5794 masm.branchTestUndefined(Assembler::NotEqual, tag, &bail);
5795 bailoutFrom(&bail, lir->snapshot());
5796
5797 masm.bind(&done);
5798}
5799
5800void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) {
5801 ValueOperand input = ToValue(lir->value());
5802
5803 Label bail;
5804 masm.branchTestObject(Assembler::Equal, input, &bail);
5805 bailoutFrom(&bail, lir->snapshot());
5806}
5807
5808void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) {
5809 Register function = ToRegister(lir->function());
5810
5811 Label bail;
5812 if (uint16_t flags = lir->mir()->expectedFlags()) {
5813 masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail);
5814 }
5815 if (uint16_t flags = lir->mir()->unexpectedFlags()) {
5816 masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail);
5817 }
5818 bailoutFrom(&bail, lir->snapshot());
5819}
5820
5821void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor(
5822 LGuardFunctionIsNonBuiltinCtor* lir) {
5823 Register function = ToRegister(lir->function());
5824 Register temp = ToRegister(lir->temp0());
5825
5826 Label bail;
5827 masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail);
5828 bailoutFrom(&bail, lir->snapshot());
5829}
5830
5831void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) {
5832 Register function = ToRegister(lir->function());
5833 Register temp = ToRegister(lir->temp0());
5834
5835 Assembler::Condition cond =
5836 lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
5837
5838 Label bail;
5839 masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail);
5840 bailoutFrom(&bail, lir->snapshot());
5841}
5842
5843void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) {
5844 Register function = ToRegister(lir->function());
5845
5846 Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript());
5847 bailoutCmpPtr(Assembler::NotEqual, scriptAddr,
5848 ImmGCPtr(lir->mir()->expected()), lir->snapshot());
5849}
5850
5851// Out-of-line path to update the store buffer.
5852class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> {
5853 LInstruction* lir_;
5854 const LAllocation* object_;
5855
5856 public:
5857 OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object)
5858 : lir_(lir), object_(object) {}
5859
5860 void accept(CodeGenerator* codegen) override {
5861 codegen->visitOutOfLineCallPostWriteBarrier(this);
5862 }
5863
5864 LInstruction* lir() const { return lir_; }
5865 const LAllocation* object() const { return object_; }
5866};
5867
5868static void EmitStoreBufferCheckForConstant(MacroAssembler& masm,
5869 const gc::TenuredCell* cell,
5870 AllocatableGeneralRegisterSet& regs,
5871 Label* exit, Label* callVM) {
5872 Register temp = regs.takeAny();
5873
5874 gc::Arena* arena = cell->arena();
5875
5876 Register cells = temp;
5877 masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells);
5878
5879 size_t index = gc::ArenaCellSet::getCellIndex(cell);
5880 auto [word, mask] = gc::ArenaCellSet::getWordIndexAndMask(index);
5881 size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t);
5882
5883 masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask),
5884 exit);
5885
5886 // Check whether this is the sentinel set and if so call the VM to allocate
5887 // one for this arena.
5888 masm.branchPtr(Assembler::Equal,
5889 Address(cells, gc::ArenaCellSet::offsetOfArena()),
5890 ImmPtr(nullptr), callVM);
5891
5892 // Add the cell to the set.
5893 masm.or32(Imm32(mask), Address(cells, offset));
5894 masm.jump(exit);
5895
5896 regs.add(temp);
5897}
5898
5899static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime,
5900 Register objreg, JSObject* maybeConstant,
5901 bool isGlobal,
5902 AllocatableGeneralRegisterSet& regs) {
5903 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", 5903); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "maybeConstant" ")"); do { MOZ_CrashSequence
(__null, 5903); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
5904
5905 Label callVM;
5906 Label exit;
5907
5908 Register temp = regs.takeAny();
5909
5910 // We already have a fast path to check whether a global is in the store
5911 // buffer.
5912 if (!isGlobal) {
5913 if (maybeConstant) {
5914 // Check store buffer bitmap directly for known object.
5915 EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs,
5916 &exit, &callVM);
5917 } else {
5918 // Check one element cache to avoid VM call.
5919 masm.branchPtr(Assembler::Equal,
5920 AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()),
5921 objreg, &exit);
5922 }
5923 }
5924
5925 // Call into the VM to barrier the write.
5926 masm.bind(&callVM);
5927
5928 Register runtimereg = temp;
5929 masm.mov(ImmPtr(runtime), runtimereg);
5930
5931 masm.setupAlignedABICall();
5932 masm.passABIArg(runtimereg);
5933 masm.passABIArg(objreg);
5934 if (isGlobal) {
5935 using Fn = void (*)(JSRuntime* rt, GlobalObject* obj);
5936 masm.callWithABI<Fn, PostGlobalWriteBarrier>();
5937 } else {
5938 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj);
5939 masm.callWithABI<Fn, PostWriteBarrier>();
5940 }
5941
5942 masm.bind(&exit);
5943}
5944
5945void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) {
5946 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5947
5948 Register objreg;
5949 JSObject* object = nullptr;
5950 bool isGlobal = false;
5951 if (obj->isConstant()) {
5952 object = &obj->toConstant()->toObject();
5953 isGlobal = isGlobalObject(object);
5954 objreg = regs.takeAny();
5955 masm.movePtr(ImmGCPtr(object), objreg);
5956 } else {
5957 objreg = ToRegister(obj);
5958 regs.takeUnchecked(objreg);
5959 }
5960
5961 EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs);
5962}
5963
5964void CodeGenerator::emitElementPostWriteBarrier(
5965 MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj,
5966 Register index, Register scratch, const ConstantOrRegister& val,
5967 int32_t indexDiff) {
5968 if (val.constant()) {
5969 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"
, 5970); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { MOZ_CrashSequence(__null, 5970); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5970 !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"
, 5970); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { MOZ_CrashSequence(__null, 5970); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5971 return;
5972 }
5973
5974 TypedOrValueRegister reg = val.reg();
5975 if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) {
5976 return;
5977 }
5978
5979 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
5980 masm.PushRegsInMask(liveVolatileRegs);
5981
5982 if (indexDiff != 0) {
5983 masm.add32(Imm32(indexDiff), index);
5984 }
5985
5986 masm.setupUnalignedABICall(scratch);
5987 masm.movePtr(ImmPtr(gen->runtime), scratch);
5988 masm.passABIArg(scratch);
5989 masm.passABIArg(obj);
5990 masm.passABIArg(index);
5991 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
5992 masm.callWithABI<Fn, PostWriteElementBarrier>();
5993
5994 // We don't need a sub32 here because index must be in liveVolatileRegs
5995 // if indexDiff is not zero, so it will be restored below.
5996 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", 5996); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "liveVolatileRegs.has(index)" ")"); do { MOZ_CrashSequence
(__null, 5996); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
5997
5998 masm.PopRegsInMask(liveVolatileRegs);
5999
6000 masm.jump(ool.rejoin());
6001 });
6002 addOutOfLineCode(ool, mir);
6003
6004 if (reg.hasValue()) {
6005 masm.branchValueIsNurseryCell(Assembler::NotEqual, reg.valueReg(), scratch,
6006 ool->rejoin());
6007 } else {
6008 masm.branchPtrInNurseryChunk(Assembler::NotEqual, reg.typedReg().gpr(),
6009 scratch, ool->rejoin());
6010 }
6011 masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, scratch, ool->entry());
6012
6013 masm.bind(ool->rejoin());
6014}
6015
6016void CodeGenerator::emitPostWriteBarrier(Register objreg) {
6017 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
6018 regs.takeUnchecked(objreg);
6019 EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs);
6020}
6021
6022void CodeGenerator::visitOutOfLineCallPostWriteBarrier(
6023 OutOfLineCallPostWriteBarrier* ool) {
6024 saveLiveVolatile(ool->lir());
6025 const LAllocation* obj = ool->object();
6026 emitPostWriteBarrier(obj);
6027 restoreLiveVolatile(ool->lir());
6028
6029 masm.jump(ool->rejoin());
6030}
6031
6032void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal,
6033 OutOfLineCode* ool) {
6034 // Check whether an object is a global that we have already barriered before
6035 // calling into the VM.
6036 //
6037 // We only check for the script's global, not other globals within the same
6038 // compartment, because we bake in a pointer to realm->globalWriteBarriered
6039 // and doing that would be invalid for other realms because they could be
6040 // collected before the Ion code is discarded.
6041
6042 if (!maybeGlobal->isConstant()) {
6043 return;
6044 }
6045
6046 JSObject* obj = &maybeGlobal->toConstant()->toObject();
6047 if (gen->realm->maybeGlobal() != obj) {
6048 return;
6049 }
6050
6051 const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered();
6052 masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0),
6053 ool->rejoin());
6054}
6055
6056template <class LPostBarrierType, MIRType nurseryType>
6057void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir,
6058 OutOfLineCode* ool) {
6059 static_assert(NeedsPostBarrier(nurseryType));
6060
6061 addOutOfLineCode(ool, lir->mir());
6062
6063 Register temp = ToTempRegisterOrInvalid(lir->temp0());
6064
6065 if (lir->object()->isConstant()) {
6066 // The object must be tenured because MIR and LIR can't contain nursery
6067 // pointers.
6068 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", 6068); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { MOZ_CrashSequence(__null, 6068); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6069 } else {
6070 masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()),
6071 temp, ool->rejoin());
6072 }
6073
6074 maybeEmitGlobalBarrierCheck(lir->object(), ool);
6075
6076 Register value = ToRegister(lir->value());
6077 if constexpr (nurseryType == MIRType::Object) {
6078 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"
, 6078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 6078); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6079 } else if constexpr (nurseryType == MIRType::String) {
6080 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"
, 6080); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 6080); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6081 } else {
6082 static_assert(nurseryType == MIRType::BigInt);
6083 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"
, 6083); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 6083); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6084 }
6085 masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry());
6086
6087 masm.bind(ool->rejoin());
6088}
6089
6090template <class LPostBarrierType>
6091void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir,
6092 OutOfLineCode* ool) {
6093 addOutOfLineCode(ool, lir->mir());
6094
6095 Register temp = ToTempRegisterOrInvalid(lir->temp0());
6096
6097 maybeEmitGlobalBarrierCheck(lir->object(), ool);
6098
6099 ValueOperand value = ToValue(lir->value());
6100 if (lir->object()->isConstant()) {
6101 // The object must be tenured because MIR and LIR can't contain nursery
6102 // pointers.
6103 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", 6103); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { MOZ_CrashSequence(__null, 6103); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6104 masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry());
6105 } else {
6106 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp,
6107 ool->rejoin());
6108 masm.branchPtrInNurseryChunk(Assembler::NotEqual, ToRegister(lir->object()),
6109 temp, ool->entry());
6110 }
6111
6112 masm.bind(ool->rejoin());
6113}
6114
6115void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) {
6116 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
6117 visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool);
6118}
6119
6120void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) {
6121 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
6122 visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool);
6123}
6124
6125void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) {
6126 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
6127 visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool);
6128}
6129
6130void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) {
6131 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
6132 visitPostWriteBarrierCommonV(lir, ool);
6133}
6134
6135// Out-of-line path to update the store buffer.
6136class OutOfLineCallPostWriteElementBarrier
6137 : public OutOfLineCodeBase<CodeGenerator> {
6138 LInstruction* lir_;
6139 const LAllocation* object_;
6140 const LAllocation* index_;
6141
6142 public:
6143 OutOfLineCallPostWriteElementBarrier(LInstruction* lir,
6144 const LAllocation* object,
6145 const LAllocation* index)
6146 : lir_(lir), object_(object), index_(index) {}
6147
6148 void accept(CodeGenerator* codegen) override {
6149 codegen->visitOutOfLineCallPostWriteElementBarrier(this);
6150 }
6151
6152 LInstruction* lir() const { return lir_; }
6153
6154 const LAllocation* object() const { return object_; }
6155
6156 const LAllocation* index() const { return index_; }
6157};
6158
6159void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier(
6160 OutOfLineCallPostWriteElementBarrier* ool) {
6161 saveLiveVolatile(ool->lir());
6162
6163 const LAllocation* obj = ool->object();
6164 const LAllocation* index = ool->index();
6165
6166 Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj);
6167 Register indexreg = ToRegister(index);
6168
6169 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
6170 regs.takeUnchecked(indexreg);
6171
6172 if (obj->isConstant()) {
6173 objreg = regs.takeAny();
6174 masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg);
6175 } else {
6176 regs.takeUnchecked(objreg);
6177 }
6178
6179 Register runtimereg = regs.takeAny();
6180 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
6181 masm.setupAlignedABICall();
6182 masm.mov(ImmPtr(gen->runtime), runtimereg);
6183 masm.passABIArg(runtimereg);
6184 masm.passABIArg(objreg);
6185 masm.passABIArg(indexreg);
6186 masm.callWithABI<Fn, PostWriteElementBarrier>();
6187
6188 restoreLiveVolatile(ool->lir());
6189
6190 masm.jump(ool->rejoin());
6191}
6192
6193void CodeGenerator::visitPostWriteElementBarrierO(
6194 LPostWriteElementBarrierO* lir) {
6195 auto ool = new (alloc())
6196 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
6197 visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir,
6198 ool);
6199}
6200
6201void CodeGenerator::visitPostWriteElementBarrierS(
6202 LPostWriteElementBarrierS* lir) {
6203 auto ool = new (alloc())
6204 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
6205 visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir,
6206 ool);
6207}
6208
6209void CodeGenerator::visitPostWriteElementBarrierBI(
6210 LPostWriteElementBarrierBI* lir) {
6211 auto ool = new (alloc())
6212 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
6213 visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir,
6214 ool);
6215}
6216
6217void CodeGenerator::visitPostWriteElementBarrierV(
6218 LPostWriteElementBarrierV* lir) {
6219 auto ool = new (alloc())
6220 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
6221 visitPostWriteBarrierCommonV(lir, ool);
6222}
6223
6224void CodeGenerator::visitAssertCanElidePostWriteBarrier(
6225 LAssertCanElidePostWriteBarrier* lir) {
6226 Register object = ToRegister(lir->object());
6227 ValueOperand value = ToValue(lir->value());
6228 Register temp = ToRegister(lir->temp0());
6229
6230 Label ok;
6231 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok);
6232 masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok);
6233
6234 masm.assumeUnreachable("Unexpected missing post write barrier");
6235
6236 masm.bind(&ok);
6237}
6238
6239template <typename LCallIns>
6240void CodeGenerator::emitCallNative(LCallIns* call, JSNative native,
6241 Register argContextReg, Register argUintNReg,
6242 Register argVpReg, Register tempReg,
6243 uint32_t unusedStack) {
6244 masm.checkStackAlignment();
6245
6246 // Native functions have the signature:
6247 // bool (*)(JSContext*, unsigned, Value* vp)
6248 // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
6249 // are the function arguments.
6250
6251 // Allocate space for the outparam, moving the StackPointer to what will be
6252 // &vp[1].
6253 masm.adjustStack(unusedStack);
6254
6255 // Push a Value containing the callee object: natives are allowed to access
6256 // their callee before setting the return value. The StackPointer is moved
6257 // to &vp[0].
6258 //
6259 // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|.
6260 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
6261 Register calleeReg = ToRegister(call->getCallee());
6262 masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg)));
6263
6264 // Enter the callee realm.
6265 if (call->mir()->maybeCrossRealm()) {
6266 masm.switchToObjectRealm(calleeReg, tempReg);
6267 }
6268 } else {
6269 WrappedFunction* target = call->mir()->getSingleTarget();
6270 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
6271
6272 // Enter the callee realm.
6273 if (call->mir()->maybeCrossRealm()) {
6274 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg);
6275 masm.switchToObjectRealm(tempReg, tempReg);
6276 }
6277 }
6278
6279 // Preload arguments into registers.
6280 masm.loadJSContext(argContextReg);
6281 masm.moveStackPtrTo(argVpReg);
6282
6283 // Initialize |NativeExitFrameLayout::savedCallee_|.
6284 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
6285 masm.Push(ToRegister(call->getCallee()));
6286 } else {
6287 masm.Push(ImmGCPtr(call->mir()->getSingleTarget()->rawNativeJSFunction()));
6288 }
6289
6290 // Initialize |NativeExitFrameLayout::argc_|.
6291 masm.Push(argUintNReg);
6292
6293 // Construct native exit frame.
6294 //
6295 // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and
6296 // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|.
6297 //
6298 // The NativeExitFrameLayout is now fully initialized.
6299 uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg);
6300 masm.enterFakeExitFrameForNative(argContextReg, tempReg,
6301 call->mir()->isConstructing());
6302
6303 markSafepointAt(safepointOffset, call);
6304
6305 // Construct and execute call.
6306 masm.setupAlignedABICall();
6307 masm.passABIArg(argContextReg);
6308 masm.passABIArg(argUintNReg);
6309 masm.passABIArg(argVpReg);
6310
6311 ensureOsiSpace();
6312 // If we're using a simulator build, `native` will already point to the
6313 // simulator's call-redirection code for LCallClassHook. Load the address in
6314 // a register first so that we don't try to redirect it a second time.
6315 bool emittedCall = false;
6316#ifdef JS_SIMULATOR
6317 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
6318 masm.movePtr(ImmPtr(native), tempReg);
6319 masm.callWithABI(tempReg);
6320 emittedCall = true;
6321 }
6322#endif
6323 if (!emittedCall) {
6324 masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General,
6325 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
6326 }
6327
6328 // Test for failure.
6329 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
6330
6331 // Exit the callee realm.
6332 if (call->mir()->maybeCrossRealm()) {
6333 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6334 }
6335
6336 // Load the outparam vp[0] into output register(s).
6337 masm.loadValue(
6338 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
6339 JSReturnOperand);
6340
6341 // Until C++ code is instrumented against Spectre, prevent speculative
6342 // execution from returning any private data.
6343 if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() &&
6344 call->mir()->hasLiveDefUses()) {
6345 masm.speculationBarrier();
6346 }
6347
6348#ifdef DEBUG1
6349 // Native constructors are guaranteed to return an Object value.
6350 if (call->mir()->isConstructing()) {
6351 Label notPrimitive;
6352 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6353 &notPrimitive);
6354 masm.assumeUnreachable("native constructors don't return primitives");
6355 masm.bind(&notPrimitive);
6356 }
6357#endif
6358}
6359
6360template <typename LCallIns>
6361void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) {
6362 uint32_t unusedStack =
6363 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6364
6365 // Registers used for callWithABI() argument-passing.
6366 const Register argContextReg = ToRegister(call->getArgContextReg());
6367 const Register argUintNReg = ToRegister(call->getArgUintNReg());
6368 const Register argVpReg = ToRegister(call->getArgVpReg());
6369
6370 // Misc. temporary registers.
6371 const Register tempReg = ToRegister(call->getTempReg());
6372
6373 DebugOnly<uint32_t> initialStack = masm.framePushed();
6374
6375 // Initialize the argc register.
6376 masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg);
6377
6378 // Create the exit frame and call the native.
6379 emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg,
6380 unusedStack);
6381
6382 // The next instruction is removing the footer of the exit frame, so there
6383 // is no need for leaveFakeExitFrame.
6384
6385 // Move the StackPointer back to its original location, unwinding the native
6386 // exit frame.
6387 masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack);
6388 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"
, 6388); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { MOZ_CrashSequence(__null, 6388); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6389}
6390
6391void CodeGenerator::visitCallNative(LCallNative* call) {
6392 WrappedFunction* target = call->getSingleTarget();
6393 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"
, 6393); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 6393); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6394 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"
, 6394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 6394); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6395
6396 JSNative native = target->native();
6397 if (call->ignoresReturnValue() && target->hasJitInfo()) {
6398 const JSJitInfo* jitInfo = target->jitInfo();
6399 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
6400 native = jitInfo->ignoresReturnValueMethod;
6401 }
6402 }
6403 emitCallNative(call, native);
6404}
6405
6406void CodeGenerator::visitCallClassHook(LCallClassHook* call) {
6407 emitCallNative(call, call->mir()->target());
6408}
6409
6410static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv,
6411 DOMObjectKind kind) {
6412 // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This
6413 // will be in the first slot but may be fixed or non-fixed.
6414 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"
, 6414); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv"
")"); do { MOZ_CrashSequence(__null, 6414); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6415
6416 switch (kind) {
6417 case DOMObjectKind::Native:
6418 // If it's a native object, the value must be in a fixed slot.
6419 // See CanAttachDOMCall in CacheIR.cpp.
6420 masm.debugAssertObjHasFixedSlots(obj, priv);
6421 masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv);
6422 break;
6423 case DOMObjectKind::Proxy: {
6424#ifdef DEBUG1
6425 // Sanity check: it must be a DOM proxy.
6426 Label isDOMProxy;
6427 masm.branchTestProxyHandlerFamily(
6428 Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy);
6429 masm.assumeUnreachable("Expected a DOM proxy");
6430 masm.bind(&isDOMProxy);
6431#endif
6432 masm.loadPrivate(Address(obj, ProxyObject::offsetOfReservedSlot(0)),
6433 priv);
6434 break;
6435 }
6436 }
6437}
6438
6439void CodeGenerator::maybeEmitSetInPureCall(MInstruction* mir, bool value,
6440 Register scratch) {
6441 AliasSet aliasSet = mir->getAliasSet();
6442 if (aliasSet.isStore() && (aliasSet.flags() & AliasSet::ObjectFields) != 0) {
6443 // If the alias set already indicates that this can reallocate slots, the
6444 // flag is unnecessary.
6445 return;
6446 }
6447 const void* addr = gen->jitRuntime()->addressOfInPureCall();
6448 masm.move32(Imm32(value), scratch);
6449 masm.store32(scratch, AbsoluteAddress(addr));
6450}
6451
6452void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) {
6453 WrappedFunction* target = call->getSingleTarget();
6454 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"
, 6454); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 6454); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6455 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"
, 6455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 6455); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6456 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", 6456); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->hasJitInfo()" ")"); do { MOZ_CrashSequence
(__null, 6456); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6457 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"
, 6457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()"
")"); do { MOZ_CrashSequence(__null, 6457); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6458
6459 // CanAttachDOMCall only optimizes DOM calls for same-realm targets. This
6460 // means that IonDOMMethod native exit frames don't need a copy of the callee.
6461 // See NativeExitFrameLayout::savedCallee_.
6462 MOZ_RELEASE_ASSERT(!call->mir()->maybeCrossRealm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!call->mir()->maybeCrossRealm())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(!call->mir()->maybeCrossRealm()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!call->mir()->maybeCrossRealm()"
, "./../../../../js/src/jit/CodeGenerator.cpp", 6462); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "!call->mir()->maybeCrossRealm()"
")"); do { MOZ_CrashSequence(__null, 6462); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6463
6464 int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6465
6466 // Registers used for callWithABI() argument-passing.
6467 const Register argJSContext = ToRegister(call->getArgJSContext());
6468 const Register argObj = ToRegister(call->getArgObj());
6469 const Register argPrivate = ToRegister(call->getArgPrivate());
6470 const Register argArgs = ToRegister(call->getArgArgs());
6471
6472 DebugOnly<uint32_t> initialStack = masm.framePushed();
6473
6474 masm.checkStackAlignment();
6475
6476 // DOM methods have the signature:
6477 // bool (*)(JSContext*, HandleObject, void* private, const
6478 // JSJitMethodCallArgs& args)
6479 // Where args is initialized from an argc and a vp, vp[0] is space for an
6480 // outparam and the callee, vp[1] is |this|, and vp[2] onward are the
6481 // function arguments. Note that args stores the argv, not the vp, and
6482 // argv == vp + 2.
6483
6484 // Nestle the stack up against the pushed arguments, leaving StackPointer at
6485 // &vp[1]
6486 masm.adjustStack(unusedStack);
6487 // argObj is filled with the extracted object, then returned.
6488 Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj);
6489 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", 6489); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj == argObj" ")"); do { MOZ_CrashSequence
(__null, 6489); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6490
6491 // Push a Value containing the callee object: natives are allowed to access
6492 // their callee before setting the return value. After this the StackPointer
6493 // points to &vp[0].
6494 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
6495
6496 // Now compute the argv value. Since StackPointer is pointing to &vp[0] and
6497 // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
6498 // StackPointer.
6499 static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
6500 static_assert(JSJitMethodCallArgsTraits::offsetOfArgc ==
6501 IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
6502 masm.computeEffectiveAddress(
6503 Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs);
6504
6505 LoadDOMPrivate(masm, obj, argPrivate,
6506 static_cast<MCallDOMNative*>(call->mir())->objectKind());
6507
6508 // Push argc from the call instruction into what will become the IonExitFrame
6509 masm.Push(Imm32(call->numActualArgs()));
6510
6511 // Push our argv onto the stack
6512 masm.Push(argArgs);
6513 // And store our JSJitMethodCallArgs* in argArgs.
6514 masm.moveStackPtrTo(argArgs);
6515
6516 // Push |this| object for passing HandleObject. We push after argc to
6517 // maintain the same sp-relative location of the object pointer with other
6518 // DOMExitFrames.
6519 masm.Push(argObj);
6520 masm.moveStackPtrTo(argObj);
6521
6522 bool preTenureWrapperAllocation =
6523 call->mir()->to<MCallDOMNative>()->initialHeap() == gc::Heap::Tenured;
6524 if (preTenureWrapperAllocation) {
6525 auto ptr = ImmPtr(mirGen().realm->zone()->tenuringAllocSite());
6526 masm.storeLocalAllocSite(ptr, argJSContext);
6527 }
6528
6529 // Construct native exit frame.
6530 uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext);
6531
6532 masm.loadJSContext(argJSContext);
6533 masm.enterFakeExitFrame(argJSContext, argJSContext,
6534 ExitFrameType::IonDOMMethod);
6535
6536 markSafepointAt(safepointOffset, call);
6537
6538 maybeEmitSetInPureCall(call->mir(), true, /*scratch =*/argJSContext);
6539
6540 // Construct and execute call.
6541 masm.setupAlignedABICall();
6542 masm.loadJSContext(argJSContext);
6543 masm.passABIArg(argJSContext);
6544 masm.passABIArg(argObj);
6545 masm.passABIArg(argPrivate);
6546 masm.passABIArg(argArgs);
6547 ensureOsiSpace();
6548 masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method),
6549 ABIType::General,
6550 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
6551
6552 Register notReturnReg = argJSContext == ReturnReg ? argObj : argJSContext;
6553 maybeEmitSetInPureCall(call->mir(), false, /*scratch =*/notReturnReg);
6554
6555 if (target->jitInfo()->isInfallible) {
6556 masm.loadValue(Address(masm.getStackPointer(),
6557 IonDOMMethodExitFrameLayout::offsetOfResult()),
6558 JSReturnOperand);
6559 } else {
6560 // Test for failure.
6561 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
6562
6563 // Load the outparam vp[0] into output register(s).
6564 masm.loadValue(Address(masm.getStackPointer(),
6565 IonDOMMethodExitFrameLayout::offsetOfResult()),
6566 JSReturnOperand);
6567 }
6568
6569 static_assert(!JSReturnOperand.aliases(ReturnReg),
6570 "Clobbering ReturnReg should not affect the return value");
6571
6572 // Wipe out the preTenuring bit from the local alloc site
6573 // On exception we handle this in C++
6574 if (preTenureWrapperAllocation) {
6575 masm.storeLocalAllocSite(ImmPtr(nullptr), ReturnReg);
6576 }
6577
6578 // Until C++ code is instrumented against Spectre, prevent speculative
6579 // execution from returning any private data.
6580 if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) {
6581 masm.speculationBarrier();
6582 }
6583
6584 // The next instruction is removing the footer of the exit frame, so there
6585 // is no need for leaveFakeExitFrame.
6586
6587 // Move the StackPointer back to its original location, unwinding the native
6588 // exit frame.
6589 masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
6590 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"
, 6590); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { MOZ_CrashSequence(__null, 6590); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6591}
6592
6593void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) {
6594 pushArg(ImmGCPtr(lir->mir()->name()));
6595
6596 using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue);
6597 callVM<Fn, GetIntrinsicValue>(lir);
6598}
6599
6600void CodeGenerator::emitCallInvokeFunction(
6601 LInstruction* call, Register calleereg, bool constructing,
6602 bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) {
6603 // Nestle %esp up to the argument vector.
6604 // Each path must account for framePushed_ separately, for callVM to be valid.
6605 masm.freeStack(unusedStack);
6606
6607 pushArg(masm.getStackPointer()); // argv.
6608 pushArg(Imm32(argc)); // argc.
6609 pushArg(Imm32(ignoresReturnValue));
6610 pushArg(Imm32(constructing)); // constructing.
6611 pushArg(calleereg); // JSFunction*.
6612
6613 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6614 MutableHandleValue);
6615 callVM<Fn, jit::InvokeFunction>(call);
6616
6617 // Un-nestle %esp from the argument vector. No prefix was pushed.
6618 masm.reserveStack(unusedStack);
6619}
6620
6621void CodeGenerator::visitCallGeneric(LCallGeneric* call) {
6622 // The callee is passed straight through to the trampoline.
6623 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", 6623); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg"
")"); do { MOZ_CrashSequence(__null, 6623); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6624
6625 Register argcReg = ToRegister(call->getArgc());
6626 uint32_t unusedStack =
6627 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6628
6629 // Known-target case is handled by LCallKnown.
6630 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", 6630); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!call->hasSingleTarget()" ")"); do { MOZ_CrashSequence
(__null, 6630); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6631
6632 masm.checkStackAlignment();
6633
6634 masm.move32(Imm32(call->numActualArgs()), argcReg);
6635
6636 // Nestle the StackPointer up to the argument vector.
6637 masm.freeStack(unusedStack);
6638 ensureOsiSpace();
6639
6640 auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct
6641 : IonGenericCallKind::Call;
6642
6643 TrampolinePtr genericCallStub =
6644 gen->jitRuntime()->getIonGenericCallStub(kind);
6645 uint32_t callOffset = masm.callJit(genericCallStub);
6646 markSafepointAt(callOffset, call);
6647
6648 if (call->mir()->maybeCrossRealm()) {
6649 static_assert(!JSReturnOperand.aliases(ReturnReg),
6650 "ReturnReg available as scratch after scripted calls");
6651 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6652 }
6653
6654 // If the return value of the constructing function is Primitive,
6655 // replace the return value with the Object from CreateThis.
6656 if (call->mir()->isConstructing()) {
6657 Label notPrimitive;
6658 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6659 &notPrimitive);
6660 size_t thisvOffset =
6661 JitFrameLayout::offsetOfThis() - JitFrameLayout::bytesPoppedAfterCall();
6662 masm.loadValue(Address(masm.getStackPointer(), thisvOffset),
6663 JSReturnOperand);
6664#ifdef DEBUG1
6665 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6666 &notPrimitive);
6667 masm.assumeUnreachable("CreateThis creates an object");
6668#endif
6669 masm.bind(&notPrimitive);
6670 }
6671
6672 // Restore stack pointer.
6673 masm.setFramePushed(frameSize());
6674 emitRestoreStackPointerFromFP();
6675}
6676
6677void JitRuntime::generateIonGenericCallArgumentsShift(
6678 MacroAssembler& masm, Register argc, Register curr, Register end,
6679 Register scratch, Label* done) {
6680 static_assert(sizeof(Value) == 8);
6681 // There are |argc| Values on the stack. Shift them all down by 8 bytes,
6682 // overwriting the first value.
6683
6684 // Initialize `curr` to the destination of the first copy, and `end` to the
6685 // final value of curr.
6686 masm.moveStackPtrTo(curr);
6687 masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end);
6688
6689 Label loop;
6690 masm.bind(&loop);
6691 masm.branchPtr(Assembler::Equal, curr, end, done);
6692 masm.loadPtr(Address(curr, 8), scratch);
6693 masm.storePtr(scratch, Address(curr, 0));
6694 masm.addPtr(Imm32(sizeof(uintptr_t)), curr);
6695 masm.jump(&loop);
6696}
6697
6698void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm,
6699 IonGenericCallKind kind) {
6700 AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub");
6701 ionGenericCallStubOffset_[kind] = startTrampolineCode(masm);
6702
6703 // This code is tightly coupled with visitCallGeneric.
6704 //
6705 // Upon entry:
6706 // IonGenericCallCalleeReg contains a pointer to the callee object.
6707 // IonGenericCallArgcReg contains the number of actual args.
6708 // The arguments have been pushed onto the stack:
6709 // [newTarget] (iff isConstructing)
6710 // [argN]
6711 // ...
6712 // [arg1]
6713 // [arg0]
6714 // [this]
6715 // <return address> (if not JS_USE_LINK_REGISTER)
6716 //
6717 // This trampoline is responsible for entering the callee's realm,
6718 // massaging the stack into the right shape, and then performing a
6719 // tail call. We will return directly to the Ion code from the
6720 // callee.
6721 //
6722 // To do a tail call, we keep the return address in a register, even
6723 // on platforms that don't normally use a link register, and push it
6724 // just before jumping to the callee, after we are done setting up
6725 // the stack.
6726 //
6727 // The caller is responsible for switching back to the caller's
6728 // realm and cleaning up the stack.
6729
6730 Register calleeReg = IonGenericCallCalleeReg;
6731 Register argcReg = IonGenericCallArgcReg;
6732 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
6733 Register scratch = regs.takeAny();
6734 Register scratch2 = regs.takeAny();
6735
6736#ifndef JS_USE_LINK_REGISTER
6737 Register returnAddrReg = IonGenericCallReturnAddrReg;
6738 masm.pop(returnAddrReg);
6739#endif
6740
6741#ifdef JS_CODEGEN_ARM
6742 // The default second scratch register on arm is lr, which we need
6743 // preserved for tail calls.
6744 AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg);
6745#endif
6746
6747 bool isConstructing = kind == IonGenericCallKind::Construct;
6748
6749 Label entry, notFunction, noJitEntry, vmCall;
6750 masm.bind(&entry);
6751
6752 // Guard that the callee is actually a function.
6753 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
6754 calleeReg, &notFunction);
6755
6756 // Guard that the callee supports the [[Call]] or [[Construct]] operation.
6757 // If these tests fail, we will call into the VM to throw an exception.
6758 if (isConstructing) {
6759 masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR,
6760 Assembler::Zero, &vmCall);
6761 } else {
6762 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
6763 calleeReg, scratch, &vmCall);
6764 }
6765
6766 if (isConstructing) {
6767 // Use the slow path if CreateThis was unable to create the |this| object.
6768 Address thisAddr(masm.getStackPointer(), 0);
6769 masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall);
6770 }
6771
6772 // Load jitCodeRaw for callee if it exists.
6773 masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry);
6774
6775 // ****************************
6776 // * Functions with jit entry *
6777 // ****************************
6778
6779 generateIonGenericHandleUnderflow(masm, isConstructing, &vmCall);
6780
6781 masm.switchToObjectRealm(calleeReg, scratch);
6782 masm.loadJitCodeRaw(calleeReg, scratch2);
6783
6784 // Construct the JitFrameLayout.
6785 masm.PushCalleeToken(calleeReg, isConstructing);
6786 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch);
6787#ifndef JS_USE_LINK_REGISTER
6788 masm.push(returnAddrReg);
6789#endif
6790
6791 // Tail call the jit entry.
6792 masm.jump(scratch2);
6793
6794 // ********************
6795 // * Native functions *
6796 // ********************
6797 masm.bind(&noJitEntry);
6798 if (!isConstructing) {
6799 generateIonGenericCallFunCall(masm, &entry, &vmCall);
6800 }
6801 generateIonGenericCallNativeFunction(masm, isConstructing);
6802
6803 // *******************
6804 // * Bound functions *
6805 // *******************
6806 // TODO: support class hooks?
6807 masm.bind(&notFunction);
6808 if (!isConstructing) {
6809 // TODO: support generic bound constructors?
6810 generateIonGenericCallBoundFunction(masm, &entry, &vmCall);
6811 }
6812
6813 // ********************
6814 // * Fallback VM call *
6815 // ********************
6816 masm.bind(&vmCall);
6817
6818 masm.push(masm.getStackPointer()); // argv
6819 masm.push(argcReg); // argc
6820 masm.push(Imm32(false)); // ignores return value
6821 masm.push(Imm32(isConstructing)); // constructing
6822 masm.push(calleeReg); // callee
6823
6824 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6825 MutableHandleValue);
6826 VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id;
6827 uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)];
6828 Label invokeFunctionVMEntry;
6829 bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset);
6830
6831 masm.push(FrameDescriptor(FrameType::IonJS));
6832#ifndef JS_USE_LINK_REGISTER
6833 masm.push(returnAddrReg);
6834#endif
6835 masm.jump(&invokeFunctionVMEntry);
6836}
6837
6838void JitRuntime::generateMegamorphicLoadStub(MacroAssembler& masm) {
6839 AutoCreatedBy acb(masm, "JitRuntime::generateMegamorphicLoadStub");
6840 megamorphicLoadStubOffset_ = startTrampolineCode(masm);
6841
6842 Register obj = CallTempReg3;
6843 Register id = CallTempReg0;
6844 Register idHash = CallTempReg1;
6845 Register outEntryPtr = CallTempReg2;
6846
6847#if defined(JS_NUNBOX32)
6848 auto output = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
6849 static_assert(!JSReturnReg_Type.aliases(CallTempReg2));
6850 static_assert(!JSReturnReg_Data.aliases(CallTempReg2));
6851#else
6852 auto output = ValueOperand(JSReturnReg);
6853 static_assert(!JSReturnReg.aliases(CallTempReg2));
6854#endif
6855
6856 Label cacheHit;
6857 masm.emitMegamorphicCacheLookupByValue(obj, id, idHash, outEntryPtr, output,
6858 &cacheHit);
6859
6860 masm.abiret();
6861
6862 // Given we don't need the entry if we got a cache hit, and the entry pointer
6863 // will never point into the first code page, we can use the low bits of the
6864 // outEntryPtr to indicate success
6865 masm.bind(&cacheHit);
6866 masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHit)), outEntryPtr);
6867 masm.abiret();
6868}
6869
6870void JitRuntime::generateMegamorphicLoadStubPermissive(MacroAssembler& masm) {
6871 AutoCreatedBy acb(masm, "JitRuntime::generateMegamorphicLoadStubPermissive");
6872 megamorphicLoadStubPermissiveOffset_ = startTrampolineCode(masm);
6873
6874 Register obj = CallTempReg3;
6875 Register id = CallTempReg0;
6876 Register idHash = CallTempReg1;
6877 Register outEntryPtr = CallTempReg2;
6878
6879#if defined(JS_NUNBOX32)
6880 auto output = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
6881 static_assert(!JSReturnReg_Type.aliases(CallTempReg2));
6882 static_assert(!JSReturnReg_Data.aliases(CallTempReg2));
6883#else
6884 auto output = ValueOperand(JSReturnReg);
6885 static_assert(!JSReturnReg.aliases(CallTempReg2));
6886#endif
6887
6888 Label cacheHit, cacheHitGetter;
6889 masm.emitMegamorphicCacheLookupByValue(obj, id, idHash, outEntryPtr, output,
6890 &cacheHit, &cacheHitGetter);
6891
6892 masm.abiret();
6893
6894 // Given we don't need the entry if we got a cache hit, and the entry pointer
6895 // will never point into the first code page, we can use the low bits of the
6896 // outEntryPtr to indicate success or that output holds a getter.
6897 masm.bind(&cacheHit);
6898 masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHit)), outEntryPtr);
6899 masm.abiret();
6900
6901 masm.bind(&cacheHitGetter);
6902 masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHitGetter)), outEntryPtr);
6903 masm.abiret();
6904}
6905
6906void JitRuntime::generateIonGenericHandleUnderflow(MacroAssembler& masm,
6907 bool isConstructing,
6908 Label* vmCall) {
6909 Register calleeReg = IonGenericCallCalleeReg;
6910 Register argcReg = IonGenericCallArgcReg;
6911 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
6912 Register numMissing = regs.takeAny();
6913 Register src = regs.takeAny();
6914 Register dest = regs.takeAny();
6915
6916 // On x86 we have fewer registers than we'd like, so we generate
6917 // slightly less efficient code.
6918 Register srcEnd, scratch;
6919 bool mustSpill = false;
6920 if (regs.empty()) {
6921 srcEnd = numMissing;
6922 scratch = calleeReg;
6923 mustSpill = true;
6924 } else {
6925 srcEnd = regs.takeAny();
6926 scratch = regs.takeAny();
6927 }
6928
6929 // Compute fun->nargs - argc. If it's positive, it's the number of
6930 // undefined args we must push.
6931 Label noUnderflow;
6932 masm.loadFunctionArgCount(calleeReg, numMissing);
6933 masm.sub32(argcReg, numMissing);
6934 masm.branch32(Assembler::LessThanOrEqual, numMissing, Imm32(0), &noUnderflow);
6935
6936 // Ensure that we don't adjust the stack pointer by more than a page.
6937 masm.branch32(Assembler::Above, numMissing, Imm32(JIT_ARGS_LENGTH_MAX),
6938 vmCall);
6939
6940 // If numMissing is even, we want to make the following transformation:
6941 //
6942 // INITIAL FINAL
6943 // [newTarget] (iff isConstructing) [newTarget] (iff isConstructing)
6944 // [argN] [undefined]
6945 // ... [undefined] (...)
6946 // [arg1] [argN]
6947 // [arg0] ...
6948 // [this] <- sp aligned [arg1]
6949 // [arg0]
6950 // [this] -> moved down numMissing
6951 // slots
6952 //
6953 // If numMissing is odd, we must also insert padding:
6954 // [newTarget] (iff isConstructing) (padding)
6955 // [argN] [newTarget] (iff isConstructing)
6956 // ... [undefined]
6957 // [arg1] [argN]
6958 // [arg0] ...
6959 // [this] <- sp aligned [arg1]
6960 // [arg0]
6961 // [this] -> moved down numMissing+1
6962 // slots
6963 //
6964 // Note that |newTarget|, if it exists, must be between the padding and the
6965 // undefined args. It does not move down along with the actual args.
6966
6967 // The first step is to copy the memory from [this] through [argN] into the
6968 // correct position. The source of the copy is the current stack pointer.
6969 masm.moveStackPtrTo(src);
6970
6971 // Compute how far the args must be moved and adjust the stack pointer.
6972 // If numMissing is even, this is numMissing slots. If numMissing is odd,
6973 // this is numMissing+1 slots. We can compute this as (numMissing + 1) & ~1.
6974 masm.add32(Imm32(1), numMissing, dest);
6975 masm.and32(Imm32(~1), dest);
6976 masm.lshift32(Imm32(3), dest);
6977 masm.subFromStackPtr(dest);
6978 masm.moveStackPtrTo(dest);
6979
6980 // We also set up a register pointing to the last copied argument. On x86
6981 // we don't have enough registers, so we spill the calleeReg and numMissing.
6982 if (mustSpill) {
6983 masm.pushRegs(calleeReg, numMissing);
6984 }
6985 masm.computeEffectiveAddress(BaseValueIndex(src, argcReg), srcEnd);
6986
6987 // The stack currently looks like this:
6988 //
6989 // [newTarget]
6990 // [argN] <-- srcEnd
6991 // ...
6992 // [arg0]
6993 // [this] <-- src
6994 // ...
6995 // ... <-- dest
6996 // [spill?]
6997 // [spill?]
6998
6999 // Loop to move the arguments.
7000 Label argLoop;
7001 masm.bind(&argLoop);
7002 masm.copy64(Address(src, 0), Address(dest, 0), scratch);
7003 masm.addPtr(Imm32(sizeof(Value)), src);
7004 masm.addPtr(Imm32(sizeof(Value)), dest);
7005 masm.branchPtr(Assembler::BelowOrEqual, src, srcEnd, &argLoop);
7006
7007 if (mustSpill) {
7008 // We must restore numMissing now, so that we can test if it's odd.
7009 // The copy64 below still needs calleeReg as a scratch register.
7010 masm.pop(numMissing);
7011 }
7012
7013 if (isConstructing) {
7014 // If numMissing is odd, we must move newTarget down by one slot.
7015 Label skip;
7016 masm.branchTest32(Assembler::Zero, numMissing, Imm32(1), &skip);
7017 Address newTargetSrc(src, 0);
7018 Address newTargetDest(src, -int32_t(sizeof(Value)));
7019 masm.copy64(newTargetSrc, newTargetDest, scratch);
7020 masm.bind(&skip);
7021 }
7022
7023 if (mustSpill) {
7024 masm.pop(calleeReg);
7025 }
7026
7027 // Loop to fill the remaining numMissing slots with UndefinedValue.
7028 // We do this last so that we can safely clobber numMissing.
7029 Label undefLoop;
7030 masm.bind(&undefLoop);
7031 BaseValueIndex undefSlot(dest, numMissing, -int32_t(sizeof(Value)));
7032 masm.storeValue(UndefinedValue(), undefSlot);
7033 masm.branchSub32(Assembler::NonZero, Imm32(1), numMissing, &undefLoop);
7034
7035 masm.bind(&noUnderflow);
7036}
7037
7038void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm,
7039 bool isConstructing) {
7040 Register calleeReg = IonGenericCallCalleeReg;
7041 Register argcReg = IonGenericCallArgcReg;
7042 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
7043 Register scratch = regs.takeAny();
7044 Register scratch2 = regs.takeAny();
7045 Register contextReg = regs.takeAny();
7046#ifndef JS_USE_LINK_REGISTER
7047 Register returnAddrReg = IonGenericCallReturnAddrReg;
7048#endif
7049
7050 // Push a value containing the callee, which will become argv[0].
7051 masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg);
7052
7053 // Load argv into scratch2.
7054 masm.moveStackPtrTo(scratch2);
7055
7056 // Initialize |NativeExitFrameLayout::savedCallee_|.
7057 masm.push(calleeReg);
7058
7059 // Enter the callee's realm. It's kept alive by |savedCallee_|.
7060 masm.switchToObjectRealm(calleeReg, scratch);
7061
7062 // Load the callee address into calleeReg.
7063#ifdef JS_SIMULATOR
7064 masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg);
7065#else
7066 masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
7067 calleeReg);
7068#endif
7069
7070 // Push argc.
7071 masm.push(argcReg);
7072
7073 masm.loadJSContext(contextReg);
7074
7075 // Construct native exit frame. Note that unlike other cases in this
7076 // trampoline, this code does not use a tail call.
7077 masm.push(FrameDescriptor(FrameType::IonJS));
7078#ifdef JS_USE_LINK_REGISTER
7079 masm.pushRegs(LinkRegister, FramePointer);
7080#else
7081 masm.pushRegs(returnAddrReg, FramePointer);
7082#endif
7083 masm.moveStackPtrTo(FramePointer);
7084 masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing);
7085
7086 masm.setupUnalignedABICall(scratch);
7087 masm.passABIArg(contextReg); // cx
7088 masm.passABIArg(argcReg); // argc
7089 masm.passABIArg(scratch2); // argv
7090
7091 masm.callWithABI(calleeReg);
7092
7093 // Test for failure.
7094 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
7095
7096 masm.loadValue(
7097 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
7098 JSReturnOperand);
7099
7100 // Leave the exit frame.
7101 masm.moveToStackPtr(FramePointer);
7102 masm.pop(FramePointer);
7103
7104 // Return.
7105 masm.ret();
7106}
7107
7108void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm,
7109 Label* entry, Label* vmCall) {
7110 Register calleeReg = IonGenericCallCalleeReg;
7111 Register argcReg = IonGenericCallArgcReg;
7112 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
7113 Register scratch = regs.takeAny();
7114 Register scratch2 = regs.takeAny();
7115 Register scratch3 = regs.takeAny();
7116
7117 Label notFunCall;
7118 masm.branchPtr(Assembler::NotEqual,
7119 Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
7120 ImmPtr(js::fun_call), &notFunCall);
7121
7122 // Only optimize fun_call for the caller's realm. If |this| is a proxy, the
7123 // arguments array passed to the proxy's apply trap must be allocated in
7124 // fun_call's realm. We can't enter that realm here because fun_call, which
7125 // keeps it alive, is about to be removed from the stack. See bug 2082051.
7126 masm.guardObjectHasSameRealm(calleeReg, scratch, vmCall);
7127
7128 // In general, we can implement fun_call by replacing calleeReg with
7129 // |this|, sliding all the other arguments down, and decrementing argc.
7130 //
7131 // *BEFORE* *AFTER*
7132 // [argN] argc = N+1 <padding>
7133 // ... [argN] argc = N
7134 // [arg1] ...
7135 // [arg0] [arg1] <- now arg0
7136 // [this] <- top of stack (aligned) [arg0] <- now this
7137 //
7138 // The only exception is when argc is already 0, in which case instead
7139 // of shifting arguments down we replace [this] with UndefinedValue():
7140 //
7141 // *BEFORE* *AFTER*
7142 // [this] argc = 0 [undef] argc = 0
7143 //
7144 // After making this transformation, we can jump back to the beginning
7145 // of this trampoline to handle the inner call.
7146
7147 // Guard that |this| is an object. If it is, replace calleeReg.
7148 masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall);
7149 masm.movePtr(scratch, calleeReg);
7150
7151 Label hasArgs;
7152 masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs);
7153
7154 // No arguments. Replace |this| with |undefined| and start from the top.
7155 masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0));
7156 masm.jump(entry);
7157
7158 masm.bind(&hasArgs);
7159
7160 Label doneSliding;
7161 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
7162 scratch3, &doneSliding);
7163 masm.bind(&doneSliding);
7164 masm.sub32(Imm32(1), argcReg);
7165
7166 masm.jump(entry);
7167
7168 masm.bind(&notFunCall);
7169}
7170
7171void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm,
7172 Label* entry,
7173 Label* vmCall) {
7174 Register calleeReg = IonGenericCallCalleeReg;
7175 Register argcReg = IonGenericCallArgcReg;
7176 AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs());
7177 Register scratch = regs.takeAny();
7178 Register scratch2 = regs.takeAny();
7179 Register scratch3 = regs.takeAny();
7180
7181 masm.branchTestObjClass(Assembler::NotEqual, calleeReg,
7182 &BoundFunctionObject::class_, scratch, calleeReg,
7183 vmCall);
7184
7185 Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
7186 Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot());
7187 Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot());
7188 Address firstInlineArgSlot(
7189 calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg());
7190
7191 // Check that we won't be pushing too many arguments.
7192 masm.load32(flagsSlot, scratch);
7193 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
7194 masm.add32(argcReg, scratch);
7195 masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall);
7196
7197 // The stack is currently correctly aligned for a jit call. We will
7198 // be updating the `this` value and potentially adding additional
7199 // arguments. On platforms with 16-byte alignment, if the number of
7200 // bound arguments is odd, we have to move the arguments that are
7201 // currently on the stack. For example, with one bound argument:
7202 //
7203 // *BEFORE* *AFTER*
7204 // [argN] <padding>
7205 // ... [argN] |
7206 // [arg1] ... | These arguments have been
7207 // [arg0] [arg1] | shifted down 8 bytes.
7208 // [this] <- top of stack (aligned) [arg0] v
7209 // [bound0] <- one bound argument (odd)
7210 // [boundThis] <- top of stack (aligned)
7211 //
7212 Label poppedThis;
7213 if (JitStackValueAlignment > 1) {
7214 Label alreadyAligned;
7215 masm.branchTest32(Assembler::Zero, flagsSlot,
7216 Imm32(1 << BoundFunctionObject::NumBoundArgsShift),
7217 &alreadyAligned);
7218
7219 // We have an odd number of bound arguments. Shift the existing arguments
7220 // down by 8 bytes.
7221 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
7222 scratch3, &poppedThis);
7223 masm.bind(&alreadyAligned);
7224 }
7225
7226 // Pop the current `this`. It will be replaced with the bound `this`.
7227 masm.freeStack(sizeof(Value));
7228 masm.bind(&poppedThis);
7229
7230 // Load the number of bound arguments in scratch
7231 masm.load32(flagsSlot, scratch);
7232 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
7233
7234 Label donePushingBoundArguments;
7235 masm.branch32(Assembler::Equal, scratch, Imm32(0),
7236 &donePushingBoundArguments);
7237
7238 // Update argc to include bound arguments.
7239 masm.add32(scratch, argcReg);
7240
7241 // Load &boundArgs[0] in scratch2.
7242 Label outOfLineBoundArguments, haveBoundArguments;
7243 masm.branch32(Assembler::Above, scratch,
7244 Imm32(BoundFunctionObject::MaxInlineBoundArgs),
7245 &outOfLineBoundArguments);
7246 masm.computeEffectiveAddress(firstInlineArgSlot, scratch2);
7247 masm.jump(&haveBoundArguments);
7248
7249 masm.bind(&outOfLineBoundArguments);
7250 masm.unboxObject(firstInlineArgSlot, scratch2);
7251 masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
7252
7253 masm.bind(&haveBoundArguments);
7254
7255 // Load &boundArgs[numBoundArgs] in scratch.
7256 BaseObjectElementIndex lastBoundArg(scratch2, scratch);
7257 masm.computeEffectiveAddress(lastBoundArg, scratch);
7258
7259 // Push the bound arguments, starting with the last one.
7260 // Copying pre-decrements scratch until scratch2 is reached.
7261 Label boundArgumentsLoop;
7262 masm.bind(&boundArgumentsLoop);
7263 masm.subPtr(Imm32(sizeof(Value)), scratch);
7264 masm.pushValue(Address(scratch, 0));
7265 masm.branchPtr(Assembler::Above, scratch, scratch2, &boundArgumentsLoop);
7266 masm.bind(&donePushingBoundArguments);
7267
7268 // Push the bound `this`.
7269 masm.pushValue(thisSlot);
7270
7271 // Load the target in calleeReg.
7272 masm.unboxObject(targetSlot, calleeReg);
7273
7274 // Bound targets can be nested arbitrarily deep. Check the stack limit before
7275 // recursing.
7276 const void* limitAddr = masm.runtime()->addressOfJitStackLimit();
7277 masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr),
7278 vmCall);
7279
7280 // At this point, all preconditions for entering the trampoline are met:
7281 // - calleeReg contains a pointer to the callee object
7282 // - argcReg contains the number of actual args (now including bound args)
7283 // - the arguments are on the stack with the correct alignment.
7284 // Instead of generating more code, we can jump back to the entry point
7285 // of the trampoline to call the bound target.
7286 masm.jump(entry);
7287}
7288
7289void CodeGenerator::visitCallKnown(LCallKnown* call) {
7290 Register calleereg = ToRegister(call->getFunction());
7291 Register objreg = ToRegister(call->getTempObject());
7292 uint32_t unusedStack =
7293 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
7294 WrappedFunction* target = call->getSingleTarget();
7295
7296 // Native single targets (except Wasm and TrampolineNative functions) are
7297 // handled by LCallNative.
7298 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", 7298); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->hasJitEntry()" ")"); do { MOZ_CrashSequence
(__null, 7298); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7299
7300 // Missing arguments must have been explicitly appended by WarpBuilder.
7301 DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
7302 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", 7303); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
")"); do { MOZ_CrashSequence(__null, 7303); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7303 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", 7303); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
")"); do { MOZ_CrashSequence(__null, 7303); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7304
7305 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", 7305); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "target->isConstructor()" ")"); do { MOZ_CrashSequence
(__null, 7305); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
7306
7307 masm.checkStackAlignment();
7308
7309 if (target->isClassConstructor() && !call->isConstructing()) {
7310 emitCallInvokeFunction(call, calleereg, call->isConstructing(),
7311 call->ignoresReturnValue(), call->numActualArgs(),
7312 unusedStack);
7313 return;
7314 }
7315
7316 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", 7316); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "call->isConstructing()" ")"); do { MOZ_CrashSequence
(__null, 7316); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
7317
7318 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"
, 7318); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->mir()->needsThisCheck()"
")"); do { MOZ_CrashSequence(__null, 7318); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7319
7320 if (call->mir()->maybeCrossRealm()) {
7321 masm.switchToObjectRealm(calleereg, objreg);
7322 }
7323
7324 masm.loadJitCodeRaw(calleereg, objreg);
7325
7326 // Nestle the StackPointer up to the argument vector.
7327 masm.freeStack(unusedStack);
7328
7329 // Construct the JitFrameLayout.
7330 masm.PushCalleeToken(calleereg, call->mir()->isConstructing());
7331 masm.Push(FrameDescriptor(FrameType::IonJS, call->numActualArgs()));
7332
7333 // Finally call the function in objreg.
7334 ensureOsiSpace();
7335 uint32_t callOffset = masm.callJit(objreg);
7336 markSafepointAt(callOffset, call);
7337
7338 if (call->mir()->maybeCrossRealm()) {
7339 static_assert(!JSReturnOperand.aliases(ReturnReg),
7340 "ReturnReg available as scratch after scripted calls");
7341 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
7342 }
7343
7344 // Restore stack pointer: pop JitFrameLayout fields still left on the stack
7345 // and undo the earlier |freeStack(unusedStack)|.
7346 int prefixGarbage =
7347 sizeof(JitFrameLayout) - JitFrameLayout::bytesPoppedAfterCall();
7348 masm.adjustStack(prefixGarbage - unusedStack);
7349
7350 // If the return value of the constructing function is Primitive,
7351 // replace the return value with the Object from CreateThis.
7352 if (call->mir()->isConstructing()) {
7353 Label notPrimitive;
7354 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7355 &notPrimitive);
7356 masm.loadValue(Address(masm.getStackPointer(), unusedStack),
7357 JSReturnOperand);
7358#ifdef DEBUG1
7359 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7360 &notPrimitive);
7361 masm.assumeUnreachable("CreateThis creates an object");
7362#endif
7363 masm.bind(&notPrimitive);
7364 }
7365}
7366
7367template <typename T>
7368void CodeGenerator::emitCallInvokeFunction(T* apply) {
7369 pushArg(masm.getStackPointer()); // argv.
7370 pushArg(ToRegister(apply->getArgc())); // argc.
7371 pushArg(Imm32(apply->mir()->ignoresReturnValue())); // ignoresReturnValue.
7372 pushArg(Imm32(apply->mir()->isConstructing())); // isConstructing.
7373 pushArg(ToRegister(apply->getFunction())); // JSFunction*.
7374
7375 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
7376 MutableHandleValue);
7377 callVM<Fn, jit::InvokeFunction>(apply);
7378}
7379
7380// Do not bailout after the execution of this function since the stack no longer
7381// corresponds to what is expected by the snapshots.
7382template <typename T>
7383void CodeGenerator::emitAllocateSpaceForApply(T* apply, Register calleeReg,
7384 Register argcreg,
7385 Register scratch) {
7386 Label* oolRejoin = nullptr;
7387 bool canUnderflow =
7388 !apply->hasSingleTarget() || apply->getSingleTarget()->nargs() > 0;
7389
7390 if (canUnderflow) {
7391 auto* ool =
7392 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
7393 // Align the JitFrameLayout on the JitStackAlignment by allocating
7394 // callee->nargs() slots, possibly rounded up to the nearest odd
7395 // number (see below). Leave callee->nargs() in `scratch` for the
7396 // undef loop.
7397 if (apply->hasSingleTarget()) {
7398 uint32_t nargs = apply->getSingleTarget()->nargs();
7399 uint32_t numSlots = JitStackValueAlignment == 1 ? nargs : nargs | 1;
7400 masm.subFromStackPtr(Imm32((numSlots) * sizeof(Value)));
7401 masm.move32(Imm32(nargs), scratch);
7402 } else {
7403 // `scratch` contains callee->nargs()
7404 if (JitStackValueAlignment > 1) {
7405 masm.orPtr(Imm32(1), scratch);
7406 }
7407 masm.lshiftPtr(Imm32(ValueShift), scratch);
7408 masm.subFromStackPtr(scratch);
7409
7410 // We need callee->nargs in `scratch`. If we rounded it up
7411 // above, we need to reload it. If we only shifted it, we can
7412 // simply shift it back.
7413 if (JitStackValueAlignment > 1) {
7414 masm.loadFunctionArgCount(calleeReg, scratch);
7415 } else {
7416 masm.rshiftPtr(Imm32(ValueShift), scratch);
7417 }
7418 }
7419
7420 // Count from callee->nargs() down to argc, storing undefined values.
7421 Label loop;
7422 masm.bind(&loop);
7423 masm.sub32(Imm32(1), scratch);
7424 masm.storeValue(UndefinedValue(),
7425 BaseValueIndex(masm.getStackPointer(), scratch));
7426 masm.branch32(Assembler::Above, scratch, argcreg, &loop);
7427 masm.jump(ool.rejoin());
7428 });
7429 addOutOfLineCode(ool, apply->mir());
7430 oolRejoin = ool->rejoin();
7431
7432 Label noUnderflow;
7433 if (apply->hasSingleTarget()) {
7434 masm.branch32(Assembler::AboveOrEqual, argcreg,
7435 Imm32(apply->getSingleTarget()->nargs()), &noUnderflow);
7436 } else {
7437 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
7438 calleeReg, &noUnderflow);
7439 masm.loadFunctionArgCount(calleeReg, scratch);
7440 masm.branch32(Assembler::AboveOrEqual, argcreg, scratch, &noUnderflow);
7441 }
7442 masm.branchIfFunctionHasJitEntry(calleeReg, ool->entry());
7443 masm.bind(&noUnderflow);
7444 }
7445
7446 // Use scratch register to calculate stack space (including padding).
7447 masm.movePtr(argcreg, scratch);
7448
7449 // Align the JitFrameLayout on the JitStackAlignment.
7450 if (JitStackValueAlignment > 1) {
7451 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", 7452); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") ("
"Stack padding assumes that the frameSize is correct" ")"); do
{ MOZ_CrashSequence(__null, 7452); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
7452 "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", 7452); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") ("
"Stack padding assumes that the frameSize is correct" ")"); do
{ MOZ_CrashSequence(__null, 7452); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7453 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", 7453); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence
(__null, 7453); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7454 // If the number of arguments is odd, then we do not need any padding.
7455 //
7456 // Note: The |JitStackValueAlignment == 2| condition requires that the
7457 // overall number of values on the stack is even. When we have an odd number
7458 // of arguments, we don't need any padding, because the |thisValue| is
7459 // pushed after the arguments, so the overall number of values on the stack
7460 // is even.
7461 //
7462 // We can align by unconditionally setting the low bit. If the number of
7463 // arguments is odd, the low bit was already set, so this adds no padding.
7464 // If the number of arguments is even, the low bit was not set, so this adds
7465 // 1, as we require.
7466 masm.orPtr(Imm32(1), scratch);
7467 }
7468
7469 // Reserve space for copying the arguments.
7470 NativeObject::elementsSizeMustNotOverflow();
7471 masm.lshiftPtr(Imm32(ValueShift), scratch);
7472 masm.subFromStackPtr(scratch);
7473
7474#ifdef DEBUG1
7475 // Put a magic value in the space reserved for padding. Note, this code cannot
7476 // be merged with the previous test, as not all architectures can write below
7477 // their stack pointers.
7478 if (JitStackValueAlignment > 1) {
7479 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", 7479); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence
(__null, 7479); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7480 Label noPaddingNeeded;
7481 // If the number of arguments is odd, then we do not need any padding.
7482 masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded);
7483 BaseValueIndex dstPtr(masm.getStackPointer(), argcreg);
7484 masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr);
7485 masm.bind(&noPaddingNeeded);
7486 }
7487#endif
7488
7489 if (canUnderflow) {
7490 masm.bind(oolRejoin);
7491 }
7492}
7493
7494// Do not bailout after the execution of this function since the stack no longer
7495// corresponds to what is expected by the snapshots.
7496template <typename T>
7497void CodeGenerator::emitAllocateSpaceForConstructAndPushNewTarget(
7498 T* construct, Register calleeReg, Register argcreg,
7499 Register newTargetAndScratch) {
7500 // Push newTarget.
7501 masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch);
7502 if (JitStackValueAlignment > 1) {
7503 // x86 is short on registers. To free up newTarget for use as a scratch
7504 // register before we know if we need padding, we push newTarget twice.
7505 // If the first copy pushed is correctly aligned, we will overwrite the
7506 // second. If the second copy is correctly aligned, the first is padding.
7507 masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch);
7508 }
7509 Register scratch = newTargetAndScratch;
7510
7511 Label* oolRejoin = nullptr;
7512 bool canUnderflow = !construct->hasSingleTarget() ||
7513 construct->getSingleTarget()->nargs() > 0;
7514 if (canUnderflow) {
7515 auto* ool =
7516 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
7517 // Align the JitFrameLayout on the JitStackAlignment by allocating
7518 // callee->nargs() slots, rounded down to the nearest odd number (see
7519 // below). Leave callee->nargs() in `scratch` for the undef loop.
7520 if (construct->hasSingleTarget()) {
7521 uint32_t nargs = construct->getSingleTarget()->nargs();
7522 uint32_t numSlots =
7523 JitStackValueAlignment == 1 ? nargs : ((nargs + 1) & ~1) - 1;
7524 masm.subFromStackPtr(Imm32((numSlots) * sizeof(Value)));
7525 masm.move32(Imm32(nargs), scratch);
7526 } else {
7527 // `scratch` contains callee->nargs()
7528 if (JitStackValueAlignment > 1) {
7529 // Round down to nearest odd number.
7530 masm.addPtr(Imm32(1), scratch);
7531 masm.andPtr(Imm32(~1), scratch);
7532 masm.subPtr(Imm32(1), scratch);
7533 }
7534 masm.lshiftPtr(Imm32(ValueShift), scratch);
7535 masm.subFromStackPtr(scratch);
7536
7537 // We need callee->nargs in `scratch`. If we rounded it down
7538 // above, we need to reload it. If we only shifted it, we can
7539 // simply shift it back.
7540 if (JitStackValueAlignment > 1) {
7541 masm.loadFunctionArgCount(calleeReg, scratch);
7542 } else {
7543 masm.rshiftPtr(Imm32(ValueShift), scratch);
7544 }
7545 }
7546
7547 // Count from callee->nargs() down to argc, storing undefined values.
7548 Label loop;
7549 masm.bind(&loop);
7550 masm.sub32(Imm32(1), scratch);
7551 masm.storeValue(UndefinedValue(),
7552 BaseValueIndex(masm.getStackPointer(), scratch));
7553 masm.branch32(Assembler::Above, scratch, argcreg, &loop);
7554 masm.jump(ool.rejoin());
7555 });
7556 addOutOfLineCode(ool, construct->mir());
7557 oolRejoin = ool->rejoin();
7558
7559 Label noUnderflow;
7560 if (construct->hasSingleTarget()) {
7561 masm.branch32(Assembler::AboveOrEqual, argcreg,
7562 Imm32(construct->getSingleTarget()->nargs()), &noUnderflow);
7563 } else {
7564 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
7565 calleeReg, &noUnderflow);
7566 masm.loadFunctionArgCount(calleeReg, scratch);
7567 masm.branch32(Assembler::AboveOrEqual, argcreg, scratch, &noUnderflow);
7568 }
7569 masm.branchIfFunctionHasJitEntry(calleeReg, ool->entry());
7570 masm.bind(&noUnderflow);
7571 }
7572
7573 // Use newTargetAndScratch to calculate stack space (including padding).
7574 masm.movePtr(argcreg, newTargetAndScratch);
7575
7576 // Align the JitFrameLayout on the JitStackAlignment.
7577 if (JitStackValueAlignment > 1) {
7578 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", 7579); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") ("
"Stack padding assumes that the frameSize is correct" ")"); do
{ MOZ_CrashSequence(__null, 7579); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
7579 "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", 7579); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") ("
"Stack padding assumes that the frameSize is correct" ")"); do
{ MOZ_CrashSequence(__null, 7579); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7580 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", 7580); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence
(__null, 7580); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7581 // Note: The |JitStackValueAlignment == 2| condition requires that the
7582 // overall number of values on the stack is even. We must push `newTarget`,
7583 // the args, and `this`. We've already pushed newTarget twice. Rounding
7584 // argc down to the closest odd number will give us the correct alignment:
7585 //
7586 // argc: *0* *1* *2* *3*
7587 // rounds to: -1 1 1 3
7588 // *newTarget (newTarget) newTarget (newTarget)
7589 // curr sp --> this newTarget arg1 newTarget
7590 // *arg0 *arg0 arg2
7591 // this this arg1
7592 // *arg0
7593 // this
7594 // The asterisk in each column marks the stack pointer after adding
7595 // the rounded value. In each case, pushing `this` will result in an
7596 // even number of total slots.
7597 masm.addPtr(Imm32(1), scratch);
7598 masm.andPtr(Imm32(~1), scratch);
7599 masm.subPtr(Imm32(1), scratch);
7600 }
7601
7602 // Reserve space for copying the arguments.
7603 NativeObject::elementsSizeMustNotOverflow();
7604 masm.lshiftPtr(Imm32(ValueShift), newTargetAndScratch);
7605 masm.subFromStackPtr(newTargetAndScratch);
7606
7607 if (canUnderflow) {
7608 masm.bind(oolRejoin);
7609 }
7610}
7611
7612// Destroys argvIndex and copyreg.
7613void CodeGenerator::emitCopyValuesForApply(Register argvSrcBase,
7614 Register argvIndex, Register copyreg,
7615 size_t argvSrcOffset,
7616 size_t argvDstOffset) {
7617 Label loop;
7618 masm.bind(&loop);
7619
7620 // As argvIndex is off by 1, and we use the decBranchPtr instruction to loop
7621 // back, we have to substract the size of the word which are copied.
7622 BaseValueIndex srcPtr(argvSrcBase, argvIndex,
7623 int32_t(argvSrcOffset) - sizeof(void*));
7624 BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex,
7625 int32_t(argvDstOffset) - sizeof(void*));
7626 masm.loadPtr(srcPtr, copyreg);
7627 masm.storePtr(copyreg, dstPtr);
7628
7629 // Handle 32 bits architectures.
7630 if (sizeof(Value) == 2 * sizeof(void*)) {
7631 BaseValueIndex srcPtrLow(argvSrcBase, argvIndex,
7632 int32_t(argvSrcOffset) - 2 * sizeof(void*));
7633 BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex,
7634 int32_t(argvDstOffset) - 2 * sizeof(void*));
7635 masm.loadPtr(srcPtrLow, copyreg);
7636 masm.storePtr(copyreg, dstPtrLow);
7637 }
7638
7639 masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop);
7640}
7641
7642void CodeGenerator::emitRestoreStackPointerFromFP() {
7643 // This is used to restore the stack pointer after a call with a dynamic
7644 // number of arguments.
7645
7646 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"
, 7646); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 7646); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7647
7648 int32_t offset = -int32_t(frameSize());
7649 masm.computeEffectiveAddress(Address(FramePointer, offset),
7650 masm.getStackPointer());
7651#if JS_CODEGEN_ARM64
7652 masm.syncStackPtr();
7653#endif
7654}
7655
7656void CodeGenerator::emitPushArguments(Register argcreg, Register scratch,
7657 Register copyreg, uint32_t extraFormals) {
7658 Label end;
7659
7660 // Skip the copy of arguments if there are none.
7661 masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end);
7662
7663 // clang-format off
7664 //
7665 // We are making a copy of the arguments which are above the JitFrameLayout
7666 // of the current Ion frame.
7667 //
7668 // [arg1] [arg0] <- src [this] [JitFrameLayout] [.. frameSize ..] [pad] [arg1] [arg0] <- dst
7669 //
7670 // clang-format on
7671
7672 // Compute the source and destination offsets into the stack.
7673 //
7674 // The |extraFormals| parameter is used when copying rest-parameters and
7675 // allows to skip the initial parameters before the actual rest-parameters.
7676 Register argvSrcBase = FramePointer;
7677 size_t argvSrcOffset =
7678 JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value);
7679 size_t argvDstOffset = 0;
7680
7681 Register argvIndex = scratch;
7682 masm.move32(argcreg, argvIndex);
7683
7684 // Copy arguments.
7685 emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset,
7686 argvDstOffset);
7687
7688 // Join with all arguments copied.
7689 masm.bind(&end);
7690}
7691
7692void CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply) {
7693 // Holds the function nargs.
7694 Register funcreg = ToRegister(apply->getFunction());
7695 Register argcreg = ToRegister(apply->getArgc());
7696 Register copyreg = ToRegister(apply->getTempObject());
7697 Register scratch = ToRegister(apply->getTempForArgCopy());
7698 uint32_t extraFormals = apply->numExtraFormals();
7699
7700 // Allocate space on the stack for arguments.
7701 emitAllocateSpaceForApply(apply, funcreg, argcreg, scratch);
7702
7703 emitPushArguments(argcreg, scratch, copyreg, extraFormals);
7704
7705 // Push |this|.
7706 masm.pushValue(ToValue(apply->thisValue()));
7707}
7708
7709void CodeGenerator::emitPushArguments(LApplyArgsObj* apply) {
7710 Register function = ToRegister(apply->getFunction());
7711 Register argsObj = ToRegister(apply->getArgsObj());
7712 Register tmpArgc = ToRegister(apply->getTempObject());
7713 Register scratch = ToRegister(apply->getTempForArgCopy());
7714
7715 // argc and argsObj are mapped to the same calltemp register.
7716 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", 7716); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argsObj == ToRegister(apply->getArgc())"
")"); do { MOZ_CrashSequence(__null, 7716); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7717
7718 // Load argc into tmpArgc.
7719 masm.loadArgumentsObjectLength(argsObj, tmpArgc);
7720
7721 // Allocate space on the stack for arguments.
7722 emitAllocateSpaceForApply(apply, function, tmpArgc, scratch);
7723
7724 // Load arguments data.
7725 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
7726 argsObj);
7727 size_t argsSrcOffset = ArgumentsData::offsetOfArgs();
7728
7729 // This is the end of the lifetime of argsObj.
7730 // After this call, the argsObj register holds the argument count instead.
7731 emitPushArrayAsArguments(tmpArgc, argsObj, scratch, argsSrcOffset);
7732
7733 // Push |this|.
7734 masm.pushValue(ToValue(apply->thisValue()));
7735}
7736
7737void CodeGenerator::emitPushArrayAsArguments(Register tmpArgc,
7738 Register srcBaseAndArgc,
7739 Register scratch,
7740 size_t argvSrcOffset) {
7741 // Preconditions:
7742 // 1. |tmpArgc| * sizeof(Value) bytes have been allocated at the top of
7743 // the stack to hold arguments.
7744 // 2. |srcBaseAndArgc| + |srcOffset| points to an array of |tmpArgc| values.
7745 //
7746 // Postconditions:
7747 // 1. The arguments at |srcBaseAndArgc| + |srcOffset| have been copied into
7748 // the allocated space.
7749 // 2. |srcBaseAndArgc| now contains the original value of |tmpArgc|.
7750 //
7751 // |scratch| is used as a temp register within this function and clobbered.
7752
7753 Label noCopy, epilogue;
7754
7755 // Skip the copy of arguments if there are none.
7756 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
7757 {
7758 // Copy the values. This code is skipped entirely if there are no values.
7759 size_t argvDstOffset = 0;
7760
7761 Register argvSrcBase = srcBaseAndArgc;
7762
7763 // Stash away |tmpArgc| and adjust argvDstOffset accordingly.
7764 masm.push(tmpArgc);
7765 Register argvIndex = tmpArgc;
7766 argvDstOffset += sizeof(void*);
7767
7768 // Copy
7769 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
7770 argvDstOffset);
7771
7772 // Restore.
7773 masm.pop(srcBaseAndArgc); // srcBaseAndArgc now contains argc.
7774 masm.jump(&epilogue);
7775 }
7776 masm.bind(&noCopy);
7777 {
7778 // Clear argc if we skipped the copy step.
7779 masm.movePtr(ImmWord(0), srcBaseAndArgc);
7780 }
7781
7782 // Join with all arguments copied.
7783 // Note, "srcBase" has become "argc".
7784 masm.bind(&epilogue);
7785}
7786
7787void CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply) {
7788 Register function = ToRegister(apply->getFunction());
7789 Register elements = ToRegister(apply->getElements());
7790 Register tmpArgc = ToRegister(apply->getTempObject());
7791 Register scratch = ToRegister(apply->getTempForArgCopy());
7792
7793 // argc and elements are mapped to the same calltemp register.
7794 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", 7794); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "elements == ToRegister(apply->getArgc())"
")"); do { MOZ_CrashSequence(__null, 7794); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7795
7796 // Invariants guarded in the caller:
7797 // - the array is not too long
7798 // - the array length equals its initialized length
7799
7800 // The array length is our argc for the purposes of allocating space.
7801 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
7802
7803 // Allocate space for the values.
7804 emitAllocateSpaceForApply(apply, function, tmpArgc, scratch);
7805
7806 // After this call "elements" has become "argc".
7807 size_t elementsOffset = 0;
7808 emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset);
7809
7810 // Push |this|.
7811 masm.pushValue(ToValue(apply->thisValue()));
7812}
7813
7814void CodeGenerator::emitPushArguments(LConstructArgsGeneric* construct) {
7815 // Holds the function nargs.
7816 Register argcreg = ToRegister(construct->getArgc());
7817 Register function = ToRegister(construct->getFunction());
7818 Register copyreg = ToRegister(construct->getTempObject());
7819 Register scratch = ToRegister(construct->getTempForArgCopy());
7820 uint32_t extraFormals = construct->numExtraFormals();
7821
7822 // newTarget and scratch are mapped to the same calltemp register.
7823 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", 7823); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())"
")"); do { MOZ_CrashSequence(__null, 7823); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7824
7825 // Allocate space for the values.
7826 // After this call "newTarget" has become "scratch".
7827 emitAllocateSpaceForConstructAndPushNewTarget(construct, function, argcreg,
7828 scratch);
7829
7830 emitPushArguments(argcreg, scratch, copyreg, extraFormals);
7831
7832 // Push |this|.
7833 masm.pushValue(ToValue(construct->thisValue()));
7834}
7835
7836void CodeGenerator::emitPushArguments(LConstructArrayGeneric* construct) {
7837 Register function = ToRegister(construct->getFunction());
7838 Register elements = ToRegister(construct->getElements());
7839 Register tmpArgc = ToRegister(construct->getTempObject());
7840 Register scratch = ToRegister(construct->getTempForArgCopy());
7841
7842 // argc and elements are mapped to the same calltemp register.
7843 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", 7843); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "elements == ToRegister(construct->getArgc())"
")"); do { MOZ_CrashSequence(__null, 7843); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7844
7845 // newTarget and scratch are mapped to the same calltemp register.
7846 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", 7846); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())"
")"); do { MOZ_CrashSequence(__null, 7846); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7847
7848 // Invariants guarded in the caller:
7849 // - the array is not too long
7850 // - the array length equals its initialized length
7851
7852 // The array length is our argc for the purposes of allocating space.
7853 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
7854
7855 // Allocate space for the values.
7856 // After this call "newTarget" has become "scratch".
7857 emitAllocateSpaceForConstructAndPushNewTarget(construct, function, tmpArgc,
7858 scratch);
7859
7860 // After this call "elements" has become "argc".
7861 size_t elementsOffset = 0;
7862 emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset);
7863
7864 // Push |this|.
7865 masm.pushValue(ToValue(construct->thisValue()));
7866}
7867
7868template <typename T>
7869void CodeGenerator::emitApplyGeneric(T* apply) {
7870 // Holds the function object.
7871 Register calleereg = ToRegister(apply->getFunction());
7872
7873 // Temporary register for modifying the function object.
7874 Register objreg = ToRegister(apply->getTempObject());
7875 Register scratch = ToRegister(apply->getTempForArgCopy());
7876
7877 // Holds the function nargs, computed in the invoker or (for ApplyArray,
7878 // ConstructArray, or ApplyArgsObj) in the argument pusher.
7879 Register argcreg = ToRegister(apply->getArgc());
7880
7881 // Copy the arguments of the current function.
7882 //
7883 // In the case of ApplyArray, ConstructArray, or ApplyArgsObj, also compute
7884 // argc. The argc register and the elements/argsObj register are the same;
7885 // argc must not be referenced before the call to emitPushArguments() and
7886 // elements/argsObj must not be referenced after it returns.
7887 //
7888 // In the case of ConstructArray or ConstructArgs, also overwrite newTarget;
7889 // newTarget must not be referenced after this point.
7890 //
7891 // objreg is dead across this call.
7892 emitPushArguments(apply);
7893
7894 masm.checkStackAlignment();
7895
7896 bool constructing = apply->mir()->isConstructing();
7897
7898 // If the function is native, the call is compiled through emitApplyNative.
7899 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", 7900); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 7900); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
7900 !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", 7900); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 7900); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
7901
7902 Label end, invoke;
7903
7904 // Unless already known, guard that calleereg is actually a function object.
7905 if (!apply->hasSingleTarget()) {
7906 masm.branchTestObjIsFunction(Assembler::NotEqual, calleereg, objreg,
7907 calleereg, &invoke);
7908 }
7909
7910 // Guard that calleereg is an interpreted function with a JSScript.
7911 masm.branchIfFunctionHasNoJitEntry(calleereg, &invoke);
7912
7913 // Guard that callee allows the [[Call]] or [[Construct]] operation required.
7914 if (constructing) {
7915 masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR,
7916 Assembler::Zero, &invoke);
7917 } else {
7918 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
7919 calleereg, objreg, &invoke);
7920 }
7921
7922 // Use the slow path if CreateThis was unable to create the |this| object.
7923 if (constructing) {
7924 Address thisAddr(masm.getStackPointer(), 0);
7925 masm.branchTestNull(Assembler::Equal, thisAddr, &invoke);
7926 }
7927
7928 // Call with an Ion frame
7929 {
7930 if (apply->mir()->maybeCrossRealm()) {
7931 masm.switchToObjectRealm(calleereg, objreg);
7932 }
7933
7934 // Knowing that calleereg is a non-native function, load jitcode.
7935 masm.loadJitCodeRaw(calleereg, objreg);
7936
7937 masm.PushCalleeToken(calleereg, constructing);
7938 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcreg, scratch);
7939
7940 // Call the function.
7941 ensureOsiSpace();
7942 uint32_t callOffset = masm.callJit(objreg);
7943 markSafepointAt(callOffset, apply);
7944
7945 if (apply->mir()->maybeCrossRealm()) {
7946 static_assert(!JSReturnOperand.aliases(ReturnReg),
7947 "ReturnReg available as scratch after scripted calls");
7948 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
7949 }
7950
7951 // Discard JitFrameLayout fields still left on the stack.
7952 masm.freeStack(sizeof(JitFrameLayout) -
7953 JitFrameLayout::bytesPoppedAfterCall());
7954 masm.jump(&end);
7955 }
7956
7957 // Handle uncompiled or native functions.
7958 {
7959 masm.bind(&invoke);
7960 emitCallInvokeFunction(apply);
7961 }
7962
7963 masm.bind(&end);
7964
7965 // If the return value of the constructing function is Primitive, replace the
7966 // return value with the Object from CreateThis.
7967 if (constructing) {
7968 Label notPrimitive;
7969 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7970 &notPrimitive);
7971 masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand);
7972
7973#ifdef DEBUG1
7974 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7975 &notPrimitive);
7976 masm.assumeUnreachable("CreateThis creates an object");
7977#endif
7978
7979 masm.bind(&notPrimitive);
7980 }
7981
7982 // Pop arguments and continue.
7983 emitRestoreStackPointerFromFP();
7984}
7985
7986template <typename T>
7987void CodeGenerator::emitAlignStackForApplyNative(T* apply, Register argc) {
7988 static_assert(JitStackAlignment % ABIStackAlignment == 0,
7989 "aligning on JIT stack subsumes ABI alignment");
7990
7991 // Align the arguments on the JitStackAlignment.
7992 if (JitStackValueAlignment > 1) {
7993 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"
, 7994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
") (" "Stack padding adds exactly one Value" ")"); do { MOZ_CrashSequence
(__null, 7994); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
7994 "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"
, 7994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
") (" "Stack padding adds exactly one Value" ")"); do { MOZ_CrashSequence
(__null, 7994); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7995 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", 7996); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { MOZ_CrashSequence(__null, 7996); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7996 "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", 7996); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { MOZ_CrashSequence(__null, 7996); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7997
7998 Assembler::Condition cond;
7999 if constexpr (T::isConstructing()) {
8000 // If the number of arguments is even, then we do not need any padding.
8001 //
8002 // Also see emitAllocateSpaceForApply().
8003 cond = Assembler::Zero;
8004 } else {
8005 // If the number of arguments is odd, then we do not need any padding.
8006 //
8007 // Also see emitAllocateSpaceForConstructAndPushNewTarget().
8008 cond = Assembler::NonZero;
8009 }
8010
8011 Label noPaddingNeeded;
8012 masm.branchTestPtr(cond, argc, Imm32(1), &noPaddingNeeded);
8013 masm.pushValue(MagicValue(JS_ARG_POISON));
8014 masm.bind(&noPaddingNeeded);
8015 }
8016}
8017
8018template <typename T>
8019void CodeGenerator::emitPushNativeArguments(T* apply) {
8020 Register argc = ToRegister(apply->getArgc());
8021 Register tmpArgc = ToRegister(apply->getTempObject());
8022 Register scratch = ToRegister(apply->getTempForArgCopy());
8023 uint32_t extraFormals = apply->numExtraFormals();
8024
8025 // Align stack.
8026 emitAlignStackForApplyNative(apply, argc);
8027
8028 // Push newTarget.
8029 if constexpr (T::isConstructing()) {
8030 masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget()));
8031 }
8032
8033 // Push arguments.
8034 Label noCopy;
8035 masm.branchTestPtr(Assembler::Zero, argc, argc, &noCopy);
8036 {
8037 // Use scratch register to calculate stack space.
8038 masm.movePtr(argc, scratch);
8039
8040 // Reserve space for copying the arguments.
8041 NativeObject::elementsSizeMustNotOverflow();
8042 masm.lshiftPtr(Imm32(ValueShift), scratch);
8043 masm.subFromStackPtr(scratch);
8044
8045 // Compute the source and destination offsets into the stack.
8046 Register argvSrcBase = FramePointer;
8047 size_t argvSrcOffset =
8048 JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value);
8049 size_t argvDstOffset = 0;
8050
8051 Register argvIndex = tmpArgc;
8052 masm.move32(argc, argvIndex);
8053
8054 // Copy arguments.
8055 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
8056 argvDstOffset);
8057 }
8058 masm.bind(&noCopy);
8059
8060 // Push |this|.
8061 if constexpr (T::isConstructing()) {
8062 masm.pushValue(MagicValue(JS_IS_CONSTRUCTING));
8063 } else {
8064 masm.pushValue(ToValue(apply->thisValue()));
8065 }
8066}
8067
8068template <typename T>
8069void CodeGenerator::emitPushArrayAsNativeArguments(T* apply) {
8070 Register argc = ToRegister(apply->getArgc());
8071 Register elements = ToRegister(apply->getElements());
8072 Register tmpArgc = ToRegister(apply->getTempObject());
8073 Register scratch = ToRegister(apply->getTempForArgCopy());
8074
8075 // NB: argc and elements are mapped to the same register.
8076 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", 8076); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc == elements" ")"); do { MOZ_CrashSequence
(__null, 8076); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8077
8078 // Invariants guarded in the caller:
8079 // - the array is not too long
8080 // - the array length equals its initialized length
8081
8082 // The array length is our argc.
8083 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
8084
8085 // Align stack.
8086 emitAlignStackForApplyNative(apply, tmpArgc);
8087
8088 // Push newTarget.
8089 if constexpr (T::isConstructing()) {
8090 masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget()));
8091 }
8092
8093 // Skip the copy of arguments if there are none.
8094 Label noCopy;
8095 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
8096 {
8097 // |tmpArgc| is off-by-one, so adjust the offset accordingly.
8098 BaseObjectElementIndex srcPtr(elements, tmpArgc,
8099 -int32_t(sizeof(JS::Value)));
8100
8101 Label loop;
8102 masm.bind(&loop);
8103 masm.pushValue(srcPtr, scratch);
8104 masm.decBranchPtr(Assembler::NonZero, tmpArgc, Imm32(1), &loop);
8105 }
8106 masm.bind(&noCopy);
8107
8108 // Set argc in preparation for calling the native function.
8109 masm.load32(Address(elements, ObjectElements::offsetOfLength()), argc);
8110
8111 // Push |this|.
8112 if constexpr (T::isConstructing()) {
8113 masm.pushValue(MagicValue(JS_IS_CONSTRUCTING));
8114 } else {
8115 masm.pushValue(ToValue(apply->thisValue()));
8116 }
8117}
8118
8119void CodeGenerator::emitPushArguments(LApplyArgsNative* apply) {
8120 emitPushNativeArguments(apply);
8121}
8122
8123void CodeGenerator::emitPushArguments(LApplyArrayNative* apply) {
8124 emitPushArrayAsNativeArguments(apply);
8125}
8126
8127void CodeGenerator::emitPushArguments(LConstructArgsNative* construct) {
8128 emitPushNativeArguments(construct);
8129}
8130
8131void CodeGenerator::emitPushArguments(LConstructArrayNative* construct) {
8132 emitPushArrayAsNativeArguments(construct);
8133}
8134
8135void CodeGenerator::emitPushArguments(LApplyArgsObjNative* apply) {
8136 Register argc = ToRegister(apply->getArgc());
8137 Register argsObj = ToRegister(apply->getArgsObj());
8138 Register tmpArgc = ToRegister(apply->getTempObject());
8139 Register scratch = ToRegister(apply->getTempForArgCopy());
8140 Register scratch2 = ToRegister(apply->getTempExtra());
8141
8142 // NB: argc and argsObj are mapped to the same register.
8143 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", 8143); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "argc == argsObj" ")"); do { MOZ_CrashSequence
(__null, 8143); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8144
8145 // Load argc into tmpArgc.
8146 masm.loadArgumentsObjectLength(argsObj, tmpArgc);
8147
8148 // Align stack.
8149 emitAlignStackForApplyNative(apply, tmpArgc);
8150
8151 // Push arguments.
8152 Label noCopy, epilogue;
8153 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
8154 {
8155 // Use scratch register to calculate stack space.
8156 masm.movePtr(tmpArgc, scratch);
8157
8158 // Reserve space for copying the arguments.
8159 NativeObject::elementsSizeMustNotOverflow();
8160 masm.lshiftPtr(Imm32(ValueShift), scratch);
8161 masm.subFromStackPtr(scratch);
8162
8163 // Load arguments data.
8164 Register argvSrcBase = argsObj;
8165 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
8166 argvSrcBase);
8167 size_t argvSrcOffset = ArgumentsData::offsetOfArgs();
8168 size_t argvDstOffset = 0;
8169
8170 Register argvIndex = scratch2;
8171 masm.move32(tmpArgc, argvIndex);
8172
8173 // Copy the values.
8174 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
8175 argvDstOffset);
8176 }
8177 masm.bind(&noCopy);
8178
8179 // Set argc in preparation for calling the native function.
8180 masm.movePtr(tmpArgc, argc);
8181
8182 // Push |this|.
8183 masm.pushValue(ToValue(apply->thisValue()));
8184}
8185
8186template <typename T>
8187void CodeGenerator::emitApplyNative(T* apply) {
8188 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"
, 8189); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()"
") (" "isConstructing condition must be consistent" ")"); do
{ MOZ_CrashSequence(__null, 8189); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
8189 "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"
, 8189); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()"
") (" "isConstructing condition must be consistent" ")"); do
{ MOZ_CrashSequence(__null, 8189); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
8190
8191 WrappedFunction* target = apply->mir()->getSingleTarget();
8192 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"
, 8192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { MOZ_CrashSequence(__null, 8192); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8193
8194 JSNative native = target->native();
8195 if (apply->mir()->ignoresReturnValue() && target->hasJitInfo()) {
8196 const JSJitInfo* jitInfo = target->jitInfo();
8197 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
8198 native = jitInfo->ignoresReturnValueMethod;
8199 }
8200 }
8201
8202 // Push arguments, including newTarget and |this|.
8203 emitPushArguments(apply);
8204
8205 // Registers used for callWithABI() argument-passing.
8206 Register argContextReg = ToRegister(apply->getTempObject());
8207 Register argUintNReg = ToRegister(apply->getArgc());
8208 Register argVpReg = ToRegister(apply->getTempForArgCopy());
8209 Register tempReg = ToRegister(apply->getTempExtra());
8210
8211 // No unused stack for variadic calls.
8212 uint32_t unusedStack = 0;
8213
8214 // Pushed arguments don't change the pushed frames amount.
8215 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"
, 8215); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 8215); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8216
8217 // Create the exit frame and call the native.
8218 emitCallNative(apply, native, argContextReg, argUintNReg, argVpReg, tempReg,
8219 unusedStack);
8220
8221 // The exit frame is still on the stack.
8222 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", 8222); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()"
")"); do { MOZ_CrashSequence(__null, 8222); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8223
8224 // The next instruction is removing the exit frame, so there is no need for
8225 // leaveFakeExitFrame.
8226
8227 // Pop arguments and continue.
8228 masm.setFramePushed(frameSize());
8229 emitRestoreStackPointerFromFP();
8230}
8231
8232template <typename T>
8233void CodeGenerator::emitApplyArgsGuard(T* apply) {
8234 LSnapshot* snapshot = apply->snapshot();
8235 Register argcreg = ToRegister(apply->getArgc());
8236
8237 // Ensure that we have a reasonable number of arguments.
8238 bailoutCmp32(Assembler::Above, argcreg, Imm32(JIT_ARGS_LENGTH_MAX), snapshot);
8239}
8240
8241template <typename T>
8242void CodeGenerator::emitApplyArgsObjGuard(T* apply) {
8243 Register argsObj = ToRegister(apply->getArgsObj());
8244 Register temp = ToRegister(apply->getTempObject());
8245
8246 Label bail;
8247 masm.loadArgumentsObjectLength(argsObj, temp, &bail);
8248 masm.branch32(Assembler::Above, temp, Imm32(JIT_ARGS_LENGTH_MAX), &bail);
8249 bailoutFrom(&bail, apply->snapshot());
8250}
8251
8252template <typename T>
8253void CodeGenerator::emitApplyArrayGuard(T* apply) {
8254 LSnapshot* snapshot = apply->snapshot();
8255 Register elements = ToRegister(apply->getElements());
8256 Register tmp = ToRegister(apply->getTempObject());
8257
8258 Address length(elements, ObjectElements::offsetOfLength());
8259 masm.load32(length, tmp);
8260
8261 // Ensure that we have a reasonable number of arguments.
8262 bailoutCmp32(Assembler::Above, tmp, Imm32(JIT_ARGS_LENGTH_MAX), snapshot);
8263
8264 // Ensure that the array does not contain an uninitialized tail.
8265
8266 Address initializedLength(elements,
8267 ObjectElements::offsetOfInitializedLength());
8268 masm.sub32(initializedLength, tmp);
8269 bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot);
8270}
8271
8272void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) {
8273 emitApplyArgsGuard(apply);
8274 emitApplyGeneric(apply);
8275}
8276
8277void CodeGenerator::visitApplyArgsObj(LApplyArgsObj* apply) {
8278 emitApplyArgsObjGuard(apply);
8279 emitApplyGeneric(apply);
8280}
8281
8282void CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) {
8283 emitApplyArrayGuard(apply);
8284 emitApplyGeneric(apply);
8285}
8286
8287void CodeGenerator::visitConstructArgsGeneric(LConstructArgsGeneric* lir) {
8288 emitApplyArgsGuard(lir);
8289 emitApplyGeneric(lir);
8290}
8291
8292void CodeGenerator::visitConstructArrayGeneric(LConstructArrayGeneric* lir) {
8293 emitApplyArrayGuard(lir);
8294 emitApplyGeneric(lir);
8295}
8296
8297void CodeGenerator::visitApplyArgsNative(LApplyArgsNative* lir) {
8298 emitApplyArgsGuard(lir);
8299 emitApplyNative(lir);
8300}
8301
8302void CodeGenerator::visitApplyArgsObjNative(LApplyArgsObjNative* lir) {
8303 emitApplyArgsObjGuard(lir);
8304 emitApplyNative(lir);
8305}
8306
8307void CodeGenerator::visitApplyArrayNative(LApplyArrayNative* lir) {
8308 emitApplyArrayGuard(lir);
8309 emitApplyNative(lir);
8310}
8311
8312void CodeGenerator::visitConstructArgsNative(LConstructArgsNative* lir) {
8313 emitApplyArgsGuard(lir);
8314 emitApplyNative(lir);
8315}
8316
8317void CodeGenerator::visitConstructArrayNative(LConstructArrayNative* lir) {
8318 emitApplyArrayGuard(lir);
8319 emitApplyNative(lir);
8320}
8321
8322void CodeGenerator::visitBail(LBail* lir) { bailout(lir->snapshot()); }
8323
8324void CodeGenerator::visitUnreachable(LUnreachable* lir) {
8325 masm.assumeUnreachable("end-of-block assumed unreachable");
8326}
8327
8328void CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot* lir) {
8329 encode(lir->snapshot());
8330}
8331
8332void CodeGenerator::visitUnreachableResultV(LUnreachableResultV* lir) {
8333 masm.assumeUnreachable("must be unreachable");
8334}
8335
8336void CodeGenerator::visitUnreachableResultT(LUnreachableResultT* lir) {
8337 masm.assumeUnreachable("must be unreachable");
8338}
8339
8340void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) {
8341 // If we don't push anything on the stack, skip the check.
8342 if (omitOverRecursedStackCheck()) {
8343 return;
8344 }
8345
8346 // Ensure that this frame will not cross the stack limit.
8347 // This is a weak check, justified by Ion using the C stack: we must always
8348 // be some distance away from the actual limit, since if the limit is
8349 // crossed, an error must be thrown, which requires more frames.
8350 //
8351 // It must always be possible to trespass past the stack limit.
8352 // Ion may legally place frames very close to the limit. Calling additional
8353 // C functions may then violate the limit without any checking.
8354 //
8355 // Since Ion frames exist on the C stack, the stack limit may be
8356 // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
8357
8358 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
8359 // The OOL path is hit if the recursion depth has been exceeded.
8360 // Throw an InternalError for over-recursion.
8361
8362 // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
8363 // to save all live registers to avoid crashes if CheckOverRecursed triggers
8364 // a GC.
8365 saveLive(lir);
8366
8367 using Fn = bool (*)(JSContext*);
8368 if (lir->mir()->isResumingGenerator()) {
8369 callVM<Fn, CheckOverRecursedResumingGenerator>(lir);
8370 } else {
8371 callVM<Fn, CheckOverRecursed>(lir);
8372 }
8373
8374 restoreLive(lir);
8375 masm.jump(ool.rejoin());
8376 });
8377 addOutOfLineCode(ool, lir->mir());
8378
8379 // When resuming a generator we check the no-interrupt limit, because the VM
8380 // function must not handle interrupts for a half-initialized frame.
8381 const void* limitAddr =
8382 lir->mir()->isResumingGenerator()
8383 ? gen->runtime->addressOfJitStackLimitNoInterrupt()
8384 : gen->runtime->addressOfJitStackLimit();
8385
8386 // Conditional forward (unlikely) branch to failure.
8387 masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr),
8388 ool->entry());
8389 masm.bind(ool->rejoin());
8390}
8391
8392IonScriptCounts* CodeGenerator::maybeCreateScriptCounts() {
8393 // If scripts are being profiled, create a new IonScriptCounts for the
8394 // profiling data, which will be attached to the associated JSScript or
8395 // wasm module after code generation finishes.
8396 if (!gen->hasProfilingScripts()) {
8397 return nullptr;
8398 }
8399
8400 // This test inhibits IonScriptCount creation for wasm code which is
8401 // currently incompatible with wasm codegen for two reasons: (1) wasm code
8402 // must be serializable and script count codegen bakes in absolute
8403 // addresses, (2) wasm code does not have a JSScript with which to associate
8404 // code coverage data.
8405 JSScript* script = gen->outerInfo().script();
8406 if (!script) {
8407 return nullptr;
8408 }
8409
8410 auto counts = MakeUnique<IonScriptCounts>();
8411 if (!counts || !counts->init(graph.numBlocks())) {
8412 return nullptr;
8413 }
8414
8415 for (size_t i = 0; i < graph.numBlocks(); i++) {
8416 MBasicBlock* block = graph.getBlock(i)->mir();
8417
8418 uint32_t offset = 0;
8419 char* description = nullptr;
8420 if (MResumePoint* resume = block->entryResumePoint()) {
8421 // Find a PC offset in the outermost script to use. If this
8422 // block is from an inlined script, find a location in the
8423 // outer script to associate information about the inlining
8424 // with.
8425 while (resume->caller()) {
8426 resume = resume->caller();
8427 }
8428 offset = script->pcToOffset(resume->pc());
8429
8430 if (block->entryResumePoint()->caller()) {
8431 // Get the filename and line number of the inner script.
8432 JSScript* innerScript = block->info().script();
8433 description = js_pod_calloc<char>(200);
8434 if (description) {
8435 snprintf(description, 200, "%s:%u", innerScript->filename(),
8436 innerScript->lineno());
8437 }
8438 }
8439 }
8440
8441 if (!counts->block(i).init(block->id(), offset, description,
8442 block->numSuccessors())) {
8443 return nullptr;
8444 }
8445
8446 for (size_t j = 0; j < block->numSuccessors(); j++) {
8447 counts->block(i).setSuccessor(
8448 j, skipTrivialBlocks(block->getSuccessor(j))->id());
8449 }
8450 }
8451
8452 scriptCounts_ = counts.release();
8453 return scriptCounts_;
8454}
8455
8456// Structure for managing the state tracked for a block by script counters.
8457struct ScriptCountBlockState {
8458 IonBlockCounts& block;
8459 MacroAssembler& masm;
8460
8461 Sprinter printer;
8462
8463 public:
8464 ScriptCountBlockState(IonBlockCounts* block, MacroAssembler* masm)
8465 : block(*block), masm(*masm), printer(GetJitContext()->cx, false) {}
8466
8467 bool init() {
8468 if (!printer.init()) {
8469 return false;
8470 }
8471
8472 // Bump the hit count for the block at the start. This code is not
8473 // included in either the text for the block or the instruction byte
8474 // counts.
8475 masm.inc64(AbsoluteAddress(block.addressOfHitCount()));
8476
8477 // Collect human readable assembly for the code generated in the block.
8478 masm.setPrinter(&printer);
8479
8480 return true;
8481 }
8482
8483 void visitInstruction(LInstruction* ins) {
8484#ifdef JS_JITSPEW1
8485 // Prefix stream of assembly instructions with their LIR instruction
8486 // name and any associated high level info.
8487 if (const char* extra = ins->getExtraName()) {
8488 printer.printf("[%s:%s]\n", ins->opName(), extra);
8489 } else {
8490 printer.printf("[%s]\n", ins->opName());
8491 }
8492#endif
8493 }
8494
8495 ~ScriptCountBlockState() {
8496 masm.setPrinter(nullptr);
8497
8498 if (JS::UniqueChars str = printer.release()) {
8499 block.setCode(str.get());
8500 }
8501 }
8502};
8503
8504void CodeGenerator::branchIfInvalidated(Register temp, Label* invalidated) {
8505 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp);
8506 masm.propagateOOM(ionScriptLabels_.append(label));
8507
8508 // If IonScript::invalidationCount_ != 0, the script has been invalidated.
8509 masm.branch32(Assembler::NotEqual,
8510 Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0),
8511 invalidated);
8512}
8513
8514#ifdef DEBUG1
8515void CodeGenerator::emitAssertGCThingResult(Register input,
8516 const MDefinition* mir) {
8517 MIRType type = mir->type();
8518 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", 8519); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 8519); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
8519 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", 8519); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
")"); do { MOZ_CrashSequence(__null, 8519); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8520
8521 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
8522 regs.take(input);
8523
8524 Register temp = regs.takeAny();
8525 masm.push(temp);
8526
8527 // Don't check if the script has been invalidated. In that case invalid
8528 // types are expected (until we reach the OsiPoint and bailout).
8529 Label done;
8530 branchIfInvalidated(temp, &done);
8531
8532# ifndef JS_SIMULATOR
8533 // Check that we have a valid GC pointer.
8534 // Disable for wasm because we don't have a context on wasm compilation
8535 // threads and this needs a context.
8536 // Also disable for simulator builds because the C++ call is a lot slower
8537 // there than on actual hardware.
8538 if (JitOptions.fullDebugChecks && !IsCompilingWasm()) {
8539 saveVolatile();
8540 masm.setupUnalignedABICall(temp);
8541 masm.loadJSContext(temp);
8542 masm.passABIArg(temp);
8543 masm.passABIArg(input);
8544
8545 switch (type) {
8546 case MIRType::Object: {
8547 using Fn = void (*)(JSContext* cx, JSObject* obj);
8548 masm.callWithABI<Fn, AssertValidObjectPtr>();
8549 break;
8550 }
8551 case MIRType::String: {
8552 using Fn = void (*)(JSContext* cx, JSString* str);
8553 masm.callWithABI<Fn, AssertValidStringPtr>();
8554 break;
8555 }
8556 case MIRType::Symbol: {
8557 using Fn = void (*)(JSContext* cx, JS::Symbol* sym);
8558 masm.callWithABI<Fn, AssertValidSymbolPtr>();
8559 break;
8560 }
8561 case MIRType::BigInt: {
8562 using Fn = void (*)(JSContext* cx, JS::BigInt* bi);
8563 masm.callWithABI<Fn, AssertValidBigIntPtr>();
8564 break;
8565 }
8566 default:
8567 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp"
, 8567); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 8567); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
8568 }
8569
8570 restoreVolatile();
8571 }
8572# endif
8573
8574 masm.bind(&done);
8575 masm.pop(temp);
8576}
8577
8578void CodeGenerator::emitAssertResultV(const ValueOperand input,
8579 const MDefinition* mir) {
8580 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
8581 regs.take(input);
8582
8583 Register temp1 = regs.takeAny();
8584 Register temp2 = regs.takeAny();
8585 masm.pushRegs(temp1, temp2);
8586
8587 // Don't check if the script has been invalidated. In that case invalid
8588 // types are expected (until we reach the OsiPoint and bailout).
8589 Label done;
8590 branchIfInvalidated(temp1, &done);
8591
8592 // Check that we have a valid GC pointer.
8593 if (JitOptions.fullDebugChecks) {
8594 saveVolatile();
8595
8596 masm.pushValue(input);
8597 masm.moveStackPtrTo(temp1);
8598
8599 using Fn = void (*)(JSContext* cx, Value* v);
8600 masm.setupUnalignedABICall(temp2);
8601 masm.loadJSContext(temp2);
8602 masm.passABIArg(temp2);
8603 masm.passABIArg(temp1);
8604 masm.callWithABI<Fn, AssertValidValue>();
8605 masm.popValue(input);
8606 restoreVolatile();
8607 }
8608
8609 masm.bind(&done);
8610 masm.popRegs(temp2, temp1);
8611}
8612
8613void CodeGenerator::emitGCThingResultChecks(LInstruction* lir,
8614 MDefinition* mir) {
8615 if (lir->numDefs() == 0) {
8616 return;
8617 }
8618
8619 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", 8619); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { MOZ_CrashSequence
(__null, 8619); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8620 if (lir->getDef(0)->isBogusTemp()) {
8621 return;
8622 }
8623
8624 Register output = ToRegister(lir->getDef(0));
8625 emitAssertGCThingResult(output, mir);
8626}
8627
8628void CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir) {
8629 if (lir->numDefs() == 0) {
8630 return;
8631 }
8632
8633 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"
, 8633); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == BOX_PIECES"
")"); do { MOZ_CrashSequence(__null, 8633); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8634 if (!lir->getDef(0)->output()->isGeneralReg()) {
8635 return;
8636 }
8637
8638 ValueOperand output = ToOutValue(lir);
8639
8640 emitAssertResultV(output, mir);
8641}
8642
8643void CodeGenerator::emitWasmAnyrefResultChecks(LInstruction* lir,
8644 MDefinition* mir) {
8645 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", 8645); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mir->type() == MIRType::WasmAnyRef" ")"
); do { MOZ_CrashSequence(__null, 8645); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8646
8647 if (!JitOptions.fullDebugChecks) {
8648 return;
8649 }
8650
8651 wasm::MaybeRefType destType = mir->wasmRefType();
8652 if (!destType || !destType.value().isCastable()) {
8653 return;
8654 }
8655
8656 if (lir->numDefs() == 0) {
8657 return;
8658 }
8659
8660 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", 8660); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { MOZ_CrashSequence
(__null, 8660); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8661 if (lir->getDef(0)->isBogusTemp()) {
8662 return;
8663 }
8664
8665 if (lir->getDef(0)->output()->isMemory()) {
8666 return;
8667 }
8668 Register output = ToRegister(lir->getDef(0));
8669
8670 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
8671 regs.take(output);
8672
8673 BranchWasmRefIsSubtypeRegisters needs =
8674 MacroAssembler::regsForBranchWasmRefIsSubtype(destType.value());
8675
8676 Register temp1;
8677 Register temp2;
8678 Register temp3;
8679 if (needs.needSuperSTV) {
8680 temp1 = regs.takeAny();
8681 masm.push(temp1);
8682 }
8683 if (needs.needScratch1) {
8684 temp2 = regs.takeAny();
8685 masm.push(temp2);
8686 }
8687 if (needs.needScratch2) {
8688 temp3 = regs.takeAny();
8689 masm.push(temp3);
8690 }
8691
8692 if (needs.needSuperSTV) {
8693 uint32_t typeIndex =
8694 wasmCodeMeta()->types->indexOf(*destType.value().typeDef());
8695
8696 // When full debug checks are enabled, we always write the callee instance
8697 // pointer into its usual slot in the frame in our function prologue, so
8698 // that we can get it even if the InstanceReg is currently being used for
8699 // something else.
8700 masm.loadPtr(
8701 Address(FramePointer, wasm::FrameWithInstances::calleeInstanceOffset()),
8702 temp1);
8703 masm.loadPtr(
8704 Address(temp1, wasm::Instance::offsetInData(
8705 wasmCodeMeta()->offsetOfSuperTypeVector(typeIndex))),
8706 temp1);
8707 }
8708
8709 Label ok;
8710 masm.branchWasmRefIsSubtype(output, wasm::MaybeRefType(), destType.value(),
8711 &ok, /*onSuccess=*/true,
8712 /*signalNullChecks=*/false, temp1, temp2, temp3);
8713 masm.breakpoint();
8714 masm.bind(&ok);
8715
8716 if (needs.needScratch2) {
8717 masm.pop(temp3);
8718 }
8719 if (needs.needScratch1) {
8720 masm.pop(temp2);
8721 }
8722 if (needs.needSuperSTV) {
8723 masm.pop(temp1);
8724 }
8725
8726# ifdef JS_CODEGEN_ARM64
8727 masm.syncStackPtr();
8728# endif
8729}
8730
8731void CodeGenerator::emitDebugResultChecks(LInstruction* ins) {
8732 // In debug builds, check that LIR instructions return valid values.
8733
8734 MDefinition* mir = ins->mirRaw();
8735 if (!mir) {
8736 return;
8737 }
8738
8739 switch (mir->type()) {
8740 case MIRType::Object:
8741 case MIRType::String:
8742 case MIRType::Symbol:
8743 case MIRType::BigInt:
8744 emitGCThingResultChecks(ins, mir);
8745 break;
8746 case MIRType::Value:
8747 emitValueResultChecks(ins, mir);
8748 break;
8749 case MIRType::WasmAnyRef:
8750 emitWasmAnyrefResultChecks(ins, mir);
8751 break;
8752 default:
8753 break;
8754 }
8755}
8756
8757void CodeGenerator::emitDebugForceBailing(LInstruction* lir) {
8758 if (MOZ_LIKELY(!gen->options.ionBailAfterEnabled())(__builtin_expect(!!(!gen->options.ionBailAfterEnabled()),
1))
) {
8759 return;
8760 }
8761 if (!lir->snapshot()) {
8762 return;
8763 }
8764 if (lir->isOsiPoint()) {
8765 return;
8766 }
8767
8768 masm.comment("emitDebugForceBailing");
8769 const void* bailAfterCounterAddr =
8770 gen->runtime->addressOfIonBailAfterCounter();
8771
8772 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
8773
8774 Label done, notBail;
8775 masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterCounterAddr),
8776 Imm32(0), &done);
8777 {
8778 Register temp = regs.takeAny();
8779
8780 masm.push(temp);
8781 masm.load32(AbsoluteAddress(bailAfterCounterAddr), temp);
8782 masm.sub32(Imm32(1), temp);
8783 masm.store32(temp, AbsoluteAddress(bailAfterCounterAddr));
8784
8785 masm.branch32(Assembler::NotEqual, temp, Imm32(0), &notBail);
8786 {
8787 masm.pop(temp);
8788 bailout(lir->snapshot());
8789 }
8790 masm.bind(&notBail);
8791 masm.pop(temp);
8792 }
8793 masm.bind(&done);
8794}
8795#endif // DEBUG
8796
8797bool CodeGenerator::generateBody() {
8798 JitSpew(JitSpew_Codegen, "\n");
8799 AutoCreatedBy acb(masm, "CodeGenerator::generateBody");
8800
8801 JitSpew(JitSpew_Codegen, "==== BEGIN CodeGenerator::generateBody ====");
8802 counts_ = maybeCreateScriptCounts();
8803
8804 const bool compilingWasm = gen->compilingWasm();
8805
8806 for (size_t i = 0; i < graph.numBlocks(); i++) {
8807 current = graph.getBlock(i);
8808
8809 // Don't emit any code for trivial blocks, containing just a goto. Such
8810 // blocks are created to split critical edges, and if we didn't end up
8811 // putting any instructions in them, we can skip them.
8812 if (current->isTrivial()) {
8813 continue;
8814 }
8815
8816 if (gen->shouldCancel("Generate Code (block loop)")) {
8817 return false;
8818 }
8819
8820 // Skip out of line blocks for now. They will be emitted in
8821 // generateOutOfLineBlocks.
8822 if (current->isOutOfLine()) {
8823 continue;
8824 }
8825
8826 // Generate a basic block
8827 if (!generateBlock(current, i, counts_, compilingWasm)) {
8828 return false;
8829 }
8830 }
8831
8832 JitSpew(JitSpew_Codegen, "==== END CodeGenerator::generateBody ====\n");
8833 return true;
8834}
8835
8836bool CodeGenerator::generateBlock(LBlock* current, size_t blockNumber,
8837 IonScriptCounts* counts, bool compilingWasm) {
8838#ifdef JS_JITSPEW1
8839 const char* filename = nullptr;
8840 size_t lineNumber = 0;
8841 JS::LimitedColumnNumberOneOrigin columnNumber;
8842 if (current->mir()->info().script()) {
8843 filename = current->mir()->info().script()->filename();
8844 if (current->mir()->pc()) {
8845 lineNumber = PCToLineNumber(current->mir()->info().script(),
8846 current->mir()->pc(), &columnNumber);
8847 }
8848 }
8849 JitSpew(JitSpew_Codegen, "--------------------------------");
8850 JitSpew(JitSpew_Codegen, "# block%zu %s:%zu:%u%s:", blockNumber,
8851 filename ? filename : "?", lineNumber, columnNumber.oneOriginValue(),
8852 current->mir()->isLoopHeader() ? " (loop header)" : "");
8853#endif
8854
8855 if (current->mir()->isLoopHeader() && compilingWasm) {
8856 masm.nopAlign(CodeAlignment);
8857 }
8858
8859 masm.bind(current->label());
8860
8861 mozilla::Maybe<ScriptCountBlockState> blockCounts;
8862 if (counts) {
8863 blockCounts.emplace(&counts->block(blockNumber), &masm);
8864 if (!blockCounts->init()) {
8865 return false;
8866 }
8867 }
8868
8869 for (LInstructionIterator iter = current->begin(); iter != current->end();
8870 iter++) {
8871 if (gen->shouldCancel("Generate Code (instruction loop)")) {
8872 return false;
8873 }
8874 if (!alloc().ensureBallast()) {
8875 return false;
8876 }
8877
8878 perfSpewer().recordInstruction(masm, *iter);
8879#ifdef JS_JITSPEW1
8880 {
8881 AutoJitSpewMessage msg(JitSpew_Codegen,
8882 " # LIR=%s",
8883 iter->opName());
8884 if (const char* extra = iter->getExtraName()) {
8885 msg.append(":%s", extra);
8886 }
8887 }
8888#endif
8889
8890 if (counts) {
8891 blockCounts->visitInstruction(*iter);
8892 }
8893
8894#ifdef CHECK_OSIPOINT_REGISTERS1
8895 if (iter->safepoint() && !compilingWasm) {
8896 resetOsiPointRegs(iter->safepoint());
8897 }
8898#endif
8899
8900 if (!compilingWasm) {
8901 if (MDefinition* mir = iter->mirRaw()) {
8902 if (!addNativeToBytecodeEntry(mir->trackedSite())) {
8903 return false;
8904 }
8905 }
8906 }
8907
8908 setElement(*iter); // needed to encode correct snapshot location.
8909
8910#ifdef DEBUG1
8911 emitDebugForceBailing(*iter);
8912#endif
8913
8914 switch (iter->op()) {
8915#ifndef JS_CODEGEN_NONE
8916# define LIROP(op) \
8917 case LNode::Opcode::op: \
8918 visit##op(iter->to##op()); \
8919 break;
8920 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
(Uint32ToDouble)LIROP(Uint32ToFloat32)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(UnpackTime)LIROP(EpochMilliseconds)LIROP
(WasmBoundsCheckRange32)LIROP(WasmClampTable64Address)LIROP(WasmFence
)LIROP(WasmCallLandingPrePad)LIROP(WasmFindHandler)LIROP(WasmResumeBarrier
)LIROP(WasmMoveLaneSimd128)LIROP(WasmUnarySimd128)LIROP(AddDisposableResource
)LIROP(TakeDisposeCapability)
8921# undef LIROP
8922#endif
8923 case LNode::Opcode::Invalid:
8924 default:
8925 MOZ_CRASH("Invalid LIR op")do { do { } while (false); MOZ_ReportCrash("" "Invalid LIR op"
, "./../../../../js/src/jit/CodeGenerator.cpp", 8925); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid LIR op" ")"); do { MOZ_CrashSequence(__null
, 8925); __attribute__((nomerge)) ::abort(); } while (false);
} while (false)
;
8926 }
8927
8928#ifdef DEBUG1
8929 if (!counts) {
8930 emitDebugResultChecks(*iter);
8931 }
8932#endif
8933
8934 // To reduce the blast radius of OOM during codegen, bail out early if we've
8935 // OOM'ed.
8936 if (masm.oom()) {
8937 return false;
8938 }
8939 }
8940
8941 return true;
8942}
8943
8944bool CodeGenerator::generateOutOfLineBlocks() {
8945 AutoCreatedBy acb(masm, "CodeGeneratorShared::generateOutOfLineBlocks");
8946
8947 // Generate out of line basic blocks.
8948 // If we are generated some blocks at the end of the function, we need
8949 // to adjust the frame depth.
8950 if (!gen->branchHintingEnabled()) {
8951 return true;
8952 }
8953 masm.setFramePushed(frameDepth_);
8954
8955 const bool compilingWasm = gen->compilingWasm();
8956
8957 for (size_t i = 0; i < graph.numBlocks(); i++) {
8958 current = graph.getBlock(i);
8959
8960 if (gen->shouldCancel("Generate Code (block loop)")) {
8961 return false;
8962 }
8963
8964 if (current->isTrivial()) {
8965 continue;
8966 }
8967
8968 // If this block is marked as out of line, we need to generate it now.
8969 if (!current->isOutOfLine()) {
8970 continue;
8971 }
8972
8973 if (!generateBlock(current, i, counts_, compilingWasm)) {
8974 return false;
8975 }
8976 }
8977
8978 return !masm.oom();
8979}
8980
8981void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) {
8982 Register objReg = ToRegister(lir->output());
8983
8984 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", 8984); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { MOZ_CrashSequence
(__null, 8984); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8985 saveLive(lir);
8986
8987 JSObject* templateObject = lir->mir()->templateObject();
8988
8989 if (templateObject) {
8990 pushArg(ImmGCPtr(templateObject->shape()));
8991 pushArg(Imm32(lir->mir()->length()));
8992
8993 using Fn = ArrayObject* (*)(JSContext*, uint32_t, Handle<Shape*>);
8994 callVM<Fn, NewArrayWithShape>(lir);
8995 } else {
8996 pushArg(Imm32(GenericObject));
8997 pushArg(Imm32(lir->mir()->length()));
8998
8999 using Fn = ArrayObject* (*)(JSContext*, uint32_t, NewObjectKind);
9000 callVM<Fn, NewArrayOperation>(lir);
9001 }
9002
9003 masm.storeCallPointerResult(objReg);
9004
9005 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", 9005); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)"
")"); do { MOZ_CrashSequence(__null, 9005); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9006 restoreLive(lir);
9007}
9008
9009void CodeGenerator::visitAtan2D(LAtan2D* lir) {
9010 FloatRegister y = ToFloatRegister(lir->y());
9011 FloatRegister x = ToFloatRegister(lir->x());
9012
9013 using Fn = double (*)(double x, double y);
9014 masm.setupAlignedABICall();
9015 masm.passABIArg(y, ABIType::Float64);
9016 masm.passABIArg(x, ABIType::Float64);
9017 masm.callWithABI<Fn, ecmaAtan2>(ABIType::Float64);
9018
9019 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"
, 9019); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 9019); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9020}
9021
9022void CodeGenerator::visitHypot(LHypot* lir) {
9023 uint32_t numArgs = lir->numArgs();
9024 masm.setupAlignedABICall();
9025
9026 for (uint32_t i = 0; i < numArgs; ++i) {
9027 masm.passABIArg(ToFloatRegister(lir->getOperand(i)), ABIType::Float64);
9028 }
9029
9030 switch (numArgs) {
9031 case 2: {
9032 using Fn = double (*)(double x, double y);
9033 masm.callWithABI<Fn, ecmaHypot>(ABIType::Float64);
9034 break;
9035 }
9036 case 3: {
9037 using Fn = double (*)(double x, double y, double z);
9038 masm.callWithABI<Fn, hypot3>(ABIType::Float64);
9039 break;
9040 }
9041 case 4: {
9042 using Fn = double (*)(double x, double y, double z, double w);
9043 masm.callWithABI<Fn, hypot4>(ABIType::Float64);
9044 break;
9045 }
9046 default:
9047 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", 9047); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected number of arguments to hypot function."
")"); do { MOZ_CrashSequence(__null, 9047); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
9048 }
9049 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"
, 9049); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 9049); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9050}
9051
9052void CodeGenerator::visitNewArray(LNewArray* lir) {
9053 Register objReg = ToRegister(lir->output());
9054 Register tempReg = ToRegister(lir->temp0());
9055 DebugOnly<uint32_t> length = lir->mir()->length();
9056
9057 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", 9057); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT"
")"); do { MOZ_CrashSequence(__null, 9057); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9058
9059 if (lir->mir()->isVMCall()) {
9060 visitNewArrayCallVM(lir);
9061 return;
9062 }
9063
9064 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
9065 visitNewArrayCallVM(lir);
9066 masm.jump(ool.rejoin());
9067 });
9068 addOutOfLineCode(ool, lir->mir());
9069 TemplateObject templateObject(lir->mir()->templateObject());
9070#ifdef DEBUG1
9071 size_t numInlineElements = gc::GetGCKindSlots(templateObject.getAllocKind()) -
9072 ObjectElements::VALUES_PER_HEADER;
9073 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", 9074); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements"
")"); do { MOZ_CrashSequence(__null, 9074); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9074 "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", 9074); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements"
")"); do { MOZ_CrashSequence(__null, 9074); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9075#endif
9076 masm.createGCObject(objReg, tempReg, templateObject,
9077 lir->mir()->initialHeap(), ool->entry());
9078
9079 masm.bind(ool->rejoin());
9080}
9081
9082void CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) {
9083 Register lengthReg = ToRegister(lir->length());
9084 Register objReg = ToRegister(lir->output());
9085 Register tempReg = ToRegister(lir->temp0());
9086
9087 JSObject* templateObject = lir->mir()->templateObject();
9088 gc::Heap initialHeap = lir->mir()->initialHeap();
9089
9090 using Fn = ArrayObject* (*)(JSContext*, Handle<ArrayObject*>, int32_t length,
9091 gc::AllocSite*);
9092 OutOfLineCode* ool = oolCallVM<Fn, ArrayConstructorOneArg>(
9093 lir, ArgList(ImmGCPtr(templateObject), lengthReg, ImmPtr(nullptr)),
9094 StoreRegisterTo(objReg));
9095
9096 bool canInline = true;
9097 size_t inlineLength = 0;
9098 if (templateObject->as<ArrayObject>().hasFixedElements()) {
9099 size_t numSlots =
9100 gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
9101 inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
9102 } else {
9103 canInline = false;
9104 }
9105
9106 if (canInline) {
9107 // Try to do the allocation inline if the template object is big enough
9108 // for the length in lengthReg. If the length is bigger we could still
9109 // use the template object and not allocate the elements, but it's more
9110 // efficient to do a single big allocation than (repeatedly) reallocating
9111 // the array later on when filling it.
9112 masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength),
9113 ool->entry());
9114
9115 TemplateObject templateObj(templateObject);
9116 masm.createGCObject(objReg, tempReg, templateObj, initialHeap,
9117 ool->entry());
9118
9119 size_t lengthOffset = NativeObject::offsetOfFixedElements() +
9120 ObjectElements::offsetOfLength();
9121 masm.store32(lengthReg, Address(objReg, lengthOffset));
9122 } else {
9123 masm.jump(ool->entry());
9124 }
9125
9126 masm.bind(ool->rejoin());
9127}
9128
9129void CodeGenerator::visitNewIterator(LNewIterator* lir) {
9130 Register objReg = ToRegister(lir->output());
9131 Register tempReg = ToRegister(lir->temp0());
9132
9133 OutOfLineCode* ool;
9134 switch (lir->mir()->type()) {
9135 case MNewIterator::ArrayIterator: {
9136 using Fn = ArrayIteratorObject* (*)(JSContext*);
9137 ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(),
9138 StoreRegisterTo(objReg));
9139 break;
9140 }
9141 case MNewIterator::StringIterator: {
9142 using Fn = StringIteratorObject* (*)(JSContext*);
9143 ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(),
9144 StoreRegisterTo(objReg));
9145 break;
9146 }
9147 case MNewIterator::RegExpStringIterator: {
9148 using Fn = RegExpStringIteratorObject* (*)(JSContext*);
9149 ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(),
9150 StoreRegisterTo(objReg));
9151 break;
9152 }
9153 default:
9154 MOZ_CRASH("unexpected iterator type")do { do { } while (false); MOZ_ReportCrash("" "unexpected iterator type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9154); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected iterator type" ")"); do { MOZ_CrashSequence
(__null, 9154); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
9155 }
9156
9157 TemplateObject templateObject(lir->mir()->templateObject());
9158 masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default,
9159 ool->entry());
9160
9161 masm.bind(ool->rejoin());
9162}
9163
9164void CodeGenerator::visitNewTypedArrayInline(LNewTypedArrayInline* lir) {
9165 Register objReg = ToRegister(lir->output());
9166 Register tempReg = ToRegister(lir->temp0());
9167
9168 auto* templateObject = lir->mir()->templateObject();
9169 gc::Heap initialHeap = lir->mir()->initialHeap();
9170
9171 size_t n = templateObject->length();
9172 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", 9173); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths"
")"); do { MOZ_CrashSequence(__null, 9173); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9173 "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", 9173); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths"
")"); do { MOZ_CrashSequence(__null, 9173); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9174
9175 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t);
9176 auto* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
9177 lir, ArgList(ImmGCPtr(templateObject), Imm32(n)),
9178 StoreRegisterTo(objReg));
9179
9180 TemplateObject templateObj(templateObject);
9181 masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry());
9182
9183 masm.initTypedArraySlotsInline(objReg, tempReg, templateObject);
9184
9185 masm.bind(ool->rejoin());
9186}
9187
9188void CodeGenerator::visitNewTypedArray(LNewTypedArray* lir) {
9189 Register output = ToRegister(lir->output());
9190 Register temp1Reg = ToRegister(lir->temp0());
9191 Register temp2Reg = ToRegister(lir->temp1());
9192 Register lengthReg = ToRegister(lir->temp2());
9193 Register temp4Reg = ToRegister(lir->temp3());
9194
9195 auto* templateObject = lir->mir()->templateObject();
9196 gc::Heap initialHeap = lir->mir()->initialHeap();
9197
9198 size_t n = templateObject->length();
9199 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", 9200); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths"
")"); do { MOZ_CrashSequence(__null, 9200); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9200 "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", 9200); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths"
")"); do { MOZ_CrashSequence(__null, 9200); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9201
9202 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length);
9203 OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
9204 lir, ArgList(ImmGCPtr(templateObject), Imm32(n)),
9205 StoreRegisterTo(output));
9206
9207 TemplateObject templateObj(templateObject);
9208 masm.createGCObject(temp4Reg, temp1Reg, templateObj, initialHeap,
9209 ool->entry());
9210
9211 masm.move32(Imm32(n), lengthReg);
9212
9213 masm.initTypedArraySlots(temp4Reg, lengthReg, temp1Reg, temp2Reg,
9214 ool->entry(), templateObject);
9215 masm.mov(temp4Reg, output);
9216
9217 masm.bind(ool->rejoin());
9218}
9219
9220void CodeGenerator::visitNewTypedArrayDynamicLength(
9221 LNewTypedArrayDynamicLength* lir) {
9222 Register lengthReg = ToRegister(lir->length());
9223 Register output = ToRegister(lir->output());
9224 Register temp1Reg = ToRegister(lir->temp0());
9225 Register temp2Reg = ToRegister(lir->temp1());
9226 Register temp3Reg = ToRegister(lir->temp2());
9227
9228 JSObject* templateObject = lir->mir()->templateObject();
9229 gc::Heap initialHeap = lir->mir()->initialHeap();
9230
9231 auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>();
9232
9233 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length);
9234 OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
9235 lir, ArgList(ImmGCPtr(templateObject), lengthReg),
9236 StoreRegisterTo(output));
9237
9238 TemplateObject templateObj(templateObject);
9239 masm.createGCObject(temp3Reg, temp1Reg, templateObj, initialHeap,
9240 ool->entry());
9241
9242 masm.initTypedArraySlots(temp3Reg, lengthReg, temp1Reg, temp2Reg,
9243 ool->entry(), ttemplate);
9244 masm.mov(temp3Reg, output);
9245
9246 masm.bind(ool->rejoin());
9247}
9248
9249void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) {
9250 pushArg(ToRegister(lir->array()));
9251 pushArg(ImmGCPtr(lir->mir()->templateObject()));
9252
9253 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject);
9254 callVM<Fn, js::NewTypedArrayWithTemplateAndArray>(lir);
9255}
9256
9257void CodeGenerator::visitNewTypedArrayFromArrayBuffer(
9258 LNewTypedArrayFromArrayBuffer* lir) {
9259 pushArg(ToValue(lir->length()));
9260 pushArg(ToValue(lir->byteOffset()));
9261 pushArg(ToRegister(lir->arrayBuffer()));
9262 pushArg(ImmGCPtr(lir->mir()->templateObject()));
9263
9264 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject,
9265 HandleValue, HandleValue);
9266 callVM<Fn, js::NewTypedArrayWithTemplateAndBuffer>(lir);
9267}
9268
9269void CodeGenerator::visitBindFunction(LBindFunction* lir) {
9270 Register target = ToRegister(lir->target());
9271 Register temp1 = ToRegister(lir->temp0());
9272 Register temp2 = ToRegister(lir->temp1());
9273
9274 // Try to allocate a new BoundFunctionObject we can pass to the VM function.
9275 // If this fails, we set temp1 to nullptr so we do the allocation in C++.
9276 TemplateObject templateObject(lir->mir()->templateObject());
9277 Label allocOk, allocFailed;
9278 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
9279 &allocFailed);
9280 masm.jump(&allocOk);
9281
9282 masm.bind(&allocFailed);
9283 masm.movePtr(ImmWord(0), temp1);
9284
9285 masm.bind(&allocOk);
9286
9287 // Set temp2 to the address of the first argument on the stack.
9288 // Note that the Value slots used for arguments are currently aligned for a
9289 // JIT call, even though that's not strictly necessary for calling into C++.
9290 uint32_t argc = lir->mir()->numStackArgs();
9291 if (JitStackValueAlignment > 1) {
9292 argc = AlignBytes(argc, JitStackValueAlignment);
9293 }
9294 uint32_t unusedStack = UnusedStackBytesForCall(argc);
9295 masm.computeEffectiveAddress(Address(masm.getStackPointer(), unusedStack),
9296 temp2);
9297
9298 pushArg(temp1);
9299 pushArg(Imm32(lir->mir()->numStackArgs()));
9300 pushArg(temp2);
9301 pushArg(target);
9302
9303 using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*,
9304 uint32_t, Handle<BoundFunctionObject*>);
9305 callVM<Fn, js::BoundFunctionObject::functionBindImpl>(lir);
9306}
9307
9308void CodeGenerator::visitNewBoundFunction(LNewBoundFunction* lir) {
9309 Register output = ToRegister(lir->output());
9310 Register temp = ToRegister(lir->temp0());
9311
9312 JSObject* templateObj = lir->mir()->templateObj();
9313
9314 using Fn = BoundFunctionObject* (*)(JSContext*, Handle<BoundFunctionObject*>);
9315 OutOfLineCode* ool = oolCallVM<Fn, BoundFunctionObject::createWithTemplate>(
9316 lir, ArgList(ImmGCPtr(templateObj)), StoreRegisterTo(output));
9317
9318 TemplateObject templateObject(templateObj);
9319 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
9320 ool->entry());
9321
9322 masm.bind(ool->rejoin());
9323}
9324
9325void CodeGenerator::visitNewObjectVMCall(LNewObject* lir) {
9326 Register objReg = ToRegister(lir->output());
9327
9328 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", 9328); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { MOZ_CrashSequence
(__null, 9328); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9329 saveLive(lir);
9330
9331 JSObject* templateObject = lir->mir()->templateObject();
9332
9333 // If we're making a new object with a class prototype (that is, an object
9334 // that derives its class from its prototype instead of being
9335 // PlainObject::class_'d) from self-hosted code, we need a different init
9336 // function.
9337 switch (lir->mir()->mode()) {
9338 case MNewObject::ObjectLiteral: {
9339 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", 9339); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!templateObject" ")"); do { MOZ_CrashSequence
(__null, 9339); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9340 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
9341 pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
9342
9343 using Fn = JSObject* (*)(JSContext*, HandleScript, const jsbytecode* pc);
9344 callVM<Fn, NewObjectOperation>(lir);
9345 break;
9346 }
9347 case MNewObject::ObjectCreate: {
9348 pushArg(ImmGCPtr(templateObject));
9349
9350 using Fn = PlainObject* (*)(JSContext*, Handle<PlainObject*>);
9351 callVM<Fn, ObjectCreateWithTemplate>(lir);
9352 break;
9353 }
9354 }
9355
9356 masm.storeCallPointerResult(objReg);
9357
9358 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", 9358); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)"
")"); do { MOZ_CrashSequence(__null, 9358); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9359 restoreLive(lir);
9360}
9361
9362static bool ShouldInitFixedSlots(MIRGenerator* gen, LNewPlainObject* lir,
9363 const Shape* shape, uint32_t nfixed) {
9364 // Look for StoreFixedSlot instructions following an object allocation
9365 // that write to this object before a GC is triggered or this object is
9366 // passed to a VM call. If all fixed slots will be initialized, the
9367 // allocation code doesn't need to set the slots to |undefined|.
9368
9369 if (nfixed == 0) {
9370 return false;
9371 }
9372
9373#ifdef DEBUG1
9374 // The bailAfter testing function can trigger a bailout between allocating the
9375 // object and initializing the slots.
9376 if (gen->options.ionBailAfterEnabled()) {
9377 return true;
9378 }
9379#endif
9380
9381 // Keep track of the fixed slots that are initialized. initializedSlots is
9382 // a bit mask with a bit for each slot.
9383 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", 9383); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nfixed <= NativeObject::MAX_FIXED_SLOTS"
")"); do { MOZ_CrashSequence(__null, 9383); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9384 static_assert(NativeObject::MAX_FIXED_SLOTS <= 32,
9385 "Slot bits must fit in 32 bits");
9386 uint32_t initializedSlots = 0;
9387 uint32_t numInitialized = 0;
9388
9389 MInstruction* allocMir = lir->mir();
9390 MBasicBlock* block = allocMir->block();
9391
9392 // Skip the allocation instruction.
9393 MInstructionIterator iter = block->begin(allocMir);
9394 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", 9394); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "*iter == allocMir" ")"); do { MOZ_CrashSequence
(__null, 9394); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9395 iter++;
9396
9397 // Handle the leading shape guard, if present.
9398 for (; iter != block->end(); iter++) {
9399 if (iter->isConstant()) {
9400 // This instruction won't trigger a GC or read object slots.
9401 continue;
9402 }
9403 if (iter->isGuardShape()) {
9404 auto* guard = iter->toGuardShape();
9405 if (guard->object() != allocMir || guard->shape() != shape) {
9406 return true;
9407 }
9408 allocMir = guard;
9409 iter++;
9410 }
9411 break;
9412 }
9413
9414 for (; iter != block->end(); iter++) {
9415 if (iter->isConstant() || iter->isPostWriteBarrier()) {
9416 // These instructions won't trigger a GC or read object slots.
9417 continue;
9418 }
9419
9420 if (iter->isStoreFixedSlot()) {
9421 MStoreFixedSlot* store = iter->toStoreFixedSlot();
9422 if (store->object() != allocMir) {
9423 return true;
9424 }
9425
9426 // We may not initialize this object slot on allocation, so the
9427 // pre-barrier could read uninitialized memory. Simply disable
9428 // the barrier for this store: the object was just initialized
9429 // so the barrier is not necessary.
9430 store->setNeedsPreBarrier(false);
9431
9432 uint32_t slot = store->slot();
9433 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", 9433); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "slot < nfixed" ")"); do { MOZ_CrashSequence
(__null, 9433); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9434 if ((initializedSlots & (1 << slot)) == 0) {
9435 numInitialized++;
9436 initializedSlots |= (1 << slot);
9437
9438 if (numInitialized == nfixed) {
9439 // All fixed slots will be initialized.
9440 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", 9440); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uint32_t(std::popcount(initializedSlots)) == nfixed"
")"); do { MOZ_CrashSequence(__null, 9440); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9441 return false;
9442 }
9443 }
9444 continue;
9445 }
9446
9447 // Unhandled instruction, assume it bails or reads object slots.
9448 return true;
9449 }
9450
9451 MOZ_CRASH("Shouldn't get here")do { do { } while (false); MOZ_ReportCrash("" "Shouldn't get here"
, "./../../../../js/src/jit/CodeGenerator.cpp", 9451); AnnotateMozCrashReason
("MOZ_CRASH(" "Shouldn't get here" ")"); do { MOZ_CrashSequence
(__null, 9451); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
9452}
9453
9454void CodeGenerator::visitNewObject(LNewObject* lir) {
9455 Register objReg = ToRegister(lir->output());
9456 Register tempReg = ToRegister(lir->temp0());
9457
9458 if (lir->mir()->isVMCall()) {
9459 visitNewObjectVMCall(lir);
9460 return;
9461 }
9462
9463 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
9464 visitNewObjectVMCall(lir);
9465 masm.jump(ool.rejoin());
9466 });
9467 addOutOfLineCode(ool, lir->mir());
9468
9469 TemplateObject templateObject(lir->mir()->templateObject());
9470
9471 masm.createGCObject(objReg, tempReg, templateObject,
9472 lir->mir()->initialHeap(), ool->entry());
9473
9474 masm.bind(ool->rejoin());
9475}
9476
9477void CodeGenerator::visitNewPlainObject(LNewPlainObject* lir) {
9478 Register objReg = ToRegister(lir->output());
9479 Register temp0Reg = ToRegister(lir->temp0());
9480 Register temp1Reg = ToRegister(lir->temp1());
9481 Register shapeReg = ToRegister(lir->temp2());
9482
9483 auto* mir = lir->mir();
9484 const Shape* shape = mir->shape();
9485 gc::Heap initialHeap = mir->initialHeap();
9486 gc::AllocKind allocKind = mir->allocKind();
9487
9488 using Fn =
9489 JSObject* (*)(JSContext*, Handle<SharedShape*>, gc::AllocKind, gc::Heap);
9490 OutOfLineCode* ool = oolCallVM<Fn, NewPlainObjectOptimizedFallback>(
9491 lir,
9492 ArgList(ImmGCPtr(shape), Imm32(int32_t(allocKind)),
9493 Imm32(int32_t(initialHeap))),
9494 StoreRegisterTo(objReg));
9495
9496 bool initContents =
9497 ShouldInitFixedSlots(gen, lir, shape, mir->numFixedSlots());
9498
9499 masm.movePtr(ImmGCPtr(shape), shapeReg);
9500 masm.createPlainGCObject(
9501 objReg, shapeReg, temp0Reg, temp1Reg, mir->numFixedSlots(),
9502 mir->numDynamicSlots(), allocKind, initialHeap, ool->entry(),
9503 AllocSiteInput(gc::CatchAllAllocSite::Optimized), initContents);
9504
9505#ifdef DEBUG1
9506 // ShouldInitFixedSlots expects that the leading GuardShape will never fail,
9507 // so ensure the newly created object has the correct shape. Should the guard
9508 // ever fail, we may end up with uninitialized fixed slots, which can confuse
9509 // the GC.
9510 Label ok;
9511 masm.branchTestObjShape(Assembler::Equal, objReg, shape, temp0Reg, objReg,
9512 &ok);
9513 masm.assumeUnreachable("Newly created object has the correct shape");
9514 masm.bind(&ok);
9515#endif
9516
9517 masm.bind(ool->rejoin());
9518}
9519
9520void CodeGenerator::visitNewArrayObject(LNewArrayObject* lir) {
9521 Register objReg = ToRegister(lir->output());
9522 Register temp0Reg = ToRegister(lir->temp0());
9523 Register shapeReg = ToRegister(lir->temp1());
9524
9525 auto* mir = lir->mir();
9526 uint32_t arrayLength = mir->length();
9527
9528 gc::AllocKind allocKind = GuessArrayGCKind(arrayLength);
9529 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", 9530); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 9530); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9530 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", 9530); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 9530); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9531 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", 9531); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFinalizedKind(allocKind)" ")"); do { MOZ_CrashSequence
(__null, 9531); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9532
9533 uint32_t slotCount = GetGCKindSlots(allocKind);
9534 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", 9534); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "slotCount >= ObjectElements::VALUES_PER_HEADER"
")"); do { MOZ_CrashSequence(__null, 9534); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9535 uint32_t arrayCapacity = slotCount - ObjectElements::VALUES_PER_HEADER;
9536
9537 const Shape* shape = mir->shape();
9538
9539 NewObjectKind objectKind =
9540 mir->initialHeap() == gc::Heap::Tenured ? TenuredObject : GenericObject;
9541
9542 using Fn =
9543 ArrayObject* (*)(JSContext*, uint32_t, gc::AllocKind, NewObjectKind);
9544 OutOfLineCode* ool = oolCallVM<Fn, NewArrayObjectOptimizedFallback>(
9545 lir,
9546 ArgList(Imm32(arrayLength), Imm32(int32_t(allocKind)), Imm32(objectKind)),
9547 StoreRegisterTo(objReg));
9548
9549 masm.movePtr(ImmGCPtr(shape), shapeReg);
9550 masm.createArrayWithFixedElements(
9551 objReg, shapeReg, temp0Reg, InvalidReg, arrayLength, arrayCapacity, 0, 0,
9552 allocKind, mir->initialHeap(), ool->entry(),
9553 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
9554 masm.bind(ool->rejoin());
9555}
9556
9557void CodeGenerator::visitNewNamedLambdaObject(LNewNamedLambdaObject* lir) {
9558 Register objReg = ToRegister(lir->output());
9559 Register tempReg = ToRegister(lir->temp0());
9560 const CompileInfo& info = lir->mir()->block()->info();
9561 gc::Heap heap = lir->mir()->initialHeap();
9562
9563 using Fn = js::NamedLambdaObject* (*)(JSContext*, HandleFunction, gc::Heap);
9564 OutOfLineCode* ool = oolCallVM<Fn, NamedLambdaObject::createWithoutEnclosing>(
9565 lir, ArgList(info.funMaybeLazy(), Imm32(uint32_t(heap))),
9566 StoreRegisterTo(objReg));
9567
9568 TemplateObject templateObject(lir->mir()->templateObj());
9569
9570 masm.createGCObject(objReg, tempReg, templateObject, heap, ool->entry(),
9571 /* initContents = */ true,
9572 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
9573
9574 masm.bind(ool->rejoin());
9575}
9576
9577void CodeGenerator::visitNewCallObject(LNewCallObject* lir) {
9578 Register objReg = ToRegister(lir->output());
9579 Register tempReg = ToRegister(lir->temp0());
9580
9581 CallObject* templateObj = lir->mir()->templateObject();
9582 gc::Heap heap = lir->mir()->initialHeap();
9583
9584 // todo: should get a specialized fallback that passes site
9585 using Fn = CallObject* (*)(JSContext*, Handle<SharedShape*>, gc::Heap);
9586 OutOfLineCode* ool = oolCallVM<Fn, CallObject::createWithShape>(
9587 lir, ArgList(ImmGCPtr(templateObj->sharedShape()), Imm32(uint32_t(heap))),
9588 StoreRegisterTo(objReg));
9589
9590 // Inline call object creation, using the OOL path only for tricky cases.
9591 TemplateObject templateObject(templateObj);
9592
9593 masm.createGCObject(objReg, tempReg, templateObject, heap, ool->entry(),
9594 /* initContents = */ true,
9595 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
9596
9597 masm.bind(ool->rejoin());
9598}
9599
9600void CodeGenerator::visitNewMapObject(LNewMapObject* lir) {
9601 Register output = ToRegister(lir->output());
9602 Register temp = ToRegister(lir->temp0());
9603
9604 // Note: pass nullptr for |proto| to use |Map.prototype|.
9605 using Fn = MapObject* (*)(JSContext*, HandleObject);
9606 auto* ool = oolCallVM<Fn, MapObject::create>(lir, ArgList(ImmPtr(nullptr)),
9607 StoreRegisterTo(output));
9608
9609 TemplateObject templateObject(lir->mir()->templateObject());
9610 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
9611 ool->entry());
9612 masm.bind(ool->rejoin());
9613}
9614
9615void CodeGenerator::visitNewSetObject(LNewSetObject* lir) {
9616 Register output = ToRegister(lir->output());
9617 Register temp = ToRegister(lir->temp0());
9618
9619 // Note: pass nullptr for |proto| to use |Set.prototype|.
9620 using Fn = SetObject* (*)(JSContext*, HandleObject);
9621 auto* ool = oolCallVM<Fn, SetObject::create>(lir, ArgList(ImmPtr(nullptr)),
9622 StoreRegisterTo(output));
9623
9624 TemplateObject templateObject(lir->mir()->templateObject());
9625 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
9626 ool->entry());
9627 masm.bind(ool->rejoin());
9628}
9629
9630void CodeGenerator::visitNewMapObjectFromIterable(
9631 LNewMapObjectFromIterable* lir) {
9632 ValueOperand iterable = ToValue(lir->iterable());
9633 Register output = ToRegister(lir->output());
9634 Register temp1 = ToRegister(lir->temp0());
9635 Register temp2 = ToRegister(lir->temp1());
9636
9637 // Allocate a new MapObject. If this fails we pass nullptr for
9638 // allocatedFromJit.
9639 Label failedAlloc, vmCall, done;
9640 TemplateObject templateObject(lir->mir()->templateObject());
9641 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
9642 &failedAlloc);
9643
9644 // We're done if |iterable| is null or undefined.
9645 masm.branchIfNotNullOrUndefined(iterable, &vmCall);
9646 masm.movePtr(temp1, output);
9647 masm.jump(&done);
9648
9649 masm.bind(&failedAlloc);
9650 masm.movePtr(ImmPtr(nullptr), temp1);
9651
9652 masm.bind(&vmCall);
9653
9654 pushArg(temp1); // allocatedFromJit
9655 pushArg(iterable);
9656 pushArg(ImmPtr(nullptr)); // proto
9657
9658 using Fn = MapObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>,
9659 Handle<MapObject*>);
9660 callVM<Fn, MapObject::createFromIterable>(lir);
9661
9662 masm.bind(&done);
9663}
9664
9665void CodeGenerator::visitNewSetObjectFromIterable(
9666 LNewSetObjectFromIterable* lir) {
9667 ValueOperand iterable = ToValue(lir->iterable());
9668 Register output = ToRegister(lir->output());
9669 Register temp1 = ToRegister(lir->temp0());
9670 Register temp2 = ToRegister(lir->temp1());
9671
9672 // Allocate a new SetObject. If this fails we pass nullptr for
9673 // allocatedFromJit.
9674 Label failedAlloc, vmCall, done;
9675 TemplateObject templateObject(lir->mir()->templateObject());
9676 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
9677 &failedAlloc);
9678
9679 // We're done if |iterable| is null or undefined.
9680 masm.branchIfNotNullOrUndefined(iterable, &vmCall);
9681 masm.movePtr(temp1, output);
9682 masm.jump(&done);
9683
9684 masm.bind(&failedAlloc);
9685 masm.movePtr(ImmPtr(nullptr), temp1);
9686
9687 masm.bind(&vmCall);
9688
9689 pushArg(temp1); // allocatedFromJit
9690 pushArg(iterable);
9691 pushArg(ImmPtr(nullptr)); // proto
9692
9693 using Fn = SetObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>,
9694 Handle<SetObject*>);
9695 callVM<Fn, SetObject::createFromIterable>(lir);
9696
9697 masm.bind(&done);
9698}
9699
9700void CodeGenerator::visitNewStringObject(LNewStringObject* lir) {
9701 Register input = ToRegister(lir->input());
9702 Register output = ToRegister(lir->output());
9703 Register temp = ToRegister(lir->temp0());
9704
9705 StringObject* templateObj = lir->mir()->templateObj();
9706
9707 using Fn = JSObject* (*)(JSContext*, HandleString);
9708 OutOfLineCode* ool = oolCallVM<Fn, NewStringObject>(lir, ArgList(input),
9709 StoreRegisterTo(output));
9710
9711 TemplateObject templateObject(templateObj);
9712 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
9713 ool->entry());
9714
9715 masm.loadStringLength(input, temp);
9716
9717 masm.storeValue(JSVAL_TYPE_STRING, input,
9718 Address(output, StringObject::offsetOfPrimitiveValue()));
9719 masm.storeValue(JSVAL_TYPE_INT32, temp,
9720 Address(output, StringObject::offsetOfLength()));
9721
9722 masm.bind(ool->rejoin());
9723}
9724
9725void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) {
9726 Register obj = ToRegister(lir->object());
9727 Register value = ToRegister(lir->value());
9728
9729 pushArg(value);
9730 pushArg(ToValue(lir->id()));
9731 pushArg(obj);
9732 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
9733
9734 using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue,
9735 HandleObject);
9736 callVM<Fn, InitElemGetterSetterOperation>(lir);
9737}
9738
9739void CodeGenerator::visitMutateProto(LMutateProto* lir) {
9740 Register objReg = ToRegister(lir->object());
9741
9742 pushArg(ToValue(lir->value()));
9743 pushArg(objReg);
9744
9745 using Fn =
9746 bool (*)(JSContext* cx, Handle<PlainObject*> obj, HandleValue value);
9747 callVM<Fn, MutatePrototype>(lir);
9748}
9749
9750void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) {
9751 Register obj = ToRegister(lir->object());
9752 Register value = ToRegister(lir->value());
9753
9754 pushArg(value);
9755 pushArg(ImmGCPtr(lir->mir()->name()));
9756 pushArg(obj);
9757 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
9758
9759 using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject,
9760 Handle<PropertyName*>, HandleObject);
9761 callVM<Fn, InitPropGetterSetterOperation>(lir);
9762}
9763
9764void CodeGenerator::visitCreateThis(LCreateThis* lir) {
9765 const LAllocation* callee = lir->callee();
9766 const LAllocation* newTarget = lir->newTarget();
9767
9768 if (newTarget->isConstant()) {
9769 pushArg(ImmGCPtr(&newTarget->toConstant()->toObject()));
9770 } else {
9771 pushArg(ToRegister(newTarget));
9772 }
9773
9774 if (callee->isConstant()) {
9775 pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
9776 } else {
9777 pushArg(ToRegister(callee));
9778 }
9779
9780 using Fn = bool (*)(JSContext* cx, HandleObject callee,
9781 HandleObject newTarget, MutableHandleValue rval);
9782 callVM<Fn, jit::CreateThisFromIon>(lir);
9783}
9784
9785void CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) {
9786#ifdef DEBUG1
9787 // This should be getting constructed in the first block only, and not any OSR
9788 // entry blocks. Warp builds it in block 0 except in a generator or async
9789 // function, where block 0 forks between the fresh call and a resume.
9790 MBasicBlock* block = lir->mir()->block();
9791 JSScript* script = block->info().script();
9792 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", 9792); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "block != block->graph().osrBlock()" ")"
); do { MOZ_CrashSequence(__null, 9792); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9793 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", 9793); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "block->id() == 0" ")"); do { MOZ_CrashSequence
(__null, 9793); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
9794#endif
9795
9796 Register callObj = ToRegister(lir->callObject());
9797 Register temp0 = ToRegister(lir->temp0());
9798 Label done;
9799
9800 if (ArgumentsObject* templateObj = lir->mir()->templateObject()) {
9801 Register objTemp = ToRegister(lir->temp1());
9802 Register cxTemp = ToRegister(lir->temp2());
9803
9804 masm.Push(callObj);
9805
9806 // Try to allocate an arguments object. This will leave the reserved
9807 // slots uninitialized, so it's important we don't GC until we
9808 // initialize these slots in ArgumentsObject::finishForIonPure.
9809 Label failure;
9810 TemplateObject templateObject(templateObj);
9811 masm.createGCObject(objTemp, temp0, templateObject, gc::Heap::Default,
9812 &failure,
9813 /* initContents = */ false);
9814
9815 masm.moveStackPtrTo(temp0);
9816 masm.addPtr(Imm32(masm.framePushed()), temp0);
9817
9818 using Fn =
9819 ArgumentsObject* (*)(JSContext * cx, jit::JitFrameLayout * frame,
9820 JSObject * scopeChain, ArgumentsObject * obj);
9821 masm.setupAlignedABICall();
9822 masm.loadJSContext(cxTemp);
9823 masm.passABIArg(cxTemp);
9824 masm.passABIArg(temp0);
9825 masm.passABIArg(callObj);
9826 masm.passABIArg(objTemp);
9827
9828 masm.callWithABI<Fn, ArgumentsObject::finishForIonPure>();
9829 masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
9830
9831 // Discard saved callObj on the stack.
9832 masm.addToStackPtr(Imm32(sizeof(uintptr_t)));
9833 masm.jump(&done);
9834
9835 masm.bind(&failure);
9836 masm.Pop(callObj);
9837 }
9838
9839 masm.moveStackPtrTo(temp0);
9840 masm.addPtr(Imm32(frameSize()), temp0);
9841
9842 pushArg(callObj);
9843 pushArg(temp0);
9844
9845 using Fn = ArgumentsObject* (*)(JSContext*, JitFrameLayout*, HandleObject);
9846 callVM<Fn, ArgumentsObject::createForIon>(lir);
9847
9848 masm.bind(&done);
9849}
9850
9851void CodeGenerator::visitCreateInlinedArgumentsObject(
9852 LCreateInlinedArgumentsObject* lir) {
9853 Register callObj = ToRegister(lir->getCallObject());
9854 Register callee = ToRegister(lir->getCallee());
9855 Register argsAddress = ToRegister(lir->temp1());
9856 Register argsObj = ToRegister(lir->temp2());
9857
9858 // TODO: Do we have to worry about alignment here?
9859
9860 // Create a contiguous array of values for ArgumentsObject::create
9861 // by pushing the arguments onto the stack in reverse order.
9862 uint32_t argc = lir->mir()->numActuals();
9863 for (uint32_t i = 0; i < argc; i++) {
9864 uint32_t argNum = argc - i - 1;
9865 uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(argNum);
9866 ConstantOrRegister arg =
9867 toConstantOrRegister(lir, index, lir->mir()->getArg(argNum)->type());
9868 masm.Push(arg);
9869 }
9870 masm.moveStackPtrTo(argsAddress);
9871
9872 Label done;
9873 if (ArgumentsObject* templateObj = lir->mir()->templateObject()) {
9874 LiveRegisterSet liveRegs;
9875 liveRegs.add(callObj);
9876 liveRegs.add(callee);
9877
9878 masm.PushRegsInMask(liveRegs);
9879
9880 // We are free to clobber all registers, as LCreateInlinedArgumentsObject is
9881 // a call instruction.
9882 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
9883 allRegs.take(callObj);
9884 allRegs.take(callee);
9885 allRegs.take(argsObj);
9886 allRegs.take(argsAddress);
9887
9888 Register temp3 = allRegs.takeAny();
9889 Register temp4 = allRegs.takeAny();
9890
9891 // Try to allocate an arguments object. This will leave the reserved slots
9892 // uninitialized, so it's important we don't GC until we initialize these
9893 // slots in ArgumentsObject::finishForIonPure.
9894 Label failure;
9895 TemplateObject templateObject(templateObj);
9896 masm.createGCObject(argsObj, temp3, templateObject, gc::Heap::Default,
9897 &failure,
9898 /* initContents = */ false);
9899
9900 Register numActuals = temp3;
9901 masm.move32(Imm32(argc), numActuals);
9902
9903 using Fn = ArgumentsObject* (*)(JSContext*, JSObject*, JSFunction*, Value*,
9904 uint32_t, ArgumentsObject*);
9905 masm.setupAlignedABICall();
9906 masm.loadJSContext(temp4);
9907 masm.passABIArg(temp4);
9908 masm.passABIArg(callObj);
9909 masm.passABIArg(callee);
9910 masm.passABIArg(argsAddress);
9911 masm.passABIArg(numActuals);
9912 masm.passABIArg(argsObj);
9913
9914 masm.callWithABI<Fn, ArgumentsObject::finishInlineForIonPure>();
9915 masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
9916
9917 // Discard saved callObj, callee, and values array on the stack.
9918 masm.addToStackPtr(
9919 Imm32(MacroAssembler::PushRegsInMaskSizeInBytes(liveRegs) +
9920 argc * sizeof(Value)));
9921 masm.jump(&done);
9922
9923 masm.bind(&failure);
9924 masm.PopRegsInMask(liveRegs);
9925
9926 // Reload argsAddress because it may have been overridden.
9927 masm.moveStackPtrTo(argsAddress);
9928 }
9929
9930 pushArg(Imm32(argc));
9931 pushArg(callObj);
9932 pushArg(callee);
9933 pushArg(argsAddress);
9934
9935 using Fn = ArgumentsObject* (*)(JSContext*, Value*, HandleFunction,
9936 HandleObject, uint32_t);
9937 callVM<Fn, ArgumentsObject::createForInlinedIon>(lir);
9938
9939 // Discard the array of values.
9940 masm.freeStack(argc * sizeof(Value));
9941
9942 masm.bind(&done);
9943}
9944
9945template <class GetInlinedArgument>
9946void CodeGenerator::emitGetInlinedArgument(GetInlinedArgument* lir,
9947 Register index,
9948 ValueOperand output) {
9949 uint32_t numActuals = lir->mir()->numActuals();
9950 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", 9950); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "numActuals <= ArgumentsObject::MaxInlinedArgs"
")"); do { MOZ_CrashSequence(__null, 9950); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9951
9952 // The index has already been bounds-checked, so the code we
9953 // generate here should be unreachable. We can end up in this
9954 // situation in self-hosted code using GetArgument(), or in a
9955 // monomorphically inlined function if we've inlined some CacheIR
9956 // that was created for a different caller.
9957 if (numActuals == 0) {
9958 masm.assumeUnreachable("LGetInlinedArgument: invalid index");
9959 return;
9960 }
9961
9962 // Check the first n-1 possible indices.
9963 Label done;
9964 for (uint32_t i = 0; i < numActuals - 1; i++) {
9965 Label skip;
9966 ConstantOrRegister arg = toConstantOrRegister(
9967 lir, GetInlinedArgument::ArgIndex(i), lir->mir()->getArg(i)->type());
9968 masm.branch32(Assembler::NotEqual, index, Imm32(i), &skip);
9969 masm.moveValue(arg, output);
9970
9971 masm.jump(&done);
9972 masm.bind(&skip);
9973 }
9974
9975#ifdef DEBUG1
9976 Label skip;
9977 masm.branch32(Assembler::Equal, index, Imm32(numActuals - 1), &skip);
9978 masm.assumeUnreachable("LGetInlinedArgument: invalid index");
9979 masm.bind(&skip);
9980#endif
9981
9982 // The index has already been bounds-checked, so load the last argument.
9983 uint32_t lastIdx = numActuals - 1;
9984 ConstantOrRegister arg =
9985 toConstantOrRegister(lir, GetInlinedArgument::ArgIndex(lastIdx),
9986 lir->mir()->getArg(lastIdx)->type());
9987 masm.moveValue(arg, output);
9988 masm.bind(&done);
9989}
9990
9991void CodeGenerator::visitGetInlinedArgument(LGetInlinedArgument* lir) {
9992 Register index = ToRegister(lir->getIndex());
9993 ValueOperand output = ToOutValue(lir);
9994
9995 emitGetInlinedArgument(lir, index, output);
9996}
9997
9998void CodeGenerator::visitGetInlinedArgumentHole(LGetInlinedArgumentHole* lir) {
9999 Register index = ToRegister(lir->getIndex());
10000 ValueOperand output = ToOutValue(lir);
10001
10002 uint32_t numActuals = lir->mir()->numActuals();
10003
10004 if (numActuals == 0) {
10005 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
10006 masm.moveValue(UndefinedValue(), output);
10007 return;
10008 }
10009
10010 Label outOfBounds, done;
10011 masm.branch32(Assembler::AboveOrEqual, index, Imm32(numActuals),
10012 &outOfBounds);
10013
10014 emitGetInlinedArgument(lir, index, output);
10015 masm.jump(&done);
10016
10017 masm.bind(&outOfBounds);
10018 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
10019 masm.moveValue(UndefinedValue(), output);
10020
10021 masm.bind(&done);
10022}
10023
10024void CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir) {
10025 Register temp = ToRegister(lir->temp0());
10026 Register argsObj = ToRegister(lir->argsObject());
10027 ValueOperand out = ToOutValue(lir);
10028
10029 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
10030 temp);
10031 Address argAddr(temp, ArgumentsData::offsetOfArgs() +
10032 lir->mir()->argno() * sizeof(Value));
10033 masm.loadValue(argAddr, out);
10034#ifdef DEBUG1
10035 Label success;
10036 masm.branchTestMagic(Assembler::NotEqual, out, &success);
10037 masm.assumeUnreachable(
10038 "Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
10039 masm.bind(&success);
10040#endif
10041}
10042
10043void CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir) {
10044 Register temp = ToRegister(lir->temp0());
10045 Register argsObj = ToRegister(lir->argsObject());
10046 ValueOperand value = ToValue(lir->value());
10047
10048 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
10049 temp);
10050 Address argAddr(temp, ArgumentsData::offsetOfArgs() +
10051 lir->mir()->argno() * sizeof(Value));
10052 emitPreBarrier(argAddr);
10053 MIRType valueType = lir->mir()->value()->type();
10054 MOZ_RELEASE_ASSERT(!IsMagicType(valueType))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsMagicType(valueType))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsMagicType(valueType)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!IsMagicType(valueType)"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10054); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "!IsMagicType(valueType)" ")"); do {
MOZ_CrashSequence(__null, 10054); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
10055 if (valueType == MIRType::Value) {
10056 Label notMagic;
10057 masm.branchTestMagic(Assembler::NotEqual, value, &notMagic);
10058 masm.assumeUnreachable("Unexpected magic value stored to ArgumentsObject");
10059 masm.bind(&notMagic);
10060 }
10061 masm.storeValue(value, argAddr);
10062}
10063
10064void CodeGenerator::visitLoadArgumentsObjectArg(LLoadArgumentsObjectArg* lir) {
10065 Register temp = ToRegister(lir->temp0());
10066 Register argsObj = ToRegister(lir->argsObject());
10067 Register index = ToRegister(lir->index());
10068 ValueOperand out = ToOutValue(lir);
10069
10070 Label bail;
10071 masm.loadArgumentsObjectElement(argsObj, index, out, temp, &bail);
10072 bailoutFrom(&bail, lir->snapshot());
10073}
10074
10075void CodeGenerator::visitLoadArgumentsObjectArgHole(
10076 LLoadArgumentsObjectArgHole* lir) {
10077 Register temp = ToRegister(lir->temp0());
10078 Register argsObj = ToRegister(lir->argsObject());
10079 Register index = ToRegister(lir->index());
10080 ValueOperand out = ToOutValue(lir);
10081
10082 Label bail;
10083 masm.loadArgumentsObjectElementHole(argsObj, index, out, temp, &bail);
10084 bailoutFrom(&bail, lir->snapshot());
10085}
10086
10087void CodeGenerator::visitInArgumentsObjectArg(LInArgumentsObjectArg* lir) {
10088 Register temp = ToRegister(lir->temp0());
10089 Register argsObj = ToRegister(lir->argsObject());
10090 Register index = ToRegister(lir->index());
10091 Register out = ToRegister(lir->output());
10092
10093 Label bail;
10094 masm.loadArgumentsObjectElementExists(argsObj, index, out, temp, &bail);
10095 bailoutFrom(&bail, lir->snapshot());
10096}
10097
10098void CodeGenerator::visitArgumentsObjectLength(LArgumentsObjectLength* lir) {
10099 Register argsObj = ToRegister(lir->argsObject());
10100 Register out = ToRegister(lir->output());
10101
10102 Label bail;
10103 masm.loadArgumentsObjectLength(argsObj, out, &bail);
10104 bailoutFrom(&bail, lir->snapshot());
10105}
10106
10107void CodeGenerator::visitArrayFromArgumentsObject(
10108 LArrayFromArgumentsObject* lir) {
10109 pushArg(ToRegister(lir->argsObject()));
10110
10111 using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>);
10112 callVM<Fn, js::ArrayFromArgumentsObject>(lir);
10113}
10114
10115void CodeGenerator::visitGuardArgumentsObjectFlags(
10116 LGuardArgumentsObjectFlags* lir) {
10117 Register argsObj = ToRegister(lir->argsObject());
10118 Register temp = ToRegister(lir->temp0());
10119
10120 Label bail;
10121 masm.branchTestArgumentsObjectFlags(argsObj, temp, lir->mir()->flags(),
10122 Assembler::NonZero, &bail);
10123 bailoutFrom(&bail, lir->snapshot());
10124}
10125
10126void CodeGenerator::visitGuardObjectHasSameRealm(
10127 LGuardObjectHasSameRealm* lir) {
10128 Register obj = ToRegister(lir->object());
10129 Register temp = ToRegister(lir->temp0());
10130
10131 Label bail;
10132 masm.guardObjectHasSameRealm(obj, temp, &bail);
10133 bailoutFrom(&bail, lir->snapshot());
10134}
10135
10136void CodeGenerator::visitBoundFunctionNumArgs(LBoundFunctionNumArgs* lir) {
10137 Register obj = ToRegister(lir->object());
10138 Register output = ToRegister(lir->output());
10139
10140 masm.unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()),
10141 output);
10142 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), output);
10143}
10144
10145void CodeGenerator::visitGuardBoundFunctionIsConstructor(
10146 LGuardBoundFunctionIsConstructor* lir) {
10147 Register obj = ToRegister(lir->object());
10148
10149 Label bail;
10150 Address flagsSlot(obj, BoundFunctionObject::offsetOfFlagsSlot());
10151 masm.branchTest32(Assembler::Zero, flagsSlot,
10152 Imm32(BoundFunctionObject::IsConstructorFlag), &bail);
10153 bailoutFrom(&bail, lir->snapshot());
10154}
10155
10156void CodeGenerator::visitReturnFromCtor(LReturnFromCtor* lir) {
10157 ValueOperand value = ToValue(lir->value());
10158 Register obj = ToRegister(lir->object());
10159 Register output = ToRegister(lir->output());
10160
10161 Label valueIsObject, end;
10162
10163 masm.branchTestObject(Assembler::Equal, value, &valueIsObject);
10164
10165 // Value is not an object. Return that other object.
10166 masm.movePtr(obj, output);
10167 masm.jump(&end);
10168
10169 // Value is an object. Return unbox(Value).
10170 masm.bind(&valueIsObject);
10171 Register payload = masm.extractObject(value, output);
10172 if (payload != output) {
10173 masm.movePtr(payload, output);
10174 }
10175
10176 masm.bind(&end);
10177}
10178
10179void CodeGenerator::visitBoxNonStrictThis(LBoxNonStrictThis* lir) {
10180 ValueOperand value = ToValue(lir->value());
10181 Register output = ToRegister(lir->output());
10182
10183 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
10184 Label notNullOrUndefined;
10185 {
10186 Label isNullOrUndefined;
10187 ScratchTagScope tag(masm, value);
10188 masm.splitTagForTest(value, tag);
10189 masm.branchTestUndefined(Assembler::Equal, tag, &isNullOrUndefined);
10190 masm.branchTestNull(Assembler::NotEqual, tag, &notNullOrUndefined);
10191 masm.bind(&isNullOrUndefined);
10192 masm.movePtr(ImmGCPtr(lir->mir()->globalThis()), output);
10193 masm.jump(ool.rejoin());
10194 }
10195
10196 masm.bind(&notNullOrUndefined);
10197
10198 saveLive(lir);
10199
10200 pushArg(value);
10201 using Fn = JSObject* (*)(JSContext*, HandleValue);
10202 callVM<Fn, BoxNonStrictThis>(lir);
10203
10204 StoreRegisterTo(output).generate(this);
10205 restoreLiveIgnore(lir, StoreRegisterTo(output).clobbered());
10206
10207 masm.jump(ool.rejoin());
10208 });
10209 addOutOfLineCode(ool, lir->mir());
10210
10211 masm.fallibleUnboxObject(value, output, ool->entry());
10212 masm.bind(ool->rejoin());
10213}
10214
10215void CodeGenerator::visitImplicitThis(LImplicitThis* lir) {
10216 Register env = ToRegister(lir->env());
10217 ValueOperand output = ToOutValue(lir);
10218
10219 using Fn = void (*)(JSContext*, HandleObject, MutableHandleValue);
10220 auto* ool = oolCallVM<Fn, ImplicitThisOperation>(lir, ArgList(env),
10221 StoreValueTo(output));
10222
10223 masm.computeImplicitThis(env, output, ool->entry());
10224 masm.bind(ool->rejoin());
10225}
10226
10227void CodeGenerator::visitArrayLength(LArrayLength* lir) {
10228 Register elements = ToRegister(lir->elements());
10229 Register output = ToRegister(lir->output());
10230
10231 Address length(elements, ObjectElements::offsetOfLength());
10232 masm.load32(length, output);
10233
10234 bool intact = hasSeenArrayExceedsInt32LengthFuseIntactAndDependencyNoted();
10235
10236 if (intact) {
10237#ifdef DEBUG1
10238 Label done;
10239 masm.branchTest32(Assembler::NotSigned, output, output, &done);
10240 masm.assumeUnreachable("Unexpected array with length > INT32_MAX");
10241 masm.bind(&done);
10242#endif
10243 } else {
10244 // Bail out if the length doesn't fit in int32.
10245 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
10246 }
10247}
10248
10249void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) {
10250 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
10251 masm.store32(Imm32(lir->mir()->length()), length);
10252}
10253
10254void CodeGenerator::visitFunctionLength(LFunctionLength* lir) {
10255 Register function = ToRegister(lir->function());
10256 Register output = ToRegister(lir->output());
10257
10258 Label bail;
10259
10260 // Get the JSFunction flags.
10261 masm.load32(Address(function, JSFunction::offsetOfFlagsAndArgCount()),
10262 output);
10263
10264 // Functions with a SelfHostedLazyScript must be compiled with the slow-path
10265 // before the function length is known. If the length was previously resolved,
10266 // the length property may be shadowed.
10267 masm.branchTest32(
10268 Assembler::NonZero, output,
10269 Imm32(FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH),
10270 &bail);
10271
10272 masm.loadFunctionLength(function, output, output, &bail);
10273
10274 bailoutFrom(&bail, lir->snapshot());
10275}
10276
10277void CodeGenerator::visitFunctionName(LFunctionName* lir) {
10278 Register function = ToRegister(lir->function());
10279 Register output = ToRegister(lir->output());
10280
10281 Label bail;
10282
10283 const JSAtomState& names = gen->runtime->names();
10284 masm.loadFunctionName(function, output, ImmGCPtr(names.empty_), &bail);
10285
10286 bailoutFrom(&bail, lir->snapshot());
10287}
10288
10289template <class TableObject>
10290static void TableIteratorLoadEntry(MacroAssembler&, Register, Register,
10291 Register);
10292
10293template <>
10294void TableIteratorLoadEntry<MapObject>(MacroAssembler& masm, Register iter,
10295 Register i, Register front) {
10296 masm.unboxObject(Address(iter, MapIteratorObject::offsetOfTarget()), front);
10297 masm.loadPrivate(Address(front, MapObject::offsetOfData()), front);
10298
10299 static_assert(MapObject::Table::offsetOfImplDataElement() == 0,
10300 "offsetof(Data, element) is 0");
10301 static_assert(MapObject::Table::sizeofImplData() == 24, "sizeof(Data) is 24");
10302 masm.mulBy3(i, i);
10303 masm.lshiftPtr(Imm32(3), i);
10304 masm.addPtr(i, front);
10305}
10306
10307template <>
10308void TableIteratorLoadEntry<SetObject>(MacroAssembler& masm, Register iter,
10309 Register i, Register front) {
10310 masm.unboxObject(Address(iter, SetIteratorObject::offsetOfTarget()), front);
10311 masm.loadPrivate(Address(front, SetObject::offsetOfData()), front);
10312
10313 static_assert(SetObject::Table::offsetOfImplDataElement() == 0,
10314 "offsetof(Data, element) is 0");
10315 static_assert(SetObject::Table::sizeofImplData() == 16, "sizeof(Data) is 16");
10316 masm.lshiftPtr(Imm32(4), i);
10317 masm.addPtr(i, front);
10318}
10319
10320template <class TableObject>
10321static void TableIteratorAdvance(MacroAssembler& masm, Register iter,
10322 Register front, Register dataLength,
10323 Register temp) {
10324 Register i = temp;
10325
10326 // Note: |count| and |index| are stored as PrivateUint32Value. We use add32
10327 // and store32 to change the payload.
10328 masm.add32(Imm32(1), Address(iter, TableIteratorObject::offsetOfCount()));
10329
10330 masm.unboxInt32(Address(iter, TableIteratorObject::offsetOfIndex()), i);
10331
10332 Label done, seek;
10333 masm.bind(&seek);
10334 masm.add32(Imm32(1), i);
10335 masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done);
10336
10337 // We can add sizeof(Data) to |front| to select the next element, because
10338 // |front| and |mapOrSetObject.data[i]| point to the same location.
10339 static_assert(TableObject::Table::offsetOfImplDataElement() == 0,
10340 "offsetof(Data, element) is 0");
10341 masm.addPtr(Imm32(TableObject::Table::sizeofImplData()), front);
10342
10343 masm.branchTestMagic(Assembler::Equal,
10344 Address(front, TableObject::Table::offsetOfEntryKey()),
10345 JS_HASH_KEY_EMPTY, &seek);
10346
10347 masm.bind(&done);
10348 masm.store32(i, Address(iter, TableIteratorObject::offsetOfIndex()));
10349}
10350
10351// Corresponds to TableIteratorObject::finish.
10352static void TableIteratorFinish(MacroAssembler& masm, Register iter,
10353 Register temp0, Register temp1) {
10354 Register next = temp0;
10355 Register prevp = temp1;
10356 masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfNext()), next);
10357 masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfPrevPtr()),
10358 prevp);
10359 masm.storePtr(next, Address(prevp, 0));
10360
10361 Label hasNoNext;
10362 masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext);
10363 masm.storePrivateValue(prevp,
10364 Address(next, TableIteratorObject::offsetOfPrevPtr()));
10365 masm.bind(&hasNoNext);
10366
10367 // Mark iterator inactive.
10368 Address targetAddr(iter, TableIteratorObject::offsetOfTarget());
10369 masm.guardedCallPreBarrier(targetAddr, MIRType::Value);
10370 masm.storeValue(UndefinedValue(), targetAddr);
10371}
10372
10373template <>
10374void CodeGenerator::emitLoadIteratorValues<MapObject>(Register result,
10375 Register temp,
10376 Register front) {
10377 size_t elementsOffset = NativeObject::offsetOfFixedElements();
10378
10379 Address keyAddress(front, MapObject::Table::Entry::offsetOfKey());
10380 Address valueAddress(front, MapObject::Table::Entry::offsetOfValue());
10381 Address keyElemAddress(result, elementsOffset);
10382 Address valueElemAddress(result, elementsOffset + sizeof(Value));
10383 masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value);
10384 masm.guardedCallPreBarrier(valueElemAddress, MIRType::Value);
10385 masm.storeValue(keyAddress, keyElemAddress, temp);
10386 masm.storeValue(valueAddress, valueElemAddress, temp);
10387
10388 Label emitBarrier, skipBarrier;
10389 masm.branchValueIsNurseryCell(Assembler::Equal, keyAddress, temp,
10390 &emitBarrier);
10391 masm.branchValueIsNurseryCell(Assembler::NotEqual, valueAddress, temp,
10392 &skipBarrier);
10393 {
10394 masm.bind(&emitBarrier);
10395 saveVolatile(temp);
10396 emitPostWriteBarrier(result);
10397 restoreVolatile(temp);
10398 }
10399 masm.bind(&skipBarrier);
10400}
10401
10402template <>
10403void CodeGenerator::emitLoadIteratorValues<SetObject>(Register result,
10404 Register temp,
10405 Register front) {
10406 size_t elementsOffset = NativeObject::offsetOfFixedElements();
10407
10408 Address keyAddress(front, SetObject::Table::offsetOfEntryKey());
10409 Address keyElemAddress(result, elementsOffset);
10410 masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value);
10411 masm.storeValue(keyAddress, keyElemAddress, temp);
10412
10413 Label skipBarrier;
10414 masm.branchValueIsNurseryCell(Assembler::NotEqual, keyAddress, temp,
10415 &skipBarrier);
10416 {
10417 saveVolatile(temp);
10418 emitPostWriteBarrier(result);
10419 restoreVolatile(temp);
10420 }
10421 masm.bind(&skipBarrier);
10422}
10423
10424template <class IteratorObject, class TableObject>
10425void CodeGenerator::emitGetNextEntryForIterator(LGetNextEntryForIterator* lir) {
10426 Register iter = ToRegister(lir->iter());
10427 Register result = ToRegister(lir->result());
10428 Register temp = ToRegister(lir->temp0());
10429 Register dataLength = ToRegister(lir->temp1());
10430 Register front = ToRegister(lir->temp2());
10431 Register output = ToRegister(lir->output());
10432
10433#ifdef DEBUG1
10434 // Self-hosted code is responsible for ensuring GetNextEntryForIterator is
10435 // only called with the correct iterator class. Assert here all self-
10436 // hosted callers of GetNextEntryForIterator perform this class check.
10437 // No Spectre mitigations are needed because this is DEBUG-only code.
10438 Label success;
10439 masm.branchTestObjClassNoSpectreMitigations(
10440 Assembler::Equal, iter, &IteratorObject::class_, temp, &success);
10441 masm.assumeUnreachable("Iterator object should have the correct class.");
10442 masm.bind(&success);
10443#endif
10444
10445 // If the iterator has no target, it's already done.
10446 // See TableIteratorObject::isActive.
10447 Label iterAlreadyDone, iterDone, done;
10448 masm.branchTestUndefined(Assembler::Equal,
10449 Address(iter, IteratorObject::offsetOfTarget()),
10450 &iterAlreadyDone);
10451
10452 // Load |iter->index| in |temp| and |iter->target->dataLength| in
10453 // |dataLength|. Both values are stored as PrivateUint32Value.
10454 masm.unboxInt32(Address(iter, IteratorObject::offsetOfIndex()), temp);
10455 masm.unboxObject(Address(iter, IteratorObject::offsetOfTarget()), dataLength);
10456 masm.unboxInt32(Address(dataLength, TableObject::offsetOfDataLength()),
10457 dataLength);
10458 masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone);
10459 {
10460 TableIteratorLoadEntry<TableObject>(masm, iter, temp, front);
10461
10462 emitLoadIteratorValues<TableObject>(result, temp, front);
10463
10464 TableIteratorAdvance<TableObject>(masm, iter, front, dataLength, temp);
10465
10466 masm.move32(Imm32(0), output);
10467 masm.jump(&done);
10468 }
10469 {
10470 masm.bind(&iterDone);
10471 TableIteratorFinish(masm, iter, temp, dataLength);
10472
10473 masm.bind(&iterAlreadyDone);
10474 masm.move32(Imm32(1), output);
10475 }
10476 masm.bind(&done);
10477}
10478
10479void CodeGenerator::visitGetNextEntryForIterator(
10480 LGetNextEntryForIterator* lir) {
10481 if (lir->mir()->mode() == MGetNextEntryForIterator::Map) {
10482 emitGetNextEntryForIterator<MapIteratorObject, MapObject>(lir);
10483 } else {
10484 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", 10484); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->mode() == MGetNextEntryForIterator::Set"
")"); do { MOZ_CrashSequence(__null, 10484); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10485 emitGetNextEntryForIterator<SetIteratorObject, SetObject>(lir);
10486 }
10487}
10488
10489// The point of these is to inform Ion of where these values already are; they
10490// don't normally generate (much) code.
10491void CodeGenerator::visitWasmRegisterPairResult(LWasmRegisterPairResult* lir) {}
10492void CodeGenerator::visitWasmStackResult(LWasmStackResult* lir) {}
10493void CodeGenerator::visitWasmStackResult64(LWasmStackResult64* lir) {}
10494
10495void CodeGenerator::visitWasmStackResultArea(LWasmStackResultArea* lir) {
10496 LAllocation* output = lir->getDef(0)->output();
10497 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", 10497); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output->isStackArea()" ")"); do { MOZ_CrashSequence
(__null, 10497); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10498 bool tempInit = false;
10499 for (auto iter = output->toStackArea()->results(); iter; iter.next()) {
10500 // Zero out ref stack results.
10501 if (iter.isWasmAnyRef()) {
10502 Register temp = ToRegister(lir->temp0());
10503 if (!tempInit) {
10504 masm.xorPtr(temp, temp);
10505 tempInit = true;
10506 }
10507 masm.storePtr(temp, ToAddress(iter.alloc()));
10508 }
10509 }
10510}
10511
10512void CodeGenerator::visitWasmRegisterResult(LWasmRegisterResult* lir) {
10513#ifdef JS_64BIT1
10514 if (MWasmRegisterResult* mir = lir->mir()) {
10515 if (mir->type() == MIRType::Int32) {
10516 masm.widenInt32(ToRegister(lir->output()));
10517 }
10518 }
10519#endif
10520}
10521
10522void CodeGenerator::visitWasmSystemFloatRegisterResult(
10523 LWasmSystemFloatRegisterResult* lir) {
10524 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", 10525); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 10525); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10525 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", 10525); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 10525); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10526 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"
, 10527); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg"
")"); do { MOZ_CrashSequence(__null, 10527); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
10527 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"
, 10527); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg"
")"); do { MOZ_CrashSequence(__null, 10527); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
10528 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"
, 10529); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 10529); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
10529 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"
, 10529); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 10529); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
10530
10531#ifdef JS_CODEGEN_ARM
10532 MWasmSystemFloatRegisterResult* mir = lir->mir();
10533 if (!mir->hardFP()) {
10534 if (mir->type() == MIRType::Float32) {
10535 // Move float32 from r0 to ReturnFloatReg.
10536 masm.ma_vxfer(r0, ReturnFloat32Reg);
10537 } else if (mir->type() == MIRType::Double) {
10538 // Move double from r0/r1 to ReturnDoubleReg.
10539 masm.ma_vxfer(r0, r1, ReturnDoubleReg);
10540 } else {
10541 MOZ_CRASH("SIMD type not supported")do { do { } while (false); MOZ_ReportCrash("" "SIMD type not supported"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10541); AnnotateMozCrashReason
("MOZ_CRASH(" "SIMD type not supported" ")"); do { MOZ_CrashSequence
(__null, 10541); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
10542 }
10543 }
10544#elif JS_CODEGEN_X86
10545 MWasmSystemFloatRegisterResult* mir = lir->mir();
10546 if (mir->type() == MIRType::Double) {
10547 masm.reserveStack(sizeof(double));
10548 masm.fstp(Operand(esp, 0));
10549 masm.loadDouble(Operand(esp, 0), ReturnDoubleReg);
10550 masm.freeStack(sizeof(double));
10551 } else if (mir->type() == MIRType::Float32) {
10552 masm.reserveStack(sizeof(float));
10553 masm.fstp32(Operand(esp, 0));
10554 masm.loadFloat32(Operand(esp, 0), ReturnFloat32Reg);
10555 masm.freeStack(sizeof(float));
10556 }
10557#endif
10558}
10559
10560void CodeGenerator::visitWasmCall(LWasmCall* lir) {
10561 const MWasmCallBase* callBase = lir->callBase();
10562 bool isReturnCall = lir->isReturnCall();
10563
10564 // If this call is in Wasm try code block, initialise a wasm::TryNote for this
10565 // call.
10566 bool inTry = callBase->inTry();
10567 if (inTry) {
10568 size_t tryNoteIndex = callBase->tryNoteIndex();
10569 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10570 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
10571 tryNote.setTryBodyBegin(masm.currentOffset());
10572 }
10573
10574 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", 10575); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
")"); do { MOZ_CrashSequence(__null, 10575); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10575 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", 10575); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
")"); do { MOZ_CrashSequence(__null, 10575); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10576 static_assert(
10577 WasmStackAlignment >= ABIStackAlignment &&
10578 WasmStackAlignment % ABIStackAlignment == 0,
10579 "The wasm stack alignment should subsume the ABI-required alignment");
10580
10581#ifdef DEBUG1
10582 Label ok;
10583 masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok);
10584 masm.breakpoint();
10585 masm.bind(&ok);
10586#endif
10587
10588 // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the
10589 // instance and pinned regs. The only case where where we don't have to
10590 // reload the instance and pinned regs is when the callee preserves them.
10591 bool reloadInstance = true;
10592 bool reloadPinnedRegs = true;
10593 bool switchRealm = true;
10594
10595 const wasm::CallSiteDesc& desc = callBase->desc();
10596 const wasm::CalleeDesc& callee = callBase->callee();
10597 CodeOffset retOffset;
10598 CodeOffset secondRetOffset;
10599 switch (callee.which()) {
10600 case wasm::CalleeDesc::Func:
10601 if (isReturnCall) {
10602 ReturnCallAdjustmentInfo retCallInfo(
10603 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
10604 masm.wasmReturnCall(desc, callee.funcIndex(), retCallInfo);
10605 // The rest of the method is unnecessary for a return call.
10606 return;
10607 }
10608 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", 10608); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10608); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10609 retOffset = masm.call(desc, callee.funcIndex());
10610 reloadInstance = false;
10611 reloadPinnedRegs = false;
10612 switchRealm = false;
10613 break;
10614 case wasm::CalleeDesc::Import:
10615 if (isReturnCall) {
10616 ReturnCallAdjustmentInfo retCallInfo(
10617 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
10618 masm.wasmReturnCallImport(desc, callee, retCallInfo);
10619 // The rest of the method is unnecessary for a return call.
10620 return;
10621 }
10622 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", 10622); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10622); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10623 retOffset = masm.wasmCallImport(desc, callee);
10624 break;
10625 case wasm::CalleeDesc::WasmTable: {
10626 Label* nullCheckFailed = nullptr;
10627#ifndef WASM_HAS_HEAPREG1
10628 {
10629 auto* ool = new (
10630 alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
10631 masm.wasmTrap(wasm::Trap::IndirectCallToNull, desc.toTrapSiteDesc());
10632 });
10633 if (lir->isCatchable()) {
10634 addOutOfLineCode(ool, lir->mirCatchable());
10635 } else if (isReturnCall) {
10636 addOutOfLineCode(ool, lir->mirReturnCall());
10637 } else {
10638 addOutOfLineCode(ool, lir->mirUncatchable());
10639 }
10640 nullCheckFailed = ool->entry();
10641 }
10642#endif
10643 if (isReturnCall) {
10644 ReturnCallAdjustmentInfo retCallInfo(
10645 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
10646 // Discard the FaultingCodeRange returned by the following; we won't
10647 // want to generate a stackmap here.
10648 (void)masm.wasmReturnCallIndirect(desc, callee, nullCheckFailed,
10649 retCallInfo);
10650 // The rest of the method is unnecessary for a return call.
10651 return;
10652 }
10653 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", 10653); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10653); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10654 // As above, discard the returned FaultingCodeRange.
10655 (void)masm.wasmCallIndirect(desc, callee, nullCheckFailed, &retOffset,
10656 &secondRetOffset);
10657 // Register reloading and realm switching are handled dynamically inside
10658 // wasmCallIndirect. There are two return offsets, one for each call
10659 // instruction (fast path and slow path).
10660 reloadInstance = false;
10661 reloadPinnedRegs = false;
10662 switchRealm = false;
10663 break;
10664 }
10665 case wasm::CalleeDesc::Builtin:
10666 retOffset = masm.call(desc, callee.builtin());
10667 // The builtin ABI preserves the instance and pinned registers. However,
10668 // builtins may grow the memory which requires us to reload the pinned
10669 // registers.
10670 reloadInstance = false;
10671 reloadPinnedRegs = true;
10672 switchRealm = false;
10673 break;
10674 case wasm::CalleeDesc::BuiltinInstanceMethod: {
10675 masm.wasmCallBuiltinInstanceMethod(
10676 desc, callBase->instanceArg(), callee.builtin(),
10677 callBase->builtinMethodFailureMode(),
10678 callBase->builtinMethodFailureTrap(), &retOffset, &secondRetOffset);
10679 // The builtin ABI preserves the instance and pinned registers. However,
10680 // builtins may grow the memory which requires us to reload the pinned
10681 // registers.
10682 reloadInstance = false;
10683 reloadPinnedRegs = true;
10684 switchRealm = false;
10685 break;
10686 }
10687 case wasm::CalleeDesc::FuncRef:
10688 if (isReturnCall) {
10689 ReturnCallAdjustmentInfo retCallInfo(
10690 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
10691 masm.wasmReturnCallRef(desc, callee, retCallInfo, nullptr, nullptr);
10692 // The rest of the method is unnecessary for a return call.
10693 return;
10694 }
10695 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", 10695); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10695); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10696 // Register reloading and realm switching are handled dynamically inside
10697 // wasmCallRef. There are two return offsets, one for each call
10698 // instruction (fast path and slow path).
10699 masm.wasmCallRef(desc, callee, &retOffset, &secondRetOffset, nullptr,
10700 nullptr);
10701 reloadInstance = false;
10702 reloadPinnedRegs = false;
10703 switchRealm = false;
10704 break;
10705 }
10706
10707 // Note the assembler offset for the associated LSafePoint.
10708 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", 10708); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence
(__null, 10708); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10709 markSafepointAt(retOffset.offset(), lir);
10710
10711 // Now that all the outbound in-memory args are on the stack, note the
10712 // required lower boundary point of the associated StackMap.
10713 uint32_t framePushedAtStackMapBase =
10714 masm.framePushed() -
10715 wasm::AlignStackArgAreaSize(callBase->stackArgAreaSizeUnaligned());
10716 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAtStackMapBase);
10717 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", 10718); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
")"); do { MOZ_CrashSequence(__null, 10718); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10718 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", 10718); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
")"); do { MOZ_CrashSequence(__null, 10718); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10719
10720 // Indirect calls (WasmTable/FuncRef) emit two call instructions (fast and
10721 // slow path) with two distinct return offsets. The two paths are mutually
10722 // exclusive and rejoin with the same live references and frame layout, so a
10723 // single stackmap serves both: register this call's LSafepoint a second time
10724 // at the slow-path return offset.
10725 if (callee.which() == wasm::CalleeDesc::WasmTable ||
10726 callee.which() == wasm::CalleeDesc::FuncRef ||
10727 (callee.which() == wasm::CalleeDesc::BuiltinInstanceMethod &&
10728 secondRetOffset.bound())) {
10729 markSafepointAt(secondRetOffset.offset(), lir);
10730 }
10731
10732 if (reloadInstance) {
10733 masm.loadPtr(
10734 Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall),
10735 InstanceReg);
10736 if (switchRealm) {
10737 masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
10738 }
10739 } else {
10740 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"
, 10740); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!switchRealm"
")"); do { MOZ_CrashSequence(__null, 10740); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10741 }
10742 if (reloadPinnedRegs) {
10743 masm.loadWasmPinnedRegsFromInstance();
10744 }
10745
10746 switch (callee.which()) {
10747 case wasm::CalleeDesc::Func:
10748 case wasm::CalleeDesc::Import:
10749 case wasm::CalleeDesc::WasmTable:
10750 case wasm::CalleeDesc::FuncRef:
10751 // Stack allocation could change during Wasm (return) calls,
10752 // recover pre-call state.
10753 masm.freeStackTo(masm.framePushed());
10754 break;
10755 default:
10756 break;
10757 }
10758
10759 if (inTry) {
10760 // Set the end of the try note range
10761 size_t tryNoteIndex = callBase->tryNoteIndex();
10762 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10763 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
10764
10765 // Don't set the end of the try note if we've OOM'ed, as the above
10766 // instructions may not have been emitted, which will trigger an assert
10767 // about zero-length try-notes. This is okay as this compilation will be
10768 // thrown away.
10769 if (!masm.oom()) {
10770 tryNote.setTryBodyEnd(masm.currentOffset());
10771 }
10772
10773 // This instruction must be the last instruction in the block. No other
10774 // instructions may be inserted.
10775 LBlock* block = lir->block();
10776 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", 10776); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir" ")"); do
{ MOZ_CrashSequence(__null, 10776); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
;
10777
10778 // Jump to the fallthrough block
10779 jumpToBlock(lir->mirCatchable()->getSuccessor(
10780 MWasmCallCatchable::FallthroughBranchIndex));
10781 }
10782}
10783
10784#ifdef ENABLE_WASM_JSPI1
10785void CodeGenerator::visitWasmFindHandler(LWasmFindHandler* lir) {
10786 MWasmFindHandler* mir = lir->mir();
10787 Register instance = ToRegister(lir->instance());
10788 Register tag = ToRegister(lir->tag());
10789 Register output = ToRegister(lir->output());
10790 Register scratch1 = ToRegister(lir->temp0());
10791 Register scratch2 = ToRegister(lir->temp1());
10792 Register scratch3 = ToRegister(lir->temp2());
10793 Register scratch4 = ToRegister(lir->temp3());
10794 const wasm::Trap& trap = mir->trap();
10795 const wasm::TrapSiteDesc& trapSiteDesc = mir->trapSiteDesc();
10796
10797 auto* ool = new (alloc())
10798 LambdaOutOfLineCode([this, trap, trapSiteDesc](OutOfLineCode& ool) {
10799 masm.wasmTrap(trap, trapSiteDesc);
10800 });
10801 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
10802 wasm::EmitFindHandler(masm, instance, tag, output, scratch1, scratch2,
10803 scratch3, scratch4, ool->entry());
10804}
10805
10806void CodeGenerator::visitWasmSuspend(LWasmSuspend* lir) {
10807 Register instance = ToRegister(lir->instance());
10808 Register suspendedCont = ToRegister(lir->suspendedCont());
10809 Register handler = ToRegister(lir->handler());
10810 Register scratch1 = ToRegister(lir->temp0());
10811 Register scratch2 = ToRegister(lir->temp1());
10812 Register scratch3 = ToRegister(lir->temp2());
10813
10814 uint32_t suspendResultsAreaBase =
10815 lir->mir()->suspendResultsArea()->toWasmStackResultArea()->base();
10816
10817 CodeOffset suspendedCodeOffset;
10818 uint32_t suspendedFramePushed;
10819 wasm::EmitSuspend(masm, instance, suspendedCont, handler, scratch1, scratch2,
10820 scratch3, lir->mir()->callSiteDesc(), &suspendedCodeOffset,
10821 &suspendedFramePushed, suspendResultsAreaBase);
10822
10823 if (masm.oom()) {
10824 return;
10825 }
10826
10827 markSafepointAt(suspendedCodeOffset.offset(), lir);
10828 lir->safepoint()->setFramePushedAtStackMapBase(suspendedFramePushed);
10829 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
10830}
10831
10832void CodeGenerator::visitWasmPrepareResume(LWasmPrepareResume* lir) {
10833 MWasmPrepareResume* mir = lir->mir();
10834 Register cont = ToRegister(lir->cont());
10835 Register output = ToRegister(lir->output());
10836 Register scratch1 = ToRegister(lir->temp0());
10837 Register scratch2 = ToRegister(lir->temp1());
10838 uint32_t resumeParamsAreaBase =
10839 mir->resumeParamsArea()->toWasmStackResultArea()->base();
10840 wasm::TrapSiteDesc trapSiteDesc = mir->trapSiteDesc();
10841
10842 auto* ool = new (alloc())
10843 LambdaOutOfLineCode([this, trapSiteDesc](OutOfLineCode& ool) {
10844 masm.wasmTrap(wasm::Trap::NullPointerDereference, trapSiteDesc);
10845 });
10846 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
10847
10848 wasm::EmitPrepareResume(masm, cont, resumeParamsAreaBase, output, scratch1,
10849 scratch2, ool->entry());
10850}
10851
10852void CodeGenerator::visitWasmResume(LWasmResume* lir) {
10853 MWasmResume* mir = lir->mir();
10854 Register instance = ToRegister(lir->instance());
10855 Register cont = ToRegister(lir->cont());
10856 uint32_t handlersParamsAreaBase = mir->handlersParamsArea()->base();
10857 uint32_t contResultsAreaBase = mir->contResultsArea()->base();
10858 Register scratch1 = ToRegister(lir->temp0());
10859 Register scratch2 = ToRegister(lir->temp1());
10860 Register scratch3 = ToRegister(lir->temp2());
10861
10862 // If this resume is in a wasm try code block, initialise a wasm::TryNote for
10863 // this resume.
10864 bool inTry = mir->hasTryNote();
10865 if (inTry) {
10866 size_t tryNoteIndex = mir->tryNoteIndex().value();
10867 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10868 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
10869 tryNote.setTryBodyBegin(masm.currentOffset());
10870 }
10871
10872 mozilla::Vector<jit::Label*, 2, JitAllocPolicy> handlerLabels(alloc());
10873 if (!handlerLabels.reserve(mir->numHandlers())) {
10874 masm.setOOM();
10875 return;
10876 }
10877 for (size_t i = 0; i < mir->numHandlers(); i++) {
10878 handlerLabels.infallibleAppend(getJumpLabelForBranch(mir->handlerBlock(i)));
10879 }
10880
10881 CodeOffset resumeCodeOffset;
10882 uint32_t resumeFramePushed;
10883 wasm::EmitResume(masm, instance, cont, handlersParamsAreaBase, scratch1,
10884 scratch2, scratch3, mir->handlers(), handlerLabels,
10885 mir->callSiteDesc(), &resumeCodeOffset, &resumeFramePushed,
10886 contResultsAreaBase);
10887
10888 if (masm.oom()) {
10889 return;
10890 }
10891
10892 markSafepointAt(resumeCodeOffset.offset(), lir);
10893 lir->safepoint()->setFramePushedAtStackMapBase(resumeFramePushed);
10894 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
10895
10896 if (inTry) {
10897 // Set the end of the try note range
10898 size_t tryNoteIndex = mir->tryNoteIndex().value();
10899 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10900 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
10901
10902 // Don't set the end of the try note if we've OOM'ed, as the above
10903 // instructions may not have been emitted, which will trigger an assert
10904 // about zero-length try-notes. This is okay as this compilation will be
10905 // thrown away.
10906 if (!masm.oom()) {
10907 tryNote.setTryBodyEnd(masm.currentOffset());
10908 }
10909
10910 // This instruction must be the last instruction in the block. No other
10911 // instructions may be inserted.
10912 LBlock* block = lir->block();
10913 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", 10913); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir" ")"); do
{ MOZ_CrashSequence(__null, 10913); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
;
10914 }
10915
10916 // Jump to the fallthrough block
10917 jumpToBlock(mir->fallthroughBlock());
10918}
10919#endif // ENABLE_WASM_JSPI
10920
10921void CodeGenerator::visitWasmCallLandingPrePad(LWasmCallLandingPrePad* lir) {
10922 LBlock* block = lir->block();
10923 MWasmCallLandingPrePad* mir = lir->mir();
10924 MBasicBlock* mirBlock = mir->block();
10925 MBasicBlock* callMirBlock = mir->callBlock();
10926
10927 // This block must be the pre-pad successor of the call block. No blocks may
10928 // be inserted between us, such as for critical edge splitting.
10929 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", 10930); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
")"); do { MOZ_CrashSequence(__null, 10930); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10930 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", 10930); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
")"); do { MOZ_CrashSequence(__null, 10930); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10931
10932 // This instruction or a move group must be the first instruction in the
10933 // block. No other instructions may be inserted.
10934 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", 10935); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
")"); do { MOZ_CrashSequence(__null, 10935); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10935 *(++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", 10935); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
")"); do { MOZ_CrashSequence(__null, 10935); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10936
10937 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10938 wasm::TryNote& tryNote = tryNotes[mir->tryNoteIndex()];
10939 // Set the entry point for the call try note to be the beginning of this
10940 // block. The above assertions (and assertions in visitWasmCall) guarantee
10941 // that we are not skipping over instructions that should be executed.
10942 tryNote.setLandingPad(block->label()->offset(), masm.framePushed());
10943}
10944
10945template <typename InstructionWithMaybeTrapSite>
10946void EmitSignalNullCheckTrapSite(MacroAssembler& masm,
10947 InstructionWithMaybeTrapSite* ins,
10948 FaultingCodeRange fcr,
10949 wasm::TrapMachineInsn tmi) {
10950 if (!ins->maybeTrap()) {
10951 return;
10952 }
10953 masm.appendAndVerify(wasm::Trap::NullPointerDereference, tmi, fcr,
10954 *ins->maybeTrap());
10955}
10956
10957template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndexT>
10958void CodeGenerator::emitWasmValueLoad(InstructionWithMaybeTrapSite* ins,
10959 MIRType type, MWideningOp wideningOp,
10960 AddressOrBaseIndexT addr,
10961 AnyRegister dst) {
10962 FaultingCodeRange fcr;
10963 switch (type) {
10964 case MIRType::Int32:
10965 switch (wideningOp) {
10966 case MWideningOp::None:
10967 fcr = masm.load32(addr, dst.gpr());
10968 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10969 wasm::TrapMachineInsn::Load32);
10970 break;
10971 case MWideningOp::FromU16:
10972 fcr = masm.load16ZeroExtend(addr, dst.gpr());
10973 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10974 wasm::TrapMachineInsn::Load16);
10975 break;
10976 case MWideningOp::FromS16:
10977 fcr = masm.load16SignExtend(addr, dst.gpr());
10978 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10979 wasm::TrapMachineInsn::Load16);
10980 break;
10981 case MWideningOp::FromU8:
10982 fcr = masm.load8ZeroExtend(addr, dst.gpr());
10983 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10984 wasm::TrapMachineInsn::Load8);
10985 break;
10986 case MWideningOp::FromS8:
10987 fcr = masm.load8SignExtend(addr, dst.gpr());
10988 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10989 wasm::TrapMachineInsn::Load8);
10990 break;
10991 default:
10992 MOZ_CRASH("unexpected widening op in ::visitWasmLoadElement")do { do { } while (false); MOZ_ReportCrash("" "unexpected widening op in ::visitWasmLoadElement"
, "./../../../../js/src/jit/CodeGenerator.cpp", 10992); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected widening op in ::visitWasmLoadElement"
")"); do { MOZ_CrashSequence(__null, 10992); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10993 }
10994 break;
10995 case MIRType::Float32:
10996 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"
, 10996); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 10996); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10997 fcr = masm.loadFloat32(addr, dst.fpu());
10998 EmitSignalNullCheckTrapSite(masm, ins, fcr,
10999 wasm::TrapMachineInsn::Load32);
11000 break;
11001 case MIRType::Double:
11002 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"
, 11002); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 11002); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11003 fcr = masm.loadDouble(addr, dst.fpu());
11004 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11005 wasm::TrapMachineInsn::Load64);
11006 break;
11007 case MIRType::Pointer:
11008 case MIRType::WasmAnyRef:
11009 case MIRType::WasmStructData:
11010 case MIRType::WasmArrayData:
11011 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"
, 11011); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 11011); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11012 fcr = masm.loadPtr(addr, dst.gpr());
11013 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11014 wasm::TrapMachineInsnForLoadWord());
11015 break;
11016 default:
11017 MOZ_CRASH("unexpected type in ::emitWasmValueLoad")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueLoad"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11017); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type in ::emitWasmValueLoad" ")"); do
{ MOZ_CrashSequence(__null, 11017); __attribute__((nomerge))
::abort(); } while (false); } while (false)
;
11018 }
11019}
11020
11021template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndexT>
11022void CodeGenerator::emitWasmValueStore(InstructionWithMaybeTrapSite* ins,
11023 MIRType type, MNarrowingOp narrowingOp,
11024 AnyRegister src,
11025 AddressOrBaseIndexT addr) {
11026 FaultingCodeRange fcr;
11027 switch (type) {
11028 case MIRType::Int32:
11029 switch (narrowingOp) {
11030 case MNarrowingOp::None:
11031 fcr = masm.store32(src.gpr(), addr);
11032 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11033 wasm::TrapMachineInsn::Store32);
11034 break;
11035 case MNarrowingOp::To16:
11036 fcr = masm.store16(src.gpr(), addr);
11037 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11038 wasm::TrapMachineInsn::Store16);
11039 break;
11040 case MNarrowingOp::To8:
11041 fcr = masm.store8(src.gpr(), addr);
11042 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11043 wasm::TrapMachineInsn::Store8);
11044 break;
11045 default:
11046 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp"
, 11046); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 11046); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
11047 }
11048 break;
11049 case MIRType::Float32:
11050 fcr = masm.storeFloat32(src.fpu(), addr);
11051 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11052 wasm::TrapMachineInsn::Store32);
11053 break;
11054 case MIRType::Double:
11055 fcr = masm.storeDouble(src.fpu(), addr);
11056 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11057 wasm::TrapMachineInsn::Store64);
11058 break;
11059 case MIRType::Pointer:
11060 // This could be correct, but it would be a new usage, so check carefully.
11061 MOZ_CRASH("Unexpected type in ::emitWasmValueStore.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type in ::emitWasmValueStore."
, "./../../../../js/src/jit/CodeGenerator.cpp", 11061); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected type in ::emitWasmValueStore." ")")
; do { MOZ_CrashSequence(__null, 11061); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
11062 case MIRType::WasmAnyRef:
11063 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", 11063); AnnotateMozCrashReason
("MOZ_CRASH(" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef."
")"); do { MOZ_CrashSequence(__null, 11063); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
11064 default:
11065 MOZ_CRASH("unexpected type in ::emitWasmValueStore")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueStore"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11065); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type in ::emitWasmValueStore" ")");
do { MOZ_CrashSequence(__null, 11065); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
11066 }
11067}
11068
11069void CodeGenerator::visitWasmLoadSlot(LWasmLoadSlot* ins) {
11070 MIRType type = ins->type();
11071 MWideningOp wideningOp = ins->wideningOp();
11072 Register container = ToRegister(ins->containerRef());
11073 Address addr(container, ins->offset());
11074 AnyRegister dst = ToAnyRegister(ins->output());
11075
11076#ifdef ENABLE_JIT_SIMD1
11077 if (type == MIRType::Simd128) {
11078 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"
, 11078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 11078); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11079 FaultingCodeRange fcr = masm.loadUnalignedSimd128(addr, dst.fpu());
11080 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load128);
11081 return;
11082 }
11083#endif
11084 emitWasmValueLoad(ins, type, wideningOp, addr, dst);
11085}
11086
11087void CodeGenerator::visitWasmLoadElement(LWasmLoadElement* ins) {
11088 MIRType type = ins->type();
11089 MWideningOp wideningOp = ins->wideningOp();
11090 Scale scale = ins->scale();
11091 Register base = ToRegister(ins->base());
11092 Register index = ToRegister(ins->index());
11093 AnyRegister dst = ToAnyRegister(ins->output());
11094
11095#ifdef ENABLE_JIT_SIMD1
11096 if (type == MIRType::Simd128) {
11097 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"
, 11097); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { MOZ_CrashSequence(__null, 11097); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11098 FaultingCodeRange fcr;
11099 Register temp = ToRegister(ins->temp0());
11100 masm.lshiftPtr(Imm32(4), index, temp);
11101 fcr = masm.loadUnalignedSimd128(BaseIndex(base, temp, Scale::TimesOne),
11102 dst.fpu());
11103 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load128);
11104 return;
11105 }
11106#endif
11107 emitWasmValueLoad(ins, type, wideningOp, BaseIndex(base, index, scale), dst);
11108}
11109
11110void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) {
11111 MIRType type = ins->type();
11112 MNarrowingOp narrowingOp = ins->narrowingOp();
11113 Register container = ToRegister(ins->containerRef());
11114 Address addr(container, ins->offset());
11115 AnyRegister src = ToAnyRegister(ins->value());
11116 if (type != MIRType::Int32) {
11117 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"
, 11117); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { MOZ_CrashSequence(__null, 11117); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11118 }
11119
11120#ifdef ENABLE_JIT_SIMD1
11121 if (type == MIRType::Simd128) {
11122 FaultingCodeRange fcr = masm.storeUnalignedSimd128(src.fpu(), addr);
11123 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11124 wasm::TrapMachineInsn::Store128);
11125 return;
11126 }
11127#endif
11128 emitWasmValueStore(ins, type, narrowingOp, src, addr);
11129}
11130
11131void CodeGenerator::visitWasmStoreStackResult(LWasmStoreStackResult* ins) {
11132 const LAllocation* value = ins->value();
11133 Address addr(ToRegister(ins->stackResultsArea()), ins->offset());
11134
11135 switch (ins->type()) {
11136 case MIRType::Int32:
11137 masm.storePtr(ToRegister(value), addr);
11138 break;
11139 case MIRType::Float32:
11140 masm.storeFloat32(ToFloatRegister(value), addr);
11141 break;
11142 case MIRType::Double:
11143 masm.storeDouble(ToFloatRegister(value), addr);
11144 break;
11145#ifdef ENABLE_JIT_SIMD1
11146 case MIRType::Simd128:
11147 masm.storeUnalignedSimd128(ToFloatRegister(value), addr);
11148 break;
11149#endif
11150 case MIRType::WasmAnyRef:
11151 masm.storePtr(ToRegister(value), addr);
11152 break;
11153 default:
11154 MOZ_CRASH("unexpected type in ::visitWasmStoreStackResult")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::visitWasmStoreStackResult"
, "./../../../../js/src/jit/CodeGenerator.cpp", 11154); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type in ::visitWasmStoreStackResult"
")"); do { MOZ_CrashSequence(__null, 11154); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
11155 }
11156}
11157
11158void CodeGenerator::visitWasmStoreStackResultI64(
11159 LWasmStoreStackResultI64* ins) {
11160 masm.store64(ToRegister64(ins->value()),
11161 Address(ToRegister(ins->stackResultsArea()), ins->offset()));
11162}
11163
11164void CodeGenerator::visitWasmStoreElement(LWasmStoreElement* ins) {
11165 MIRType type = ins->type();
11166 MNarrowingOp narrowingOp = ins->narrowingOp();
11167 Scale scale = ins->scale();
11168 Register base = ToRegister(ins->base());
11169 Register index = ToRegister(ins->index());
11170 AnyRegister src = ToAnyRegister(ins->value());
11171 if (type != MIRType::Int32) {
11172 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"
, 11172); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { MOZ_CrashSequence(__null, 11172); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11173 }
11174
11175#ifdef ENABLE_JIT_SIMD1
11176 if (type == MIRType::Simd128) {
11177 Register temp = ToRegister(ins->temp0());
11178 masm.lshiftPtr(Imm32(4), index, temp);
11179 FaultingCodeRange fcr = masm.storeUnalignedSimd128(
11180 src.fpu(), BaseIndex(base, temp, Scale::TimesOne));
11181 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11182 wasm::TrapMachineInsn::Store128);
11183 return;
11184 }
11185#endif
11186 emitWasmValueStore(ins, type, narrowingOp, src,
11187 BaseIndex(base, index, scale));
11188}
11189
11190void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) {
11191 Register elements = ToRegister(ins->elements());
11192 Register index = ToRegister(ins->index());
11193 Register output = ToRegister(ins->output());
11194 masm.loadPtr(BaseIndex(elements, index, ScalePointer), output);
11195}
11196
11197void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) {
11198 masm.computeEffectiveAddress(
11199 Address(ToRegister(ins->base()), int32_t(ins->mir()->offset())),
11200 ToRegister(ins->output()));
11201}
11202
11203void CodeGenerator::visitWasmDerivedIndexPointer(
11204 LWasmDerivedIndexPointer* ins) {
11205 Register base = ToRegister(ins->base());
11206 Register index = ToRegister(ins->index());
11207 Register output = ToRegister(ins->output());
11208 masm.computeEffectiveAddress(BaseIndex(base, index, ins->mir()->scale()),
11209 output);
11210}
11211
11212void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) {
11213 Register instance = ToRegister(ins->instance());
11214 Register valueBase = ToRegister(ins->valueBase());
11215 size_t offset = ins->offset();
11216 Register temp = ToRegister(ins->temp0());
11217
11218 Address addr(valueBase, offset);
11219
11220 if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) {
11221 Label skipPreBarrier;
11222 wasm::EmitWasmPreBarrierGuard(masm, instance, temp, addr, &skipPreBarrier,
11223 ins->maybeTrap());
11224 wasm::EmitWasmPreBarrierCallImmediate(masm, instance, temp, valueBase,
11225 offset);
11226 masm.bind(&skipPreBarrier);
11227 }
11228
11229 FaultingCodeRange fcr;
11230 if (ins->value()->isBogus()) {
11231 fcr = masm.storePtr(ImmWord(0), addr);
11232 } else {
11233 Register value = ToRegister(ins->value());
11234 fcr = masm.storePtr(value, addr);
11235 }
11236
11237 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11238 wasm::TrapMachineInsnForStoreWord());
11239 // The postbarrier is handled separately.
11240}
11241
11242void CodeGenerator::visitWasmStoreElementRef(LWasmStoreElementRef* ins) {
11243 Register instance = ToRegister(ins->instance());
11244 Register base = ToRegister(ins->base());
11245 Register index = ToRegister(ins->index());
11246 Register temp0 = ToTempRegisterOrInvalid(ins->temp0());
11247 Register temp1 = ToTempRegisterOrInvalid(ins->temp1());
11248
11249 BaseIndex addr(base, index, ScalePointer);
11250
11251 if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) {
11252 Label skipPreBarrier;
11253 wasm::EmitWasmPreBarrierGuard(masm, instance, temp0, addr, &skipPreBarrier,
11254 ins->maybeTrap());
11255 wasm::EmitWasmPreBarrierCallIndex(masm, instance, temp0, temp1, addr);
11256 masm.bind(&skipPreBarrier);
11257 }
11258
11259 FaultingCodeRange fcr;
11260 if (ins->value()->isBogus()) {
11261 fcr = masm.storePtr(ImmWord(0), addr);
11262 } else {
11263 Register value = ToRegister(ins->value());
11264 fcr = masm.storePtr(value, addr);
11265 }
11266
11267 EmitSignalNullCheckTrapSite(masm, ins, fcr,
11268 wasm::TrapMachineInsnForStoreWord());
11269 // The postbarrier is handled separately.
11270}
11271
11272void CodeGenerator::visitWasmPostWriteBarrierWholeCell(
11273 LWasmPostWriteBarrierWholeCell* lir) {
11274 Register object = ToRegister(lir->object());
11275 Register value = ToRegister(lir->value());
11276 Register temp = ToRegister(lir->temp0());
11277 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", 11277); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg"
")"); do { MOZ_CrashSequence(__null, 11277); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11278 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
11279 // Skip the barrier if this object was previously added to the store buffer.
11280 // We perform this check out of line because in practice the prior guards
11281 // eliminate most calls to the barrier.
11282 wasm::CheckWholeCellLastElementCache(masm, InstanceReg, object, temp,
11283 ool.rejoin());
11284
11285 saveLive(lir);
11286 masm.Push(InstanceReg);
11287 int32_t framePushedAfterInstance = masm.framePushed();
11288
11289 // Call Instance::postBarrierWholeCell
11290 masm.setupWasmABICall(wasm::SymbolicAddress::PostBarrierWholeCell);
11291 masm.passABIArg(InstanceReg);
11292 masm.passABIArg(object);
11293 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
11294 masm.callWithABI(wasm::BytecodeOffset(0),
11295 wasm::SymbolicAddress::PostBarrierWholeCell,
11296 mozilla::Some(instanceOffset), ABIType::General);
11297
11298 masm.Pop(InstanceReg);
11299 restoreLive(lir);
11300
11301 masm.jump(ool.rejoin());
11302 });
11303 addOutOfLineCode(ool, lir->mir());
11304
11305 wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value,
11306 ool->rejoin());
11307 masm.jump(ool->entry());
11308 masm.bind(ool->rejoin());
11309}
11310
11311void CodeGenerator::visitWasmPostWriteBarrierEdgeAtIndex(
11312 LWasmPostWriteBarrierEdgeAtIndex* lir) {
11313 Register object = ToRegister(lir->object());
11314 Register value = ToRegister(lir->value());
11315 Register valueBase = ToRegister(lir->valueBase());
11316 Register index = ToRegister(lir->index());
11317 Register temp = ToRegister(lir->temp0());
11318 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", 11318); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg"
")"); do { MOZ_CrashSequence(__null, 11318); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11319 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
11320 saveLive(lir);
11321 masm.Push(InstanceReg);
11322 int32_t framePushedAfterInstance = masm.framePushed();
11323
11324 // Fold the value offset into the value base
11325 if (lir->elemSize() == 16) {
11326 masm.lshiftPtr(Imm32(4), index, temp);
11327 masm.addPtr(valueBase, temp);
11328 } else {
11329 masm.computeEffectiveAddress(
11330 BaseIndex(valueBase, index, ScaleFromElemWidth(lir->elemSize())),
11331 temp);
11332 }
11333
11334 // Call Instance::postBarrier
11335 masm.setupWasmABICall(wasm::SymbolicAddress::PostBarrierEdge);
11336 masm.passABIArg(InstanceReg);
11337 masm.passABIArg(temp);
11338 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
11339 masm.callWithABI(wasm::BytecodeOffset(0),
11340 wasm::SymbolicAddress::PostBarrierEdge,
11341 mozilla::Some(instanceOffset), ABIType::General);
11342
11343 masm.Pop(InstanceReg);
11344 restoreLive(lir);
11345
11346 masm.jump(ool.rejoin());
11347 });
11348 addOutOfLineCode(ool, lir->mir());
11349
11350 wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value,
11351 ool->rejoin());
11352 masm.jump(ool->entry());
11353 masm.bind(ool->rejoin());
11354}
11355
11356#ifdef ENABLE_WASM_JSPI1
11357void CodeGenerator::visitWasmResumeBarrier(LWasmResumeBarrier* lir) {
11358 Register instance = ToRegister(lir->instance());
11359 Register cont = ToRegister(lir->cont());
11360 Register scratch1 = ToRegister(lir->temp0());
11361
11362 auto* ool = new (alloc())
11363 LambdaOutOfLineCode([this, lir, instance, cont](OutOfLineCode& ool) {
11364 saveLive(lir);
11365 wasm::EmitWasmResumeBarrier(masm, instance, cont);
11366 restoreLive(lir);
11367 masm.jump(ool.rejoin());
11368 });
11369 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
11370
11371 wasm::EmitWasmResumeBarrierGuard(masm, instance, scratch1, ool->entry());
11372 masm.bind(ool->rejoin());
11373}
11374#endif // ENABLE_WASM_JSPI
11375
11376void CodeGenerator::visitWasmLoadSlotI64(LWasmLoadSlotI64* ins) {
11377 Register container = ToRegister(ins->containerRef());
11378 Address addr(container, ins->offset());
11379 Register64 output = ToOutRegister64(ins);
11380 // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one
11381 // transaction will always trap before the other, so it seems safest to
11382 // register both of them as potentially trapping.
11383#ifdef JS_64BIT1
11384 FaultingCodeRange fcr = masm.load64(addr, output);
11385 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load64);
11386#else
11387 FaultingCodeRangePair fcrp = masm.load64(addr, output);
11388 EmitSignalNullCheckTrapSite(masm, ins, fcrp.first,
11389 wasm::TrapMachineInsn::Load32);
11390 EmitSignalNullCheckTrapSite(masm, ins, fcrp.second,
11391 wasm::TrapMachineInsn::Load32);
11392#endif
11393}
11394
11395void CodeGenerator::visitWasmLoadElementI64(LWasmLoadElementI64* ins) {
11396 Register base = ToRegister(ins->base());
11397 Register index = ToRegister(ins->index());
11398 BaseIndex addr(base, index, Scale::TimesEight);
11399 Register64 output = ToOutRegister64(ins);
11400 // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one
11401 // transaction will always trap before the other, so it seems safest to
11402 // register both of them as potentially trapping.
11403#ifdef JS_64BIT1
11404 FaultingCodeRange fcr = masm.load64(addr, output);
11405 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load64);
11406#else
11407 FaultingCodeRangePair fcrp = masm.load64(addr, output);
11408 EmitSignalNullCheckTrapSite(masm, ins, fcrp.first,
11409 wasm::TrapMachineInsn::Load32);
11410 EmitSignalNullCheckTrapSite(masm, ins, fcrp.second,
11411 wasm::TrapMachineInsn::Load32);
11412#endif
11413}
11414
11415void CodeGenerator::visitWasmStoreSlotI64(LWasmStoreSlotI64* ins) {
11416 Register container = ToRegister(ins->containerRef());
11417 Address addr(container, ins->offset());
11418 Register64 value = ToRegister64(ins->value());
11419 // Either 1 or 2 words. As above we register both transactions in the
11420 // 2-word case.
11421#ifdef JS_64BIT1
11422 FaultingCodeRange fcr = masm.store64(value, addr);
11423 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Store64);
11424#else
11425 FaultingCodeRangePair fcrp = masm.store64(value, addr);
11426 EmitSignalNullCheckTrapSite(masm, ins, fcrp.first,
11427 wasm::TrapMachineInsn::Store32);
11428 EmitSignalNullCheckTrapSite(masm, ins, fcrp.second,
11429 wasm::TrapMachineInsn::Store32);
11430#endif
11431}
11432
11433void CodeGenerator::visitWasmStoreElementI64(LWasmStoreElementI64* ins) {
11434 Register base = ToRegister(ins->base());
11435 Register index = ToRegister(ins->index());
11436 BaseIndex addr(base, index, Scale::TimesEight);
11437 Register64 value = ToRegister64(ins->value());
11438 // Either 1 or 2 words. As above we register both transactions in the
11439 // 2-word case.
11440#ifdef JS_64BIT1
11441 FaultingCodeRange fcr = masm.store64(value, addr);
11442 EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Store64);
11443#else
11444 FaultingCodeRangePair fcrp = masm.store64(value, addr);
11445 EmitSignalNullCheckTrapSite(masm, ins, fcrp.first,
11446 wasm::TrapMachineInsn::Store32);
11447 EmitSignalNullCheckTrapSite(masm, ins, fcrp.second,
11448 wasm::TrapMachineInsn::Store32);
11449#endif
11450}
11451
11452void CodeGenerator::visitWasmClampTable64Address(
11453 LWasmClampTable64Address* lir) {
11454 Register64 address = ToRegister64(lir->address());
11455 Register out = ToRegister(lir->output());
11456 masm.wasmClampTable64Address(address, out);
11457}
11458
11459void CodeGenerator::visitArrayBufferByteLength(LArrayBufferByteLength* lir) {
11460 Register obj = ToRegister(lir->object());
11461 Register out = ToRegister(lir->output());
11462 masm.loadArrayBufferByteLengthIntPtr(obj, out);
11463}
11464
11465void CodeGenerator::visitArrayBufferViewLength(LArrayBufferViewLength* lir) {
11466 Register obj = ToRegister(lir->object());
11467 Register out = ToRegister(lir->output());
11468 masm.loadArrayBufferViewLengthIntPtr(obj, out);
11469}
11470
11471void CodeGenerator::visitArrayBufferViewByteOffset(
11472 LArrayBufferViewByteOffset* lir) {
11473 Register obj = ToRegister(lir->object());
11474 Register out = ToRegister(lir->output());
11475 masm.loadArrayBufferViewByteOffsetIntPtr(obj, out);
11476}
11477
11478void CodeGenerator::visitArrayBufferViewElements(
11479 LArrayBufferViewElements* lir) {
11480 Register obj = ToRegister(lir->object());
11481 Register out = ToRegister(lir->output());
11482 masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), out);
11483}
11484
11485void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) {
11486 Register obj = ToRegister(lir->object());
11487 Register out = ToRegister(lir->output());
11488
11489 masm.typedArrayElementSize(obj, out);
11490}
11491
11492void CodeGenerator::visitResizableTypedArrayLength(
11493 LResizableTypedArrayLength* lir) {
11494 Register obj = ToRegister(lir->object());
11495 Register out = ToRegister(lir->output());
11496 Register temp = ToRegister(lir->temp0());
11497
11498 auto sync = SynchronizeLoad(lir->mir()->requiresMemoryBarrier());
11499 masm.loadResizableTypedArrayLengthIntPtr(sync, obj, out, temp);
11500}
11501
11502void CodeGenerator::visitResizableDataViewByteLength(
11503 LResizableDataViewByteLength* lir) {
11504 Register obj = ToRegister(lir->object());
11505 Register out = ToRegister(lir->output());
11506 Register temp = ToRegister(lir->temp0());
11507
11508 auto sync = SynchronizeLoad(lir->mir()->requiresMemoryBarrier());
11509 masm.loadResizableDataViewByteLengthIntPtr(sync, obj, out, temp);
11510}
11511
11512void CodeGenerator::visitGrowableSharedArrayBufferByteLength(
11513 LGrowableSharedArrayBufferByteLength* lir) {
11514 Register obj = ToRegister(lir->object());
11515 Register out = ToRegister(lir->output());
11516
11517 // Explicit |byteLength| accesses are seq-consistent atomic loads.
11518 auto sync = Synchronization::Load();
11519
11520 masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out);
11521}
11522
11523void CodeGenerator::visitGuardResizableArrayBufferViewInBounds(
11524 LGuardResizableArrayBufferViewInBounds* lir) {
11525 Register obj = ToRegister(lir->object());
11526 Register temp = ToRegister(lir->temp0());
11527
11528 Label bail;
11529 masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail);
11530 bailoutFrom(&bail, lir->snapshot());
11531}
11532
11533void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached(
11534 LGuardResizableArrayBufferViewInBoundsOrDetached* lir) {
11535 Register obj = ToRegister(lir->object());
11536 Register temp = ToRegister(lir->temp0());
11537
11538 Label done, bail;
11539 masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done);
11540 masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail);
11541 masm.bind(&done);
11542 bailoutFrom(&bail, lir->snapshot());
11543}
11544
11545void CodeGenerator::visitGuardHasAttachedArrayBuffer(
11546 LGuardHasAttachedArrayBuffer* lir) {
11547 Register obj = ToRegister(lir->object());
11548 Register temp = ToRegister(lir->temp0());
11549
11550 Label bail;
11551 masm.branchIfHasDetachedArrayBuffer(obj, temp, &bail);
11552 bailoutFrom(&bail, lir->snapshot());
11553}
11554
11555void CodeGenerator::visitGuardTypedArraySetOffset(
11556 LGuardTypedArraySetOffset* lir) {
11557 Register offset = ToRegister(lir->offset());
11558 Register targetLength = ToRegister(lir->targetLength());
11559 Register sourceLength = ToRegister(lir->sourceLength());
11560 Register temp = ToRegister(lir->temp0());
11561
11562 Label bail;
11563
11564 // Ensure `offset <= target.length`.
11565 masm.movePtr(targetLength, temp);
11566 masm.branchSubPtr(Assembler::Signed, offset, temp, &bail);
11567
11568 // Ensure `source.length <= (target.length - offset)`.
11569 masm.branchPtr(Assembler::GreaterThan, sourceLength, temp, &bail);
11570
11571 bailoutFrom(&bail, lir->snapshot());
11572}
11573
11574void CodeGenerator::visitTypedArrayFill(LTypedArrayFill* lir) {
11575 auto elementType = lir->mir()->elementType();
11576 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"
, 11576); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!Scalar::isBigIntType(elementType)"
")"); do { MOZ_CrashSequence(__null, 11576); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11577
11578 masm.setupAlignedABICall();
11579 masm.passABIArg(ToRegister(lir->object()));
11580 if (elementType == Scalar::Float64) {
11581 masm.passABIArg(ToFloatRegister(lir->value()), ABIType::Float64);
11582 } else if (elementType == Scalar::Float32 || elementType == Scalar::Float16) {
11583 masm.passABIArg(ToFloatRegister(lir->value()), ABIType::Float32);
11584 } else {
11585 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"
, 11585); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!Scalar::isFloatingType(elementType)"
")"); do { MOZ_CrashSequence(__null, 11585); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11586 masm.passABIArg(ToRegister(lir->value()));
11587 }
11588 masm.passABIArg(ToRegister(lir->start()));
11589 masm.passABIArg(ToRegister(lir->end()));
11590
11591 if (elementType == Scalar::Float64) {
11592 using Fn = void (*)(TypedArrayObject*, double, intptr_t, intptr_t);
11593 masm.callWithABI<Fn, js::TypedArrayFillDouble>();
11594 } else if (elementType == Scalar::Float32 || elementType == Scalar::Float16) {
11595 using Fn = void (*)(TypedArrayObject*, float, intptr_t, intptr_t);
11596 masm.callWithABI<Fn, js::TypedArrayFillFloat32>();
11597 } else {
11598 // All other types are managed using int32.
11599 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", 11599); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "Scalar::byteSize(elementType) <= sizeof(int32_t)"
")"); do { MOZ_CrashSequence(__null, 11599); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11600
11601 using Fn = void (*)(TypedArrayObject*, int32_t, intptr_t, intptr_t);
11602 masm.callWithABI<Fn, js::TypedArrayFillInt32>();
11603 }
11604}
11605
11606void CodeGenerator::visitTypedArrayFill64(LTypedArrayFill64* lir) {
11607 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"
, 11607); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->elementType())"
")"); do { MOZ_CrashSequence(__null, 11607); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11608
11609 masm.setupAlignedABICall();
11610 masm.passABIArg(ToRegister(lir->object()));
11611 masm.passABIArg(ToRegister64(lir->value()));
11612 masm.passABIArg(ToRegister(lir->start()));
11613 masm.passABIArg(ToRegister(lir->end()));
11614
11615 using Fn = void (*)(TypedArrayObject*, int64_t, intptr_t, intptr_t);
11616 masm.callWithABI<Fn, js::TypedArrayFillInt64>();
11617}
11618
11619void CodeGenerator::visitTypedArraySet(LTypedArraySet* lir) {
11620 Register target = ToRegister(lir->target());
11621 Register source = ToRegister(lir->source());
11622 Register offset = ToRegister(lir->offset());
11623
11624 // Bit-wise copying is infallible because it doesn't need to allocate any
11625 // temporary memory, even if the underlying buffers are the same.
11626 if (lir->mir()->canUseBitwiseCopy()) {
11627 masm.setupAlignedABICall();
11628 masm.passABIArg(target);
11629 masm.passABIArg(source);
11630 masm.passABIArg(offset);
11631
11632 using Fn = void (*)(TypedArrayObject*, TypedArrayObject*, intptr_t);
11633 masm.callWithABI<Fn, js::TypedArraySetInfallible>();
11634 } else {
11635 pushArg(offset);
11636 pushArg(source);
11637 pushArg(target);
11638
11639 using Fn =
11640 bool (*)(JSContext*, TypedArrayObject*, TypedArrayObject*, intptr_t);
11641 callVM<Fn, js::TypedArraySet>(lir);
11642 }
11643}
11644
11645void CodeGenerator::visitTypedArraySetFromSubarray(
11646 LTypedArraySetFromSubarray* lir) {
11647 Register target = ToRegister(lir->target());
11648 Register source = ToRegister(lir->source());
11649 Register offset = ToRegister(lir->offset());
11650 Register sourceOffset = ToRegister(lir->sourceOffset());
11651 Register sourceLength = ToRegister(lir->sourceLength());
11652
11653 // Bit-wise copying is infallible because it doesn't need to allocate any
11654 // temporary memory, even if the underlying buffers are the same.
11655 if (lir->mir()->canUseBitwiseCopy()) {
11656 masm.setupAlignedABICall();
11657 masm.passABIArg(target);
11658 masm.passABIArg(source);
11659 masm.passABIArg(offset);
11660 masm.passABIArg(sourceOffset);
11661 masm.passABIArg(sourceLength);
11662
11663 using Fn = void (*)(TypedArrayObject*, TypedArrayObject*, intptr_t,
11664 intptr_t, intptr_t);
11665 masm.callWithABI<Fn, js::TypedArraySetFromSubarrayInfallible>();
11666 } else {
11667 pushArg(sourceLength);
11668 pushArg(sourceOffset);
11669 pushArg(offset);
11670 pushArg(source);
11671 pushArg(target);
11672
11673 using Fn = bool (*)(JSContext*, TypedArrayObject*, TypedArrayObject*,
11674 intptr_t, intptr_t, intptr_t);
11675 callVM<Fn, js::TypedArraySetFromSubarray>(lir);
11676 }
11677}
11678
11679void CodeGenerator::visitTypedArraySubarray(LTypedArraySubarray* lir) {
11680 pushArg(ToRegister(lir->length()));
11681 pushArg(ToRegister(lir->start()));
11682 pushArg(ToRegister(lir->object()));
11683
11684 using Fn = TypedArrayObject* (*)(JSContext*, Handle<TypedArrayObject*>,
11685 intptr_t, intptr_t);
11686 callVM<Fn, js::TypedArraySubarrayWithLength>(lir);
11687}
11688
11689void CodeGenerator::visitToIntegerIndex(LToIntegerIndex* lir) {
11690 Register index = ToRegister(lir->index());
11691 Register length = ToRegister(lir->length());
11692 Register output = ToRegister(lir->output());
11693
11694 masm.movePtr(index, output);
11695
11696 Label done, notNegative;
11697 masm.branchTestPtr(Assembler::NotSigned, index, index, &notNegative);
11698 {
11699 masm.branchAddPtr(Assembler::NotSigned, length, output, &done);
11700 masm.movePtr(ImmWord(0), output);
11701 masm.jump(&done);
11702 }
11703 masm.bind(&notNegative);
11704 {
11705 masm.cmpPtrMovePtr(Assembler::GreaterThan, index, length, length, output);
11706 }
11707 masm.bind(&done);
11708}
11709
11710void CodeGenerator::visitGuardNumberToIntPtrIndex(
11711 LGuardNumberToIntPtrIndex* lir) {
11712 FloatRegister input = ToFloatRegister(lir->input());
11713 Register output = ToRegister(lir->output());
11714
11715 if (!lir->mir()->supportOOB()) {
11716 Label bail;
11717 masm.convertDoubleToPtr(input, output, &bail, false);
11718 bailoutFrom(&bail, lir->snapshot());
11719 return;
11720 }
11721
11722 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
11723 // Substitute the invalid index with an arbitrary out-of-bounds index.
11724 masm.movePtr(ImmWord(-1), output);
11725 masm.jump(ool.rejoin());
11726 });
11727 addOutOfLineCode(ool, lir->mir());
11728
11729 masm.convertDoubleToPtr(input, output, ool->entry(), false);
11730 masm.bind(ool->rejoin());
11731}
11732
11733void CodeGenerator::visitStringLength(LStringLength* lir) {
11734 Register input = ToRegister(lir->string());
11735 Register output = ToRegister(lir->output());
11736
11737 masm.loadStringLength(input, output);
11738}
11739
11740void CodeGenerator::visitMinMaxI(LMinMaxI* ins) {
11741 Register first = ToRegister(ins->first());
11742 Register output = ToRegister(ins->output());
11743
11744 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", 11744); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "first == output" ")"); do { MOZ_CrashSequence
(__null, 11744); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11745
11746 if (ins->second()->isConstant()) {
11747 auto second = Imm32(ToInt32(ins->second()));
11748
11749 if (ins->mir()->isMax()) {
11750 masm.max32(first, second, output);
11751 } else {
11752 masm.min32(first, second, output);
11753 }
11754 } else {
11755 Register second = ToRegister(ins->second());
11756
11757 if (ins->mir()->isMax()) {
11758 masm.max32(first, second, output);
11759 } else {
11760 masm.min32(first, second, output);
11761 }
11762 }
11763}
11764
11765void CodeGenerator::visitMinMaxIntPtr(LMinMaxIntPtr* ins) {
11766 Register first = ToRegister(ins->first());
11767 Register output = ToRegister(ins->output());
11768
11769 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", 11769); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "first == output" ")"); do { MOZ_CrashSequence
(__null, 11769); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11770
11771 if (ins->second()->isConstant()) {
11772 auto second = ImmWord(ToIntPtr(ins->second()));
11773
11774 if (ins->mir()->isMax()) {
11775 masm.maxPtr(first, second, output);
11776 } else {
11777 masm.minPtr(first, second, output);
11778 }
11779 } else {
11780 Register second = ToRegister(ins->second());
11781
11782 if (ins->mir()->isMax()) {
11783 masm.maxPtr(first, second, output);
11784 } else {
11785 masm.minPtr(first, second, output);
11786 }
11787 }
11788}
11789
11790void CodeGenerator::visitMinMaxArrayI(LMinMaxArrayI* ins) {
11791 Register array = ToRegister(ins->array());
11792 Register output = ToRegister(ins->output());
11793 Register temp1 = ToRegister(ins->temp0());
11794 Register temp2 = ToRegister(ins->temp1());
11795 Register temp3 = ToRegister(ins->temp2());
11796 bool isMax = ins->mir()->isMax();
11797
11798 Label bail;
11799 masm.minMaxArrayInt32(array, output, temp1, temp2, temp3, isMax, &bail);
11800 bailoutFrom(&bail, ins->snapshot());
11801}
11802
11803void CodeGenerator::visitMinMaxArrayD(LMinMaxArrayD* ins) {
11804 Register array = ToRegister(ins->array());
11805 FloatRegister output = ToFloatRegister(ins->output());
11806 FloatRegister floatTemp = ToFloatRegister(ins->temp0());
11807 Register temp1 = ToRegister(ins->temp1());
11808 Register temp2 = ToRegister(ins->temp2());
11809 bool isMax = ins->mir()->isMax();
11810
11811 Label bail;
11812 masm.minMaxArrayNumber(array, output, floatTemp, temp1, temp2, isMax, &bail);
11813 bailoutFrom(&bail, ins->snapshot());
11814}
11815
11816// For Abs*, lowering will have tied input to output on platforms where that is
11817// sensible, and otherwise left them untied.
11818
11819void CodeGenerator::visitAbsI(LAbsI* ins) {
11820 Register input = ToRegister(ins->input());
11821 Register output = ToRegister(ins->output());
11822
11823 if (ins->mir()->fallible()) {
11824 Label positive;
11825 if (input != output) {
11826 masm.move32(input, output);
11827 }
11828 masm.branchTest32(Assembler::NotSigned, output, output, &positive);
11829 Label bail;
11830 masm.branchNeg32(Assembler::Overflow, output, &bail);
11831 bailoutFrom(&bail, ins->snapshot());
11832 masm.bind(&positive);
11833 } else {
11834 masm.abs32(input, output);
11835 }
11836}
11837
11838void CodeGenerator::visitAbsD(LAbsD* ins) {
11839 masm.absDouble(ToFloatRegister(ins->input()), ToFloatRegister(ins->output()));
11840}
11841
11842void CodeGenerator::visitAbsF(LAbsF* ins) {
11843 masm.absFloat32(ToFloatRegister(ins->input()),
11844 ToFloatRegister(ins->output()));
11845}
11846
11847void CodeGenerator::visitPowII(LPowII* ins) {
11848 Register value = ToRegister(ins->value());
11849 Register power = ToRegister(ins->power());
11850 Register output = ToRegister(ins->output());
11851 Register temp0 = ToRegister(ins->temp0());
11852 Register temp1 = ToRegister(ins->temp1());
11853
11854 Label bailout;
11855 masm.pow32(value, power, output, temp0, temp1, &bailout);
11856 bailoutFrom(&bailout, ins->snapshot());
11857}
11858
11859void CodeGenerator::visitPowI(LPowI* ins) {
11860 FloatRegister value = ToFloatRegister(ins->value());
11861 Register power = ToRegister(ins->power());
11862
11863 using Fn = double (*)(double x, int32_t y);
11864 masm.setupAlignedABICall();
11865 masm.passABIArg(value, ABIType::Float64);
11866 masm.passABIArg(power);
11867
11868 masm.callWithABI<Fn, js::powi>(ABIType::Float64);
11869 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"
, 11869); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 11869); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11870}
11871
11872void CodeGenerator::visitPowD(LPowD* ins) {
11873 FloatRegister value = ToFloatRegister(ins->value());
11874 FloatRegister power = ToFloatRegister(ins->power());
11875
11876 using Fn = double (*)(double x, double y);
11877 masm.setupAlignedABICall();
11878 masm.passABIArg(value, ABIType::Float64);
11879 masm.passABIArg(power, ABIType::Float64);
11880 masm.callWithABI<Fn, ecmaPow>(ABIType::Float64);
11881
11882 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"
, 11882); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 11882); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11883}
11884
11885void CodeGenerator::visitPowOfTwoI(LPowOfTwoI* ins) {
11886 Register power = ToRegister(ins->power());
11887 Register output = ToRegister(ins->output());
11888
11889 uint32_t base = ins->base();
11890 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", 11890); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "std::has_single_bit(base)" ")"); do { MOZ_CrashSequence
(__null, 11890); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11891
11892 uint32_t n = mozilla::FloorLog2(base);
11893 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"
, 11893); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n != 0" ")"
); do { MOZ_CrashSequence(__null, 11893); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
11894
11895 // Hacker's Delight, 2nd edition, theorem D2.
11896 auto ceilingDiv = [](uint32_t x, uint32_t y) { return (x + y - 1) / y; };
11897
11898 // Take bailout if |power| is greater-or-equals |log_y(2^31)| or is negative.
11899 // |2^(n*y) < 2^31| must hold, hence |n*y < 31| resp. |y < 31/n|.
11900 //
11901 // Note: it's important for this condition to match the code in CacheIR.cpp
11902 // (CanAttachInt32Pow) to prevent failure loops.
11903 bailoutCmp32(Assembler::AboveOrEqual, power, Imm32(ceilingDiv(31, n)),
11904 ins->snapshot());
11905
11906 // Compute (2^n)^y as 2^(n*y) using repeated shifts. We could directly scale
11907 // |power| and perform a single shift, but due to the lack of necessary
11908 // MacroAssembler functionality, like multiplying a register with an
11909 // immediate, we restrict the number of generated shift instructions when
11910 // lowering this operation.
11911 masm.move32(Imm32(1), output);
11912 do {
11913 masm.lshift32(power, output);
11914 n--;
11915 } while (n > 0);
11916}
11917
11918void CodeGenerator::visitSqrtD(LSqrtD* ins) {
11919 FloatRegister input = ToFloatRegister(ins->input());
11920 FloatRegister output = ToFloatRegister(ins->output());
11921 masm.sqrtDouble(input, output);
11922}
11923
11924void CodeGenerator::visitSqrtF(LSqrtF* ins) {
11925 FloatRegister input = ToFloatRegister(ins->input());
11926 FloatRegister output = ToFloatRegister(ins->output());
11927 masm.sqrtFloat32(input, output);
11928}
11929
11930void CodeGenerator::visitSignI(LSignI* ins) {
11931 Register input = ToRegister(ins->input());
11932 Register output = ToRegister(ins->output());
11933 masm.signInt32(input, output);
11934}
11935
11936void CodeGenerator::visitSignD(LSignD* ins) {
11937 FloatRegister input = ToFloatRegister(ins->input());
11938 FloatRegister output = ToFloatRegister(ins->output());
11939 masm.signDouble(input, output);
11940}
11941
11942void CodeGenerator::visitSignDI(LSignDI* ins) {
11943 FloatRegister input = ToFloatRegister(ins->input());
11944 FloatRegister temp = ToFloatRegister(ins->temp0());
11945 Register output = ToRegister(ins->output());
11946
11947 Label bail;
11948 masm.signDoubleToInt32(input, output, temp, &bail);
11949 bailoutFrom(&bail, ins->snapshot());
11950}
11951
11952void CodeGenerator::visitSignID(LSignID* ins) {
11953 Register input = ToRegister(ins->input());
11954 Register temp = ToRegister(ins->temp0());
11955 FloatRegister output = ToFloatRegister(ins->output());
11956
11957 masm.signInt32(input, temp);
11958 masm.convertInt32ToDouble(temp, output);
11959}
11960
11961void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) {
11962 FloatRegister input = ToFloatRegister(ins->input());
11963 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"
, 11963); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 11963); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11964
11965 UnaryMathFunction fun = ins->mir()->function();
11966 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(fun);
11967
11968 masm.setupAlignedABICall();
11969
11970 masm.passABIArg(input, ABIType::Float64);
11971 masm.callWithABI(DynamicFunction<UnaryMathFunctionType>(funPtr),
11972 ABIType::Float64);
11973}
11974
11975void CodeGenerator::visitMathFunctionF(LMathFunctionF* ins) {
11976 FloatRegister input = ToFloatRegister(ins->input());
11977 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"
, 11977); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnFloat32Reg"
")"); do { MOZ_CrashSequence(__null, 11977); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11978
11979 masm.setupAlignedABICall();
11980 masm.passABIArg(input, ABIType::Float32);
11981
11982 using Fn = float (*)(float x);
11983 Fn funptr = nullptr;
11984 CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check;
11985 switch (ins->mir()->function()) {
11986 case UnaryMathFunction::Floor:
11987 funptr = std::floor;
11988 check = CheckUnsafeCallWithABI::DontCheckOther;
11989 break;
11990 case UnaryMathFunction::Round:
11991 funptr = math_roundf_impl;
11992 break;
11993 case UnaryMathFunction::Trunc:
11994 funptr = std::trunc;
11995 check = CheckUnsafeCallWithABI::DontCheckOther;
11996 break;
11997 case UnaryMathFunction::Ceil:
11998 funptr = std::ceil;
11999 check = CheckUnsafeCallWithABI::DontCheckOther;
12000 break;
12001 default:
12002 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", 12002); AnnotateMozCrashReason
("MOZ_CRASH(" "Unknown or unsupported float32 math function" ")"
); do { MOZ_CrashSequence(__null, 12002); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
12003 }
12004
12005 masm.callWithABI(DynamicFunction<Fn>(funptr), ABIType::Float32, check);
12006}
12007
12008void CodeGenerator::visitModD(LModD* ins) {
12009 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", 12009); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 12009); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12010
12011 FloatRegister lhs = ToFloatRegister(ins->lhs());
12012 FloatRegister rhs = ToFloatRegister(ins->rhs());
12013 FloatRegister output = ToFloatRegister(ins->output());
12014 Register temp1 = ToRegister(ins->temp0());
12015 Register temp2 = ToRegister(ins->temp1());
12016
12017 LiveRegisterSet liveRegisterSet = liveVolatileRegs(ins);
12018
12019 Label call, done;
12020 // The fast path's internal integer division may itself call out to a runtime
12021 // routine (on ARM without a hardware divide), and needs the volatile live set
12022 // to know which of our registers to preserve across that call.
12023 masm.modDoubleIntegerFastPath(lhs, rhs, output, temp1, temp2, liveRegisterSet,
12024 &call);
12025 masm.jump(&done);
12026
12027 masm.bind(&call);
12028 {
12029 LiveRegisterSet save = liveRegisterSet;
12030 save.takeUnchecked(output);
12031
12032 masm.PushRegsInMask(save);
12033 using Fn = double (*)(double a, double b);
12034 masm.setupUnalignedABICall(temp1);
12035 masm.passABIArg(lhs, ABIType::Float64);
12036 masm.passABIArg(rhs, ABIType::Float64);
12037 masm.callWithABI<Fn, NumberMod>(ABIType::Float64);
12038 masm.storeCallFloatResult(output);
12039 masm.PopRegsInMask(save);
12040 }
12041
12042 masm.bind(&done);
12043}
12044
12045void CodeGenerator::visitModPowTwoD(LModPowTwoD* ins) {
12046 FloatRegister lhs = ToFloatRegister(ins->lhs());
12047 uint32_t divisor = ins->divisor();
12048 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"
, 12048); AnnotateMozCrashReason("MOZ_ASSERT" "(" "std::has_single_bit(divisor)"
")"); do { MOZ_CrashSequence(__null, 12048); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12049
12050 FloatRegister output = ToFloatRegister(ins->output());
12051
12052 // Compute |n % d| using |copysign(n - (d * trunc(n / d)), n)|.
12053 //
12054 // This doesn't work if |d| isn't a power of two, because we may lose too much
12055 // precision. For example |Number.MAX_VALUE % 3 == 2|, but
12056 // |3 * trunc(Number.MAX_VALUE / 3) == Infinity|.
12057
12058 Label done;
12059 {
12060 ScratchDoubleScope scratch(masm);
12061
12062 // Subnormals can lead to performance degradation, which can make calling
12063 // |fmod| faster than this inline implementation. Work around this issue by
12064 // directly returning the input for any value in the interval ]-1, +1[.
12065 Label notSubnormal;
12066 masm.loadConstantDouble(1.0, scratch);
12067 masm.loadConstantDouble(-1.0, output);
12068 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, lhs, scratch,
12069 &notSubnormal);
12070 masm.branchDouble(Assembler::DoubleLessThanOrEqual, lhs, output,
12071 &notSubnormal);
12072
12073 masm.moveDouble(lhs, output);
12074 masm.jump(&done);
12075
12076 masm.bind(&notSubnormal);
12077
12078 if (divisor == 1) {
12079 // The pattern |n % 1 == 0| is used to detect integer numbers. We can skip
12080 // the multiplication by one in this case.
12081 masm.moveDouble(lhs, output);
12082 masm.nearbyIntDouble(RoundingMode::TowardsZero, output, scratch);
12083 masm.subDouble(scratch, output);
12084 } else {
12085 masm.loadConstantDouble(1.0 / double(divisor), scratch);
12086 masm.loadConstantDouble(double(divisor), output);
12087
12088 masm.mulDouble(lhs, scratch);
12089 masm.nearbyIntDouble(RoundingMode::TowardsZero, scratch, scratch);
12090 masm.mulDouble(output, scratch);
12091
12092 masm.moveDouble(lhs, output);
12093 masm.subDouble(scratch, output);
12094 }
12095 }
12096
12097 masm.copySignDouble(output, lhs, output);
12098 masm.bind(&done);
12099}
12100
12101void CodeGenerator::visitWasmBuiltinModD(LWasmBuiltinModD* ins) {
12102 masm.Push(InstanceReg);
12103 int32_t framePushedAfterInstance = masm.framePushed();
12104
12105 FloatRegister lhs = ToFloatRegister(ins->lhs());
12106 FloatRegister rhs = ToFloatRegister(ins->rhs());
12107
12108 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"
, 12108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 12108); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12109
12110 masm.setupWasmABICall(wasm::SymbolicAddress::ModD);
12111 masm.passABIArg(lhs, ABIType::Float64);
12112 masm.passABIArg(rhs, ABIType::Float64);
12113
12114 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
12115 masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD,
12116 mozilla::Some(instanceOffset), ABIType::Float64);
12117
12118 masm.Pop(InstanceReg);
12119}
12120
12121void CodeGenerator::visitClzI(LClzI* ins) {
12122 Register input = ToRegister(ins->input());
12123 Register output = ToRegister(ins->output());
12124 bool knownNotZero = ins->mir()->operandIsNeverZero();
12125
12126 masm.clz32(input, output, knownNotZero);
12127}
12128
12129void CodeGenerator::visitCtzI(LCtzI* ins) {
12130 Register input = ToRegister(ins->input());
12131 Register output = ToRegister(ins->output());
12132 bool knownNotZero = ins->mir()->operandIsNeverZero();
12133
12134 masm.ctz32(input, output, knownNotZero);
12135}
12136
12137void CodeGenerator::visitPopcntI(LPopcntI* ins) {
12138 Register input = ToRegister(ins->input());
12139 Register output = ToRegister(ins->output());
12140 Register temp = ToRegister(ins->temp0());
12141
12142 masm.popcnt32(input, output, temp);
12143}
12144
12145void CodeGenerator::visitClzI64(LClzI64* ins) {
12146 Register64 input = ToRegister64(ins->input());
12147 Register64 output = ToOutRegister64(ins);
12148
12149 masm.clz64(input, output);
12150}
12151
12152void CodeGenerator::visitCtzI64(LCtzI64* ins) {
12153 Register64 input = ToRegister64(ins->input());
12154 Register64 output = ToOutRegister64(ins);
12155
12156 masm.ctz64(input, output);
12157}
12158
12159void CodeGenerator::visitPopcntI64(LPopcntI64* ins) {
12160 Register64 input = ToRegister64(ins->input());
12161 Register64 output = ToOutRegister64(ins);
12162 Register temp = ToRegister(ins->temp0());
12163
12164 masm.popcnt64(input, output, temp);
12165}
12166
12167void CodeGenerator::visitBigIntAdd(LBigIntAdd* ins) {
12168 pushArg(ToRegister(ins->rhs()));
12169 pushArg(ToRegister(ins->lhs()));
12170
12171 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12172 callVM<Fn, BigInt::add>(ins);
12173}
12174
12175void CodeGenerator::visitBigIntSub(LBigIntSub* ins) {
12176 pushArg(ToRegister(ins->rhs()));
12177 pushArg(ToRegister(ins->lhs()));
12178
12179 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12180 callVM<Fn, BigInt::sub>(ins);
12181}
12182
12183void CodeGenerator::visitBigIntMul(LBigIntMul* ins) {
12184 pushArg(ToRegister(ins->rhs()));
12185 pushArg(ToRegister(ins->lhs()));
12186
12187 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12188 callVM<Fn, BigInt::mul>(ins);
12189}
12190
12191void CodeGenerator::visitBigIntDiv(LBigIntDiv* ins) {
12192 pushArg(ToRegister(ins->rhs()));
12193 pushArg(ToRegister(ins->lhs()));
12194
12195 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12196 callVM<Fn, BigInt::div>(ins);
12197}
12198
12199void CodeGenerator::visitBigIntMod(LBigIntMod* ins) {
12200 pushArg(ToRegister(ins->rhs()));
12201 pushArg(ToRegister(ins->lhs()));
12202
12203 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12204 callVM<Fn, BigInt::mod>(ins);
12205}
12206
12207void CodeGenerator::visitBigIntPow(LBigIntPow* ins) {
12208 pushArg(ToRegister(ins->rhs()));
12209 pushArg(ToRegister(ins->lhs()));
12210
12211 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12212 callVM<Fn, BigInt::pow>(ins);
12213}
12214
12215void CodeGenerator::visitBigIntBitAnd(LBigIntBitAnd* ins) {
12216 pushArg(ToRegister(ins->rhs()));
12217 pushArg(ToRegister(ins->lhs()));
12218
12219 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12220 callVM<Fn, BigInt::bitAnd>(ins);
12221}
12222
12223void CodeGenerator::visitBigIntBitOr(LBigIntBitOr* ins) {
12224 pushArg(ToRegister(ins->rhs()));
12225 pushArg(ToRegister(ins->lhs()));
12226
12227 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12228 callVM<Fn, BigInt::bitOr>(ins);
12229}
12230
12231void CodeGenerator::visitBigIntBitXor(LBigIntBitXor* ins) {
12232 pushArg(ToRegister(ins->rhs()));
12233 pushArg(ToRegister(ins->lhs()));
12234
12235 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12236 callVM<Fn, BigInt::bitXor>(ins);
12237}
12238
12239void CodeGenerator::visitBigIntLsh(LBigIntLsh* ins) {
12240 pushArg(ToRegister(ins->rhs()));
12241 pushArg(ToRegister(ins->lhs()));
12242
12243 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12244 callVM<Fn, BigInt::lsh>(ins);
12245}
12246
12247void CodeGenerator::visitBigIntRsh(LBigIntRsh* ins) {
12248 pushArg(ToRegister(ins->rhs()));
12249 pushArg(ToRegister(ins->lhs()));
12250
12251 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
12252 callVM<Fn, BigInt::rsh>(ins);
12253}
12254
12255void CodeGenerator::visitBigIntIncrement(LBigIntIncrement* ins) {
12256 pushArg(ToRegister(ins->input()));
12257
12258 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
12259 callVM<Fn, BigInt::inc>(ins);
12260}
12261
12262void CodeGenerator::visitBigIntDecrement(LBigIntDecrement* ins) {
12263 pushArg(ToRegister(ins->input()));
12264
12265 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
12266 callVM<Fn, BigInt::dec>(ins);
12267}
12268
12269void CodeGenerator::visitBigIntNegate(LBigIntNegate* ins) {
12270 Register input = ToRegister(ins->input());
12271 Register temp = ToRegister(ins->temp0());
12272 Register output = ToRegister(ins->output());
12273
12274 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
12275 auto* ool =
12276 oolCallVM<Fn, BigInt::neg>(ins, ArgList(input), StoreRegisterTo(output));
12277
12278 // -0n == 0n
12279 Label lhsNonZero;
12280 masm.branchIfBigIntIsNonZero(input, &lhsNonZero);
12281 masm.movePtr(input, output);
12282 masm.jump(ool->rejoin());
12283 masm.bind(&lhsNonZero);
12284
12285 // Call into the VM when the input uses heap digits.
12286 masm.copyBigIntWithInlineDigits(input, output, temp, initialBigIntHeap(),
12287 ool->entry());
12288
12289 // Flip the sign bit.
12290 masm.xor32(Imm32(BigInt::signBitMask()),
12291 Address(output, BigInt::offsetOfFlags()));
12292
12293 masm.bind(ool->rejoin());
12294}
12295
12296void CodeGenerator::visitBigIntBitNot(LBigIntBitNot* ins) {
12297 pushArg(ToRegister(ins->input()));
12298
12299 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
12300 callVM<Fn, BigInt::bitNot>(ins);
12301}
12302
12303void CodeGenerator::visitBigIntToIntPtr(LBigIntToIntPtr* ins) {
12304 Register input = ToRegister(ins->input());
12305 Register output = ToRegister(ins->output());
12306
12307 Label bail;
12308 masm.loadBigIntPtr(input, output, &bail);
12309 bailoutFrom(&bail, ins->snapshot());
12310}
12311
12312void CodeGenerator::visitIntPtrToBigInt(LIntPtrToBigInt* ins) {
12313 Register input = ToRegister(ins->input());
12314 Register temp = ToRegister(ins->temp0());
12315 Register output = ToRegister(ins->output());
12316
12317 using Fn = BigInt* (*)(JSContext*, intptr_t);
12318 auto* ool = oolCallVM<Fn, JS::BigInt::createFromIntPtr>(
12319 ins, ArgList(input), StoreRegisterTo(output));
12320
12321 masm.newGCBigInt(output, temp, initialBigIntHeap(), ool->entry());
12322 masm.movePtr(input, temp);
12323 masm.initializeBigIntPtr(output, temp);
12324
12325 masm.bind(ool->rejoin());
12326}
12327
12328void CodeGenerator::visitBigIntPtrAdd(LBigIntPtrAdd* ins) {
12329 Register lhs = ToRegister(ins->lhs());
12330 const LAllocation* rhs = ins->rhs();
12331 Register output = ToRegister(ins->output());
12332
12333 if (rhs->isConstant()) {
12334 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12335 } else {
12336 masm.movePtr(ToRegister(rhs), output);
12337 }
12338
12339 Label bail;
12340 masm.branchAddPtr(Assembler::Overflow, lhs, output, &bail);
12341 bailoutFrom(&bail, ins->snapshot());
12342}
12343
12344void CodeGenerator::visitBigIntPtrSub(LBigIntPtrSub* ins) {
12345 Register lhs = ToRegister(ins->lhs());
12346 Register rhs = ToRegister(ins->rhs());
12347 Register output = ToRegister(ins->output());
12348
12349 Label bail;
12350 masm.movePtr(lhs, output);
12351 masm.branchSubPtr(Assembler::Overflow, rhs, output, &bail);
12352 bailoutFrom(&bail, ins->snapshot());
12353}
12354
12355void CodeGenerator::visitBigIntPtrMul(LBigIntPtrMul* ins) {
12356 Register lhs = ToRegister(ins->lhs());
12357 const LAllocation* rhs = ins->rhs();
12358 Register output = ToRegister(ins->output());
12359
12360 if (rhs->isConstant()) {
12361 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12362 } else {
12363 masm.movePtr(ToRegister(rhs), output);
12364 }
12365
12366 Label bail;
12367 masm.branchMulPtr(Assembler::Overflow, lhs, output, &bail);
12368 bailoutFrom(&bail, ins->snapshot());
12369}
12370
12371void CodeGenerator::visitBigIntPtrDiv(LBigIntPtrDiv* ins) {
12372 Register lhs = ToRegister(ins->lhs());
12373 Register rhs = ToRegister(ins->rhs());
12374 Register output = ToRegister(ins->output());
12375
12376 // x / 0 throws an error.
12377 Label bail;
12378 if (ins->mir()->canBeDivideByZero()) {
12379 masm.branchPtr(Assembler::Equal, rhs, Imm32(0), &bail);
12380 }
12381
12382 static constexpr auto DigitMin = std::numeric_limits<
12383 mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min();
12384
12385 // Handle an integer overflow from INT{32,64}_MIN / -1.
12386 Label notOverflow;
12387 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), &notOverflow);
12388 masm.branchPtr(Assembler::Equal, rhs, Imm32(-1), &bail);
12389 masm.bind(&notOverflow);
12390
12391 emitBigIntPtrDiv(ins, lhs, rhs, output);
12392
12393 bailoutFrom(&bail, ins->snapshot());
12394}
12395
12396void CodeGenerator::visitBigIntPtrDivPowTwo(LBigIntPtrDivPowTwo* ins) {
12397 Register lhs = ToRegister(ins->lhs());
12398 Register output = ToRegister(ins->output());
12399 int32_t shift = ins->shift();
12400 bool negativeDivisor = ins->negativeDivisor();
12401
12402 masm.movePtr(lhs, output);
12403
12404 if (shift) {
12405 // Adjust the value so that shifting produces a correctly rounded result
12406 // when the numerator is negative.
12407 // See 10-1 "Signed Division by a Known Power of 2" in Henry S. Warren,
12408 // Jr.'s Hacker's Delight.
12409
12410 constexpr size_t bits = BigInt::DigitBits;
12411
12412 if (shift > 1) {
12413 // Copy the sign bit of the numerator. (= (2^bits - 1) or 0)
12414 masm.rshiftPtrArithmetic(Imm32(bits - 1), output);
12415 }
12416
12417 // Divide by 2^(bits - shift)
12418 // i.e. (= (2^bits - 1) / 2^(bits - shift) or 0)
12419 // i.e. (= (2^shift - 1) or 0)
12420 masm.rshiftPtr(Imm32(bits - shift), output);
12421
12422 // If signed, make any 1 bit below the shifted bits to bubble up, such that
12423 // once shifted the value would be rounded towards 0.
12424 masm.addPtr(lhs, output);
12425
12426 masm.rshiftPtrArithmetic(Imm32(shift), output);
12427
12428 if (negativeDivisor) {
12429 masm.negPtr(output);
12430 }
12431 } else if (negativeDivisor) {
12432 Label bail;
12433 masm.branchNegPtr(Assembler::Overflow, output, &bail);
12434 bailoutFrom(&bail, ins->snapshot());
12435 }
12436}
12437
12438void CodeGenerator::visitBigIntPtrMod(LBigIntPtrMod* ins) {
12439 Register lhs = ToRegister(ins->lhs());
12440 Register rhs = ToRegister(ins->rhs());
12441 Register output = ToRegister(ins->output());
12442 Register temp = ToRegister(ins->temp0());
12443
12444 // x % 0 throws an error.
12445 if (ins->mir()->canBeDivideByZero()) {
12446 bailoutCmpPtr(Assembler::Equal, rhs, Imm32(0), ins->snapshot());
12447 }
12448
12449 static constexpr auto DigitMin = std::numeric_limits<
12450 mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min();
12451
12452 masm.movePtr(lhs, temp);
12453
12454 // Handle an integer overflow from INT{32,64}_MIN / -1.
12455 Label notOverflow;
12456 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), &notOverflow);
12457 masm.branchPtr(Assembler::NotEqual, rhs, Imm32(-1), &notOverflow);
12458 masm.movePtr(ImmWord(0), temp);
12459 masm.bind(&notOverflow);
12460
12461 emitBigIntPtrMod(ins, temp, rhs, output);
12462}
12463
12464void CodeGenerator::visitBigIntPtrModPowTwo(LBigIntPtrModPowTwo* ins) {
12465 Register lhs = ToRegister(ins->lhs());
12466 Register output = ToRegister(ins->output());
12467 Register temp = ToRegister(ins->temp0());
12468 int32_t shift = ins->shift();
12469
12470 masm.movePtr(lhs, output);
12471 masm.movePtr(ImmWord((uintptr_t(1) << shift) - uintptr_t(1)), temp);
12472
12473 // Switch based on sign of the lhs.
12474
12475 // Positive numbers are just a bitmask.
12476 Label negative;
12477 masm.branchTestPtr(Assembler::Signed, lhs, lhs, &negative);
12478
12479 masm.andPtr(temp, output);
12480
12481 Label done;
12482 masm.jump(&done);
12483
12484 // Negative numbers need a negate, bitmask, negate
12485 masm.bind(&negative);
12486
12487 masm.negPtr(output);
12488 masm.andPtr(temp, output);
12489 masm.negPtr(output);
12490
12491 masm.bind(&done);
12492}
12493
12494void CodeGenerator::visitBigIntPtrPow(LBigIntPtrPow* ins) {
12495 Register lhs = ToRegister(ins->lhs());
12496 Register rhs = ToRegister(ins->rhs());
12497 Register output = ToRegister(ins->output());
12498 Register temp0 = ToRegister(ins->temp0());
12499 Register temp1 = ToRegister(ins->temp1());
12500
12501 Label bail;
12502 masm.powPtr(lhs, rhs, output, temp0, temp1, &bail);
12503 bailoutFrom(&bail, ins->snapshot());
12504}
12505
12506void CodeGenerator::visitBigIntPtrBitAnd(LBigIntPtrBitAnd* ins) {
12507 Register lhs = ToRegister(ins->lhs());
12508 const LAllocation* rhs = ins->rhs();
12509 Register output = ToRegister(ins->output());
12510
12511 if (rhs->isConstant()) {
12512 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12513 } else {
12514 masm.movePtr(ToRegister(rhs), output);
12515 }
12516 masm.andPtr(lhs, output);
12517}
12518
12519void CodeGenerator::visitBigIntPtrBitOr(LBigIntPtrBitOr* ins) {
12520 Register lhs = ToRegister(ins->lhs());
12521 const LAllocation* rhs = ins->rhs();
12522 Register output = ToRegister(ins->output());
12523
12524 if (rhs->isConstant()) {
12525 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12526 } else {
12527 masm.movePtr(ToRegister(rhs), output);
12528 }
12529 masm.orPtr(lhs, output);
12530}
12531
12532void CodeGenerator::visitBigIntPtrBitXor(LBigIntPtrBitXor* ins) {
12533 Register lhs = ToRegister(ins->lhs());
12534 const LAllocation* rhs = ins->rhs();
12535 Register output = ToRegister(ins->output());
12536
12537 if (rhs->isConstant()) {
12538 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
12539 } else {
12540 masm.movePtr(ToRegister(rhs), output);
12541 }
12542 masm.xorPtr(lhs, output);
12543}
12544
12545void CodeGenerator::visitBigIntPtrLsh(LBigIntPtrLsh* ins) {
12546 Register lhs = ToRegister(ins->lhs());
12547 Register output = ToRegister(ins->output());
12548 Register temp = ToTempRegisterOrInvalid(ins->temp0());
12549 Register tempShift = ToTempRegisterOrInvalid(ins->temp1());
12550
12551 if (ins->rhs()->isConstant()) {
12552 intptr_t rhs = ToIntPtr(ins->rhs());
12553
12554 if (rhs >= intptr_t(BigInt::DigitBits)) {
12555 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"
, 12555); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12555); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12556
12557 // x << DigitBits with x != 0n always exceeds pointer-sized storage.
12558 masm.movePtr(ImmWord(0), output);
12559 bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot());
12560 } else if (rhs <= -intptr_t(BigInt::DigitBits)) {
12561 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"
, 12561); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12561); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12562
12563 // x << -DigitBits == x >> DigitBits, which is either 0n or -1n.
12564 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), lhs, output);
12565 } else if (rhs <= 0) {
12566 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"
, 12566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12566); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12567
12568 // |x << -y| is computed as |x >> y|.
12569 masm.rshiftPtrArithmetic(Imm32(-rhs), lhs, output);
12570 } else {
12571 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"
, 12571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12571); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12572
12573 masm.lshiftPtr(Imm32(rhs), lhs, output);
12574
12575 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12576 masm.rshiftPtrArithmetic(Imm32(rhs), output, temp);
12577 bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot());
12578 }
12579 } else {
12580 Register rhs = ToRegister(ins->rhs());
12581
12582 Label done, bail;
12583 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"
, 12583); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12583); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12584
12585 masm.movePtr(lhs, output);
12586
12587 // 0n << x == 0n
12588 masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done);
12589
12590 // x << DigitBits with x != 0n always exceeds pointer-sized storage.
12591 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(BigInt::DigitBits),
12592 &bail);
12593
12594 // x << -DigitBits == x >> DigitBits, which is either 0n or -1n.
12595 Label shift;
12596 masm.branchPtr(Assembler::GreaterThan, rhs,
12597 Imm32(-int32_t(BigInt::DigitBits)), &shift);
12598 {
12599 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output);
12600 masm.jump(&done);
12601 }
12602 masm.bind(&shift);
12603
12604 // Move |rhs| into the designated shift register.
12605 masm.movePtr(rhs, tempShift);
12606
12607 // |x << -y| is computed as |x >> y|.
12608 Label leftShift;
12609 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &leftShift);
12610 {
12611 masm.negPtr(tempShift);
12612 masm.rshiftPtrArithmetic(tempShift, output);
12613 masm.jump(&done);
12614 }
12615 masm.bind(&leftShift);
12616
12617 masm.lshiftPtr(tempShift, output);
12618
12619 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12620 masm.movePtr(output, temp);
12621 masm.rshiftPtrArithmetic(tempShift, temp);
12622 masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail);
12623
12624 masm.bind(&done);
12625 bailoutFrom(&bail, ins->snapshot());
12626 }
12627}
12628
12629void CodeGenerator::visitBigIntPtrRsh(LBigIntPtrRsh* ins) {
12630 Register lhs = ToRegister(ins->lhs());
12631 Register output = ToRegister(ins->output());
12632 Register temp = ToTempRegisterOrInvalid(ins->temp0());
12633 Register tempShift = ToTempRegisterOrInvalid(ins->temp1());
12634
12635 if (ins->rhs()->isConstant()) {
12636 intptr_t rhs = ToIntPtr(ins->rhs());
12637
12638 if (rhs <= -intptr_t(BigInt::DigitBits)) {
12639 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"
, 12639); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12639); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12640
12641 // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage.
12642 masm.movePtr(ImmWord(0), output);
12643 bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot());
12644 } else if (rhs >= intptr_t(BigInt::DigitBits)) {
12645 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"
, 12645); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12645); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12646
12647 // x >> DigitBits is either 0n or -1n.
12648 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), lhs, output);
12649 } else if (rhs < 0) {
12650 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"
, 12650); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12650); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12651
12652 // |x >> -y| is computed as |x << y|.
12653 masm.lshiftPtr(Imm32(-rhs), lhs, output);
12654
12655 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12656 masm.rshiftPtrArithmetic(Imm32(-rhs), output, temp);
12657 bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot());
12658 } else {
12659 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"
, 12659); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12659); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12660
12661 masm.rshiftPtrArithmetic(Imm32(rhs), lhs, output);
12662 }
12663 } else {
12664 Register rhs = ToRegister(ins->rhs());
12665
12666 Label done, bail;
12667 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"
, 12667); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { MOZ_CrashSequence(__null, 12667); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12668
12669 masm.movePtr(lhs, output);
12670
12671 // 0n >> x == 0n
12672 masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done);
12673
12674 // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage.
12675 masm.branchPtr(Assembler::LessThanOrEqual, rhs,
12676 Imm32(-int32_t(BigInt::DigitBits)), &bail);
12677
12678 // x >> DigitBits is either 0n or -1n.
12679 Label shift;
12680 masm.branchPtr(Assembler::LessThan, rhs, Imm32(BigInt::DigitBits), &shift);
12681 {
12682 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output);
12683 masm.jump(&done);
12684 }
12685 masm.bind(&shift);
12686
12687 // Move |rhs| into the designated shift register.
12688 masm.movePtr(rhs, tempShift);
12689
12690 // |x >> -y| is computed as |x << y|.
12691 Label rightShift;
12692 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &rightShift);
12693 {
12694 masm.negPtr(tempShift);
12695 masm.lshiftPtr(tempShift, output);
12696
12697 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12698 masm.movePtr(output, temp);
12699 masm.rshiftPtrArithmetic(tempShift, temp);
12700 masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail);
12701
12702 masm.jump(&done);
12703 }
12704 masm.bind(&rightShift);
12705
12706 masm.rshiftPtrArithmetic(tempShift, output);
12707
12708 masm.bind(&done);
12709 bailoutFrom(&bail, ins->snapshot());
12710 }
12711}
12712
12713void CodeGenerator::visitBigIntPtrBitNot(LBigIntPtrBitNot* ins) {
12714 Register input = ToRegister(ins->input());
12715 Register output = ToRegister(ins->output());
12716
12717 masm.movePtr(input, output);
12718 masm.notPtr(output);
12719}
12720
12721void CodeGenerator::visitInt32ToStringWithBase(LInt32ToStringWithBase* lir) {
12722 Register input = ToRegister(lir->input());
12723 RegisterOrInt32 base = ToRegisterOrInt32(lir->base());
12724 Register output = ToRegister(lir->output());
12725 Register temp0 = ToRegister(lir->temp0());
12726 Register temp1 = ToRegister(lir->temp1());
12727
12728 bool lowerCase = lir->mir()->stringCase() == StringCase::Lower;
12729
12730 using Fn = JSLinearString* (*)(JSContext*, int32_t, int32_t, bool);
12731 if (base.is<Register>()) {
12732 auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>(
12733 lir, ArgList(input, base.as<Register>(), Imm32(lowerCase)),
12734 StoreRegisterTo(output));
12735
12736 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
12737 masm.loadInt32ToStringWithBase(input, base.as<Register>(), output, temp0,
12738 temp1, gen->runtime->staticStrings(),
12739 liveRegs, lowerCase, ool->entry());
12740 masm.bind(ool->rejoin());
12741 } else {
12742 auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>(
12743 lir, ArgList(input, Imm32(base.as<int32_t>()), Imm32(lowerCase)),
12744 StoreRegisterTo(output));
12745
12746 masm.loadInt32ToStringWithBase(input, base.as<int32_t>(), output, temp0,
12747 temp1, gen->runtime->staticStrings(),
12748 lowerCase, ool->entry());
12749 masm.bind(ool->rejoin());
12750 }
12751}
12752
12753void CodeGenerator::visitNumberParseInt(LNumberParseInt* lir) {
12754 Register string = ToRegister(lir->string());
12755 Register radix = ToRegister(lir->radix());
12756 ValueOperand output = ToOutValue(lir);
12757 Register temp = ToRegister(lir->temp0());
12758
12759#ifdef DEBUG1
12760 Label ok;
12761 masm.branch32(Assembler::Equal, radix, Imm32(0), &ok);
12762 masm.branch32(Assembler::Equal, radix, Imm32(10), &ok);
12763 masm.assumeUnreachable("radix must be 0 or 10 for indexed value fast path");
12764 masm.bind(&ok);
12765#endif
12766
12767 // Use indexed value as fast path if possible.
12768 Label vmCall, done;
12769 masm.loadStringIndexValue(string, temp, &vmCall);
12770 masm.tagValue(JSVAL_TYPE_INT32, temp, output);
12771 masm.jump(&done);
12772 {
12773 masm.bind(&vmCall);
12774
12775 pushArg(radix);
12776 pushArg(string);
12777
12778 using Fn = bool (*)(JSContext*, HandleString, int32_t, MutableHandleValue);
12779 callVM<Fn, js::NumberParseInt>(lir);
12780 }
12781 masm.bind(&done);
12782}
12783
12784void CodeGenerator::visitDoubleParseInt(LDoubleParseInt* lir) {
12785 FloatRegister number = ToFloatRegister(lir->number());
12786 Register output = ToRegister(lir->output());
12787 FloatRegister temp = ToFloatRegister(lir->temp0());
12788
12789 Label bail;
12790 masm.branchDouble(Assembler::DoubleUnordered, number, number, &bail);
12791 masm.branchTruncateDoubleToInt32(number, output, &bail);
12792
12793 Label ok;
12794 masm.branch32(Assembler::NotEqual, output, Imm32(0), &ok);
12795 {
12796 // Accept both +0 and -0 and return 0.
12797 masm.loadConstantDouble(0.0, temp);
12798 masm.branchDouble(Assembler::DoubleEqual, number, temp, &ok);
12799
12800 // Fail if a non-zero input is in the exclusive range (-1, 1.0e-6).
12801 masm.loadConstantDouble(DOUBLE_DECIMAL_IN_SHORTEST_LOW, temp);
12802 masm.branchDouble(Assembler::DoubleLessThan, number, temp, &bail);
12803 }
12804 masm.bind(&ok);
12805
12806 bailoutFrom(&bail, lir->snapshot());
12807}
12808
12809void CodeGenerator::visitFloor(LFloor* lir) {
12810 FloatRegister input = ToFloatRegister(lir->input());
12811 Register output = ToRegister(lir->output());
12812
12813 Label bail;
12814 masm.floorDoubleToInt32(input, output, &bail);
12815 bailoutFrom(&bail, lir->snapshot());
12816}
12817
12818void CodeGenerator::visitFloorF(LFloorF* lir) {
12819 FloatRegister input = ToFloatRegister(lir->input());
12820 Register output = ToRegister(lir->output());
12821
12822 Label bail;
12823 masm.floorFloat32ToInt32(input, output, &bail);
12824 bailoutFrom(&bail, lir->snapshot());
12825}
12826
12827void CodeGenerator::visitCeil(LCeil* lir) {
12828 FloatRegister input = ToFloatRegister(lir->input());
12829 Register output = ToRegister(lir->output());
12830
12831 Label bail;
12832 masm.ceilDoubleToInt32(input, output, &bail);
12833 bailoutFrom(&bail, lir->snapshot());
12834}
12835
12836void CodeGenerator::visitCeilF(LCeilF* lir) {
12837 FloatRegister input = ToFloatRegister(lir->input());
12838 Register output = ToRegister(lir->output());
12839
12840 Label bail;
12841 masm.ceilFloat32ToInt32(input, output, &bail);
12842 bailoutFrom(&bail, lir->snapshot());
12843}
12844
12845void CodeGenerator::visitRound(LRound* lir) {
12846 FloatRegister input = ToFloatRegister(lir->input());
12847 FloatRegister temp = ToFloatRegister(lir->temp0());
12848 Register output = ToRegister(lir->output());
12849
12850 Label bail;
12851 masm.roundDoubleToInt32(input, output, temp, &bail);
12852 bailoutFrom(&bail, lir->snapshot());
12853}
12854
12855void CodeGenerator::visitRoundF(LRoundF* lir) {
12856 FloatRegister input = ToFloatRegister(lir->input());
12857 FloatRegister temp = ToFloatRegister(lir->temp0());
12858 Register output = ToRegister(lir->output());
12859
12860 Label bail;
12861 masm.roundFloat32ToInt32(input, output, temp, &bail);
12862 bailoutFrom(&bail, lir->snapshot());
12863}
12864
12865void CodeGenerator::visitTrunc(LTrunc* lir) {
12866 FloatRegister input = ToFloatRegister(lir->input());
12867 Register output = ToRegister(lir->output());
12868
12869 Label bail;
12870 masm.truncDoubleToInt32(input, output, &bail);
12871 bailoutFrom(&bail, lir->snapshot());
12872}
12873
12874void CodeGenerator::visitTruncF(LTruncF* lir) {
12875 FloatRegister input = ToFloatRegister(lir->input());
12876 Register output = ToRegister(lir->output());
12877
12878 Label bail;
12879 masm.truncFloat32ToInt32(input, output, &bail);
12880 bailoutFrom(&bail, lir->snapshot());
12881}
12882
12883void CodeGenerator::visitNearbyInt(LNearbyInt* lir) {
12884 FloatRegister input = ToFloatRegister(lir->input());
12885 FloatRegister output = ToFloatRegister(lir->output());
12886
12887 RoundingMode roundingMode = lir->mir()->roundingMode();
12888 masm.nearbyIntDouble(roundingMode, input, output);
12889}
12890
12891void CodeGenerator::visitNearbyIntF(LNearbyIntF* lir) {
12892 FloatRegister input = ToFloatRegister(lir->input());
12893 FloatRegister output = ToFloatRegister(lir->output());
12894
12895 RoundingMode roundingMode = lir->mir()->roundingMode();
12896 masm.nearbyIntFloat32(roundingMode, input, output);
12897}
12898
12899void CodeGenerator::visitRoundToDouble(LRoundToDouble* lir) {
12900 FloatRegister input = ToFloatRegister(lir->input());
12901 FloatRegister output = ToFloatRegister(lir->output());
12902
12903 masm.roundDouble(input, output);
12904}
12905
12906void CodeGenerator::visitRoundToFloat32(LRoundToFloat32* lir) {
12907 FloatRegister input = ToFloatRegister(lir->input());
12908 FloatRegister output = ToFloatRegister(lir->output());
12909
12910 masm.roundFloat32(input, output);
12911}
12912
12913void CodeGenerator::visitCopySignF(LCopySignF* lir) {
12914 FloatRegister lhs = ToFloatRegister(lir->lhs());
12915 FloatRegister rhs = ToFloatRegister(lir->rhs());
12916 FloatRegister out = ToFloatRegister(lir->output());
12917
12918 if (lhs == rhs) {
12919 if (lhs != out) {
12920 masm.moveFloat32(lhs, out);
12921 }
12922 return;
12923 }
12924
12925 masm.copySignFloat32(lhs, rhs, out);
12926}
12927
12928void CodeGenerator::visitCopySignD(LCopySignD* lir) {
12929 FloatRegister lhs = ToFloatRegister(lir->lhs());
12930 FloatRegister rhs = ToFloatRegister(lir->rhs());
12931 FloatRegister out = ToFloatRegister(lir->output());
12932
12933 if (lhs == rhs) {
12934 if (lhs != out) {
12935 masm.moveDouble(lhs, out);
12936 }
12937 return;
12938 }
12939
12940 masm.copySignDouble(lhs, rhs, out);
12941}
12942
12943void CodeGenerator::visitCompareS(LCompareS* lir) {
12944 JSOp op = lir->mir()->jsop();
12945 Register left = ToRegister(lir->left());
12946 Register right = ToRegister(lir->right());
12947 Register output = ToRegister(lir->output());
12948
12949 OutOfLineCode* ool = nullptr;
12950
12951 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
12952 if (op == JSOp::Eq || op == JSOp::StrictEq) {
12953 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
12954 lir, ArgList(left, right), StoreRegisterTo(output));
12955 } else if (op == JSOp::Ne || op == JSOp::StrictNe) {
12956 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
12957 lir, ArgList(left, right), StoreRegisterTo(output));
12958 } else if (op == JSOp::Lt) {
12959 ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>(
12960 lir, ArgList(left, right), StoreRegisterTo(output));
12961 } else if (op == JSOp::Le) {
12962 // Push the operands in reverse order for JSOp::Le:
12963 // - |left <= right| is implemented as |right >= left|.
12964 ool =
12965 oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>(
12966 lir, ArgList(right, left), StoreRegisterTo(output));
12967 } else if (op == JSOp::Gt) {
12968 // Push the operands in reverse order for JSOp::Gt:
12969 // - |left > right| is implemented as |right < left|.
12970 ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>(
12971 lir, ArgList(right, left), StoreRegisterTo(output));
12972 } else {
12973 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", 12973); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "op == JSOp::Ge" ")"); do { MOZ_CrashSequence
(__null, 12973); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12974 ool =
12975 oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>(
12976 lir, ArgList(left, right), StoreRegisterTo(output));
12977 }
12978
12979 masm.compareStrings(op, left, right, output, ool->entry());
12980
12981 masm.bind(ool->rejoin());
12982}
12983
12984void CodeGenerator::visitCompareSInline(LCompareSInline* lir) {
12985 JSOp op = lir->mir()->jsop();
12986 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", 12986); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 12986); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12987
12988 Register input = ToRegister(lir->input());
12989 Register output = ToRegister(lir->output());
12990
12991 const JSOffThreadAtom* str = lir->constant();
12992 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", 12992); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "str->length() > 0" ")"); do { MOZ_CrashSequence
(__null, 12992); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12993
12994 OutOfLineCode* ool = nullptr;
12995
12996 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
12997 if (op == JSOp::Eq || op == JSOp::StrictEq) {
12998 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
12999 lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output));
13000 } else {
13001 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", 13001); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "op == JSOp::Ne || op == JSOp::StrictNe" ")"
); do { MOZ_CrashSequence(__null, 13001); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
13002 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
13003 lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output));
13004 }
13005
13006 masm.equalStrings(op, input, str, output, ool->entry());
13007 masm.bind(ool->rejoin());
13008}
13009
13010void CodeGenerator::visitCompareSSingle(LCompareSSingle* lir) {
13011 JSOp op = lir->jsop();
13012 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", 13012); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsRelationalOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13012); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13013
13014 Register input = ToRegister(lir->input());
13015 Register output = ToRegister(lir->output());
13016 Register temp = ToRegister(lir->temp0());
13017
13018 const JSOffThreadAtom* str = lir->constant();
13019 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", 13019); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "str->length() == 1" ")"); do { MOZ_CrashSequence
(__null, 13019); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13020
13021 char16_t ch = str->latin1OrTwoByteChar(0);
13022
13023 masm.movePtr(input, temp);
13024
13025 // Check if the string is empty.
13026 Label compareLength;
13027 masm.branch32(Assembler::Equal, Address(temp, JSString::offsetOfLength()),
13028 Imm32(0), &compareLength);
13029
13030 // The first character is in the left-most rope child.
13031 Label notRope;
13032 masm.branchIfNotRope(temp, &notRope);
13033 {
13034 // Unwind ropes at the start if possible.
13035 Label unwindRope;
13036 masm.bind(&unwindRope);
13037 masm.loadRopeLeftChild(temp, output);
13038 masm.movePtr(output, temp);
13039
13040#ifdef DEBUG1
13041 Label notEmpty;
13042 masm.branch32(Assembler::NotEqual,
13043 Address(temp, JSString::offsetOfLength()), Imm32(0),
13044 &notEmpty);
13045 masm.assumeUnreachable("rope children are non-empty");
13046 masm.bind(&notEmpty);
13047#endif
13048
13049 // Otherwise keep unwinding ropes.
13050 masm.branchIfRope(temp, &unwindRope);
13051 }
13052 masm.bind(&notRope);
13053
13054 // Load the first character into |output|.
13055 auto loadFirstChar = [&](auto encoding) {
13056 masm.loadStringChars(temp, output, encoding);
13057 masm.loadChar(Address(output, 0), output, encoding);
13058 };
13059
13060 Label done;
13061 if (ch <= JSString::MAX_LATIN1_CHAR) {
13062 // Handle both encodings when the search character is Latin-1.
13063 Label twoByte, compare;
13064 masm.branchTwoByteString(temp, &twoByte);
13065
13066 loadFirstChar(CharEncoding::Latin1);
13067 masm.jump(&compare);
13068
13069 masm.bind(&twoByte);
13070 loadFirstChar(CharEncoding::TwoByte);
13071
13072 masm.bind(&compare);
13073 } else {
13074 // The search character is a two-byte character, so it can't be equal to any
13075 // character of a Latin-1 string.
13076 masm.move32(Imm32(int32_t(op == JSOp::Lt || op == JSOp::Le)), output);
13077 masm.branchLatin1String(temp, &done);
13078
13079 loadFirstChar(CharEncoding::TwoByte);
13080 }
13081
13082 // Compare the string length when the search character is equal to the
13083 // input's first character.
13084 masm.branch32(Assembler::Equal, output, Imm32(ch), &compareLength);
13085
13086 // Otherwise compute the result and jump to the end.
13087 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), output, Imm32(ch),
13088 output);
13089 masm.jump(&done);
13090
13091 // Compare the string length to compute the overall result.
13092 masm.bind(&compareLength);
13093 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false),
13094 Address(input, JSString::offsetOfLength()), Imm32(1), output);
13095
13096 masm.bind(&done);
13097}
13098
13099void CodeGenerator::visitStrictConstantCompareString(
13100 LStrictConstantCompareString* lir) {
13101 ValueOperand value = ToValue(lir->value());
13102 Register output = ToRegister(lir->output());
13103 Register temp = ToRegister(lir->temp0());
13104
13105 JSOffThreadAtom* str = lir->mir()->constant();
13106 JSOp op = lir->mir()->jsop();
13107 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", 13107); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13107); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13108
13109 OutOfLineCode* ool = nullptr;
13110
13111 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
13112 if (op == JSOp::StrictEq) {
13113 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
13114 lir, ArgList(temp, ImmGCPtr(str)), StoreRegisterTo(output));
13115 } else {
13116 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", 13116); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "op == JSOp::StrictNe" ")"); do { MOZ_CrashSequence
(__null, 13116); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13117 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
13118 lir, ArgList(temp, ImmGCPtr(str)), StoreRegisterTo(output));
13119 }
13120
13121 masm.move32(Imm32(op == JSOp::StrictNe), output);
13122 masm.fallibleUnboxString(value, temp, ool->rejoin());
13123
13124 masm.equalStrings(op, temp, str, output, ool->entry());
13125 masm.bind(ool->rejoin());
13126}
13127
13128void CodeGenerator::visitStrictConstantCompareObject(
13129 LStrictConstantCompareObject* lir) {
13130 ValueOperand value = ToValue(lir->value());
13131 Register output = ToRegister(lir->output());
13132
13133 JSObject* obj = lir->mir()->constant();
13134 JSOp op = lir->mir()->jsop();
13135 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", 13135); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13135); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13136
13137 masm.testValueSet(JSOpToCondition(MCompare::Compare_Object, op), value,
13138 JS::ObjectValue(*obj), output);
13139}
13140
13141void CodeGenerator::visitCompareBigInt(LCompareBigInt* lir) {
13142 JSOp op = lir->mir()->jsop();
13143 Register left = ToRegister(lir->left());
13144 Register right = ToRegister(lir->right());
13145 Register temp0 = ToRegister(lir->temp0());
13146 Register temp1 = ToRegister(lir->temp1());
13147 Register temp2 = ToRegister(lir->temp2());
13148 Register output = ToRegister(lir->output());
13149
13150 Label notSame;
13151 Label compareSign;
13152 Label compareLength;
13153 Label compareDigit;
13154
13155 Label* notSameSign;
13156 Label* notSameLength;
13157 Label* notSameDigit;
13158 if (IsEqualityOp(op)) {
13159 notSameSign = &notSame;
13160 notSameLength = &notSame;
13161 notSameDigit = &notSame;
13162 } else {
13163 notSameSign = &compareSign;
13164 notSameLength = &compareLength;
13165 notSameDigit = &compareDigit;
13166 }
13167
13168 masm.equalBigInts(left, right, temp0, temp1, temp2, output, notSameSign,
13169 notSameLength, notSameDigit);
13170
13171 Label done;
13172 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le ||
13173 op == JSOp::Ge),
13174 output);
13175 masm.jump(&done);
13176
13177 if (IsEqualityOp(op)) {
13178 masm.bind(&notSame);
13179 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
13180 } else {
13181 Label invertWhenNegative;
13182
13183 // There are two cases when sign(left) != sign(right):
13184 // 1. sign(left) = positive and sign(right) = negative,
13185 // 2. or the dual case with reversed signs.
13186 //
13187 // For case 1, |left| <cmp> |right| is true for cmp=Gt or cmp=Ge and false
13188 // for cmp=Lt or cmp=Le. Initialize the result for case 1 and handle case 2
13189 // with |invertWhenNegative|.
13190 masm.bind(&compareSign);
13191 masm.move32(Imm32(op == JSOp::Gt || op == JSOp::Ge), output);
13192 masm.jump(&invertWhenNegative);
13193
13194 // For sign(left) = sign(right) and len(digits(left)) != len(digits(right)),
13195 // we have to consider the two cases:
13196 // 1. len(digits(left)) < len(digits(right))
13197 // 2. len(digits(left)) > len(digits(right))
13198 //
13199 // For |left| <cmp> |right| with cmp=Lt:
13200 // Assume both BigInts are positive, then |left < right| is true for case 1
13201 // and false for case 2. When both are negative, the result is reversed.
13202 //
13203 // The other comparison operators can be handled similarly.
13204 //
13205 // |temp0| holds the digits length of the right-hand side operand.
13206 masm.bind(&compareLength);
13207 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false),
13208 Address(left, BigInt::offsetOfLength()), temp0, output);
13209 masm.jump(&invertWhenNegative);
13210
13211 // Similar to the case above, compare the current digit to determine the
13212 // overall comparison result.
13213 //
13214 // |temp1| points to the current digit of the left-hand side operand.
13215 // |output| holds the current digit of the right-hand side operand.
13216 masm.bind(&compareDigit);
13217 masm.cmpPtrSet(JSOpToCondition(op, /* isSigned = */ false),
13218 Address(temp1, 0), output, output);
13219
13220 Label nonNegative;
13221 masm.bind(&invertWhenNegative);
13222 masm.branchIfBigIntIsNonNegative(left, &nonNegative);
13223 masm.xor32(Imm32(1), output);
13224 masm.bind(&nonNegative);
13225 }
13226
13227 masm.bind(&done);
13228}
13229
13230void CodeGenerator::visitCompareBigIntInt32(LCompareBigIntInt32* lir) {
13231 JSOp op = lir->mir()->jsop();
13232 Register left = ToRegister(lir->left());
13233 Register temp0 = ToRegister(lir->temp0());
13234 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
13235 Register output = ToRegister(lir->output());
13236
13237 Label ifTrue, ifFalse;
13238 if (lir->right()->isConstant()) {
13239 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", 13239); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 == InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 13239); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13240
13241 Imm32 right = Imm32(ToInt32(lir->right()));
13242 masm.compareBigIntAndInt32(op, left, right, temp0, &ifTrue, &ifFalse);
13243 } else {
13244 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", 13244); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp1 != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 13244); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13245
13246 Register right = ToRegister(lir->right());
13247 masm.compareBigIntAndInt32(op, left, right, temp0, temp1, &ifTrue,
13248 &ifFalse);
13249 }
13250
13251 Label done;
13252 masm.bind(&ifFalse);
13253 masm.move32(Imm32(0), output);
13254 masm.jump(&done);
13255 masm.bind(&ifTrue);
13256 masm.move32(Imm32(1), output);
13257 masm.bind(&done);
13258}
13259
13260void CodeGenerator::visitCompareBigIntInt32AndBranch(
13261 LCompareBigIntInt32AndBranch* lir) {
13262 JSOp op = lir->cmpMir()->jsop();
13263 Register left = ToRegister(lir->left());
13264 Register temp1 = ToRegister(lir->temp0());
13265 Register temp2 = ToTempRegisterOrInvalid(lir->temp1());
13266
13267 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
13268 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
13269
13270 // compareBigIntAndInt32 falls through to the false case. If the next block
13271 // is the true case, negate the comparison so we can fall through.
13272 if (isNextBlock(lir->ifTrue()->lir())) {
13273 op = NegateCompareOp(op);
13274 std::swap(ifTrue, ifFalse);
13275 }
13276
13277 if (lir->right()->isConstant()) {
13278 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", 13278); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 == InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 13278); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13279
13280 Imm32 right = Imm32(ToInt32(lir->right()));
13281 masm.compareBigIntAndInt32(op, left, right, temp1, ifTrue, ifFalse);
13282 } else {
13283 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", 13283); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp2 != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 13283); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13284
13285 Register right = ToRegister(lir->right());
13286 masm.compareBigIntAndInt32(op, left, right, temp1, temp2, ifTrue, ifFalse);
13287 }
13288
13289 if (!isNextBlock(lir->ifTrue()->lir())) {
13290 jumpToBlock(lir->ifFalse());
13291 }
13292}
13293
13294void CodeGenerator::visitCompareBigIntDouble(LCompareBigIntDouble* lir) {
13295 JSOp op = lir->mir()->jsop();
13296 Register left = ToRegister(lir->left());
13297 FloatRegister right = ToFloatRegister(lir->right());
13298 Register output = ToRegister(lir->output());
13299
13300 masm.setupAlignedABICall();
13301
13302 // Push the operands in reverse order for JSOp::Le and JSOp::Gt:
13303 // - |left <= right| is implemented as |right >= left|.
13304 // - |left > right| is implemented as |right < left|.
13305 if (op == JSOp::Le || op == JSOp::Gt) {
13306 masm.passABIArg(right, ABIType::Float64);
13307 masm.passABIArg(left);
13308 } else {
13309 masm.passABIArg(left);
13310 masm.passABIArg(right, ABIType::Float64);
13311 }
13312
13313 using FnBigIntNumber = bool (*)(BigInt*, double);
13314 using FnNumberBigInt = bool (*)(double, BigInt*);
13315 switch (op) {
13316 case JSOp::Eq: {
13317 masm.callWithABI<FnBigIntNumber,
13318 jit::BigIntNumberEqual<EqualityKind::Equal>>();
13319 break;
13320 }
13321 case JSOp::Ne: {
13322 masm.callWithABI<FnBigIntNumber,
13323 jit::BigIntNumberEqual<EqualityKind::NotEqual>>();
13324 break;
13325 }
13326 case JSOp::Lt: {
13327 masm.callWithABI<FnBigIntNumber,
13328 jit::BigIntNumberCompare<ComparisonKind::LessThan>>();
13329 break;
13330 }
13331 case JSOp::Gt: {
13332 masm.callWithABI<FnNumberBigInt,
13333 jit::NumberBigIntCompare<ComparisonKind::LessThan>>();
13334 break;
13335 }
13336 case JSOp::Le: {
13337 masm.callWithABI<
13338 FnNumberBigInt,
13339 jit::NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>>();
13340 break;
13341 }
13342 case JSOp::Ge: {
13343 masm.callWithABI<
13344 FnBigIntNumber,
13345 jit::BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>>();
13346 break;
13347 }
13348 default:
13349 MOZ_CRASH("unhandled op")do { do { } while (false); MOZ_ReportCrash("" "unhandled op",
"./../../../../js/src/jit/CodeGenerator.cpp", 13349); AnnotateMozCrashReason
("MOZ_CRASH(" "unhandled op" ")"); do { MOZ_CrashSequence(__null
, 13349); __attribute__((nomerge)) ::abort(); } while (false)
; } while (false)
;
13350 }
13351
13352 masm.storeCallBoolResult(output);
13353}
13354
13355void CodeGenerator::visitCompareBigIntString(LCompareBigIntString* lir) {
13356 JSOp op = lir->mir()->jsop();
13357 Register left = ToRegister(lir->left());
13358 Register right = ToRegister(lir->right());
13359
13360 // Push the operands in reverse order for JSOp::Le and JSOp::Gt:
13361 // - |left <= right| is implemented as |right >= left|.
13362 // - |left > right| is implemented as |right < left|.
13363 if (op == JSOp::Le || op == JSOp::Gt) {
13364 pushArg(left);
13365 pushArg(right);
13366 } else {
13367 pushArg(right);
13368 pushArg(left);
13369 }
13370
13371 using FnBigIntString =
13372 bool (*)(JSContext*, HandleBigInt, HandleString, bool*);
13373 using FnStringBigInt =
13374 bool (*)(JSContext*, HandleString, HandleBigInt, bool*);
13375
13376 switch (op) {
13377 case JSOp::Eq: {
13378 constexpr auto Equal = EqualityKind::Equal;
13379 callVM<FnBigIntString, BigIntStringEqual<Equal>>(lir);
13380 break;
13381 }
13382 case JSOp::Ne: {
13383 constexpr auto NotEqual = EqualityKind::NotEqual;
13384 callVM<FnBigIntString, BigIntStringEqual<NotEqual>>(lir);
13385 break;
13386 }
13387 case JSOp::Lt: {
13388 constexpr auto LessThan = ComparisonKind::LessThan;
13389 callVM<FnBigIntString, BigIntStringCompare<LessThan>>(lir);
13390 break;
13391 }
13392 case JSOp::Gt: {
13393 constexpr auto LessThan = ComparisonKind::LessThan;
13394 callVM<FnStringBigInt, StringBigIntCompare<LessThan>>(lir);
13395 break;
13396 }
13397 case JSOp::Le: {
13398 constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual;
13399 callVM<FnStringBigInt, StringBigIntCompare<GreaterThanOrEqual>>(lir);
13400 break;
13401 }
13402 case JSOp::Ge: {
13403 constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual;
13404 callVM<FnBigIntString, BigIntStringCompare<GreaterThanOrEqual>>(lir);
13405 break;
13406 }
13407 default:
13408 MOZ_CRASH("Unexpected compare op")do { do { } while (false); MOZ_ReportCrash("" "Unexpected compare op"
, "./../../../../js/src/jit/CodeGenerator.cpp", 13408); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected compare op" ")"); do { MOZ_CrashSequence
(__null, 13408); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
13409 }
13410}
13411
13412void CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir) {
13413 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", 13414); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13414); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
13414 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", 13414); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13414); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13415
13416 JSOp op = lir->mir()->jsop();
13417 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", 13417); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13417); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13418
13419 ValueOperand value = ToValue(lir->value());
13420 Register output = ToRegister(lir->output());
13421
13422 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
13423 if (!intact) {
13424 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
13425 addOutOfLineCode(ool, lir->mir());
13426
13427 Label* nullOrLikeUndefined = ool->label1();
13428 Label* notNullOrLikeUndefined = ool->label2();
13429
13430 {
13431 ScratchTagScope tag(masm, value);
13432 masm.splitTagForTest(value, tag);
13433
13434 masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
13435 masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
13436
13437 // Check whether it's a truthy object or a falsy object that emulates
13438 // undefined.
13439 masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
13440 }
13441
13442 Register objreg =
13443 masm.extractObject(value, ToTempUnboxRegister(lir->temp0()));
13444 branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined,
13445 notNullOrLikeUndefined, output, ool);
13446 // fall through
13447
13448 Label done;
13449
13450 // It's not null or undefined, and if it's an object it doesn't
13451 // emulate undefined, so it's not like undefined.
13452 masm.move32(Imm32(op == JSOp::Ne), output);
13453 masm.jump(&done);
13454
13455 masm.bind(nullOrLikeUndefined);
13456 masm.move32(Imm32(op == JSOp::Eq), output);
13457
13458 // Both branches meet here.
13459 masm.bind(&done);
13460 } else {
13461 Label nullOrUndefined, notNullOrLikeUndefined;
13462#if defined(DEBUG1) || defined(FUZZING)
13463 Register objreg = Register::Invalid();
13464#endif
13465 {
13466 ScratchTagScope tag(masm, value);
13467 masm.splitTagForTest(value, tag);
13468
13469 masm.branchTestNull(Assembler::Equal, tag, &nullOrUndefined);
13470 masm.branchTestUndefined(Assembler::Equal, tag, &nullOrUndefined);
13471
13472#if defined(DEBUG1) || defined(FUZZING)
13473 // Check whether it's a truthy object or a falsy object that emulates
13474 // undefined.
13475 masm.branchTestObject(Assembler::NotEqual, tag, &notNullOrLikeUndefined);
13476 objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp0()));
13477#endif
13478 }
13479
13480#if defined(DEBUG1) || defined(FUZZING)
13481 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
13482 masm.bind(&notNullOrLikeUndefined);
13483#endif
13484
13485 Label done;
13486
13487 // It's not null or undefined, and if it's an object it doesn't
13488 // emulate undefined.
13489 masm.move32(Imm32(op == JSOp::Ne), output);
13490 masm.jump(&done);
13491
13492 masm.bind(&nullOrUndefined);
13493 masm.move32(Imm32(op == JSOp::Eq), output);
13494
13495 // Both branches meet here.
13496 masm.bind(&done);
13497 }
13498}
13499
13500void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV(
13501 LIsNullOrLikeUndefinedAndBranchV* lir) {
13502 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", 13503); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13503); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
13503 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", 13503); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13503); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13504
13505 JSOp op = lir->cmpMir()->jsop();
13506 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", 13506); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13506); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13507
13508 ValueOperand value = ToValue(lir->value());
13509
13510 MBasicBlock* ifTrue = lir->ifTrue();
13511 MBasicBlock* ifFalse = lir->ifFalse();
13512
13513 if (op == JSOp::Ne) {
13514 // Swap branches.
13515 std::swap(ifTrue, ifFalse);
13516 }
13517
13518 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
13519
13520 Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
13521 Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
13522
13523 bool extractObject = !intact;
Value stored to 'extractObject' during its initialization is never read
13524 Register objreg = Register::Invalid();
13525#if defined(DEBUG1) || defined(FUZZING)
13526 // always extract objreg if we're in debug and
13527 // assertObjectDoesNotEmulateUndefined;
13528 extractObject = true;
13529#endif
13530
13531 {
13532 ScratchTagScope tag(masm, value);
13533 masm.splitTagForTest(value, tag);
13534
13535 masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
13536 masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
13537
13538 if (extractObject) {
13539 masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
13540 objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp1()));
13541 }
13542 }
13543
13544 Register scratch = ToRegister(lir->temp0());
13545 if (!intact) {
13546 // Objects that emulate undefined are loosely equal to null/undefined.
13547 OutOfLineTestObject* ool = new (alloc()) OutOfLineTestObject();
13548 addOutOfLineCode(ool, lir->cmpMir());
13549 testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch,
13550 ool);
13551 } else {
13552 assertObjectDoesNotEmulateUndefined(objreg, scratch, lir->cmpMir());
13553 // Bug 1874905. This would be nice to optimize out at the MIR level.
13554 if (!isNextBlock(ifFalse->lir())) {
13555 masm.jump(ifFalseLabel);
13556 }
13557 }
13558}
13559
13560void CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir) {
13561 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", 13562); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13562); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
13562 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", 13562); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13562); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13563 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"
, 13563); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->lhs()->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 13563); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13564
13565 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
13566 JSOp op = lir->mir()->jsop();
13567 Register output = ToRegister(lir->output());
13568 Register objreg = ToRegister(lir->input());
13569 if (!intact) {
13570 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"
, 13571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence
(__null, 13571); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
13571 "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"
, 13571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence
(__null, 13571); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13572
13573 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
13574 addOutOfLineCode(ool, lir->mir());
13575
13576 Label* emulatesUndefined = ool->label1();
13577 Label* doesntEmulateUndefined = ool->label2();
13578
13579 branchTestObjectEmulatesUndefined(objreg, emulatesUndefined,
13580 doesntEmulateUndefined, output, ool);
13581
13582 Label done;
13583
13584 masm.move32(Imm32(op == JSOp::Ne), output);
13585 masm.jump(&done);
13586
13587 masm.bind(emulatesUndefined);
13588 masm.move32(Imm32(op == JSOp::Eq), output);
13589 masm.bind(&done);
13590 } else {
13591 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
13592 masm.move32(Imm32(op == JSOp::Ne), output);
13593 }
13594}
13595
13596void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT(
13597 LIsNullOrLikeUndefinedAndBranchT* lir) {
13598 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", 13599); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13599); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
13599 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", 13599); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { MOZ_CrashSequence(__null, 13599); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13600 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"
, 13600); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->lhs()->type() == MIRType::Object"
")"); do { MOZ_CrashSequence(__null, 13600); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13601
13602 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
13603
13604 JSOp op = lir->cmpMir()->jsop();
13605 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"
, 13605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence
(__null, 13605); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13606
13607 MBasicBlock* ifTrue = lir->ifTrue();
13608 MBasicBlock* ifFalse = lir->ifFalse();
13609
13610 if (op == JSOp::Ne) {
13611 // Swap branches.
13612 std::swap(ifTrue, ifFalse);
13613 }
13614
13615 Register input = ToRegister(lir->value());
13616 Register scratch = ToRegister(lir->temp0());
13617 Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
13618 Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
13619
13620 if (intact) {
13621 // Bug 1874905. Ideally branches like this would be optimized out.
13622 assertObjectDoesNotEmulateUndefined(input, scratch, lir->mir());
13623 masm.jump(ifFalseLabel);
13624 } else {
13625 auto* ool = new (alloc()) OutOfLineTestObject();
13626 addOutOfLineCode(ool, lir->cmpMir());
13627
13628 // Objects that emulate undefined are loosely equal to null/undefined.
13629 testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool);
13630 }
13631}
13632
13633void CodeGenerator::visitIsNull(LIsNull* lir) {
13634 MCompare::CompareType compareType = lir->mir()->compareType();
13635 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", 13635); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"
); do { MOZ_CrashSequence(__null, 13635); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
13636
13637 JSOp op = lir->mir()->jsop();
13638 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", 13638); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13638); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13639
13640 ValueOperand value = ToValue(lir->value());
13641 Register output = ToRegister(lir->output());
13642
13643 Assembler::Condition cond = JSOpToCondition(compareType, op);
13644 masm.testNullSet(cond, value, output);
13645}
13646
13647void CodeGenerator::visitIsUndefined(LIsUndefined* lir) {
13648 MCompare::CompareType compareType = lir->mir()->compareType();
13649 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", 13649); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined"
")"); do { MOZ_CrashSequence(__null, 13649); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13650
13651 JSOp op = lir->mir()->jsop();
13652 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", 13652); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13652); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13653
13654 ValueOperand value = ToValue(lir->value());
13655 Register output = ToRegister(lir->output());
13656
13657 Assembler::Condition cond = JSOpToCondition(compareType, op);
13658 masm.testUndefinedSet(cond, value, output);
13659}
13660
13661void CodeGenerator::visitIsNullAndBranch(LIsNullAndBranch* lir) {
13662 MCompare::CompareType compareType = lir->cmpMir()->compareType();
13663 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", 13663); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"
); do { MOZ_CrashSequence(__null, 13663); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
13664
13665 JSOp op = lir->cmpMir()->jsop();
13666 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", 13666); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13666); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13667
13668 ValueOperand value = ToValue(lir->value());
13669
13670 Assembler::Condition cond = JSOpToCondition(compareType, op);
13671
13672 MBasicBlock* ifTrue = lir->ifTrue();
13673 MBasicBlock* ifFalse = lir->ifFalse();
13674
13675 if (isNextBlock(ifFalse->lir())) {
13676 masm.branchTestNull(cond, value, getJumpLabelForBranch(ifTrue));
13677 } else {
13678 masm.branchTestNull(Assembler::InvertCondition(cond), value,
13679 getJumpLabelForBranch(ifFalse));
13680 jumpToBlock(ifTrue);
13681 }
13682}
13683
13684void CodeGenerator::visitIsUndefinedAndBranch(LIsUndefinedAndBranch* lir) {
13685 MCompare::CompareType compareType = lir->cmpMir()->compareType();
13686 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", 13686); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined"
")"); do { MOZ_CrashSequence(__null, 13686); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13687
13688 JSOp op = lir->cmpMir()->jsop();
13689 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", 13689); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence
(__null, 13689); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13690
13691 ValueOperand value = ToValue(lir->value());
13692
13693 Assembler::Condition cond = JSOpToCondition(compareType, op);
13694
13695 MBasicBlock* ifTrue = lir->ifTrue();
13696 MBasicBlock* ifFalse = lir->ifFalse();
13697
13698 if (isNextBlock(ifFalse->lir())) {
13699 masm.branchTestUndefined(cond, value, getJumpLabelForBranch(ifTrue));
13700 } else {
13701 masm.branchTestUndefined(Assembler::InvertCondition(cond), value,
13702 getJumpLabelForBranch(ifFalse));
13703 jumpToBlock(ifTrue);
13704 }
13705}
13706
13707void CodeGenerator::visitSameValueDouble(LSameValueDouble* lir) {
13708 FloatRegister left = ToFloatRegister(lir->left());
13709 FloatRegister right = ToFloatRegister(lir->right());
13710 FloatRegister temp = ToFloatRegister(lir->temp0());
13711 Register output = ToRegister(lir->output());
13712
13713 masm.sameValueDouble(left, right, temp, output);
13714}
13715
13716void CodeGenerator::visitSameValue(LSameValue* lir) {
13717 ValueOperand lhs = ToValue(lir->left());
13718 ValueOperand rhs = ToValue(lir->right());
13719 Register output = ToRegister(lir->output());
13720
13721 using Fn = bool (*)(JSContext*, const Value&, const Value&, bool*);
13722 OutOfLineCode* ool =
13723 oolCallVM<Fn, SameValue>(lir, ArgList(lhs, rhs), StoreRegisterTo(output));
13724
13725 // First check to see if the values have identical bits.
13726 // This is correct for SameValue because SameValue(NaN,NaN) is true,
13727 // and SameValue(0,-0) is false.
13728 masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(),
13729 ool->entry());
13730 masm.move32(Imm32(1), output);
13731
13732 // If this fails, call SameValue.
13733 masm.bind(ool->rejoin());
13734}
13735
13736void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs,
13737 Register output) {
13738 using Fn =
13739 JSString* (*)(JSContext*, HandleString, HandleString, js::gc::Heap);
13740 OutOfLineCode* ool = oolCallVM<Fn, ConcatStrings<CanGC>>(
13741 lir, ArgList(lhs, rhs, static_cast<Imm32>(int32_t(gc::Heap::Default))),
13742 StoreRegisterTo(output));
13743
13744 JitCode* stringConcatStub =
13745 snapshot_->getZoneStub(JitZone::StubKind::StringConcat);
13746 masm.call(stringConcatStub);
13747 masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
13748
13749 masm.bind(ool->rejoin());
13750}
13751
13752void CodeGenerator::visitConcat(LConcat* lir) {
13753 Register lhs = ToRegister(lir->lhs());
13754 Register rhs = ToRegister(lir->rhs());
13755
13756 Register output = ToRegister(lir->output());
13757
13758 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", 13758); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lhs == CallTempReg0" ")"); do { MOZ_CrashSequence
(__null, 13758); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13759 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", 13759); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "rhs == CallTempReg1" ")"); do { MOZ_CrashSequence
(__null, 13759); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13760 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", 13760); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp0()) == CallTempReg0"
")"); do { MOZ_CrashSequence(__null, 13760); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13761 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", 13761); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp1()) == CallTempReg1"
")"); do { MOZ_CrashSequence(__null, 13761); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13762 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", 13762); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp2()) == CallTempReg2"
")"); do { MOZ_CrashSequence(__null, 13762); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13763 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", 13763); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp3()) == CallTempReg3"
")"); do { MOZ_CrashSequence(__null, 13763); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13764 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", 13764); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->temp4()) == CallTempReg4"
")"); do { MOZ_CrashSequence(__null, 13764); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13765 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", 13765); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output == CallTempReg5" ")"); do { MOZ_CrashSequence
(__null, 13765); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13766
13767 emitConcat(lir, lhs, rhs, output);
13768}
13769
13770static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
13771 Register len, Register byteOpScratch,
13772 CharEncoding fromEncoding, CharEncoding toEncoding,
13773 size_t maximumLength = SIZE_MAX(18446744073709551615UL)) {
13774 // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
13775 // (checked below in debug builds), and when done |to| must point to the
13776 // next available char.
13777
13778#ifdef DEBUG1
13779 Label ok;
13780 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
13781 masm.assumeUnreachable("Length should be greater than 0.");
13782 masm.bind(&ok);
13783
13784 if (maximumLength != SIZE_MAX(18446744073709551615UL)) {
13785 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", 13785); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "maximumLength <= (2147483647)" ") (" "maximum length fits into int32"
")"); do { MOZ_CrashSequence(__null, 13785); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13786
13787 Label ok;
13788 masm.branchPtr(Assembler::BelowOrEqual, len, Imm32(maximumLength), &ok);
13789 masm.assumeUnreachable("Length should not exceed maximum length.");
13790 masm.bind(&ok);
13791 }
13792#endif
13793
13794 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"
, 13795); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1"
")"); do { MOZ_CrashSequence(__null, 13795); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
13795 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"
, 13795); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1"
")"); do { MOZ_CrashSequence(__null, 13795); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
13796
13797 size_t fromWidth =
13798 fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
13799 size_t toWidth =
13800 toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
13801
13802 // Try to copy multiple characters at once when both encoding are equal.
13803 if (fromEncoding == toEncoding) {
13804 constexpr size_t ptrWidth = sizeof(uintptr_t);
13805
13806 // Copy |width| bytes and then adjust |from| and |to|.
13807 auto copyCharacters = [&](size_t width) {
13808 static_assert(ptrWidth <= 8, "switch handles only up to eight bytes");
13809
13810 switch (width) {
13811 case 1:
13812 masm.load8ZeroExtend(Address(from, 0), byteOpScratch);
13813 masm.store8(byteOpScratch, Address(to, 0));
13814 break;
13815 case 2:
13816 masm.load16ZeroExtend(Address(from, 0), byteOpScratch);
13817 masm.store16(byteOpScratch, Address(to, 0));
13818 break;
13819 case 4:
13820 masm.load32(Address(from, 0), byteOpScratch);
13821 masm.store32(byteOpScratch, Address(to, 0));
13822 break;
13823 case 8:
13824 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", 13824); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "width == ptrWidth" ")"); do { MOZ_CrashSequence
(__null, 13824); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
13825 masm.loadPtr(Address(from, 0), byteOpScratch);
13826 masm.storePtr(byteOpScratch, Address(to, 0));
13827 break;
13828 }
13829
13830 masm.addPtr(Imm32(width), from);
13831 masm.addPtr(Imm32(width), to);
13832 };
13833
13834 // First align |len| to pointer width.
13835 Label done;
13836 for (size_t width = fromWidth; width < ptrWidth; width *= 2) {
13837 // Number of characters which fit into |width| bytes.
13838 size_t charsPerWidth = width / fromWidth;
13839
13840 if (charsPerWidth < maximumLength) {
13841 Label next;
13842 masm.branchTest32(Assembler::Zero, len, Imm32(charsPerWidth), &next);
13843
13844 copyCharacters(width);
13845
13846 masm.branchSub32(Assembler::Zero, Imm32(charsPerWidth), len, &done);
13847 masm.bind(&next);
13848 } else if (charsPerWidth == maximumLength) {
13849 copyCharacters(width);
13850 masm.sub32(Imm32(charsPerWidth), len);
13851 }
13852 }
13853
13854 size_t maxInlineLength;
13855 if (fromEncoding == CharEncoding::Latin1) {
13856 maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1;
13857 } else {
13858 maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
13859 }
13860
13861 // Number of characters which fit into a single register.
13862 size_t charsPerPtr = ptrWidth / fromWidth;
13863
13864 // Unroll small loops.
13865 constexpr size_t unrollLoopLimit = 3;
13866 size_t loopCount = std::min(maxInlineLength, maximumLength) / charsPerPtr;
13867
13868#ifdef JS_64BIT1
13869 static constexpr size_t latin1MaxInlineByteLength =
13870 JSFatInlineString::MAX_LENGTH_LATIN1 * sizeof(char);
13871 static constexpr size_t twoByteMaxInlineByteLength =
13872 JSFatInlineString::MAX_LENGTH_TWO_BYTE * sizeof(char16_t);
13873
13874 // |unrollLoopLimit| should be large enough to allow loop unrolling on
13875 // 64-bit targets.
13876 static_assert(latin1MaxInlineByteLength / ptrWidth == unrollLoopLimit,
13877 "Latin-1 loops are unrolled on 64-bit");
13878 static_assert(twoByteMaxInlineByteLength / ptrWidth == unrollLoopLimit,
13879 "Two-byte loops are unrolled on 64-bit");
13880#endif
13881
13882 if (loopCount <= unrollLoopLimit) {
13883 Label labels[unrollLoopLimit];
13884
13885 // Check up front how many characters can be copied.
13886 for (size_t i = 1; i < loopCount; i++) {
13887 masm.branch32(Assembler::Below, len, Imm32((i + 1) * charsPerPtr),
13888 &labels[i]);
13889 }
13890
13891 // Generate the unrolled loop body.
13892 for (size_t i = loopCount; i > 0; i--) {
13893 copyCharacters(ptrWidth);
13894 masm.sub32(Imm32(charsPerPtr), len);
13895
13896 // Jump target for the previous length check.
13897 if (i != 1) {
13898 masm.bind(&labels[i - 1]);
13899 }
13900 }
13901 } else {
13902 Label start;
13903 masm.bind(&start);
13904 copyCharacters(ptrWidth);
13905 masm.branchSub32(Assembler::NonZero, Imm32(charsPerPtr), len, &start);
13906 }
13907
13908 masm.bind(&done);
13909 } else {
13910 Label start;
13911 masm.bind(&start);
13912 masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding);
13913 masm.storeChar(byteOpScratch, Address(to, 0), toEncoding);
13914 masm.addPtr(Imm32(fromWidth), from);
13915 masm.addPtr(Imm32(toWidth), to);
13916 masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
13917 }
13918}
13919
13920static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
13921 Register len, Register byteOpScratch,
13922 CharEncoding encoding, size_t maximumLength) {
13923 CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding,
13924 maximumLength);
13925}
13926
13927static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input,
13928 Register destChars, Register temp1,
13929 Register temp2) {
13930 // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may
13931 // have to inflate.
13932
13933 Label isLatin1, done;
13934 masm.loadStringLength(input, temp1);
13935 masm.branchLatin1String(input, &isLatin1);
13936 {
13937 masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
13938 masm.movePtr(temp2, input);
13939 CopyStringChars(masm, destChars, input, temp1, temp2,
13940 CharEncoding::TwoByte);
13941 masm.jump(&done);
13942 }
13943 masm.bind(&isLatin1);
13944 {
13945 masm.loadStringChars(input, temp2, CharEncoding::Latin1);
13946 masm.movePtr(temp2, input);
13947 CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1,
13948 CharEncoding::TwoByte);
13949 }
13950 masm.bind(&done);
13951}
13952
13953static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output,
13954 Register length, Register temp,
13955 gc::Heap initialStringHeap,
13956 Label* failure,
13957 CharEncoding encoding) {
13958#ifdef DEBUG1
13959 size_t maxInlineLength;
13960 if (encoding == CharEncoding::Latin1) {
13961 maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1;
13962 } else {
13963 maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
13964 }
13965
13966 Label ok;
13967 masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok);
13968 masm.assumeUnreachable("string length too large to be allocated as inline");
13969 masm.bind(&ok);
13970#endif
13971
13972 size_t maxThinInlineLength;
13973 if (encoding == CharEncoding::Latin1) {
13974 maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1;
13975 } else {
13976 maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE;
13977 }
13978
13979 Label isFat, allocDone;
13980 masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat);
13981 {
13982 uint32_t flags = StringFlags::thinInlineStringFlags(encoding);
13983 masm.newGCString(output, temp, initialStringHeap, failure);
13984 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13985 masm.jump(&allocDone);
13986 }
13987 masm.bind(&isFat);
13988 {
13989 uint32_t flags = StringFlags::fatInlineStringFlags(encoding);
13990 masm.newGCFatInlineString(output, temp, initialStringHeap, failure);
13991 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13992 }
13993 masm.bind(&allocDone);
13994
13995 // Store length.
13996 masm.store32(length, Address(output, JSString::offsetOfLength()));
13997}
13998
13999static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs,
14000 Register output, Register andedFlags,
14001 Register temp2, Register temp3,
14002 gc::Heap initialStringHeap, Label* failure,
14003 CharEncoding encoding) {
14004 JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)",
14005 (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
14006
14007 // State: result length in temp2.
14008
14009#ifdef DEBUG1
14010 Label skip, rope;
14011
14012 // Ensure both strings are linear.
14013 masm.branchIfRope(lhs, &rope);
14014 masm.branchIfRope(rhs, &rope);
14015
14016 masm.jump(&skip);
14017 masm.bind(&rope);
14018 masm.assertUnreachable("Ropes encountered in ConcatInlineString.");
14019 masm.bind(&skip);
14020#endif
14021
14022 // Allocate a JSThinInlineString or JSFatInlineString.
14023 AllocateThinOrFatInlineString(masm, output, temp2, temp3, initialStringHeap,
14024 failure, encoding);
14025
14026 // Load chars pointer in temp2.
14027 masm.loadInlineStringCharsForStore(output, temp2);
14028
14029#if defined(JS_64BIT1) && defined(ENABLE_JIT_SIMD1)
14030 Label fastPath, done;
14031
14032 bool useVectorizedCopy = true;
14033# if defined(JS_CODEGEN_LOONG64)
14034 useVectorizedCopy = Assembler::HasLSX();
14035# endif
14036
14037 if (useVectorizedCopy) {
14038 masm.branchTest32(Assembler::NonZero, andedFlags,
14039 Imm32(StringFlags::INLINE_CHARS_BIT), &fastPath);
14040 }
14041#endif
14042
14043 Register temp1 = andedFlags;
14044 auto copyChars = [&](Register src) {
14045 if (encoding == CharEncoding::TwoByte) {
14046 CopyStringCharsMaybeInflate(masm, src, temp2, temp3, temp1);
14047 } else {
14048 masm.loadStringLength(src, temp1);
14049 masm.loadStringChars(src, temp3, CharEncoding::Latin1);
14050 masm.movePtr(temp3, src);
14051 CopyStringChars(masm, temp2, src, temp1, temp3, CharEncoding::Latin1);
14052 }
14053 };
14054
14055 // Copy lhs chars. Note that this advances temp2 to point to the next
14056 // char. This also clobbers the lhs register.
14057 copyChars(lhs);
14058
14059 // Copy rhs chars. Clobbers the rhs register.
14060 copyChars(rhs);
14061
14062 // There's a lot of assumptions in here that inline strings are at least
14063 // 16 bytes, so while it's possible to write a faster version for 32-bit,
14064 // we elect to just leave 32-bit platforms behind with a little bit slower
14065 // string copying.
14066#if defined(JS_64BIT1) && defined(ENABLE_JIT_SIMD1)
14067 masm.jump(&done);
14068 masm.bind(&fastPath);
14069
14070 // Note: these assertions are here just to trip if this changes, because all
14071 // the code below is very much dependent on the specific sizes.
14072 static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 == 16);
14073 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE == 8);
14074 static_assert(JSFatInlineString::MAX_LENGTH_LATIN1 == 24);
14075 static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE == 12);
14076
14077 size_t thinInlineLength = encoding == CharEncoding::Latin1
14078 ? JSThinInlineString::MAX_LENGTH_LATIN1
14079 : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
14080
14081 // Given that for the lhs we know we're copying into the start of an inline
14082 // string then on 64-bit we know we're safe to write 16 bytes, regardless
14083 // of the actual length.
14084 auto copyLhsFast = [&]() {
14085 masm.loadStringLength(lhs, temp1);
14086 masm.loadInlineStringCharsForStore(lhs, temp3);
14087
14088 masm.loadUnalignedSimd128(Address(temp3, 0), ScratchSimd128Reg);
14089 masm.storeUnalignedSimd128(ScratchSimd128Reg, Address(temp2, 0));
14090
14091 Label lhsDone;
14092 masm.branch32(Assembler::BelowOrEqual, temp1, Imm32(thinInlineLength),
14093 &lhsDone);
14094 masm.loadPtr(Address(temp3, 16), temp3);
14095 masm.storePtr(temp3, Address(temp2, 16));
14096 masm.bind(&lhsDone);
14097
14098 if (encoding == CharEncoding::Latin1) {
14099 masm.addPtr(temp1, temp2);
14100 } else {
14101 masm.computeEffectiveAddress(BaseIndex(temp2, temp1, TimesTwo), temp2);
14102 }
14103 };
14104
14105 // For the RHS however we don't have any guarantees, but we know we
14106 // can handle everything >= 8 bytes with at most three overlapping
14107 // 8 byte copies.
14108 auto copyRhsFast = [&]() {
14109 masm.loadStringLength(rhs, temp1);
14110 masm.loadInlineStringCharsForStore(rhs, temp3);
14111
14112 if (encoding == CharEncoding::TwoByte) {
14113 masm.lshift32(Imm32(1), temp1);
14114 }
14115
14116 Label rhsBelow8, rhsBelow4, rhsDone;
14117
14118 // byteLen >= 8: head + conditional middle + tail.
14119 masm.branch32(Assembler::Below, temp1, Imm32(8), &rhsBelow8);
14120
14121 masm.loadPtr(Address(temp3, 0), lhs);
14122 masm.storePtr(lhs, Address(temp2, 0));
14123
14124 Label rhsTail;
14125 masm.branch32(Assembler::BelowOrEqual, temp1,
14126 Imm32(JSThinInlineString::InlineBytes), &rhsTail);
14127 masm.loadPtr(Address(temp3, 8), lhs);
14128 masm.storePtr(lhs, Address(temp2, 8));
14129
14130 masm.bind(&rhsTail);
14131 masm.loadPtr(BaseIndex(temp3, temp1, TimesOne, -8), lhs);
14132 masm.storePtr(lhs, BaseIndex(temp2, temp1, TimesOne, -8));
14133 masm.jump(&rhsDone);
14134
14135 // byteLen 4-7: two overlapping 4-byte copies.
14136 masm.bind(&rhsBelow8);
14137 masm.branch32(Assembler::Below, temp1, Imm32(4), &rhsBelow4);
14138 masm.load32(Address(temp3, 0), lhs);
14139 masm.store32(lhs, Address(temp2, 0));
14140 masm.load32(BaseIndex(temp3, temp1, TimesOne, -4), lhs);
14141 masm.store32(lhs, BaseIndex(temp2, temp1, TimesOne, -4));
14142 masm.jump(&rhsDone);
14143
14144 // byteLen 1-3: first byte + overlapping 2-byte tail.
14145 masm.bind(&rhsBelow4);
14146 masm.load8ZeroExtend(Address(temp3, 0), lhs);
14147 masm.store8(lhs, Address(temp2, 0));
14148 masm.branch32(Assembler::Below, temp1, Imm32(2), &rhsDone);
14149 masm.load16ZeroExtend(BaseIndex(temp3, temp1, TimesOne, -2), lhs);
14150 masm.store16(lhs, BaseIndex(temp2, temp1, TimesOne, -2));
14151
14152 masm.bind(&rhsDone);
14153 };
14154
14155 // If the output encoding is Latin1, both inputs are Latin1. However if the
14156 // output encoding is TwoByte, we only know that at least one of the inputs
14157 // is TwoByte.
14158 if (encoding == CharEncoding::Latin1) {
14159 copyLhsFast();
14160 copyRhsFast();
14161 } else {
14162 auto copyCharsInflate = [&](Register src) {
14163 masm.loadStringLength(src, temp3);
14164 masm.loadStringChars(src, temp1, CharEncoding::Latin1);
14165 masm.movePtr(temp1, src);
14166 CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1,
14167 CharEncoding::TwoByte);
14168 };
14169
14170 Label lhsInflate, beginRhs, rhsInflate;
14171 masm.branchLatin1String(lhs, &lhsInflate);
14172 copyLhsFast();
14173 masm.jump(&beginRhs);
14174
14175 masm.bind(&lhsInflate);
14176 copyCharsInflate(lhs);
14177
14178 // If lhs was latin1, we know rhs must be TwoByte, so we can skip
14179 // a branch here and just copy rhs directly
14180 copyRhsFast();
14181 masm.jump(&done);
14182
14183 masm.bind(&beginRhs);
14184 masm.branchLatin1String(rhs, &rhsInflate);
14185 copyRhsFast();
14186 masm.jump(&done);
14187
14188 masm.bind(&rhsInflate);
14189 copyCharsInflate(rhs);
14190 }
14191 masm.bind(&done);
14192#endif
14193}
14194
14195void CodeGenerator::visitSubstr(LSubstr* lir) {
14196 Register string = ToRegister(lir->string());
14197 Register begin = ToRegister(lir->begin());
14198 Register length = ToRegister(lir->length());
14199 Register output = ToRegister(lir->output());
14200 Register temp0 = ToRegister(lir->temp0());
14201 Register temp2 = ToRegister(lir->temp2());
14202
14203 // On x86 there are not enough registers. In that case reuse the string
14204 // register as temporary.
14205 Register temp1 =
14206 lir->temp1()->isBogusTemp() ? string : ToRegister(lir->temp1());
14207
14208 size_t maximumLength = SIZE_MAX(18446744073709551615UL);
14209
14210 Range range(lir->mir()->length());
14211 if (range.hasInt32UpperBound()) {
14212 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", 14212); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "range.upper() >= 0" ")"); do { MOZ_CrashSequence
(__null, 14212); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
14213 maximumLength = size_t(range.upper());
14214 }
14215
14216 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <=
14217 JSThinInlineString::MAX_LENGTH_LATIN1);
14218
14219 static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE <=
14220 JSFatInlineString::MAX_LENGTH_LATIN1);
14221
14222 bool tryFatInlineOrDependent =
14223 maximumLength > JSThinInlineString::MAX_LENGTH_TWO_BYTE;
14224 bool tryDependent = maximumLength > JSFatInlineString::MAX_LENGTH_TWO_BYTE;
14225
14226#ifdef DEBUG1
14227 if (maximumLength != SIZE_MAX(18446744073709551615UL)) {
14228 Label ok;
14229 masm.branch32(Assembler::BelowOrEqual, length, Imm32(maximumLength), &ok);
14230 masm.assumeUnreachable("length should not exceed maximum length");
14231 masm.bind(&ok);
14232 }
14233#endif
14234
14235 Label nonZero, nonInput;
14236
14237 // For every edge case use the C++ variant.
14238 // Note: we also use this upon allocation failure in newGCString and
14239 // newGCFatInlineString. To squeeze out even more performance those failures
14240 // can be handled by allocate in ool code and returning to jit code to fill
14241 // in all data.
14242 using Fn = JSString* (*)(JSContext * cx, HandleString str, int32_t begin,
14243 int32_t len);
14244 OutOfLineCode* ool = oolCallVM<Fn, SubstringKernel>(
14245 lir, ArgList(string, begin, length), StoreRegisterTo(output));
14246 Label* slowPath = ool->entry();
14247 Label* done = ool->rejoin();
14248
14249 // Zero length, return emptystring.
14250 masm.branchTest32(Assembler::NonZero, length, length, &nonZero);
14251 const JSAtomState& names = gen->runtime->names();
14252 masm.movePtr(ImmGCPtr(names.empty_), output);
14253 masm.jump(done);
14254
14255 // Substring from 0..|str.length|, return str.
14256 masm.bind(&nonZero);
14257 masm.branch32(Assembler::NotEqual,
14258 Address(string, JSString::offsetOfLength()), length, &nonInput);
14259#ifdef DEBUG1
14260 {
14261 Label ok;
14262 masm.branchTest32(Assembler::Zero, begin, begin, &ok);
14263 masm.assumeUnreachable("length == str.length implies begin == 0");
14264 masm.bind(&ok);
14265 }
14266#endif
14267 masm.movePtr(string, output);
14268 masm.jump(done);
14269
14270 // Use slow path for ropes.
14271 masm.bind(&nonInput);
14272 masm.branchIfRope(string, slowPath);
14273
14274 // Optimize one and two character strings.
14275 Label nonStatic;
14276 masm.branch32(Assembler::Above, length, Imm32(2), &nonStatic);
14277 {
14278 Label loadLengthOne, loadLengthTwo;
14279
14280 auto loadChars = [&](CharEncoding encoding, bool fallthru) {
14281 size_t size = encoding == CharEncoding::Latin1 ? sizeof(JS::Latin1Char)
14282 : sizeof(char16_t);
14283
14284 masm.loadStringChars(string, temp0, encoding);
14285 masm.loadChar(temp0, begin, temp2, encoding);
14286 masm.branch32(Assembler::Equal, length, Imm32(1), &loadLengthOne);
14287 masm.loadChar(temp0, begin, temp0, encoding, int32_t(size));
14288 if (!fallthru) {
14289 masm.jump(&loadLengthTwo);
14290 }
14291 };
14292
14293 Label isLatin1;
14294 masm.branchLatin1String(string, &isLatin1);
14295 loadChars(CharEncoding::TwoByte, /* fallthru = */ false);
14296
14297 masm.bind(&isLatin1);
14298 loadChars(CharEncoding::Latin1, /* fallthru = */ true);
14299
14300 // Try to load a length-two static string.
14301 masm.bind(&loadLengthTwo);
14302 masm.lookupStaticString(temp2, temp0, output, gen->runtime->staticStrings(),
14303 &nonStatic);
14304 masm.jump(done);
14305
14306 // Try to load a length-one static string.
14307 masm.bind(&loadLengthOne);
14308 masm.lookupStaticString(temp2, output, gen->runtime->staticStrings(),
14309 &nonStatic);
14310 masm.jump(done);
14311 }
14312 masm.bind(&nonStatic);
14313
14314 // Allocate either a JSThinInlineString or JSFatInlineString, or jump to
14315 // notInline if we need a dependent string.
14316 Label notInline;
14317 {
14318 static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 <
14319 JSFatInlineString::MAX_LENGTH_LATIN1);
14320 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <
14321 JSFatInlineString::MAX_LENGTH_TWO_BYTE);
14322
14323 // Use temp2 to store the JS(Thin|Fat)InlineString flags. This avoids having
14324 // duplicate newGCString/newGCFatInlineString codegen for Latin1 vs TwoByte
14325 // strings.
14326
14327 Label allocFat, allocDone;
14328 if (tryFatInlineOrDependent) {
14329 Label isLatin1, allocThin;
14330 masm.branchLatin1String(string, &isLatin1);
14331 {
14332 if (tryDependent) {
14333 masm.branch32(Assembler::Above, length,
14334 Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
14335 &notInline);
14336 }
14337 masm.move32(Imm32(0), temp2);
14338 masm.branch32(Assembler::Above, length,
14339 Imm32(JSThinInlineString::MAX_LENGTH_TWO_BYTE),
14340 &allocFat);
14341 masm.jump(&allocThin);
14342 }
14343
14344 masm.bind(&isLatin1);
14345 {
14346 if (tryDependent) {
14347 masm.branch32(Assembler::Above, length,
14348 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1),
14349 &notInline);
14350 }
14351 masm.move32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp2);
14352 masm.branch32(Assembler::Above, length,
14353 Imm32(JSThinInlineString::MAX_LENGTH_LATIN1), &allocFat);
14354 }
14355
14356 masm.bind(&allocThin);
14357 } else {
14358 masm.load32(Address(string, JSString::offsetOfFlags()), temp2);
14359 masm.and32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp2);
14360 }
14361
14362 {
14363 masm.newGCString(output, temp0, initialStringHeap(), slowPath);
14364 masm.or32(Imm32(StringFlags::INIT_THIN_INLINE_FLAGS), temp2);
14365 }
14366
14367 if (tryFatInlineOrDependent) {
14368 masm.jump(&allocDone);
14369
14370 masm.bind(&allocFat);
14371 {
14372 masm.newGCFatInlineString(output, temp0, initialStringHeap(), slowPath);
14373 masm.or32(Imm32(StringFlags::INIT_FAT_INLINE_FLAGS), temp2);
14374 }
14375
14376 masm.bind(&allocDone);
14377 }
14378
14379 masm.store32(temp2, Address(output, JSString::offsetOfFlags()));
14380 masm.store32(length, Address(output, JSString::offsetOfLength()));
14381
14382 auto initializeInlineString = [&](CharEncoding encoding) {
14383 masm.loadStringChars(string, temp0, encoding);
14384 masm.addToCharPtr(temp0, begin, encoding);
14385 if (temp1 == string) {
14386 masm.push(string);
14387 }
14388 masm.loadInlineStringCharsForStore(output, temp1);
14389 CopyStringChars(masm, temp1, temp0, length, temp2, encoding,
14390 maximumLength);
14391 masm.loadStringLength(output, length);
14392 if (temp1 == string) {
14393 masm.pop(string);
14394 }
14395 };
14396
14397 Label isInlineLatin1;
14398 masm.branchTest32(Assembler::NonZero, temp2,
14399 Imm32(StringFlags::LATIN1_CHARS_BIT), &isInlineLatin1);
14400 initializeInlineString(CharEncoding::TwoByte);
14401 masm.jump(done);
14402
14403 masm.bind(&isInlineLatin1);
14404 initializeInlineString(CharEncoding::Latin1);
14405 }
14406
14407 // Handle other cases with a DependentString.
14408 if (tryDependent) {
14409 masm.jump(done);
14410
14411 masm.bind(&notInline);
14412 masm.newGCString(output, temp0, gen->initialStringHeap(), slowPath);
14413 masm.store32(length, Address(output, JSString::offsetOfLength()));
14414
14415 // Note: no post barrier is needed because the dependent string is either
14416 // allocated in the nursery or both strings are tenured (if nursery strings
14417 // are disabled for this zone).
14418 EmitInitDependentStringBase(masm, output, string, temp0, temp2,
14419 /* needsPostBarrier = */ false);
14420
14421 auto initializeDependentString = [&](CharEncoding encoding) {
14422 uint32_t flags = StringFlags::dependentStringFlags(encoding);
14423 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
14424 masm.loadNonInlineStringChars(string, temp0, encoding);
14425 masm.addToCharPtr(temp0, begin, encoding);
14426 masm.storeNonInlineStringChars(temp0, output);
14427 };
14428
14429 Label isLatin1;
14430 masm.branchLatin1String(string, &isLatin1);
14431 initializeDependentString(CharEncoding::TwoByte);
14432 masm.jump(done);
14433
14434 masm.bind(&isLatin1);
14435 initializeDependentString(CharEncoding::Latin1);
14436 }
14437
14438 masm.bind(done);
14439}
14440
14441JitCode* JitZone::generateStringConcatStub(JSContext* cx) {
14442 JitSpew(JitSpew_Codegen, "# Emitting StringConcat stub");
14443
14444 TempAllocator temp(&cx->tempLifoAlloc());
14445 JitContext jcx(cx);
14446 StackMacroAssembler masm(cx, temp);
14447 AutoCreatedBy acb(masm, "JitZone::generateStringConcatStub");
14448
14449 Register lhs = CallTempReg0;
14450 Register rhs = CallTempReg1;
14451 Register temp1 = CallTempReg2;
14452 Register temp2 = CallTempReg3;
14453 Register temp3 = CallTempReg4;
14454 Register output = CallTempReg5;
14455
14456 Label failure;
14457#ifdef JS_USE_LINK_REGISTER
14458 masm.pushRegs(LinkRegister, FramePointer);
14459 masm.adjustFrame(sizeof(intptr_t));
14460#else
14461 masm.Push(FramePointer);
14462#endif
14463 masm.moveStackPtrTo(FramePointer);
14464
14465 // If lhs is empty, return rhs.
14466 Label leftEmpty;
14467 masm.loadStringLength(lhs, temp1);
14468 masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty);
14469
14470 // If rhs is empty, return lhs.
14471 Label rightEmpty;
14472 masm.loadStringLength(rhs, temp2);
14473 masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty);
14474
14475 masm.add32(temp1, temp2);
14476
14477 // Check if we can use a JSInlineString. The result is a Latin1 string if
14478 // lhs and rhs are both Latin1, so we AND the flags.
14479 Label isInlineTwoByte, isInlineLatin1;
14480 masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1);
14481 masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1);
14482
14483 Label isLatin1, notInline;
14484 masm.branchTest32(Assembler::NonZero, temp1,
14485 Imm32(StringFlags::LATIN1_CHARS_BIT), &isLatin1);
14486 {
14487 masm.branch32(Assembler::BelowOrEqual, temp2,
14488 Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
14489 &isInlineTwoByte);
14490 masm.jump(&notInline);
14491 }
14492 masm.bind(&isLatin1);
14493 {
14494 masm.branch32(Assembler::BelowOrEqual, temp2,
14495 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1);
14496 }
14497 masm.bind(&notInline);
14498
14499 // Keep AND'ed flags in temp1.
14500
14501 // Ensure result length <= JSString::MAX_LENGTH.
14502 masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
14503
14504 // Allocate a new rope, guaranteed to be in the nursery if initialStringHeap
14505 // == gc::Heap::Default. (As a result, no post barriers are needed below.)
14506 masm.newGCString(output, temp3, initialStringHeap, &failure);
14507
14508 // Store rope length and flags. temp1 still holds the result of AND'ing the
14509 // lhs and rhs flags, so we just have to clear the other flags to get our rope
14510 // flags (Latin1 if both lhs and rhs are Latin1).
14511 static_assert(StringFlags::INIT_ROPE_FLAGS == 0,
14512 "Rope type flags must have no bits set");
14513 masm.and32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp1);
14514 masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
14515 masm.store32(temp2, Address(output, JSString::offsetOfLength()));
14516
14517 // Store left and right nodes.
14518 masm.storeRopeChildren(lhs, rhs, output);
14519 masm.pop(FramePointer);
14520 masm.ret();
14521
14522 masm.bind(&leftEmpty);
14523 masm.mov(rhs, output);
14524 masm.pop(FramePointer);
14525 masm.ret();
14526
14527 masm.bind(&rightEmpty);
14528 masm.mov(lhs, output);
14529 masm.pop(FramePointer);
14530 masm.ret();
14531
14532 masm.bind(&isInlineTwoByte);
14533 ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
14534 initialStringHeap, &failure, CharEncoding::TwoByte);
14535 masm.pop(FramePointer);
14536 masm.ret();
14537
14538 masm.bind(&isInlineLatin1);
14539 ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
14540 initialStringHeap, &failure, CharEncoding::Latin1);
14541 masm.pop(FramePointer);
14542 masm.ret();
14543
14544 masm.bind(&failure);
14545 masm.movePtr(ImmPtr(nullptr), output);
14546 masm.pop(FramePointer);
14547 masm.ret();
14548
14549 Linker linker(masm);
14550 JitCode* code = linker.newCode(cx, CodeKind::Other);
14551
14552 CollectPerfSpewerJitCodeProfile(code, "StringConcatStub");
14553#ifdef MOZ_VTUNE1
14554 vtune::MarkStub(code, "StringConcatStub");
14555#endif
14556
14557 return code;
14558}
14559
14560void JitRuntime::generateLazyLinkStub(MacroAssembler& masm) {
14561 AutoCreatedBy acb(masm, "JitRuntime::generateLazyLinkStub");
14562
14563 lazyLinkStubOffset_ = startTrampolineCode(masm);
14564
14565#ifdef JS_USE_LINK_REGISTER
14566 masm.pushRegs(LinkRegister, FramePointer);
14567 masm.adjustFrame(sizeof(intptr_t));
14568#else
14569 masm.Push(FramePointer);
14570#endif
14571 masm.moveStackPtrTo(FramePointer);
14572
14573 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
14574 Register temp0 = regs.takeAny();
14575 Register temp1 = regs.takeAny();
14576 Register temp2 = regs.takeAny();
14577
14578 masm.loadJSContext(temp0);
14579 masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink);
14580 masm.moveStackPtrTo(temp1);
14581
14582 using Fn = uint8_t* (*)(JSContext * cx, LazyLinkExitFrameLayout * frame);
14583 masm.setupUnalignedABICall(temp2);
14584 masm.passABIArg(temp0);
14585 masm.passABIArg(temp1);
14586 masm.callWithABI<Fn, LazyLinkTopActivation>(
14587 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
14588
14589 // Discard exit frame and restore frame pointer.
14590 masm.leaveExitFrame(0);
14591#ifdef JS_USE_LINK_REGISTER
14592 // Restore the return address such that the emitPrologue function of the
14593 // CodeGenerator can push it back on the stack with pushReturnAddress.
14594 masm.popRegs(FramePointer, LinkRegister);
14595#else
14596 masm.pop(FramePointer);
14597#endif
14598 masm.jump(ReturnReg);
14599}
14600
14601void JitRuntime::generateInterpreterStub(MacroAssembler& masm) {
14602 AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterStub");
14603
14604 interpreterStubOffset_ = startTrampolineCode(masm);
14605
14606#ifdef JS_USE_LINK_REGISTER
14607 masm.pushRegs(LinkRegister, FramePointer);
14608 masm.adjustFrame(sizeof(intptr_t));
14609#else
14610 masm.Push(FramePointer);
14611#endif
14612 masm.moveStackPtrTo(FramePointer);
14613
14614 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
14615 Register temp0 = regs.takeAny();
14616 Register temp1 = regs.takeAny();
14617 Register temp2 = regs.takeAny();
14618
14619 masm.loadJSContext(temp0);
14620 masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub);
14621 masm.moveStackPtrTo(temp1);
14622
14623 using Fn = bool (*)(JSContext* cx, InterpreterStubExitFrameLayout* frame);
14624 masm.setupUnalignedABICall(temp2);
14625 masm.passABIArg(temp0);
14626 masm.passABIArg(temp1);
14627 masm.callWithABI<Fn, InvokeFromInterpreterStub>(
14628 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
14629
14630 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
14631
14632 // Discard exit frame and restore frame pointer.
14633 masm.leaveExitFrame(0);
14634 masm.pop(FramePointer);
14635
14636 // InvokeFromInterpreterStub stores the return value in argv[0], where the
14637 // caller stored |this|. Subtract |sizeof(void*)| for the frame pointer we
14638 // just popped.
14639 masm.loadValue(Address(masm.getStackPointer(),
14640 JitFrameLayout::offsetOfThis() - sizeof(void*)),
14641 JSReturnOperand);
14642 masm.ret();
14643}
14644
14645void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) {
14646 AutoCreatedBy acb(masm, "JitRuntime::generateDoubleToInt32ValueStub");
14647 doubleToInt32ValueStubOffset_ = startTrampolineCode(masm);
14648
14649 Label done;
14650 masm.branchTestDouble(Assembler::NotEqual, R0, &done);
14651
14652 masm.unboxDouble(R0, FloatReg0);
14653 masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done,
14654 /* negativeZeroCheck = */ false);
14655 masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0);
14656
14657 masm.bind(&done);
14658 masm.abiret();
14659}
14660
14661void CodeGenerator::visitLinearizeString(LLinearizeString* lir) {
14662 Register str = ToRegister(lir->string());
14663 Register output = ToRegister(lir->output());
14664
14665 using Fn = JSLinearString* (*)(JSContext*, JSString*);
14666 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
14667 lir, ArgList(str), StoreRegisterTo(output));
14668
14669 masm.branchIfRope(str, ool->entry());
14670
14671 if (str != output) {
14672 masm.movePtr(str, output);
14673 }
14674 masm.bind(ool->rejoin());
14675}
14676
14677void CodeGenerator::visitLinearizeForCharAccess(LLinearizeForCharAccess* lir) {
14678 Register str = ToRegister(lir->string());
14679 Register index = ToRegister(lir->index());
14680 Register output = ToRegister(lir->output());
14681
14682 using Fn = JSLinearString* (*)(JSContext*, JSString*);
14683 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
14684 lir, ArgList(str), StoreRegisterTo(output));
14685
14686 masm.branchIfNotCanLoadStringChar(str, index, output, ool->entry());
14687
14688 masm.movePtr(str, output);
14689 masm.bind(ool->rejoin());
14690}
14691
14692void CodeGenerator::visitLinearizeForCodePointAccess(
14693 LLinearizeForCodePointAccess* lir) {
14694 Register str = ToRegister(lir->string());
14695 Register index = ToRegister(lir->index());
14696 Register output = ToRegister(lir->output());
14697 Register temp = ToRegister(lir->temp0());
14698
14699 using Fn = JSLinearString* (*)(JSContext*, JSString*);
14700 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
14701 lir, ArgList(str), StoreRegisterTo(output));
14702
14703 masm.branchIfNotCanLoadStringCodePoint(str, index, output, temp,
14704 ool->entry());
14705
14706 masm.movePtr(str, output);
14707 masm.bind(ool->rejoin());
14708}
14709
14710void CodeGenerator::visitToRelativeStringIndex(LToRelativeStringIndex* lir) {
14711 Register index = ToRegister(lir->index());
14712 Register length = ToRegister(lir->length());
14713 Register output = ToRegister(lir->output());
14714
14715 masm.move32(Imm32(0), output);
14716 masm.cmp32Move32(Assembler::LessThan, index, Imm32(0), length, output);
14717 masm.add32(index, output);
14718}
14719
14720void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) {
14721 Register str = ToRegister(lir->string());
14722 Register output = ToRegister(lir->output());
14723 Register temp0 = ToRegister(lir->temp0());
14724 Register temp1 = ToRegister(lir->temp1());
14725
14726 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
14727
14728 if (lir->index()->isBogus()) {
14729 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)),
14730 StoreRegisterTo(output));
14731 masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry());
14732 masm.bind(ool->rejoin());
14733 } else {
14734 Register index = ToRegister(lir->index());
14735
14736 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index),
14737 StoreRegisterTo(output));
14738 masm.loadStringChar(str, index, output, temp0, temp1, ool->entry());
14739 masm.bind(ool->rejoin());
14740 }
14741}
14742
14743void CodeGenerator::visitCharCodeAtOrNegative(LCharCodeAtOrNegative* lir) {
14744 Register str = ToRegister(lir->string());
14745 Register output = ToRegister(lir->output());
14746 Register temp0 = ToRegister(lir->temp0());
14747 Register temp1 = ToRegister(lir->temp1());
14748
14749 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
14750
14751 // Return -1 for out-of-bounds access.
14752 masm.move32(Imm32(-1), output);
14753
14754 if (lir->index()->isBogus()) {
14755 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)),
14756 StoreRegisterTo(output));
14757
14758 masm.branch32(Assembler::Equal, Address(str, JSString::offsetOfLength()),
14759 Imm32(0), ool->rejoin());
14760 masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry());
14761 masm.bind(ool->rejoin());
14762 } else {
14763 Register index = ToRegister(lir->index());
14764
14765 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index),
14766 StoreRegisterTo(output));
14767
14768 masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
14769 temp0, ool->rejoin());
14770 masm.loadStringChar(str, index, output, temp0, temp1, ool->entry());
14771 masm.bind(ool->rejoin());
14772 }
14773}
14774
14775void CodeGenerator::visitCodePointAt(LCodePointAt* lir) {
14776 Register str = ToRegister(lir->string());
14777 Register index = ToRegister(lir->index());
14778 Register output = ToRegister(lir->output());
14779 Register temp0 = ToRegister(lir->temp0());
14780 Register temp1 = ToRegister(lir->temp1());
14781
14782 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
14783 auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index),
14784 StoreRegisterTo(output));
14785
14786 masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry());
14787 masm.bind(ool->rejoin());
14788}
14789
14790void CodeGenerator::visitCodePointAtOrNegative(LCodePointAtOrNegative* lir) {
14791 Register str = ToRegister(lir->string());
14792 Register index = ToRegister(lir->index());
14793 Register output = ToRegister(lir->output());
14794 Register temp0 = ToRegister(lir->temp0());
14795 Register temp1 = ToRegister(lir->temp1());
14796
14797 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
14798 auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index),
14799 StoreRegisterTo(output));
14800
14801 // Return -1 for out-of-bounds access.
14802 masm.move32(Imm32(-1), output);
14803
14804 masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
14805 temp0, ool->rejoin());
14806 masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry());
14807 masm.bind(ool->rejoin());
14808}
14809
14810void CodeGenerator::visitNegativeToNaN(LNegativeToNaN* lir) {
14811 Register input = ToRegister(lir->input());
14812 ValueOperand output = ToOutValue(lir);
14813
14814 masm.tagValue(JSVAL_TYPE_INT32, input, output);
14815
14816 Label done;
14817 masm.branchTest32(Assembler::NotSigned, input, input, &done);
14818 masm.moveValue(JS::NaNValue(), output);
14819 masm.bind(&done);
14820}
14821
14822void CodeGenerator::visitNegativeToUndefined(LNegativeToUndefined* lir) {
14823 Register input = ToRegister(lir->input());
14824 ValueOperand output = ToOutValue(lir);
14825
14826 masm.tagValue(JSVAL_TYPE_INT32, input, output);
14827
14828 Label done;
14829 masm.branchTest32(Assembler::NotSigned, input, input, &done);
14830 masm.moveValue(JS::UndefinedValue(), output);
14831 masm.bind(&done);
14832}
14833
14834void CodeGenerator::visitFromCharCode(LFromCharCode* lir) {
14835 Register code = ToRegister(lir->code());
14836 Register output = ToRegister(lir->output());
14837
14838 using Fn = JSLinearString* (*)(JSContext*, int32_t);
14839 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
14840 StoreRegisterTo(output));
14841
14842 // OOL path if code >= UNIT_STATIC_LIMIT.
14843 masm.lookupStaticString(code, output, gen->runtime->staticStrings(),
14844 ool->entry());
14845
14846 masm.bind(ool->rejoin());
14847}
14848
14849void CodeGenerator::visitFromCharCodeEmptyIfNegative(
14850 LFromCharCodeEmptyIfNegative* lir) {
14851 Register code = ToRegister(lir->code());
14852 Register output = ToRegister(lir->output());
14853
14854 using Fn = JSLinearString* (*)(JSContext*, int32_t);
14855 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
14856 StoreRegisterTo(output));
14857
14858 // Return the empty string for negative inputs.
14859 const JSAtomState& names = gen->runtime->names();
14860 masm.movePtr(ImmGCPtr(names.empty_), output);
14861 masm.branchTest32(Assembler::Signed, code, code, ool->rejoin());
14862
14863 // OOL path if code >= UNIT_STATIC_LIMIT.
14864 masm.lookupStaticString(code, output, gen->runtime->staticStrings(),
14865 ool->entry());
14866
14867 masm.bind(ool->rejoin());
14868}
14869
14870void CodeGenerator::visitFromCharCodeUndefinedIfNegative(
14871 LFromCharCodeUndefinedIfNegative* lir) {
14872 Register code = ToRegister(lir->code());
14873 ValueOperand output = ToOutValue(lir);
14874 Register temp = output.scratchReg();
14875
14876 using Fn = JSLinearString* (*)(JSContext*, int32_t);
14877 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
14878 StoreRegisterTo(temp));
14879
14880 // Return |undefined| for negative inputs.
14881 Label done;
14882 masm.moveValue(UndefinedValue(), output);
14883 masm.branchTest32(Assembler::Signed, code, code, &done);
14884
14885 // OOL path if code >= UNIT_STATIC_LIMIT.
14886 masm.lookupStaticString(code, temp, gen->runtime->staticStrings(),
14887 ool->entry());
14888
14889 masm.bind(ool->rejoin());
14890 masm.tagValue(JSVAL_TYPE_STRING, temp, output);
14891
14892 masm.bind(&done);
14893}
14894
14895void CodeGenerator::visitFromCodePoint(LFromCodePoint* lir) {
14896 Register codePoint = ToRegister(lir->codePoint());
14897 Register output = ToRegister(lir->output());
14898 Register temp0 = ToRegister(lir->temp0());
14899 Register temp1 = ToRegister(lir->temp1());
14900 LSnapshot* snapshot = lir->snapshot();
14901
14902 // The OOL path is only taken when we can't allocate the inline string.
14903 using Fn = JSLinearString* (*)(JSContext*, char32_t);
14904 auto* ool = oolCallVM<Fn, js::StringFromCodePoint>(lir, ArgList(codePoint),
14905 StoreRegisterTo(output));
14906
14907 Label isTwoByte;
14908 Label* done = ool->rejoin();
14909
14910 static_assert(
14911 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
14912 "Latin-1 strings can be loaded from static strings");
14913
14914 {
14915 masm.lookupStaticString(codePoint, output, gen->runtime->staticStrings(),
14916 &isTwoByte);
14917 masm.jump(done);
14918 }
14919 masm.bind(&isTwoByte);
14920 {
14921 // Use a bailout if the input is not a valid code point, because
14922 // MFromCodePoint is movable and it'd be observable when a moved
14923 // fromCodePoint throws an exception before its actual call site.
14924 bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax),
14925 snapshot);
14926
14927 // Allocate a JSThinInlineString.
14928 {
14929 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE >= 2,
14930 "JSThinInlineString can hold a supplementary code point");
14931
14932 uint32_t flags =
14933 StringFlags::thinInlineStringFlags(CharEncoding::TwoByte);
14934 masm.newGCString(output, temp0, gen->initialStringHeap(), ool->entry());
14935 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
14936 }
14937
14938 Label isSupplementary;
14939 masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(unicode::NonBMPMin),
14940 &isSupplementary);
14941 {
14942 // Store length.
14943 masm.store32(Imm32(1), Address(output, JSString::offsetOfLength()));
14944
14945 // Load chars pointer in temp0.
14946 masm.loadInlineStringCharsForStore(output, temp0);
14947
14948 masm.store16(codePoint, Address(temp0, 0));
14949
14950 masm.jump(done);
14951 }
14952 masm.bind(&isSupplementary);
14953 {
14954 // Store length.
14955 masm.store32(Imm32(2), Address(output, JSString::offsetOfLength()));
14956
14957 // Load chars pointer in temp0.
14958 masm.loadInlineStringCharsForStore(output, temp0);
14959
14960 // Inlined unicode::LeadSurrogate(uint32_t).
14961 masm.rshift32(Imm32(10), codePoint, temp1);
14962 masm.add32(Imm32(unicode::LeadSurrogateMin - (unicode::NonBMPMin >> 10)),
14963 temp1);
14964
14965 masm.store16(temp1, Address(temp0, 0));
14966
14967 // Inlined unicode::TrailSurrogate(uint32_t).
14968 masm.and32(Imm32(0x3FF), codePoint, temp1);
14969 masm.or32(Imm32(unicode::TrailSurrogateMin), temp1);
14970
14971 masm.store16(temp1, Address(temp0, sizeof(char16_t)));
14972 }
14973 }
14974
14975 masm.bind(done);
14976}
14977
14978void CodeGenerator::visitStringIncludes(LStringIncludes* lir) {
14979 pushArg(ToRegister(lir->searchString()));
14980 pushArg(ToRegister(lir->string()));
14981
14982 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14983 callVM<Fn, js::StringIncludes>(lir);
14984}
14985
14986template <typename LIns>
14987static void CallStringMatch(MacroAssembler& masm, LIns* lir,
14988 LiveRegisterSet volatileRegs) {
14989 Register string = ToRegister(lir->string());
14990 Register output = ToRegister(lir->output());
14991 Register tempLength = ToRegister(lir->temp0());
14992 Register tempChars = ToRegister(lir->temp1());
14993 Register maybeTempPat = ToTempRegisterOrInvalid(lir->temp2());
14994
14995 const JSOffThreadAtom* searchString = lir->searchString();
14996 size_t length = searchString->length();
14997 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", 14997); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length == 1 || length == 2" ")"); do { MOZ_CrashSequence
(__null, 14997); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
14998
14999 // The additional temp register is only needed when searching for two
15000 // pattern characters.
15001 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", 15001); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "maybeTempPat != InvalidReg" ")"); do { MOZ_CrashSequence
(__null, 15001); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
15002
15003 if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) {
15004 masm.move32(Imm32(0), output);
15005 } else {
15006 masm.move32(Imm32(-1), output);
15007 }
15008
15009 masm.loadStringLength(string, tempLength);
15010
15011 // Can't be a substring when the string is smaller than the search string.
15012 Label done;
15013 masm.branch32(Assembler::Below, tempLength, Imm32(length), &done);
15014
15015 bool searchStringIsPureTwoByte = false;
15016 if (searchString->hasTwoByteChars()) {
15017 JS::AutoCheckCannotGC nogc;
15018 searchStringIsPureTwoByte =
15019 !mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc));
15020 }
15021
15022 // Pure two-byte strings can't occur in a Latin-1 string.
15023 if (searchStringIsPureTwoByte) {
15024 masm.branchLatin1String(string, &done);
15025 }
15026
15027#ifdef DEBUG1
15028 // We don't expect to see ropes here.
15029 Label notRope;
15030 masm.branchIfNotRope(string, &notRope);
15031 masm.assumeUnreachable("input string must be linearized");
15032 masm.bind(&notRope);
15033#endif
15034
15035 Label restoreVolatile;
15036
15037 auto callMatcher = [&](CharEncoding encoding) {
15038 masm.loadStringChars(string, tempChars, encoding);
15039
15040 LiveGeneralRegisterSet liveRegs;
15041 if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) {
15042 // Save |tempChars| to compute the result index.
15043 liveRegs.add(tempChars);
15044
15045#ifdef DEBUG1
15046 // Save |tempLength| in debug-mode for assertions.
15047 liveRegs.add(tempLength);
15048#endif
15049
15050 // Exclude non-volatile registers.
15051 liveRegs.set() = GeneralRegisterSet::Intersect(
15052 liveRegs.set(), GeneralRegisterSet::Volatile());
15053
15054 masm.PushRegsInMask(liveRegs);
15055 }
15056
15057 if (length == 1) {
15058 char16_t pat = searchString->latin1OrTwoByteChar(0);
15059 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"
, 15060); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 15060); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
15060 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"
, 15060); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 15060); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
15061
15062 masm.move32(Imm32(pat), output);
15063
15064 masm.setupAlignedABICall();
15065 masm.passABIArg(tempChars);
15066 masm.passABIArg(output);
15067 masm.passABIArg(tempLength);
15068 if (encoding == CharEncoding::Latin1) {
15069 using Fn = const char* (*)(const char*, char, size_t);
15070 masm.callWithABI<Fn, mozilla::SIMD::memchr8>(
15071 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
15072 } else {
15073 using Fn = const char16_t* (*)(const char16_t*, char16_t, size_t);
15074 masm.callWithABI<Fn, mozilla::SIMD::memchr16>(
15075 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
15076 }
15077 } else {
15078 char16_t pat0 = searchString->latin1OrTwoByteChar(0);
15079 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"
, 15080); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 15080); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
15080 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"
, 15080); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 15080); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
15081
15082 char16_t pat1 = searchString->latin1OrTwoByteChar(1);
15083 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"
, 15084); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 15084); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
15084 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"
, 15084); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR"
")"); do { MOZ_CrashSequence(__null, 15084); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
15085
15086 masm.move32(Imm32(pat0), output);
15087 masm.move32(Imm32(pat1), maybeTempPat);
15088
15089 masm.setupAlignedABICall();
15090 masm.passABIArg(tempChars);
15091 masm.passABIArg(output);
15092 masm.passABIArg(maybeTempPat);
15093 masm.passABIArg(tempLength);
15094 if (encoding == CharEncoding::Latin1) {
15095 using Fn = const char* (*)(const char*, char, char, size_t);
15096 masm.callWithABI<Fn, mozilla::SIMD::memchr2x8>(
15097 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
15098 } else {
15099 using Fn =
15100 const char16_t* (*)(const char16_t*, char16_t, char16_t, size_t);
15101 masm.callWithABI<Fn, mozilla::SIMD::memchr2x16>(
15102 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
15103 }
15104 }
15105
15106 masm.storeCallPointerResult(output);
15107
15108 // Convert to string index for `indexOf`.
15109 if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) {
15110 // Restore |tempChars|. (And in debug mode |tempLength|.)
15111 masm.PopRegsInMask(liveRegs);
15112
15113 Label found;
15114 masm.branchPtr(Assembler::NotEqual, output, ImmPtr(nullptr), &found);
15115 {
15116 masm.move32(Imm32(-1), output);
15117 masm.jump(&restoreVolatile);
15118 }
15119 masm.bind(&found);
15120
15121#ifdef DEBUG1
15122 // Check lower bound.
15123 Label lower;
15124 masm.branchPtr(Assembler::AboveOrEqual, output, tempChars, &lower);
15125 masm.assumeUnreachable("result pointer below string chars");
15126 masm.bind(&lower);
15127
15128 // Compute the end position of the characters.
15129 auto scale = encoding == CharEncoding::Latin1 ? TimesOne : TimesTwo;
15130 masm.computeEffectiveAddress(BaseIndex(tempChars, tempLength, scale),
15131 tempLength);
15132
15133 // Check upper bound.
15134 Label upper;
15135 masm.branchPtr(Assembler::Below, output, tempLength, &upper);
15136 masm.assumeUnreachable("result pointer above string chars");
15137 masm.bind(&upper);
15138#endif
15139
15140 masm.subPtr(tempChars, output);
15141
15142 if (encoding == CharEncoding::TwoByte) {
15143 masm.rshiftPtr(Imm32(1), output);
15144 }
15145 }
15146 };
15147
15148 volatileRegs.takeUnchecked(output);
15149 volatileRegs.takeUnchecked(tempLength);
15150 volatileRegs.takeUnchecked(tempChars);
15151 if (maybeTempPat != InvalidReg) {
15152 volatileRegs.takeUnchecked(maybeTempPat);
15153 }
15154 masm.PushRegsInMask(volatileRegs);
15155
15156 // Handle the case when the input is a Latin-1 string.
15157 if (!searchStringIsPureTwoByte) {
15158 Label twoByte;
15159 masm.branchTwoByteString(string, &twoByte);
15160 {
15161 callMatcher(CharEncoding::Latin1);
15162 masm.jump(&restoreVolatile);
15163 }
15164 masm.bind(&twoByte);
15165 }
15166
15167 // Handle the case when the input is a two-byte string.
15168 callMatcher(CharEncoding::TwoByte);
15169
15170 masm.bind(&restoreVolatile);
15171 masm.PopRegsInMask(volatileRegs);
15172
15173 // Convert to bool for `includes`.
15174 if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) {
15175 masm.cmpPtrSet(Assembler::NotEqual, output, ImmPtr(nullptr), output);
15176 }
15177
15178 masm.bind(&done);
15179}
15180
15181void CodeGenerator::visitStringIncludesSIMD(LStringIncludesSIMD* lir) {
15182 CallStringMatch(masm, lir, liveVolatileRegs(lir));
15183}
15184
15185void CodeGenerator::visitStringIndexOf(LStringIndexOf* lir) {
15186 pushArg(ToRegister(lir->searchString()));
15187 pushArg(ToRegister(lir->string()));
15188
15189 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
15190 callVM<Fn, js::StringIndexOf>(lir);
15191}
15192
15193void CodeGenerator::visitStringIndexOfSIMD(LStringIndexOfSIMD* lir) {
15194 CallStringMatch(masm, lir, liveVolatileRegs(lir));
15195}
15196
15197void CodeGenerator::visitStringLastIndexOf(LStringLastIndexOf* lir) {
15198 pushArg(ToRegister(lir->searchString()));
15199 pushArg(ToRegister(lir->string()));
15200
15201 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
15202 callVM<Fn, js::StringLastIndexOf>(lir);
15203}
15204
15205void CodeGenerator::visitStringStartsWith(LStringStartsWith* lir) {
15206 pushArg(ToRegister(lir->searchString()));
15207 pushArg(ToRegister(lir->string()));
15208
15209 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
15210 callVM<Fn, js::StringStartsWith>(lir);
15211}
15212
15213void CodeGenerator::visitStringStartsWithInline(LStringStartsWithInline* lir) {
15214 Register string = ToRegister(lir->string());
15215 Register output = ToRegister(lir->output());
15216 Register temp = ToRegister(lir->temp0());
15217
15218 const JSOffThreadAtom* searchString = lir->searchString();
15219
15220 size_t length = searchString->length();
15221 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", 15221); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length > 0" ")"); do { MOZ_CrashSequence
(__null, 15221); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15222
15223 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
15224 auto* ool = oolCallVM<Fn, js::StringStartsWith>(
15225 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
15226
15227 masm.move32(Imm32(0), output);
15228
15229 // Can't be a prefix when the string is smaller than the search string.
15230 masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()),
15231 Imm32(length), ool->rejoin());
15232
15233 // Unwind ropes at the start if possible.
15234 Label compare;
15235 masm.movePtr(string, temp);
15236 masm.branchIfNotRope(temp, &compare);
15237
15238 Label unwindRope;
15239 masm.bind(&unwindRope);
15240 masm.loadRopeLeftChild(temp, output);
15241 masm.movePtr(output, temp);
15242
15243 // If the left child is smaller than the search string, jump into the VM to
15244 // linearize the string.
15245 masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()),
15246 Imm32(length), ool->entry());
15247
15248 // Otherwise keep unwinding ropes.
15249 masm.branchIfRope(temp, &unwindRope);
15250
15251 masm.bind(&compare);
15252
15253 // If operands point to the same instance, it's trivially a prefix.
15254 Label notPointerEqual;
15255 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString),
15256 &notPointerEqual);
15257 masm.move32(Imm32(1), output);
15258 masm.jump(ool->rejoin());
15259 masm.bind(&notPointerEqual);
15260
15261 if (searchString->hasTwoByteChars()) {
15262 // Pure two-byte strings can't be a prefix of Latin-1 strings.
15263 JS::AutoCheckCannotGC nogc;
15264 if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) {
15265 Label compareChars;
15266 masm.branchTwoByteString(temp, &compareChars);
15267 masm.move32(Imm32(0), output);
15268 masm.jump(ool->rejoin());
15269 masm.bind(&compareChars);
15270 }
15271 }
15272
15273 // Load the input string's characters.
15274 Register stringChars = output;
15275 masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry());
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::visitStringEndsWith(LStringEndsWith* lir) {
15284 pushArg(ToRegister(lir->searchString()));
15285 pushArg(ToRegister(lir->string()));
15286
15287 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
15288 callVM<Fn, js::StringEndsWith>(lir);
15289}
15290
15291void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) {
15292 Register string = ToRegister(lir->string());
15293 Register output = ToRegister(lir->output());
15294 Register temp = ToRegister(lir->temp0());
15295
15296 const JSOffThreadAtom* searchString = lir->searchString();
15297
15298 size_t length = searchString->length();
15299 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", 15299); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "length > 0" ")"); do { MOZ_CrashSequence
(__null, 15299); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15300
15301 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
15302 auto* ool = oolCallVM<Fn, js::StringEndsWith>(
15303 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
15304
15305 masm.move32(Imm32(0), output);
15306
15307 // Can't be a suffix when the string is smaller than the search string.
15308 masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()),
15309 Imm32(length), ool->rejoin());
15310
15311 // Unwind ropes at the end if possible.
15312 Label compare;
15313 masm.movePtr(string, temp);
15314 masm.branchIfNotRope(temp, &compare);
15315
15316 Label unwindRope;
15317 masm.bind(&unwindRope);
15318 masm.loadRopeRightChild(temp, output);
15319 masm.movePtr(output, temp);
15320
15321 // If the right child is smaller than the search string, jump into the VM to
15322 // linearize the string.
15323 masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()),
15324 Imm32(length), ool->entry());
15325
15326 // Otherwise keep unwinding ropes.
15327 masm.branchIfRope(temp, &unwindRope);
15328
15329 masm.bind(&compare);
15330
15331 // If operands point to the same instance, it's trivially a suffix.
15332 Label notPointerEqual;
15333 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString),
15334 &notPointerEqual);
15335 masm.move32(Imm32(1), output);
15336 masm.jump(ool->rejoin());
15337 masm.bind(&notPointerEqual);
15338
15339 CharEncoding encoding = searchString->hasLatin1Chars()
15340 ? CharEncoding::Latin1
15341 : CharEncoding::TwoByte;
15342 if (encoding == CharEncoding::TwoByte) {
15343 // Pure two-byte strings can't be a suffix of Latin-1 strings.
15344 JS::AutoCheckCannotGC nogc;
15345 if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) {
15346 Label compareChars;
15347 masm.branchTwoByteString(temp, &compareChars);
15348 masm.move32(Imm32(0), output);
15349 masm.jump(ool->rejoin());
15350 masm.bind(&compareChars);
15351 }
15352 }
15353
15354 // Load the input string's characters.
15355 Register stringChars = output;
15356 masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry());
15357
15358 // Move string-char pointer to the suffix string.
15359 masm.loadStringLength(temp, temp);
15360 masm.sub32(Imm32(length), temp);
15361 masm.addToCharPtr(stringChars, temp, encoding);
15362
15363 // Start comparing character by character.
15364 masm.compareStringChars(JSOp::Eq, stringChars, searchString, output);
15365
15366 masm.bind(ool->rejoin());
15367}
15368
15369void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) {
15370 Register string = ToRegister(lir->string());
15371 Register output = ToRegister(lir->output());
15372 Register temp0 = ToRegister(lir->temp0());
15373 Register temp1 = ToRegister(lir->temp1());
15374 Register temp2 = ToRegister(lir->temp2());
15375
15376 // On x86 there are not enough registers. In that case reuse the string
15377 // register as a temporary.
15378 Register temp3 =
15379 lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3());
15380 Register temp4 = ToRegister(lir->temp4());
15381
15382 using Fn = JSLinearString* (*)(JSContext*, JSString*);
15383 OutOfLineCode* ool = oolCallVM<Fn, js::StringToLowerCase>(
15384 lir, ArgList(string), StoreRegisterTo(output));
15385
15386 // Take the slow path if the string isn't a linear Latin-1 string.
15387 Imm32 linearLatin1Bits(StringFlags::LINEAR_BIT |
15388 StringFlags::LATIN1_CHARS_BIT);
15389 Register flags = temp0;
15390 masm.load32(Address(string, JSString::offsetOfFlags()), flags);
15391 masm.and32(linearLatin1Bits, flags);
15392 masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry());
15393
15394 Register length = temp0;
15395 masm.loadStringLength(string, length);
15396
15397 // Return the input if it's the empty string.
15398 Label notEmptyString;
15399 masm.branch32(Assembler::NotEqual, length, Imm32(0), &notEmptyString);
15400 {
15401 masm.movePtr(string, output);
15402 masm.jump(ool->rejoin());
15403 }
15404 masm.bind(&notEmptyString);
15405
15406 Register inputChars = temp1;
15407 masm.loadStringChars(string, inputChars, CharEncoding::Latin1);
15408
15409 Register toLowerCaseTable = temp2;
15410 masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable);
15411
15412 // Single element strings can be directly retrieved from static strings cache.
15413 Label notSingleElementString;
15414 masm.branch32(Assembler::NotEqual, length, Imm32(1), &notSingleElementString);
15415 {
15416 Register current = temp4;
15417
15418 masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1);
15419 masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne),
15420 current);
15421 masm.lookupStaticString(current, output, gen->runtime->staticStrings());
15422
15423 masm.jump(ool->rejoin());
15424 }
15425 masm.bind(&notSingleElementString);
15426
15427 // Use the OOL-path when the string is too long. This prevents scanning long
15428 // strings which have upper case characters only near the end a second time in
15429 // the VM.
15430 constexpr int32_t MaxInlineLength = 64;
15431 masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry());
15432
15433 {
15434 // Check if there are any characters which need to be converted.
15435 //
15436 // This extra loop gives a small performance improvement for strings which
15437 // are already lower cased and lets us avoid calling into the runtime for
15438 // non-inline, all lower case strings. But more importantly it avoids
15439 // repeated inline allocation failures:
15440 // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the
15441 // |js::StringToLowerCase| runtime function when the result string can't be
15442 // allocated inline. And |js::StringToLowerCase| directly returns the input
15443 // string when no characters need to be converted. That means it won't
15444 // trigger GC to clear up the free nursery space, so the next toLowerCase()
15445 // call will again fail to inline allocate the result string.
15446 Label hasUpper;
15447 {
15448 Register checkInputChars = output;
15449 masm.movePtr(inputChars, checkInputChars);
15450
15451 Register current = temp4;
15452
15453 Label start;
15454 masm.bind(&start);
15455 masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1);
15456 masm.branch8(Assembler::NotEqual,
15457 BaseIndex(toLowerCaseTable, current, TimesOne), current,
15458 &hasUpper);
15459 masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars);
15460 masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start);
15461
15462 // Input is already in lower case.
15463 masm.movePtr(string, output);
15464 masm.jump(ool->rejoin());
15465 }
15466 masm.bind(&hasUpper);
15467
15468 // |length| was clobbered above, reload.
15469 masm.loadStringLength(string, length);
15470
15471 // Call into the runtime when we can't create an inline string.
15472 masm.branch32(Assembler::Above, length,
15473 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry());
15474
15475 AllocateThinOrFatInlineString(masm, output, length, temp4,
15476 initialStringHeap(), ool->entry(),
15477 CharEncoding::Latin1);
15478
15479 if (temp3 == string) {
15480 masm.push(string);
15481 }
15482
15483 Register outputChars = temp3;
15484 masm.loadInlineStringCharsForStore(output, outputChars);
15485
15486 {
15487 Register current = temp4;
15488
15489 Label start;
15490 masm.bind(&start);
15491 masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1);
15492 masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne),
15493 current);
15494 masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1);
15495 masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars);
15496 masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars);
15497 masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start);
15498 }
15499
15500 if (temp3 == string) {
15501 masm.pop(string);
15502 }
15503 }
15504
15505 masm.bind(ool->rejoin());
15506}
15507
15508void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) {
15509 pushArg(ToRegister(lir->string()));
15510
15511 using Fn = JSLinearString* (*)(JSContext*, JSString*);
15512 callVM<Fn, js::StringToUpperCase>(lir);
15513}
15514
15515void CodeGenerator::visitCharCodeToLowerCase(LCharCodeToLowerCase* lir) {
15516 Register code = ToRegister(lir->code());
15517 Register output = ToRegister(lir->output());
15518 Register temp = ToRegister(lir->temp0());
15519
15520 using Fn = JSString* (*)(JSContext*, int32_t);
15521 auto* ool = oolCallVM<Fn, jit::CharCodeToLowerCase>(lir, ArgList(code),
15522 StoreRegisterTo(output));
15523
15524 constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1;
15525
15526 // OOL path if code >= NonLatin1Min.
15527 masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry());
15528
15529 // Convert to lower case.
15530 masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), temp);
15531 masm.load8ZeroExtend(BaseIndex(temp, code, TimesOne), temp);
15532
15533 // Load static string for lower case character.
15534 masm.lookupStaticString(temp, output, gen->runtime->staticStrings());
15535
15536 masm.bind(ool->rejoin());
15537}
15538
15539void CodeGenerator::visitCharCodeToUpperCase(LCharCodeToUpperCase* lir) {
15540 Register code = ToRegister(lir->code());
15541 Register output = ToRegister(lir->output());
15542 Register temp = ToRegister(lir->temp0());
15543
15544 using Fn = JSString* (*)(JSContext*, int32_t);
15545 auto* ool = oolCallVM<Fn, jit::CharCodeToUpperCase>(lir, ArgList(code),
15546 StoreRegisterTo(output));
15547
15548 constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1;
15549
15550 // OOL path if code >= NonLatin1Min.
15551 masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry());
15552
15553 // Most one element Latin-1 strings can be directly retrieved from the
15554 // static strings cache, except the following three characters:
15555 //
15556 // 1. ToUpper(U+00B5) = 0+039C
15557 // 2. ToUpper(U+00FF) = 0+0178
15558 // 3. ToUpper(U+00DF) = 0+0053 0+0053
15559 masm.branch32(Assembler::Equal, code, Imm32(unicode::MICRO_SIGN),
15560 ool->entry());
15561 masm.branch32(Assembler::Equal, code,
15562 Imm32(unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS),
15563 ool->entry());
15564 masm.branch32(Assembler::Equal, code,
15565 Imm32(unicode::LATIN_SMALL_LETTER_SHARP_S), ool->entry());
15566
15567 // Inline unicode::ToUpperCase (without the special case for ASCII characters)
15568
15569 constexpr size_t shift = unicode::CharInfoShift;
15570
15571 // code >> shift
15572 masm.rshift32(Imm32(shift), code, temp);
15573
15574 // index = index1[code >> shift];
15575 masm.movePtr(ImmPtr(unicode::index1), output);
15576 masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp);
15577
15578 // (code & ((1 << shift) - 1)
15579 masm.and32(Imm32((1 << shift) - 1), code, output);
15580
15581 // (index << shift) + (code & ((1 << shift) - 1))
15582 masm.lshift32(Imm32(shift), temp);
15583 masm.add32(output, temp);
15584
15585 // index = index2[(index << shift) + (code & ((1 << shift) - 1))]
15586 masm.movePtr(ImmPtr(unicode::index2), output);
15587 masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp);
15588
15589 // Compute |index * 6| through |(index * 3) * TimesTwo|.
15590 static_assert(sizeof(unicode::CharacterInfo) == 6);
15591 masm.mulBy3(temp, temp);
15592
15593 // upperCase = js_charinfo[index].upperCase
15594 masm.movePtr(ImmPtr(unicode::js_charinfo), output);
15595 masm.load16ZeroExtend(BaseIndex(output, temp, TimesTwo,
15596 offsetof(unicode::CharacterInfo, upperCase)__builtin_offsetof(unicode::CharacterInfo, upperCase)),
15597 temp);
15598
15599 // uint16_t(ch) + upperCase
15600 masm.add32(code, temp);
15601
15602 // Clear any high bits added when performing the unsigned 16-bit addition
15603 // through a signed 32-bit addition.
15604 masm.move8ZeroExtend(temp, temp);
15605
15606 // Load static string for upper case character.
15607 masm.lookupStaticString(temp, output, gen->runtime->staticStrings());
15608
15609 masm.bind(ool->rejoin());
15610}
15611
15612void CodeGenerator::visitStringTrimStartIndex(LStringTrimStartIndex* lir) {
15613 Register string = ToRegister(lir->string());
15614 Register output = ToRegister(lir->output());
15615
15616 using Fn = int32_t (*)(const JSString*);
15617 masm.setupAlignedABICall();
15618 masm.passABIArg(string);
15619 masm.callWithABI<Fn, jit::StringTrimStartIndex>();
15620 masm.storeCallInt32Result(output);
15621}
15622
15623void CodeGenerator::visitStringTrimEndIndex(LStringTrimEndIndex* lir) {
15624 Register string = ToRegister(lir->string());
15625 Register start = ToRegister(lir->start());
15626 Register output = ToRegister(lir->output());
15627
15628 using Fn = int32_t (*)(const JSString*, int32_t);
15629 masm.setupAlignedABICall();
15630 masm.passABIArg(string);
15631 masm.passABIArg(start);
15632 masm.callWithABI<Fn, jit::StringTrimEndIndex>();
15633 masm.storeCallInt32Result(output);
15634}
15635
15636void CodeGenerator::visitStringSplit(LStringSplit* lir) {
15637 pushArg(Imm32(INT32_MAX(2147483647)));
15638 pushArg(ToRegister(lir->separator()));
15639 pushArg(ToRegister(lir->string()));
15640
15641 using Fn = ArrayObject* (*)(JSContext*, HandleString, HandleString, uint32_t);
15642 callVM<Fn, js::StringSplitString>(lir);
15643}
15644
15645void CodeGenerator::visitInitializedLength(LInitializedLength* lir) {
15646 Address initLength(ToRegister(lir->elements()),
15647 ObjectElements::offsetOfInitializedLength());
15648 masm.load32(initLength, ToRegister(lir->output()));
15649}
15650
15651void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) {
15652 Register elements = ToRegister(lir->elements());
15653 uint32_t newLength = lir->mir()->length();
15654 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
15655
15656 if (lir->mir()->needsPreBarrier()) {
15657 // The elements at or above the new initialized length are no longer part of
15658 // the object, so we need a pre-barrier.
15659 Register index = ToRegister(lir->temp0());
15660 Label done;
15661 masm.branchTestNeedsMarkingBarrier(Assembler::Zero, &done);
15662 masm.load32(initLength, index);
15663 Label loop;
15664 masm.bind(&loop);
15665 masm.branch32(Assembler::BelowOrEqual, index, Imm32(newLength), &done);
15666 masm.sub32(Imm32(1), index);
15667 masm.unguardedCallPreBarrier(BaseValueIndex(elements, index),
15668 MIRType::Value);
15669 masm.jump(&loop);
15670 masm.bind(&done);
15671 } else {
15672#ifdef DEBUG1
15673 // Callers that don't need a barrier must not remove any elements.
15674 Label ok;
15675 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(newLength), &ok);
15676 masm.assumeUnreachable("removing elements without a pre-barrier");
15677 masm.bind(&ok);
15678#endif
15679 }
15680
15681 masm.store32(Imm32(newLength), initLength);
15682}
15683
15684void CodeGenerator::visitNotI(LNotI* lir) {
15685 Register input = ToRegister(lir->input());
15686 Register output = ToRegister(lir->output());
15687
15688 masm.cmp32Set(Assembler::Equal, input, Imm32(0), output);
15689}
15690
15691void CodeGenerator::visitNotIPtr(LNotIPtr* lir) {
15692 Register input = ToRegister(lir->input());
15693 Register output = ToRegister(lir->output());
15694
15695 masm.cmpPtrSet(Assembler::Equal, input, ImmWord(0), output);
15696}
15697
15698void CodeGenerator::visitNotI64(LNotI64* lir) {
15699 Register64 input = ToRegister64(lir->inputI64());
15700 Register output = ToRegister(lir->output());
15701
15702 masm.cmp64Set(Assembler::Equal, input, Imm64(0), output);
15703}
15704
15705void CodeGenerator::visitNotBI(LNotBI* lir) {
15706 Register input = ToRegister(lir->input());
15707 Register output = ToRegister(lir->output());
15708
15709 masm.cmp32Set(Assembler::Equal, Address(input, BigInt::offsetOfLength()),
15710 Imm32(0), output);
15711}
15712
15713void CodeGenerator::visitNotO(LNotO* lir) {
15714 Register objreg = ToRegister(lir->input());
15715 Register output = ToRegister(lir->output());
15716
15717 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
15718 if (intact) {
15719 // Bug 1874905: It would be fantastic if this could be optimized out.
15720 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
15721 masm.move32(Imm32(0), output);
15722 } else {
15723 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
15724 addOutOfLineCode(ool, lir->mir());
15725
15726 Label* ifEmulatesUndefined = ool->label1();
15727 Label* ifDoesntEmulateUndefined = ool->label2();
15728
15729 branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined,
15730 ifDoesntEmulateUndefined, output, ool);
15731 // fall through
15732
15733 Label join;
15734
15735 masm.move32(Imm32(0), output);
15736 masm.jump(&join);
15737
15738 masm.bind(ifEmulatesUndefined);
15739 masm.move32(Imm32(1), output);
15740
15741 masm.bind(&join);
15742 }
15743}
15744
15745void CodeGenerator::visitNotV(LNotV* lir) {
15746 Label defaultTruthy, defaultFalsy;
15747 Label* ifTruthy = &defaultTruthy;
15748 Label* ifFalsy = &defaultFalsy;
15749 OutOfLineTestObjectWithLabels* ool = nullptr;
15750 if (!hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted()) {
15751 ool = new (alloc()) OutOfLineTestObjectWithLabels();
15752 addOutOfLineCode(ool, lir->mir());
15753 ifTruthy = ool->label1();
15754 ifFalsy = ool->label2();
15755 }
15756
15757 ValueOperand input = ToValue(lir->input());
15758 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
15759 FloatRegister floatTemp = ToFloatRegister(lir->temp0());
15760 Register output = ToRegister(lir->output());
15761 const TypeDataList& observedTypes = lir->mir()->observedTypes();
15762
15763 testValueTruthy(input, tempToUnbox, output, floatTemp, observedTypes,
15764 ifTruthy, ifFalsy, ool);
15765
15766 Label join;
15767
15768 // Note that the testValueTruthy call above may choose to fall through
15769 // to ifTruthy instead of branching there.
15770 masm.bind(ifTruthy);
15771 masm.move32(Imm32(0), output);
15772 masm.jump(&join);
15773
15774 masm.bind(ifFalsy);
15775 masm.move32(Imm32(1), output);
15776
15777 // both branches meet here.
15778 masm.bind(&join);
15779}
15780
15781void CodeGenerator::visitBoundsCheck(LBoundsCheck* lir) {
15782 const LAllocation* index = lir->index();
15783 const LAllocation* length = lir->length();
15784 LSnapshot* snapshot = lir->snapshot();
15785
15786 MIRType type = lir->mir()->type();
15787
15788 auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) {
15789 if (type == MIRType::Int32) {
15790 bailoutCmp32(cond, lhs, rhs, snapshot);
15791 } else {
15792 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", 15792); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 15792); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15793 bailoutCmpPtr(cond, lhs, rhs, snapshot);
15794 }
15795 };
15796
15797 auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs,
15798 int32_t rhs) {
15799 if (type == MIRType::Int32) {
15800 bailoutCmp32(cond, lhs, Imm32(rhs), snapshot);
15801 } else {
15802 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", 15802); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 15802); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15803 bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot);
15804 }
15805 };
15806
15807 if (index->isConstant()) {
15808 // Use uint32 so that the comparison is unsigned.
15809 uint32_t idx = ToInt32(index);
15810 if (length->isConstant()) {
15811 uint32_t len = ToInt32(lir->length());
15812 if (idx < len) {
15813 return;
15814 }
15815 bailout(snapshot);
15816 return;
15817 }
15818
15819 if (length->isGeneralReg()) {
15820 bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), idx);
15821 } else {
15822 bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), idx);
15823 }
15824 return;
15825 }
15826
15827 Register indexReg = ToRegister(index);
15828 if (length->isConstant()) {
15829 bailoutCmpConstant(Assembler::AboveOrEqual, indexReg, ToInt32(length));
15830 } else if (length->isGeneralReg()) {
15831 bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), indexReg);
15832 } else {
15833 bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), indexReg);
15834 }
15835}
15836
15837void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) {
15838 int32_t min = lir->mir()->minimum();
15839 int32_t max = lir->mir()->maximum();
15840 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", 15840); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "max >= min" ")"); do { MOZ_CrashSequence
(__null, 15840); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15841
15842 LSnapshot* snapshot = lir->snapshot();
15843 MIRType type = lir->mir()->type();
15844
15845 const LAllocation* length = lir->length();
15846 Register temp = ToRegister(lir->temp0());
15847
15848 auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) {
15849 if (type == MIRType::Int32) {
15850 bailoutCmp32(cond, lhs, rhs, snapshot);
15851 } else {
15852 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", 15852); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 15852); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15853 bailoutCmpPtr(cond, lhs, rhs, snapshot);
15854 }
15855 };
15856
15857 auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs,
15858 int32_t rhs) {
15859 if (type == MIRType::Int32) {
15860 bailoutCmp32(cond, lhs, Imm32(rhs), snapshot);
15861 } else {
15862 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", 15862); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 15862); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
15863 bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot);
15864 }
15865 };
15866
15867 if (lir->index()->isConstant()) {
15868 int32_t nmin, nmax;
15869 int32_t index = ToInt32(lir->index());
15870 if (mozilla::SafeAdd(index, min, &nmin) &&
15871 mozilla::SafeAdd(index, max, &nmax) && nmin >= 0) {
15872 if (length->isGeneralReg()) {
15873 bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax);
15874 } else {
15875 bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), nmax);
15876 }
15877 return;
15878 }
15879 masm.mov(ImmWord(index), temp);
15880 } else {
15881 masm.mov(ToRegister(lir->index()), temp);
15882 }
15883
15884 // If the minimum and maximum differ then do an underflow check first.
15885 // If the two are the same then doing an unsigned comparison on the
15886 // length will also catch a negative index.
15887 if (min != max) {
15888 if (min != 0) {
15889 Label bail;
15890 if (type == MIRType::Int32) {
15891 masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail);
15892 } else {
15893 masm.branchAddPtr(Assembler::Overflow, Imm32(min), temp, &bail);
15894 }
15895 bailoutFrom(&bail, snapshot);
15896 }
15897
15898 bailoutCmpConstant(Assembler::LessThan, temp, 0);
15899
15900 if (min != 0) {
15901 int32_t diff;
15902 if (mozilla::SafeSub(max, min, &diff)) {
15903 max = diff;
15904 } else {
15905 if (type == MIRType::Int32) {
15906 masm.sub32(Imm32(min), temp);
15907 } else {
15908 masm.subPtr(Imm32(min), temp);
15909 }
15910 }
15911 }
15912 }
15913
15914 // Compute the maximum possible index. No overflow check is needed when
15915 // max > 0. We can only wraparound to a negative number, which will test as
15916 // larger than all nonnegative numbers in the unsigned comparison, and the
15917 // length is required to be nonnegative (else testing a negative length
15918 // would succeed on any nonnegative index).
15919 if (max != 0) {
15920 if (max < 0) {
15921 Label bail;
15922 if (type == MIRType::Int32) {
15923 masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail);
15924 } else {
15925 masm.branchAddPtr(Assembler::Overflow, Imm32(max), temp, &bail);
15926 }
15927 bailoutFrom(&bail, snapshot);
15928 } else {
15929 if (type == MIRType::Int32) {
15930 masm.add32(Imm32(max), temp);
15931 } else {
15932 masm.addPtr(Imm32(max), temp);
15933 }
15934 }
15935 }
15936
15937 if (length->isGeneralReg()) {
15938 bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), temp);
15939 } else {
15940 bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), temp);
15941 }
15942}
15943
15944void CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir) {
15945 int32_t min = lir->mir()->minimum();
15946 bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min),
15947 lir->snapshot());
15948}
15949
15950void CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir) {
15951 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"
, 15951); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitOptions.spectreIndexMasking"
")"); do { MOZ_CrashSequence(__null, 15951); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15952
15953 const LAllocation* length = lir->length();
15954 Register index = ToRegister(lir->index());
15955 Register output = ToRegister(lir->output());
15956
15957 if (lir->mir()->type() == MIRType::Int32) {
15958 if (length->isGeneralReg()) {
15959 masm.spectreMaskIndex32(index, ToRegister(length), output);
15960 } else {
15961 masm.spectreMaskIndex32(index, ToAddress(length), output);
15962 }
15963 } else {
15964 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", 15964); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::IntPtr"
")"); do { MOZ_CrashSequence(__null, 15964); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15965 if (length->isGeneralReg()) {
15966 masm.spectreMaskIndexPtr(index, ToRegister(length), output);
15967 } else {
15968 masm.spectreMaskIndexPtr(index, ToAddress(length), output);
15969 }
15970 }
15971}
15972
15973CodeGenerator::AddressOrBaseObjectElementIndex
15974CodeGenerator::ToAddressOrBaseObjectElementIndex(Register elements,
15975 const LAllocation* index) {
15976 if (index->isConstant()) {
15977 NativeObject::elementsSizeMustNotOverflow();
15978 return AddressOrBaseObjectElementIndex(
15979 Address(elements, ToInt32(index) * sizeof(JS::Value)));
15980 }
15981 return AddressOrBaseObjectElementIndex(
15982 BaseObjectElementIndex(elements, ToRegister(index)));
15983}
15984
15985void CodeGenerator::emitStoreHoleCheck(Address dest, LSnapshot* snapshot) {
15986 Label bail;
15987 masm.branchTestMagic(Assembler::Equal, dest, JS_ELEMENTS_HOLE, &bail);
15988 bailoutFrom(&bail, snapshot);
15989}
15990
15991void CodeGenerator::emitStoreHoleCheck(BaseObjectElementIndex dest,
15992 LSnapshot* snapshot) {
15993 Label bail;
15994 masm.branchTestMagic(Assembler::Equal, dest, JS_ELEMENTS_HOLE, &bail);
15995 bailoutFrom(&bail, snapshot);
15996}
15997
15998void CodeGenerator::visitStoreElementT(LStoreElementT* store) {
15999 Register elements = ToRegister(store->elements());
16000 const LAllocation* index = store->index();
16001
16002 MIRType valueType = store->mir()->value()->type();
16003 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"
, 16003); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole"
")"); do { MOZ_CrashSequence(__null, 16003); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16004
16005 ConstantOrRegister value = ToConstantOrRegister(store->value(), valueType);
16006
16007 auto dest = ToAddressOrBaseObjectElementIndex(elements, index);
16008
16009 dest.match([&](const auto& dest) {
16010 if (store->mir()->needsPreBarrier()) {
16011 emitPreBarrier(dest);
16012 }
16013
16014 if (store->mir()->needsHoleCheck()) {
16015 emitStoreHoleCheck(dest, store->snapshot());
16016 }
16017
16018 masm.storeUnboxedValue(value, valueType, dest);
16019 });
16020}
16021
16022void CodeGenerator::visitStoreElementV(LStoreElementV* lir) {
16023 ValueOperand value = ToValue(lir->value());
16024 Register elements = ToRegister(lir->elements());
16025 const LAllocation* index = lir->index();
16026
16027 auto dest = ToAddressOrBaseObjectElementIndex(elements, index);
16028
16029 dest.match([&](const auto& dest) {
16030 if (lir->mir()->needsPreBarrier()) {
16031 emitPreBarrier(dest);
16032 }
16033
16034 if (lir->mir()->needsHoleCheck()) {
16035 emitStoreHoleCheck(dest, lir->snapshot());
16036 }
16037
16038 masm.storeValue(value, dest);
16039 });
16040}
16041
16042void CodeGenerator::visitStoreHoleValueElement(LStoreHoleValueElement* lir) {
16043 Register elements = ToRegister(lir->elements());
16044 Register index = ToRegister(lir->index());
16045
16046 Address elementsFlags(elements, ObjectElements::offsetOfFlags());
16047 masm.or32(Imm32(ObjectElements::NON_PACKED), elementsFlags);
16048
16049 BaseObjectElementIndex element(elements, index);
16050 masm.storeValue(MagicValue(JS_ELEMENTS_HOLE), element);
16051}
16052
16053void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) {
16054 Register obj = ToRegister(lir->object());
16055 Register elements = ToRegister(lir->elements());
16056 Register index = ToRegister(lir->index());
16057 Register temp = ToRegister(lir->temp0());
16058
16059 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
16060 Label bail;
16061 masm.prepareOOBStoreElement(obj, index, elements, temp, &bail,
16062 liveVolatileRegs(lir));
16063 bailoutFrom(&bail, lir->snapshot());
16064
16065 // Jump to the inline path where we will store the value.
16066 // We rejoin after the prebarrier, because the memory is uninitialized.
16067 masm.jump(ool.rejoin());
16068 });
16069 addOutOfLineCode(ool, lir->mir());
16070
16071 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
16072 masm.spectreBoundsCheck32(index, initLength, temp, ool->entry());
16073
16074 emitPreBarrier(BaseObjectElementIndex(elements, index));
16075
16076 masm.bind(ool->rejoin());
16077
16078 MIRType valueType = lir->mir()->value()->type();
16079 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"
, 16079); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole"
")"); do { MOZ_CrashSequence(__null, 16079); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16080
16081 ConstantOrRegister val = ToConstantOrRegister(lir->value(), valueType);
16082 masm.storeUnboxedValue(val, valueType,
16083 BaseObjectElementIndex(elements, index));
16084
16085 if (ValueNeedsPostBarrier(lir->mir()->value())) {
16086 LiveRegisterSet regs = liveVolatileRegs(lir);
16087 emitElementPostWriteBarrier(lir->mir(), regs, obj, index, temp, val);
16088 }
16089}
16090
16091void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) {
16092 Register obj = ToRegister(lir->object());
16093 Register elements = ToRegister(lir->elements());
16094 Register index = ToRegister(lir->index());
16095 ValueOperand value = ToValue(lir->value());
16096 Register temp = ToRegister(lir->temp0());
16097
16098 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
16099 Label bail;
16100 masm.prepareOOBStoreElement(obj, index, elements, temp, &bail,
16101 liveVolatileRegs(lir));
16102 bailoutFrom(&bail, lir->snapshot());
16103
16104 // Jump to the inline path where we will store the value.
16105 // We rejoin after the prebarrier, because the memory is uninitialized.
16106 masm.jump(ool.rejoin());
16107 });
16108 addOutOfLineCode(ool, lir->mir());
16109
16110 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
16111 masm.spectreBoundsCheck32(index, initLength, temp, ool->entry());
16112
16113 emitPreBarrier(BaseObjectElementIndex(elements, index));
16114
16115 masm.bind(ool->rejoin());
16116 masm.storeValue(value, BaseObjectElementIndex(elements, index));
16117
16118 if (ValueNeedsPostBarrier(lir->mir()->value())) {
16119 LiveRegisterSet regs = liveVolatileRegs(lir);
16120 emitElementPostWriteBarrier(lir->mir(), regs, obj, index, temp,
16121 ConstantOrRegister(value));
16122 }
16123}
16124
16125void CodeGenerator::visitArrayPopShift(LArrayPopShift* lir) {
16126 Register obj = ToRegister(lir->object());
16127 Register temp1 = ToRegister(lir->temp0());
16128 Register temp2 = ToRegister(lir->temp1());
16129 ValueOperand out = ToOutValue(lir);
16130
16131 Label bail;
16132 if (lir->mir()->mode() == MArrayPopShift::Pop) {
16133 masm.packedArrayPop(obj, out, temp1, temp2, &bail);
16134 } else {
16135 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", 16135); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->mode() == MArrayPopShift::Shift"
")"); do { MOZ_CrashSequence(__null, 16135); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16136 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
16137 masm.packedArrayShift(obj, out, temp1, temp2, volatileRegs, &bail);
16138 }
16139 bailoutFrom(&bail, lir->snapshot());
16140}
16141
16142void CodeGenerator::visitArrayPush(LArrayPush* lir) {
16143 Register obj = ToRegister(lir->object());
16144 Register elementsTemp = ToRegister(lir->temp0());
16145 Register length = ToRegister(lir->output());
16146 ValueOperand value = ToValue(lir->value());
16147 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
16148
16149 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
16150 Register temp = ToRegister(lir->temp0());
16151
16152 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
16153 liveRegs.takeUnchecked(temp);
16154 liveRegs.addUnchecked(ToRegister(lir->output()));
16155 liveRegs.addUnchecked(ToValue(lir->value()));
16156
16157 masm.PushRegsInMask(liveRegs);
16158
16159 masm.setupAlignedABICall();
16160 masm.loadJSContext(temp);
16161 masm.passABIArg(temp);
16162 masm.passABIArg(obj);
16163
16164 using Fn = bool (*)(JSContext*, NativeObject* obj);
16165 masm.callWithABI<Fn, NativeObject::addDenseElementPure>();
16166 masm.storeCallPointerResult(temp);
16167
16168 masm.PopRegsInMask(liveRegs);
16169 bailoutIfFalseBool(temp, lir->snapshot());
16170
16171 // Load the reallocated elements pointer.
16172 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp);
16173
16174 masm.jump(ool.rejoin());
16175 });
16176 addOutOfLineCode(ool, lir->mir());
16177
16178 // Load obj->elements in elementsTemp.
16179 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
16180
16181 Address initLengthAddr(elementsTemp,
16182 ObjectElements::offsetOfInitializedLength());
16183 Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength());
16184 Address capacityAddr(elementsTemp, ObjectElements::offsetOfCapacity());
16185
16186 // Bail out if length != initLength.
16187 masm.load32(lengthAddr, length);
16188 bailoutCmp32(Assembler::NotEqual, initLengthAddr, length, lir->snapshot());
16189
16190 // If length < capacity, we can add a dense element inline. If not, we
16191 // need to allocate more elements.
16192 masm.spectreBoundsCheck32(length, capacityAddr, spectreTemp, ool->entry());
16193 masm.bind(ool->rejoin());
16194
16195 // Store the value.
16196 masm.storeValue(value, BaseObjectElementIndex(elementsTemp, length));
16197
16198 // Update length and initialized length.
16199 masm.add32(Imm32(1), length);
16200 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
16201 masm.store32(length, Address(elementsTemp,
16202 ObjectElements::offsetOfInitializedLength()));
16203
16204 if (ValueNeedsPostBarrier(lir->mir()->value())) {
16205 LiveRegisterSet regs = liveVolatileRegs(lir);
16206 regs.addUnchecked(length);
16207 emitElementPostWriteBarrier(lir->mir(), regs, obj, length, elementsTemp,
16208 ConstantOrRegister(value),
16209 /* indexDiff = */ -1);
16210 }
16211}
16212
16213void CodeGenerator::visitArraySlice(LArraySlice* lir) {
16214 Register object = ToRegister(lir->object());
16215 Register begin = ToRegister(lir->begin());
16216 Register end = ToRegister(lir->end());
16217 Register temp0 = ToRegister(lir->temp0());
16218 Register temp1 = ToRegister(lir->temp1());
16219
16220 Label call, fail;
16221
16222 Label bail;
16223 masm.branchArrayIsNotPacked(object, temp0, temp1, &bail);
16224 bailoutFrom(&bail, lir->snapshot());
16225
16226 // Try to allocate an object.
16227 TemplateObject templateObject(lir->mir()->templateObj());
16228 masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(),
16229 &fail);
16230
16231 masm.jump(&call);
16232 {
16233 masm.bind(&fail);
16234 masm.movePtr(ImmPtr(nullptr), temp0);
16235 }
16236 masm.bind(&call);
16237
16238 pushArg(temp0);
16239 pushArg(end);
16240 pushArg(begin);
16241 pushArg(object);
16242
16243 using Fn =
16244 JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
16245 callVM<Fn, ArraySliceDense>(lir);
16246}
16247
16248void CodeGenerator::visitArgumentsSlice(LArgumentsSlice* lir) {
16249 Register object = ToRegister(lir->object());
16250 Register begin = ToRegister(lir->begin());
16251 Register end = ToRegister(lir->end());
16252 Register temp0 = ToRegister(lir->temp0());
16253 Register temp1 = ToRegister(lir->temp1());
16254
16255 Label call, fail;
16256
16257 // Try to allocate an object.
16258 TemplateObject templateObject(lir->mir()->templateObj());
16259 masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(),
16260 &fail);
16261
16262 masm.jump(&call);
16263 {
16264 masm.bind(&fail);
16265 masm.movePtr(ImmPtr(nullptr), temp0);
16266 }
16267 masm.bind(&call);
16268
16269 pushArg(temp0);
16270 pushArg(end);
16271 pushArg(begin);
16272 pushArg(object);
16273
16274 using Fn =
16275 JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
16276 callVM<Fn, ArgumentsSliceDense>(lir);
16277}
16278
16279#ifdef DEBUG1
16280void CodeGenerator::emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin,
16281 const RegisterOrInt32& count,
16282 Register numActualArgs) {
16283 // |begin| must be positive or zero.
16284 if (begin.is<Register>()) {
16285 Label beginOk;
16286 masm.branch32(Assembler::GreaterThanOrEqual, begin.as<Register>(), Imm32(0),
16287 &beginOk);
16288 masm.assumeUnreachable("begin < 0");
16289 masm.bind(&beginOk);
16290 } else {
16291 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"
, 16291); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() >= 0"
")"); do { MOZ_CrashSequence(__null, 16291); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16292 }
16293
16294 // |count| must be positive or zero.
16295 if (count.is<Register>()) {
16296 Label countOk;
16297 masm.branch32(Assembler::GreaterThanOrEqual, count.as<Register>(), Imm32(0),
16298 &countOk);
16299 masm.assumeUnreachable("count < 0");
16300 masm.bind(&countOk);
16301 } else {
16302 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"
, 16302); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count.as<int32_t>() >= 0"
")"); do { MOZ_CrashSequence(__null, 16302); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16303 }
16304
16305 // |begin| must be less-or-equal to |numActualArgs|.
16306 Label argsBeginOk;
16307 if (begin.is<Register>()) {
16308 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(),
16309 &argsBeginOk);
16310 } else {
16311 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
16312 Imm32(begin.as<int32_t>()), &argsBeginOk);
16313 }
16314 masm.assumeUnreachable("begin <= numActualArgs");
16315 masm.bind(&argsBeginOk);
16316
16317 // |count| must be less-or-equal to |numActualArgs|.
16318 Label argsCountOk;
16319 if (count.is<Register>()) {
16320 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, count.as<Register>(),
16321 &argsCountOk);
16322 } else {
16323 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
16324 Imm32(count.as<int32_t>()), &argsCountOk);
16325 }
16326 masm.assumeUnreachable("count <= numActualArgs");
16327 masm.bind(&argsCountOk);
16328
16329 // |begin| and |count| must be preserved, but |numActualArgs| can be changed.
16330 //
16331 // Pre-condition: |count| <= |numActualArgs|
16332 // Condition to test: |begin + count| <= |numActualArgs|
16333 // Transform to: |begin| <= |numActualArgs - count|
16334 if (count.is<Register>()) {
16335 masm.subPtr(count.as<Register>(), numActualArgs);
16336 } else {
16337 masm.subPtr(Imm32(count.as<int32_t>()), numActualArgs);
16338 }
16339
16340 // |begin + count| must be less-or-equal to |numActualArgs|.
16341 Label argsBeginCountOk;
16342 if (begin.is<Register>()) {
16343 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(),
16344 &argsBeginCountOk);
16345 } else {
16346 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
16347 Imm32(begin.as<int32_t>()), &argsBeginCountOk);
16348 }
16349 masm.assumeUnreachable("begin + count <= numActualArgs");
16350 masm.bind(&argsBeginCountOk);
16351}
16352#endif
16353
16354template <class ArgumentsSlice>
16355void CodeGenerator::emitNewArray(ArgumentsSlice* lir,
16356 const RegisterOrInt32& count, Register output,
16357 Register temp) {
16358 using Fn = ArrayObject* (*)(JSContext*, int32_t);
16359 auto* ool = count.match(
16360 [&](Register count) {
16361 return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>(
16362 lir, ArgList(count), StoreRegisterTo(output));
16363 },
16364 [&](int32_t count) {
16365 return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>(
16366 lir, ArgList(Imm32(count)), StoreRegisterTo(output));
16367 });
16368
16369 TemplateObject templateObject(lir->mir()->templateObj());
16370 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"
, 16370); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject.isArrayObject()"
")"); do { MOZ_CrashSequence(__null, 16370); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16371
16372 auto templateNativeObj = templateObject.asTemplateNativeObject();
16373 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", 16373); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "templateNativeObj.getArrayLength() == 0" ")"
); do { MOZ_CrashSequence(__null, 16373); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
16374 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", 16374); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "templateNativeObj.getDenseInitializedLength() == 0"
")"); do { MOZ_CrashSequence(__null, 16374); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16375 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", 16375); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!templateNativeObj.hasDynamicElements()" ")"
); do { MOZ_CrashSequence(__null, 16375); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
16376
16377 // Check array capacity. Call into the VM if the template object's capacity
16378 // is too small.
16379 bool tryAllocate = count.match(
16380 [&](Register count) {
16381 masm.branch32(Assembler::Above, count,
16382 Imm32(templateNativeObj.getDenseCapacity()),
16383 ool->entry());
16384 return true;
16385 },
16386 [&](int32_t count) {
16387 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", 16387); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "count >= 0" ")"); do { MOZ_CrashSequence
(__null, 16387); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
16388 if (uint32_t(count) > templateNativeObj.getDenseCapacity()) {
16389 masm.jump(ool->entry());
16390 return false;
16391 }
16392 return true;
16393 });
16394
16395 if (tryAllocate) {
16396 // Try to allocate an object.
16397 masm.createGCObject(output, temp, templateObject, lir->mir()->initialHeap(),
16398 ool->entry());
16399
16400 auto setInitializedLengthAndLength = [&](auto count) {
16401 const int elementsOffset = NativeObject::offsetOfFixedElements();
16402
16403 // Update initialized length.
16404 Address initLength(
16405 output, elementsOffset + ObjectElements::offsetOfInitializedLength());
16406 masm.store32(count, initLength);
16407
16408 // Update length.
16409 Address length(output, elementsOffset + ObjectElements::offsetOfLength());
16410 masm.store32(count, length);
16411 };
16412
16413 // The array object was successfully created. Set the length and initialized
16414 // length and then proceed to fill the elements.
16415 count.match([&](Register count) { setInitializedLengthAndLength(count); },
16416 [&](int32_t count) {
16417 if (count > 0) {
16418 setInitializedLengthAndLength(Imm32(count));
16419 }
16420 });
16421 }
16422
16423 masm.bind(ool->rejoin());
16424}
16425
16426void CodeGenerator::visitFrameArgumentsSlice(LFrameArgumentsSlice* lir) {
16427 Register begin = ToRegister(lir->begin());
16428 Register count = ToRegister(lir->count());
16429 Register temp = ToRegister(lir->temp0());
16430 Register output = ToRegister(lir->output());
16431
16432#ifdef DEBUG1
16433 masm.loadNumActualArgs(FramePointer, temp);
16434 emitAssertArgumentsSliceBounds(RegisterOrInt32(begin), RegisterOrInt32(count),
16435 temp);
16436#endif
16437
16438 emitNewArray(lir, RegisterOrInt32(count), output, temp);
16439
16440 Label done;
16441 masm.branch32(Assembler::Equal, count, Imm32(0), &done);
16442 {
16443 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
16444 allRegs.take(begin);
16445 allRegs.take(count);
16446 allRegs.take(temp);
16447 allRegs.take(output);
16448
16449 ValueOperand value = allRegs.takeAnyValue();
16450
16451 LiveRegisterSet liveRegs;
16452 liveRegs.add(output);
16453 liveRegs.add(begin);
16454 liveRegs.add(value);
16455
16456 masm.PushRegsInMask(liveRegs);
16457
16458 // Initialize all elements.
16459
16460 Register elements = output;
16461 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
16462
16463 Register argIndex = begin;
16464
16465 Register index = temp;
16466 masm.move32(Imm32(0), index);
16467
16468 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
16469 BaseValueIndex argPtr(FramePointer, argIndex, argvOffset);
16470
16471 Label loop;
16472 masm.bind(&loop);
16473
16474 masm.loadValue(argPtr, value);
16475
16476 // We don't need a pre-barrier, because the element at |index| is guaranteed
16477 // to be a non-GC thing (either uninitialized memory or the magic hole
16478 // value).
16479 masm.storeValue(value, BaseObjectElementIndex(elements, index));
16480
16481 masm.add32(Imm32(1), index);
16482 masm.add32(Imm32(1), argIndex);
16483
16484 masm.branch32(Assembler::LessThan, index, count, &loop);
16485
16486 masm.PopRegsInMask(liveRegs);
16487
16488 // Emit a post-write barrier if |output| is tenured.
16489 //
16490 // We expect that |output| is nursery allocated, so it isn't worth the
16491 // trouble to check if no frame argument is a nursery thing, which would
16492 // allow to omit the post-write barrier.
16493 masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done);
16494
16495 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
16496 volatileRegs.takeUnchecked(temp);
16497 if (output.volatile_()) {
16498 volatileRegs.addUnchecked(output);
16499 }
16500
16501 masm.PushRegsInMask(volatileRegs);
16502 emitPostWriteBarrier(output);
16503 masm.PopRegsInMask(volatileRegs);
16504 }
16505 masm.bind(&done);
16506}
16507
16508CodeGenerator::RegisterOrInt32 CodeGenerator::ToRegisterOrInt32(
16509 const LAllocation* allocation) {
16510 if (allocation->isConstant()) {
16511 return RegisterOrInt32(allocation->toConstant()->toInt32());
16512 }
16513 return RegisterOrInt32(ToRegister(allocation));
16514}
16515
16516void CodeGenerator::visitInlineArgumentsSlice(LInlineArgumentsSlice* lir) {
16517 RegisterOrInt32 begin = ToRegisterOrInt32(lir->begin());
16518 RegisterOrInt32 count = ToRegisterOrInt32(lir->count());
16519 Register temp = ToRegister(lir->temp());
16520 Register output = ToRegister(lir->output());
16521
16522 uint32_t numActuals = lir->mir()->numActuals();
16523
16524#ifdef DEBUG1
16525 masm.move32(Imm32(numActuals), temp);
16526
16527 emitAssertArgumentsSliceBounds(begin, count, temp);
16528#endif
16529
16530 emitNewArray(lir, count, output, temp);
16531
16532 // We're done if there are no actual arguments.
16533 if (numActuals == 0) {
16534 return;
16535 }
16536
16537 // Check if any arguments have to be copied.
16538 Label done;
16539 if (count.is<Register>()) {
16540 masm.branch32(Assembler::Equal, count.as<Register>(), Imm32(0), &done);
16541 } else if (count.as<int32_t>() == 0) {
16542 return;
16543 }
16544
16545 auto getArg = [&](uint32_t i) {
16546 return toConstantOrRegister(lir, LInlineArgumentsSlice::ArgIndex(i),
16547 lir->mir()->getArg(i)->type());
16548 };
16549
16550 auto storeArg = [&](uint32_t i, auto dest) {
16551 // We don't need a pre-barrier because the element at |index| is guaranteed
16552 // to be a non-GC thing (either uninitialized memory or the magic hole
16553 // value).
16554 masm.storeConstantOrRegister(getArg(i), dest);
16555 };
16556
16557 // Initialize all elements.
16558 if (numActuals == 1) {
16559 // There's exactly one argument. We've checked that |count| is non-zero,
16560 // which implies that |begin| must be zero.
16561 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", 16561); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "begin.as<int32_t>() == 0" ")"); do {
MOZ_CrashSequence(__null, 16561); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
16562
16563 Register elements = temp;
16564 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
16565
16566 storeArg(0, Address(elements, 0));
16567 } else if (begin.is<Register>()) {
16568 // There is more than one argument and |begin| isn't a compile-time
16569 // constant. Iterate through 0..numActuals to search for |begin| and then
16570 // start copying |count| arguments from that index.
16571
16572 LiveGeneralRegisterSet liveRegs;
16573 liveRegs.add(output);
16574 liveRegs.add(begin.as<Register>());
16575
16576 masm.PushRegsInMask(liveRegs);
16577
16578 Register elements = output;
16579 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
16580
16581 Register argIndex = begin.as<Register>();
16582
16583 Register index = temp;
16584 masm.move32(Imm32(0), index);
16585
16586 Label doneLoop;
16587 for (uint32_t i = 0; i < numActuals; ++i) {
16588 Label next;
16589 masm.branch32(Assembler::NotEqual, argIndex, Imm32(i), &next);
16590
16591 storeArg(i, BaseObjectElementIndex(elements, index));
16592
16593 masm.add32(Imm32(1), index);
16594 masm.add32(Imm32(1), argIndex);
16595
16596 if (count.is<Register>()) {
16597 masm.branch32(Assembler::GreaterThanOrEqual, index,
16598 count.as<Register>(), &doneLoop);
16599 } else {
16600 masm.branch32(Assembler::GreaterThanOrEqual, index,
16601 Imm32(count.as<int32_t>()), &doneLoop);
16602 }
16603
16604 masm.bind(&next);
16605 }
16606 masm.bind(&doneLoop);
16607
16608 masm.PopRegsInMask(liveRegs);
16609 } else {
16610 // There is more than one argument and |begin| is a compile-time constant.
16611
16612 Register elements = temp;
16613 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
16614
16615 int32_t argIndex = begin.as<int32_t>();
16616
16617 int32_t index = 0;
16618
16619 Label doneLoop;
16620 for (uint32_t i = argIndex; i < numActuals; ++i) {
16621 storeArg(i, Address(elements, index * sizeof(Value)));
16622
16623 index += 1;
16624
16625 if (count.is<Register>()) {
16626 masm.branch32(Assembler::LessThanOrEqual, count.as<Register>(),
16627 Imm32(index), &doneLoop);
16628 } else {
16629 if (index >= count.as<int32_t>()) {
16630 break;
16631 }
16632 }
16633 }
16634 masm.bind(&doneLoop);
16635 }
16636
16637 // Determine if we have to emit post-write barrier.
16638 //
16639 // If either |begin| or |count| is a constant, use their value directly.
16640 // Otherwise assume we copy all inline arguments from 0..numActuals.
16641 bool postWriteBarrier = false;
16642 uint32_t actualBegin = begin.match([](Register) { return 0; },
16643 [](int32_t value) { return value; });
16644 uint32_t actualCount =
16645 count.match([=](Register) { return numActuals; },
16646 [](int32_t value) -> uint32_t { return value; });
16647 for (uint32_t i = 0; i < actualCount; ++i) {
16648 ConstantOrRegister arg = getArg(actualBegin + i);
16649 if (arg.constant()) {
16650 Value v = arg.value();
16651 if (v.isGCThing() && IsInsideNursery(v.toGCThing())) {
16652 postWriteBarrier = true;
16653 }
16654 } else {
16655 MIRType type = arg.reg().type();
16656 if (type == MIRType::Value || NeedsPostBarrier(type)) {
16657 postWriteBarrier = true;
16658 }
16659 }
16660 }
16661
16662 // Emit a post-write barrier if |output| is tenured and we couldn't
16663 // determine at compile-time that no barrier is needed.
16664 if (postWriteBarrier) {
16665 masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done);
16666
16667 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
16668 volatileRegs.takeUnchecked(temp);
16669 if (output.volatile_()) {
16670 volatileRegs.addUnchecked(output);
16671 }
16672
16673 masm.PushRegsInMask(volatileRegs);
16674 emitPostWriteBarrier(output);
16675 masm.PopRegsInMask(volatileRegs);
16676 }
16677
16678 masm.bind(&done);
16679}
16680
16681void CodeGenerator::visitNormalizeSliceTerm(LNormalizeSliceTerm* lir) {
16682 Register value = ToRegister(lir->value());
16683 Register length = ToRegister(lir->length());
16684 Register output = ToRegister(lir->output());
16685
16686 masm.move32(value, output);
16687
16688 Label positive;
16689 masm.branch32(Assembler::GreaterThanOrEqual, value, Imm32(0), &positive);
16690
16691 Label done;
16692 masm.add32(length, output);
16693 masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(0), &done);
16694 masm.move32(Imm32(0), output);
16695 masm.jump(&done);
16696
16697 masm.bind(&positive);
16698 masm.cmp32Move32(Assembler::LessThan, length, value, length, output);
16699
16700 masm.bind(&done);
16701}
16702
16703void CodeGenerator::visitArrayJoin(LArrayJoin* lir) {
16704 Label skipCall;
16705
16706 Register output = ToRegister(lir->output());
16707 Register sep = ToRegister(lir->separator());
16708 Register array = ToRegister(lir->array());
16709 Register temp = ToRegister(lir->temp0());
16710
16711 // Fast path for simple length <= 1 cases.
16712 {
16713 masm.loadPtr(Address(array, NativeObject::offsetOfElements()), temp);
16714 Address length(temp, ObjectElements::offsetOfLength());
16715 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
16716
16717 // Check for length == 0
16718 Label notEmpty;
16719 masm.branch32(Assembler::NotEqual, length, Imm32(0), &notEmpty);
16720 const JSAtomState& names = gen->runtime->names();
16721 masm.movePtr(ImmGCPtr(names.empty_), output);
16722 masm.jump(&skipCall);
16723
16724 masm.bind(&notEmpty);
16725 Label notSingleString;
16726 // Check for length == 1, initializedLength >= 1, arr[0].isString()
16727 masm.branch32(Assembler::NotEqual, length, Imm32(1), &notSingleString);
16728 masm.branch32(Assembler::LessThan, initLength, Imm32(1), &notSingleString);
16729
16730 Address elem0(temp, 0);
16731 masm.branchTestString(Assembler::NotEqual, elem0, &notSingleString);
16732
16733 // At this point, 'output' can be used as a scratch register, since we're
16734 // guaranteed to succeed.
16735 masm.unboxString(elem0, output);
16736 masm.jump(&skipCall);
16737 masm.bind(&notSingleString);
16738 }
16739
16740 pushArg(sep);
16741 pushArg(array);
16742
16743 using Fn = JSString* (*)(JSContext*, HandleObject, HandleString);
16744 callVM<Fn, jit::ArrayJoin>(lir);
16745 masm.bind(&skipCall);
16746}
16747
16748void CodeGenerator::visitObjectKeys(LObjectKeys* lir) {
16749 Register object = ToRegister(lir->object());
16750
16751 pushArg(object);
16752
16753 using Fn = JSObject* (*)(JSContext*, HandleObject);
16754 callVM<Fn, jit::ObjectKeys>(lir);
16755}
16756
16757void CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir) {
16758 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16759 TypedOrValueRegister val =
16760 toConstantOrRegister(lir, LGetIteratorCache::ValueIndex,
16761 lir->mir()->value()->type())
16762 .reg();
16763 Register output = ToRegister(lir->output());
16764 Register temp0 = ToRegister(lir->temp0());
16765 Register temp1 = ToRegister(lir->temp1());
16766
16767 IonGetIteratorIC ic(liveRegs, val, output, temp0, temp1);
16768 addIC(lir, allocateIC(ic));
16769}
16770
16771void CodeGenerator::visitOptimizeSpreadCallCache(
16772 LOptimizeSpreadCallCache* lir) {
16773 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16774 ValueOperand val = ToValue(lir->value());
16775 ValueOperand output = ToOutValue(lir);
16776 Register temp = ToRegister(lir->temp0());
16777
16778 IonOptimizeSpreadCallIC ic(liveRegs, val, output, temp);
16779 addIC(lir, allocateIC(ic));
16780}
16781
16782void CodeGenerator::visitCloseIterCache(LCloseIterCache* lir) {
16783 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16784 Register iter = ToRegister(lir->iter());
16785 Register temp = ToRegister(lir->temp0());
16786 CompletionKind kind = CompletionKind(lir->mir()->completionKind());
16787
16788 IonCloseIterIC ic(liveRegs, iter, temp, kind);
16789 addIC(lir, allocateIC(ic));
16790}
16791
16792void CodeGenerator::visitOptimizeGetIteratorCache(
16793 LOptimizeGetIteratorCache* lir) {
16794 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16795 ValueOperand val = ToValue(lir->value());
16796 Register output = ToRegister(lir->output());
16797 Register temp = ToRegister(lir->temp0());
16798
16799 IonOptimizeGetIteratorIC ic(liveRegs, val, output, temp);
16800 addIC(lir, allocateIC(ic));
16801}
16802
16803void CodeGenerator::visitIteratorMore(LIteratorMore* lir) {
16804 const Register obj = ToRegister(lir->iterator());
16805 const ValueOperand output = ToOutValue(lir);
16806 const Register temp = ToRegister(lir->temp0());
16807
16808 masm.iteratorMore(obj, output, temp);
16809}
16810
16811void CodeGenerator::visitIteratorLength(LIteratorLength* lir) {
16812 Register obj = ToRegister(lir->iter());
16813 Register output = ToRegister(lir->output());
16814 masm.iteratorLength(obj, output);
16815}
16816
16817void CodeGenerator::visitLoadIteratorElement(LLoadIteratorElement* lir) {
16818 Register obj = ToRegister(lir->iter());
16819 Register output = ToRegister(lir->output());
16820 if (lir->index()->isConstant()) {
16821 int32_t index = ToInt32(lir->index());
16822 masm.iteratorLoadElement(obj, index, output);
16823 } else {
16824 Register index = ToRegister(lir->index());
16825 masm.iteratorLoadElement(obj, index, output);
16826 }
16827}
16828
16829void CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch* lir) {
16830 ValueOperand input = ToValue(lir->input());
16831 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
16832 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
16833
16834 masm.branchTestMagicValue(Assembler::Equal, input, JS_NO_ITER_VALUE, ifTrue);
16835
16836 if (!isNextBlock(lir->ifFalse()->lir())) {
16837 masm.jump(ifFalse);
16838 }
16839}
16840
16841void CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) {
16842 const Register obj = ToRegister(lir->iterator());
16843 const Register temp0 = ToRegister(lir->temp0());
16844 const Register temp1 = ToRegister(lir->temp1());
16845 const Register temp2 = ToRegister(lir->temp2());
16846
16847 masm.iteratorClose(obj, temp0, temp1, temp2);
16848}
16849
16850void CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) {
16851 // read number of actual arguments from the JS frame.
16852 Register argc = ToRegister(lir->output());
16853 masm.loadNumActualArgs(FramePointer, argc);
16854}
16855
16856void CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) {
16857 ValueOperand result = ToOutValue(lir);
16858 const LAllocation* index = lir->index();
16859 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
16860
16861 // This instruction is used to access actual arguments and formal arguments.
16862 // The number of Values on the stack is |max(numFormals, numActuals)|, so we
16863 // assert |index < numFormals || index < numActuals| in debug builds.
16864 DebugOnly<size_t> numFormals = gen->outerInfo().script()->function()->nargs();
16865
16866 if (index->isConstant()) {
16867 int32_t i = index->toConstant()->toInt32();
16868#ifdef DEBUG1
16869 if (uint32_t(i) >= numFormals) {
16870 Label ok;
16871 Register argc = result.scratchReg();
16872 masm.loadNumActualArgs(FramePointer, argc);
16873 masm.branch32(Assembler::Above, argc, Imm32(i), &ok);
16874 masm.assumeUnreachable("Invalid argument index");
16875 masm.bind(&ok);
16876 }
16877#endif
16878 Address argPtr(FramePointer, sizeof(Value) * i + argvOffset);
16879 masm.loadValue(argPtr, result);
16880 } else {
16881 Register i = ToRegister(index);
16882#ifdef DEBUG1
16883 Label ok;
16884 Register argc = result.scratchReg();
16885 masm.branch32(Assembler::Below, i, Imm32(numFormals), &ok);
16886 masm.loadNumActualArgs(FramePointer, argc);
16887 masm.branch32(Assembler::Above, argc, i, &ok);
16888 masm.assumeUnreachable("Invalid argument index");
16889 masm.bind(&ok);
16890#endif
16891 BaseValueIndex argPtr(FramePointer, i, argvOffset);
16892 masm.loadValue(argPtr, result);
16893 }
16894}
16895
16896void CodeGenerator::visitGetFrameArgumentHole(LGetFrameArgumentHole* lir) {
16897 ValueOperand result = ToOutValue(lir);
16898 Register index = ToRegister(lir->index());
16899 Register length = ToRegister(lir->length());
16900 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0());
16901 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
16902
16903 Label outOfBounds, done;
16904 masm.spectreBoundsCheck32(index, length, spectreTemp, &outOfBounds);
16905
16906 BaseValueIndex argPtr(FramePointer, index, argvOffset);
16907 masm.loadValue(argPtr, result);
16908 masm.jump(&done);
16909
16910 masm.bind(&outOfBounds);
16911 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
16912 masm.moveValue(UndefinedValue(), result);
16913
16914 masm.bind(&done);
16915}
16916
16917void CodeGenerator::visitRest(LRest* lir) {
16918 Register numActuals = ToRegister(lir->numActuals());
16919 Register temp0 = ToRegister(lir->temp0());
16920 Register temp1 = ToRegister(lir->temp1());
16921 Register temp2 = ToRegister(lir->temp2());
16922 Register temp3 = ToRegister(lir->temp3());
16923 unsigned numFormals = lir->mir()->numFormals();
16924
16925 // In baseline, DoRestFallback calls into NewArray to allocate the rest array.
16926 // If the length is 0, NewArray guesses a good capacity for it. We don't want
16927 // a smaller capacity in Ion, because that can lead to bailout loops.
16928 constexpr uint32_t arrayCapacity = 6;
16929 static_assert(GuessArrayGCKind(0) == GuessArrayGCKind(arrayCapacity));
16930
16931 uint32_t arrayLength = 0;
16932 gc::AllocKind allocKind = GuessArrayGCKind(arrayCapacity);
16933 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", 16934); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 16934); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16934 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", 16934); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None"
")"); do { MOZ_CrashSequence(__null, 16934); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16935 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", 16935); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFinalizedKind(allocKind)" ")"); do { MOZ_CrashSequence
(__null, 16935); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
16936 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", 16937); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
")"); do { MOZ_CrashSequence(__null, 16937); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16937 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", 16937); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
")"); do { MOZ_CrashSequence(__null, 16937); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16938
16939 Label joinAlloc, failAlloc;
16940 masm.movePtr(ImmGCPtr(lir->mir()->shape()), temp0);
16941 masm.createArrayWithFixedElements(temp2, temp0, temp1, InvalidReg,
16942 arrayLength, arrayCapacity, 0, 0, allocKind,
16943 gc::Heap::Default, &failAlloc);
16944 masm.jump(&joinAlloc);
16945 {
16946 masm.bind(&failAlloc);
16947 masm.movePtr(ImmPtr(nullptr), temp2);
16948 }
16949 masm.bind(&joinAlloc);
16950
16951 // Set temp1 to the address of the first actual argument.
16952 size_t actualsOffset = JitFrameLayout::offsetOfActualArgs();
16953 masm.computeEffectiveAddress(Address(FramePointer, actualsOffset), temp1);
16954
16955 // Compute array length: max(numActuals - numFormals, 0).
16956 Register lengthReg;
16957 if (numFormals) {
16958 lengthReg = temp0;
16959 Label emptyLength, joinLength;
16960 masm.branch32(Assembler::LessThanOrEqual, numActuals, Imm32(numFormals),
16961 &emptyLength);
16962 {
16963 masm.move32(numActuals, lengthReg);
16964 masm.sub32(Imm32(numFormals), lengthReg);
16965
16966 // Skip formal arguments.
16967 masm.addPtr(Imm32(sizeof(Value) * numFormals), temp1);
16968
16969 masm.jump(&joinLength);
16970 }
16971 masm.bind(&emptyLength);
16972 {
16973 masm.move32(Imm32(0), lengthReg);
16974
16975 // Leave temp1 pointed to the start of actuals() when the rest-array
16976 // length is zero. We don't use |actuals() + numFormals| because
16977 // |numFormals| can be any non-negative int32 value when this MRest was
16978 // created from scalar replacement optimizations. And it seems
16979 // questionable to compute a Value* pointer which points to who knows
16980 // where.
16981 }
16982 masm.bind(&joinLength);
16983 } else {
16984 // Use numActuals directly when there are no formals.
16985 lengthReg = numActuals;
16986 }
16987
16988 // Try to initialize the array elements.
16989 //
16990 // Call into C++ if we failed to allocate an array or if there are more than
16991 // |arrayCapacity| elements.
16992 Label vmCall, done;
16993 masm.branchTestPtr(Assembler::Zero, temp2, temp2, &vmCall);
16994 masm.branch32(Assembler::Above, lengthReg, Imm32(arrayCapacity), &vmCall);
16995
16996 // The array must be nursery allocated so no post barrier is needed.
16997#ifdef DEBUG1
16998 Label ok;
16999 masm.branchPtrInNurseryChunk(Assembler::Equal, temp2, temp3, &ok);
17000 masm.assumeUnreachable("Unexpected tenured object for LRest");
17001 masm.bind(&ok);
17002#endif
17003
17004 Label nonZeroLength;
17005 masm.branch32(Assembler::NotEqual, lengthReg, Imm32(0), &nonZeroLength);
17006 masm.movePtr(temp2, ReturnReg);
17007 masm.jump(&done);
17008 masm.bind(&nonZeroLength);
17009
17010 // Store length and initializedLength.
17011 Register elements = temp3;
17012 masm.loadPtr(Address(temp2, NativeObject::offsetOfElements()), elements);
17013 Address lengthAddr(elements, ObjectElements::offsetOfLength());
17014 Address initLengthAddr(elements, ObjectElements::offsetOfInitializedLength());
17015 masm.store32(lengthReg, lengthAddr);
17016 masm.store32(lengthReg, initLengthAddr);
17017
17018 masm.push(temp2); // Spill result to free up register.
17019
17020 Register end = temp0;
17021 Register args = temp1;
17022 Register scratch = temp2;
17023 masm.computeEffectiveAddress(BaseObjectElementIndex(elements, lengthReg),
17024 end);
17025
17026 Label loop;
17027 masm.bind(&loop);
17028 masm.storeValue(Address(args, 0), Address(elements, 0), scratch);
17029 masm.addPtr(Imm32(sizeof(Value)), args);
17030 masm.addPtr(Imm32(sizeof(Value)), elements);
17031 masm.branchPtr(Assembler::Below, elements, end, &loop);
17032
17033 // Pop result
17034 masm.pop(ReturnReg);
17035 masm.jump(&done);
17036
17037 masm.bind(&vmCall);
17038
17039 pushArg(temp2);
17040 pushArg(temp1);
17041 pushArg(lengthReg);
17042
17043 using Fn =
17044 ArrayObject* (*)(JSContext*, uint32_t, Value*, Handle<ArrayObject*>);
17045 callVM<Fn, InitRestParameter>(lir);
17046
17047 masm.bind(&done);
17048}
17049
17050// Create a stackmap from the given safepoint, with the structure:
17051//
17052// <reg dump, if any>
17053// | ++ <body (general spill)>
17054// | | ++ <space for Frame>
17055// | | ++ <inbound args>
17056// | | |
17057// Lowest Addr Highest Addr
17058// |
17059// framePushedAtStackMapBase
17060//
17061// The caller owns the resulting stackmap. This assumes a grow-down stack.
17062//
17063// For non-debug builds, if the stackmap would contain no pointers, no
17064// stackmap is created, and nullptr is returned. For a debug build, a
17065// stackmap is always created and returned.
17066//
17067// Depending on the type of safepoint, the stackmap may need to account for
17068// spilled registers. WasmSafepointKind::LirCall corresponds to LIR nodes where
17069// isCall() == true, for which the register allocator will spill/restore all
17070// live registers at the LIR level - in this case, the LSafepoint sees only live
17071// values on the stack, never in registers. WasmSafepointKind::CodegenCall, on
17072// the other hand, is for LIR nodes which may manually spill/restore live
17073// registers in codegen, in which case the stackmap must account for this. Traps
17074// also require tracking of live registers, but spilling is handled by the trap
17075// mechanism.
17076static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint,
17077 const RegisterOffsets& trapExitLayout,
17078 size_t trapExitLayoutNumWords,
17079 size_t nInboundStackArgBytes,
17080 wasm::StackMaps& stackMaps,
17081 wasm::StackMap** result) {
17082 // Ensure this is defined on all return paths.
17083 *result = nullptr;
17084
17085 // The size of the wasm::Frame itself.
17086 const size_t nFrameBytes = sizeof(wasm::Frame);
17087
17088 // This is the number of bytes spilled for live registers, outside of a trap.
17089 // For traps, trapExitLayout and trapExitLayoutNumWords will be used.
17090 const size_t nRegisterDumpBytes =
17091 MacroAssembler::PushRegsInMaskSizeInBytes(safepoint.liveRegs());
17092
17093 // As mentioned above, for WasmSafepointKind::LirCall, register spills and
17094 // restores are handled at the LIR level and there should therefore be no live
17095 // registers to handle here.
17096 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", 17097); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { MOZ_CrashSequence
(__null, 17097); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
17097 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", 17097); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { MOZ_CrashSequence
(__null, 17097); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
17098 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", 17098); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nRegisterDumpBytes % sizeof(void*) == 0" ")"
); do { MOZ_CrashSequence(__null, 17098); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
17099
17100 // This is the number of bytes in the general spill area, below the Frame.
17101 const size_t nBodyBytes = safepoint.framePushedAtStackMapBase();
17102
17103 // The stack map owns any alignment padding around inbound stack args.
17104 const size_t nInboundStackArgBytesAligned =
17105 wasm::AlignStackArgAreaSize(nInboundStackArgBytes);
17106
17107 // This is the number of bytes in the general spill area, the Frame, and the
17108 // incoming args, but not including any register dump area.
17109 const size_t nNonRegisterBytes =
17110 nBodyBytes + nFrameBytes + nInboundStackArgBytesAligned;
17111 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", 17111); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nNonRegisterBytes % sizeof(void*) == 0" ")"
); do { MOZ_CrashSequence(__null, 17111); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
17112
17113 // This is the number of bytes in the register dump area, if any, below the
17114 // general spill area.
17115 const size_t nRegisterBytes =
17116 (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap)
17117 ? (trapExitLayoutNumWords * sizeof(void*))
17118 : nRegisterDumpBytes;
17119
17120 // This is the total number of bytes covered by the map.
17121 const size_t nTotalBytes = nNonRegisterBytes + nRegisterBytes;
17122
17123 // This stackmap/safepoint is for a wasm frame, so there should be no
17124 // slotsOrElements-style roots.
17125 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", 17125); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "safepoint.slotsOrElementsSlots().empty()"
")"); do { MOZ_CrashSequence(__null, 17125); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17126 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", 17126); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "safepoint.slotsOrElementsRegs().empty()"
")"); do { MOZ_CrashSequence(__null, 17126); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17127
17128#ifndef DEBUG1
17129 bool needStackMap = !safepoint.wasmAnyRefRegs().empty() ||
17130 !safepoint.wasmAnyRefSlots().empty() ||
17131 !safepoint.wasmStructDataRegs().empty() ||
17132 !safepoint.wasmStructDataSlots().empty() ||
17133 !safepoint.wasmArrayDataRegs().empty() ||
17134 !safepoint.wasmArrayDataSlots().empty();
17135 // There are no references, and this is a non-debug build, so don't bother
17136 // building the stackmap.
17137 if (!needStackMap) {
17138 return true;
17139 }
17140#endif
17141
17142 wasm::StackMap* stackMap = stackMaps.create(nTotalBytes / sizeof(void*));
17143 if (!stackMap) {
17144 return false;
17145 }
17146 if (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) {
17147 stackMap->setExitStubWords(trapExitLayoutNumWords);
17148 }
17149
17150 // REG DUMP AREA, if any.
17151 size_t regDumpWords = 0;
17152 const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs();
17153 const LiveGeneralRegisterSet wasmStructDataRegs =
17154 safepoint.wasmStructDataRegs();
17155 const LiveGeneralRegisterSet wasmArrayDataRegs =
17156 safepoint.wasmArrayDataRegs();
17157
17158 // These three sets should be disjoint.
17159 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", 17161); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17161); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17160 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", 17161); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17161); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17161 .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", 17161); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17161); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17162 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", 17164); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17164); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17163 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", 17164); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17164); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17164 .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", 17164); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17164); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17165 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", 17167); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17167); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17166 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", 17167); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17167); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17167 .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", 17167); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()"
")"); do { MOZ_CrashSequence(__null, 17167); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17168 const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union(
17169 wasmAnyRefRegs.set(),
17170 GeneralRegisterSet::Union(wasmStructDataRegs.set(),
17171 wasmArrayDataRegs.set())));
17172
17173 GeneralRegisterForwardIterator refRegsIter(refRegs);
17174 switch (safepoint.wasmSafepointKind()) {
17175 case WasmSafepointKind::LirCall:
17176 case WasmSafepointKind::StackSwitch:
17177 case WasmSafepointKind::CodegenCall: {
17178 size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*);
17179 regDumpWords += spilledNumWords;
17180
17181 for (; refRegsIter.more(); ++refRegsIter) {
17182 Register reg = *refRegsIter;
17183 size_t offsetFromSpillBase =
17184 safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) /
17185 sizeof(void*);
17186 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", 17187); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
")"); do { MOZ_CrashSequence(__null, 17187); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17187 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", 17187); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
")"); do { MOZ_CrashSequence(__null, 17187); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17188 size_t index = spilledNumWords - offsetFromSpillBase;
17189
17190 if (wasmAnyRefRegs.has(reg)) {
17191 stackMap->set(index, wasm::StackMap::AnyRef);
17192 } else if (wasmStructDataRegs.has(reg)) {
17193 stackMap->set(index, wasm::StackMap::StructDataPointer);
17194 } else {
17195 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", 17195); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasmArrayDataRegs.has(reg)" ")"); do { MOZ_CrashSequence
(__null, 17195); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17196 stackMap->set(index, wasm::StackMap::ArrayDataPointer);
17197 }
17198 }
17199 // Float and vector registers do not have to be handled; they cannot
17200 // contain wasm anyrefs, and they are spilled after general-purpose
17201 // registers. Gprs are therefore closest to the spill base and thus their
17202 // offset calculation does not need to account for other spills.
17203 } break;
17204 case WasmSafepointKind::Trap: {
17205 regDumpWords += trapExitLayoutNumWords;
17206
17207 for (; refRegsIter.more(); ++refRegsIter) {
17208 Register reg = *refRegsIter;
17209 size_t offsetFromTop = trapExitLayout.getOffset(reg);
17210
17211 // If this doesn't hold, the associated register wasn't saved by
17212 // the trap exit stub. Better to crash now than much later, in
17213 // some obscure place, and possibly with security consequences.
17214 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", 17214); AnnotateMozCrashReason
("MOZ_RELEASE_ASSERT" "(" "offsetFromTop < trapExitLayoutNumWords"
")"); do { MOZ_CrashSequence(__null, 17214); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17215
17216 // offsetFromTop is an offset in words down from the highest
17217 // address in the exit stub save area. Switch it around to be an
17218 // offset up from the bottom of the (integer register) save area.
17219 size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop;
17220
17221 if (wasmAnyRefRegs.has(reg)) {
17222 stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef);
17223 } else if (wasmStructDataRegs.has(reg)) {
17224 stackMap->set(offsetFromBottom, wasm::StackMap::StructDataPointer);
17225 } else {
17226 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", 17226); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasmArrayDataRegs.has(reg)" ")"); do { MOZ_CrashSequence
(__null, 17226); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17227 stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer);
17228 }
17229 }
17230 } break;
17231 default:
17232 MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "./../../../../js/src/jit/CodeGenerator.cpp"
, 17232); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")"
); do { MOZ_CrashSequence(__null, 17232); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
17233 }
17234
17235 // Ensure other reg/slot collections on LSafepoint are empty.
17236 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", 17236); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "safepoint.gcRegs().empty() && safepoint.gcSlots().empty()"
")"); do { MOZ_CrashSequence(__null, 17236); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17237#ifdef JS_NUNBOX32
17238 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"
, 17238); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.nunboxParts().empty()"
")"); do { MOZ_CrashSequence(__null, 17238); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17239#elif JS_PUNBOX641
17240 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", 17240); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "safepoint.valueRegs().empty() && safepoint.valueSlots().empty()"
")"); do { MOZ_CrashSequence(__null, 17240); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17241#endif
17242
17243 // BODY (GENERAL SPILL) AREA and FRAME and INCOMING ARGS
17244 // Deal with roots on the stack.
17245 const LSafepoint::SlotList& wasmAnyRefSlots = safepoint.wasmAnyRefSlots();
17246 for (SafepointSlotEntry wasmAnyRefSlot : wasmAnyRefSlots) {
17247 // The following needs to correspond with JitFrameLayout::slotRef
17248 // wasmAnyRefSlot.stack == 0 means the slot is in the args area
17249 if (wasmAnyRefSlot.stack) {
17250 // It's a slot in the body allocation, so .slot is interpreted
17251 // as an index downwards from the Frame*
17252 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"
, 17252); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot <= nBodyBytes"
")"); do { MOZ_CrashSequence(__null, 17252); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17253 uint32_t offsetInBytes = nBodyBytes - wasmAnyRefSlot.slot;
17254 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"
, 17254); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { MOZ_CrashSequence(__null, 17254); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17255 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
17256 wasm::StackMap::AnyRef);
17257 } else {
17258 // It's an argument slot
17259 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", 17259); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot < nInboundStackArgBytes"
")"); do { MOZ_CrashSequence(__null, 17259); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17260 uint32_t offsetInBytes = nBodyBytes + nFrameBytes + wasmAnyRefSlot.slot;
17261 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"
, 17261); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { MOZ_CrashSequence(__null, 17261); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17262 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
17263 wasm::StackMap::AnyRef);
17264 }
17265 }
17266
17267 // Track struct data pointers on the stack
17268 for (SafepointSlotEntry slot : safepoint.wasmStructDataSlots()) {
17269 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"
, 17269); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack"
")"); do { MOZ_CrashSequence(__null, 17269); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17270 // It's a slot in the body allocation, so .slot is interpreted
17271 // as an index downwards from the Frame*
17272 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", 17272); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { MOZ_CrashSequence
(__null, 17272); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17273 uint32_t offsetInBytes = nBodyBytes - slot.slot;
17274 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"
, 17274); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { MOZ_CrashSequence(__null, 17274); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17275 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
17276 wasm::StackMap::Kind::StructDataPointer);
17277 }
17278
17279 // Track array data pointers on the stack
17280 for (SafepointSlotEntry slot : safepoint.wasmArrayDataSlots()) {
17281 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"
, 17281); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack"
")"); do { MOZ_CrashSequence(__null, 17281); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17282 // It's a slot in the body allocation, so .slot is interpreted
17283 // as an index downwards from the Frame*
17284 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", 17284); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { MOZ_CrashSequence
(__null, 17284); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17285 uint32_t offsetInBytes = nBodyBytes - slot.slot;
17286 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"
, 17286); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { MOZ_CrashSequence(__null, 17286); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17287 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
17288 wasm::StackMap::Kind::ArrayDataPointer);
17289 }
17290
17291 // Record in the map, how far down from the highest address the Frame* is.
17292 // Take the opportunity to check that we haven't marked any part of the
17293 // Frame itself as a pointer.
17294 stackMap->setFrameOffsetFromTop((nInboundStackArgBytesAligned + nFrameBytes) /
17295 sizeof(void*));
17296#ifdef DEBUG1
17297 for (uint32_t i = 0; i < nFrameBytes / sizeof(void*); i++) {
17298 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", 17300); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { MOZ_CrashSequence(__null, 17300); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17299 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", 17300); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { MOZ_CrashSequence(__null, 17300); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17300 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", 17300); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { MOZ_CrashSequence(__null, 17300); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17301 }
17302#endif
17303
17304 *result = stackMap;
17305 return true;
17306}
17307
17308bool CodeGenerator::generateWasm(wasm::CallIndirectId callIndirectId,
17309 const wasm::TrapSiteDesc& entryTrapSiteDesc,
17310 const wasm::ArgTypeVector& argTypes,
17311 const RegisterOffsets& trapExitLayout,
17312 size_t trapExitLayoutNumWords,
17313 wasm::FuncOffsets* offsets,
17314 wasm::StackMaps* stackMaps,
17315 wasm::Decoder* decoder) {
17316 AutoCreatedBy acb(masm, "CodeGenerator::generateWasm");
17317
17318 JitSpew(JitSpew_Codegen, "# Emitting wasm code");
17319
17320 size_t nInboundStackArgBytes =
17321 StackArgAreaSizeUnaligned(argTypes, ABIKind::Wasm);
17322 inboundStackArgBytes_ = nInboundStackArgBytes;
17323
17324 perfSpewer().markStartOffset(masm.currentOffset());
17325 perfSpewer().recordOffset(masm, "Prologue");
17326 wasm::GenerateFunctionPrologue(masm, callIndirectId, mozilla::Nothing(),
17327 offsets);
17328
17329#ifdef DEBUG1
17330 // If we are doing full debug checks, always load the instance pointer into
17331 // the usual spot in the frame so that it can be loaded later regardless of
17332 // what is in InstanceReg. See CodeGenerator::emitDebugResultChecks.
17333 if (JitOptions.fullDebugChecks) {
17334 masm.storePtr(InstanceReg,
17335 Address(FramePointer,
17336 wasm::FrameWithInstances::calleeInstanceOffset()));
17337 }
17338#endif
17339
17340 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", 17340); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { MOZ_CrashSequence
(__null, 17340); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17341
17342 // Very large frames are implausible, probably an attack.
17343 if (frameSize() > wasm::MaxFrameSize) {
17344 return decoder->fail(decoder->beginOffset(), "stack frame is too large");
17345 }
17346
17347 if (omitOverRecursedStackCheck()) {
17348 masm.reserveStack(frameSize());
17349
17350 // If we don't need to check the stack for recursion, we definitely don't
17351 // need to check for interrupts.
17352 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"
, 17352); AnnotateMozCrashReason("MOZ_ASSERT" "(" "omitOverRecursedInterruptCheck()"
")"); do { MOZ_CrashSequence(__null, 17352); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17353 } else {
17354 auto* ool = new (alloc())
17355 LambdaOutOfLineCode([this, entryTrapSiteDesc](OutOfLineCode& ool) {
17356 masm.wasmTrap(wasm::Trap::StackOverflow, entryTrapSiteDesc);
17357 });
17358 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
17359 masm.wasmReserveStackChecked(frameSize(), ool->entry());
17360
17361 if (!omitOverRecursedInterruptCheck()) {
17362 wasm::StackMap* functionEntryStackMap = nullptr;
17363 if (!CreateStackMapForFunctionEntryTrap(
17364 argTypes, trapExitLayout, trapExitLayoutNumWords, frameSize(),
17365 nInboundStackArgBytes, *stackMaps, &functionEntryStackMap)) {
17366 return false;
17367 }
17368
17369 // In debug builds, we'll always have a stack map, even if there are no
17370 // refs to track.
17371 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", 17371); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "functionEntryStackMap" ")"); do { MOZ_CrashSequence
(__null, 17371); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17372
17373 auto* ool = new (alloc()) LambdaOutOfLineCode(
17374 [this, stackMaps, functionEntryStackMap](OutOfLineCode& ool) {
17375 FaultingCodeRange fcr =
17376 masm.wasmTrap(wasm::Trap::CheckInterrupt, wasm::TrapSiteDesc());
17377
17378 if (functionEntryStackMap && fcr.isValid() &&
17379 !stackMaps->add(fcr.resumeOffset(), functionEntryStackMap)) {
17380 masm.setOOM();
17381 }
17382 masm.jump(ool.rejoin());
17383 });
17384
17385 addOutOfLineCode(ool, (const BytecodeSite*)nullptr);
17386 masm.branch32(Assembler::NotEqual,
17387 Address(InstanceReg, wasm::Instance::offsetOfInterrupt()),
17388 Imm32(0), ool->entry());
17389 masm.bind(ool->rejoin());
17390 }
17391 }
17392
17393 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"
, 17393); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { MOZ_CrashSequence(__null, 17393); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17394
17395 if (!generateBody()) {
17396 return false;
17397 }
17398
17399 perfSpewer().recordOffset(masm, "Epilogue");
17400 masm.bind(&returnLabel_);
17401 wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets);
17402
17403 perfSpewer().recordOffset(masm, "OOLBlocks");
17404 // This must come before we generate OOL code, as OOL blocks may
17405 // generate OOL code.
17406 if (!generateOutOfLineBlocks()) {
17407 return false;
17408 }
17409
17410 perfSpewer().recordOffset(masm, "OOLCode");
17411 if (!generateOutOfLineCode()) {
17412 return false;
17413 }
17414
17415 masm.flush();
17416 if (masm.oom()) {
17417 return false;
17418 }
17419
17420 offsets->end = masm.currentOffset();
17421
17422 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"
, 17422); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!masm.failureLabel()->used()"
")"); do { MOZ_CrashSequence(__null, 17422); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17423 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", 17423); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "snapshots_.listSize() == 0" ")"); do { MOZ_CrashSequence
(__null, 17423); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17424 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"
, 17424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.RVATableSize() == 0"
")"); do { MOZ_CrashSequence(__null, 17424); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17425 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", 17425); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "recovers_.size() == 0" ")"); do { MOZ_CrashSequence
(__null, 17425); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17426 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", 17426); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "graph.numConstants() == 0" ")"); do { MOZ_CrashSequence
(__null, 17426); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17427 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", 17427); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "osiIndices_.empty()" ")"); do { MOZ_CrashSequence
(__null, 17427); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17428 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", 17428); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "icList_.empty()" ")"); do { MOZ_CrashSequence
(__null, 17428); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17429 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", 17429); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "safepoints_.size() == 0" ")"); do { MOZ_CrashSequence
(__null, 17429); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17430 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", 17430); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!scriptCounts_" ")"); do { MOZ_CrashSequence
(__null, 17430); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17431
17432 // Convert the safepoints to stackmaps and add them to our running
17433 // collection thereof.
17434 for (CodegenSafepointIndex& index : safepointIndices_) {
17435 wasm::StackMap* stackMap = nullptr;
17436 if (!CreateStackMapFromLSafepoint(
17437 *index.safepoint(), trapExitLayout, trapExitLayoutNumWords,
17438 nInboundStackArgBytes, *stackMaps, &stackMap)) {
17439 return false;
17440 }
17441
17442 // In debug builds, we'll always have a stack map.
17443 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"
, 17443); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap" ")"
); do { MOZ_CrashSequence(__null, 17443); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
17444 if (!stackMap) {
17445 continue;
17446 }
17447
17448 if (!stackMaps->finalize(index.displacement(), stackMap)) {
17449 return false;
17450 }
17451 }
17452
17453 return true;
17454}
17455
17456bool CodeGenerator::generate(const WarpSnapshot* snapshot) {
17457 AutoCreatedBy acb(masm, "CodeGenerator::generate");
17458
17459 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"
, 17459); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshot" ")"
); do { MOZ_CrashSequence(__null, 17459); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
17460 snapshot_ = snapshot;
17461
17462 JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%u:%u",
17463 gen->outerInfo().script()->filename(),
17464 gen->outerInfo().script()->lineno(),
17465 gen->outerInfo().script()->column().oneOriginValue());
17466
17467 // Initialize native code table with an entry to the start of
17468 // top-level script.
17469 InlineScriptTree* tree = gen->outerInfo().inlineScriptTree();
17470 jsbytecode* startPC = tree->script()->code();
17471 BytecodeSite* startSite = new (gen->alloc()) BytecodeSite(tree, startPC);
17472 if (!addNativeToBytecodeEntry(startSite)) {
17473 return false;
17474 }
17475
17476 if (!safepoints_.init(gen->alloc())) {
17477 return false;
17478 }
17479
17480 size_t maxSafepointIndices =
17481 graph.numSafepoints() + graph.extraSafepointUses();
17482 if (!safepointIndices_.reserve(maxSafepointIndices)) {
17483 return false;
17484 }
17485 if (!osiIndices_.reserve(graph.numSafepoints())) {
17486 return false;
17487 }
17488
17489 perfSpewer().recordOffset(masm, "Prologue");
17490 if (!generatePrologue()) {
17491 return false;
17492 }
17493
17494 // Reset native => bytecode map table with top-level script and startPc.
17495 if (!addNativeToBytecodeEntry(startSite)) {
17496 return false;
17497 }
17498
17499 if (!generateBody()) {
17500 return false;
17501 }
17502
17503 // Reset native => bytecode map table with top-level script and startPc.
17504 if (!addNativeToBytecodeEntry(startSite)) {
17505 return false;
17506 }
17507
17508 perfSpewer().recordOffset(masm, "Epilogue");
17509 if (!generateEpilogue()) {
17510 return false;
17511 }
17512
17513 // Reset native => bytecode map table with top-level script and startPc.
17514 if (!addNativeToBytecodeEntry(startSite)) {
17515 return false;
17516 }
17517
17518 perfSpewer().recordOffset(masm, "InvalidateEpilogue");
17519 generateInvalidateEpilogue();
17520
17521 perfSpewer().recordOffset(masm, "OOLBlocks");
17522 // This must come before we generate OOL code, as OOL blocks may
17523 // generate OOL code.
17524 if (!generateOutOfLineBlocks()) {
17525 return false;
17526 }
17527
17528 // native => bytecode entries for OOL code will be added
17529 // by CodeGeneratorShared::generateOutOfLineCode
17530 perfSpewer().recordOffset(masm, "OOLCode");
17531 if (!generateOutOfLineCode()) {
17532 return false;
17533 }
17534
17535 // Add terminal entry.
17536 if (!addNativeToBytecodeEntry(startSite)) {
17537 return false;
17538 }
17539
17540 // Dump Native to bytecode entries to spew.
17541 dumpNativeToBytecodeEntries();
17542
17543 // We encode safepoints after the OSI-point offsets have been determined.
17544 if (!encodeSafepoints()) {
17545 return false;
17546 }
17547
17548 // If this assertion trips, then you have multiple things to do:
17549 //
17550 // This assertion will report if a safepoint is used multiple times for the
17551 // same instruction. To fix this assertion make sure to call
17552 // `lirGraph_.addExtraSafepointUses(..);` in the Lowering phase.
17553 //
17554 // However, this non-worrying issue might hide a more dramatic security issue,
17555 // which is that having multiple encoding of a safepoint in a single LIR
17556 // instruction is not safe, unless:
17557 //
17558 // - The multiple uses of the safepoints are in different code path. i-e
17559 // there should be not single execution trace making use of multiple
17560 // calls within a single instruction.
17561 //
17562 // - There is enough space to encode data in-place of the call instruction.
17563 // Such that a patched-call site does not corrupt the code path on another
17564 // execution trace.
17565 //
17566 // This issue is caused by the way invalidation works, to keep the code alive
17567 // when invalidated code is only referenced by the stack. This works by
17568 // storing data in-place of the calling code, which thus becomes unsafe to
17569 // execute.
17570 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"
, 17570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepointIndices_.length() <= maxSafepointIndices"
")"); do { MOZ_CrashSequence(__null, 17570); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17571
17572 // For each instruction with a safepoint, we have an OSI point inserted after
17573 // which handles bailouts in case of invalidation of the code.
17574 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", 17574); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "osiIndices_.length() == graph.numSafepoints()"
")"); do { MOZ_CrashSequence(__null, 17574); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17575
17576 return !masm.oom();
17577}
17578
17579static bool AddInlinedCompilations(JSContext* cx, HandleScript script,
17580 IonCompilationId compilationId,
17581 const WarpSnapshot* snapshot,
17582 bool* isValid) {
17583 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"
, 17583); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*isValid"
")"); do { MOZ_CrashSequence(__null, 17583); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17584 IonScriptKey ionScriptKey(script, compilationId);
17585
17586 JitZone* jitZone = cx->zone()->jitZone();
17587
17588 for (const auto* scriptSnapshot : snapshot->scripts()) {
17589 JSScript* inlinedScript = scriptSnapshot->script();
17590 if (inlinedScript == script) {
17591 continue;
17592 }
17593
17594 // TODO(post-Warp): This matches FinishCompilation and is necessary to
17595 // ensure in-progress compilations are canceled when an inlined functon
17596 // becomes a debuggee. See the breakpoint-14.js jit-test.
17597 // When TI is gone, try to clean this up by moving AddInlinedCompilations to
17598 // WarpOracle so that we can handle this as part of addPendingRecompile
17599 // instead of requiring this separate check.
17600 if (inlinedScript->isDebuggee()) {
17601 *isValid = false;
17602 return true;
17603 }
17604
17605 if (!jitZone->addInlinedCompilation(ionScriptKey, inlinedScript)) {
17606 return false;
17607 }
17608 }
17609
17610 *isValid = true;
17611 return true;
17612}
17613
17614template <auto FuseMember, CompilationDependency::Type DepType>
17615struct RuntimeFuseDependency final : public CompilationDependency {
17616 explicit RuntimeFuseDependency() : CompilationDependency(DepType) {}
17617
17618 bool registerDependency(JSContext* cx,
17619 const IonScriptKey& ionScript) override {
17620 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", 17620); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "checkDependency(cx)" ")"); do { MOZ_CrashSequence
(__null, 17620); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17621 return (cx->runtime()->runtimeFuses.ref().*FuseMember)
17622 .addFuseDependency(cx, ionScript);
17623 }
17624
17625 CompilationDependency* clone(TempAllocator& alloc) const override {
17626 return new (alloc.fallible()) RuntimeFuseDependency<FuseMember, DepType>();
17627 }
17628
17629 bool checkDependency(JSContext* cx) const override {
17630 return (cx->runtime()->runtimeFuses.ref().*FuseMember).intact();
17631 }
17632
17633 HashNumber hash() const override { return mozilla::HashGeneric(type); }
17634
17635 bool operator==(const CompilationDependency& dep) const override {
17636 // Since this dependency is runtime wide, they are all equal.
17637 return dep.type == type;
17638 }
17639};
17640
17641bool CodeGenerator::addHasSeenObjectEmulateUndefinedFuseDependency() {
17642 using Dependency =
17643 RuntimeFuseDependency<&RuntimeFuses::hasSeenObjectEmulateUndefinedFuse,
17644 CompilationDependency::Type::EmulatesUndefined>;
17645 return mirGen().tracker.addDependency(alloc(), Dependency());
17646}
17647
17648bool CodeGenerator::addHasSeenArrayExceedsInt32LengthFuseDependency() {
17649 using Dependency = RuntimeFuseDependency<
17650 &RuntimeFuses::hasSeenArrayExceedsInt32LengthFuse,
17651 CompilationDependency::Type::ArrayExceedsInt32Length>;
17652 return mirGen().tracker.addDependency(alloc(), Dependency());
17653}
17654
17655bool CodeGenerator::link(JSContext* cx) {
17656 AutoCreatedBy acb(masm, "CodeGenerator::link");
17657
17658 // We cancel off-thread Ion compilations in a few places during GC, but if
17659 // this compilation was performed off-thread it will already have been
17660 // removed from the relevant lists by this point. Don't allow GC here.
17661 JS::AutoAssertNoGC nogc(cx);
17662
17663 RootedScript script(cx, gen->outerInfo().script());
17664 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", 17664); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!script->hasIonScript()" ")"); do { MOZ_CrashSequence
(__null, 17664); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17665
17666 if (scriptCounts_ && !script->hasScriptCounts() &&
17667 !script->initScriptCounts(cx)) {
17668 return false;
17669 }
17670
17671 // Add all used nursery-values to the Value constant pool that's copied to the
17672 // IonScript.
17673 for (NurseryValueLabel& label : nurseryValueLabels_) {
17674 Value v = snapshot_->nurseryValues()[label.nurseryIndex];
17675 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", 17675); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "v.isGCThing()" ")"); do { MOZ_CrashSequence
(__null, 17675); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
17676 if (!graph.addConstantToPool(v, &label.constantPoolIndex)) {
17677 return false;
17678 }
17679 }
17680
17681 JitZone* jitZone = cx->zone()->jitZone();
17682
17683 IonCompilationId compilationId =
17684 cx->runtime()->jitRuntime()->nextCompilationId();
17685 jitZone->currentCompilationIdRef().emplace(compilationId);
17686 auto resetCurrentId = mozilla::MakeScopeExit(
17687 [jitZone] { jitZone->currentCompilationIdRef().reset(); });
17688
17689 // Record constraints. If an error occured, returns false and potentially
17690 // prevent future compilations. Otherwise, if an invalidation occured, then
17691 // skip the current compilation.
17692 bool isValid = false;
17693
17694 // If an inlined script is invalidated (for example, by attaching
17695 // a debugger), we must also invalidate the parent IonScript.
17696 if (!AddInlinedCompilations(cx, script, compilationId, snapshot_, &isValid)) {
17697 return false;
17698 }
17699
17700 // This compilation is no longer valid; don't proceed, but return true as this
17701 // isn't an error case either.
17702 if (!isValid) {
17703 return true;
17704 }
17705
17706 CompilationDependencyTracker& tracker = mirGen().tracker;
17707 // Make sure we're using the same realm as this context.
17708 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", 17708); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mirGen().realm->realmPtr() == cx->realm()"
")"); do { MOZ_CrashSequence(__null, 17708); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17709 if (!tracker.checkDependencies(cx)) {
17710 return true;
17711 }
17712
17713 IonScriptKey ionScriptKey(script, compilationId);
17714 for (auto iter = tracker.dependencies.iter(); !iter.done(); iter.next()) {
17715 CompilationDependency* dep = iter.get();
17716 if (!dep->registerDependency(cx, ionScriptKey)) {
17717 return false;
17718 }
17719 }
17720
17721 uint32_t argumentSlots = (gen->outerInfo().nargs() + 1) * sizeof(Value);
17722
17723 size_t numNurseryObjects = snapshot_->nurseryObjects().length();
17724
17725 IonScript* ionScript = IonScript::New(
17726 cx, compilationId, graph.localSlotsSize(), argumentSlots, frameDepth_,
17727 snapshots_.listSize(), snapshots_.RVATableSize(), recovers_.size(),
17728 graph.numConstants(), numNurseryObjects, safepointIndices_.length(),
17729 osiIndices_.length(), icList_.length(), runtimeData_.length(),
17730 safepoints_.size());
17731 if (!ionScript) {
17732 return false;
17733 }
17734#ifdef DEBUG1
17735 ionScript->setICHash(snapshot_->icHash());
17736#endif
17737
17738 auto freeIonScript = mozilla::MakeScopeExit([&ionScript] {
17739 // Use js_free instead of IonScript::Destroy: the cache list is still
17740 // uninitialized.
17741 js_free(ionScript);
17742 });
17743
17744 Linker linker(masm);
17745 JitCode* code = linker.newCode(cx, CodeKind::Ion);
17746 if (!code) {
17747 return false;
17748 }
17749
17750 // Encode native to bytecode map if profiling is enabled.
17751 if (isProfilerInstrumentationEnabled()) {
17752 // Generate native-to-bytecode main table.
17753 IonEntry::ScriptList scriptList;
17754 if (!generateCompactNativeToBytecodeMap(cx, code, scriptList)) {
17755 return false;
17756 }
17757
17758 // Find the realmId. We do not do cross-realm inlining, so it should be the
17759 // same for every inlined script.
17760 uint64_t realmId = script->realm()->creationOptions().profilerRealmID();
17761#ifdef DEBUG1
17762 for (const auto* scriptSnapshot : snapshot_->scripts()) {
17763 JSScript* inlinedScript = scriptSnapshot->script();
17764 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", 17765); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "inlinedScript->realm()->creationOptions().profilerRealmID() == realmId"
")"); do { MOZ_CrashSequence(__null, 17765); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
17765 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", 17765); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "inlinedScript->realm()->creationOptions().profilerRealmID() == realmId"
")"); do { MOZ_CrashSequence(__null, 17765); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17766 }
17767#endif
17768
17769 uint8_t* ionTableAddr =
17770 ((uint8_t*)nativeToBytecodeMap_.get()) + nativeToBytecodeTableOffset_;
17771 JitcodeIonTable* ionTable = (JitcodeIonTable*)ionTableAddr;
17772
17773 // Construct the IonEntry that will go into the global table.
17774 auto entry = MakeJitcodeGlobalEntry<IonEntry>(
17775 cx, code, code->raw(), code->rawEnd(), std::move(scriptList), ionTable,
17776 realmId);
17777 if (!entry) {
17778 return false;
17779 }
17780 (void)nativeToBytecodeMap_.release(); // Table is now owned by |entry|.
17781
17782 // Add entry to the global table.
17783 JitcodeGlobalTable* globalTable =
17784 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
17785 if (!globalTable->addEntry(std::move(entry))) {
17786 return false;
17787 }
17788
17789 // Mark the jitcode as having a bytecode map.
17790 code->setHasBytecodeMap();
17791 } else {
17792 // Add a dumy jitcodeGlobalTable entry.
17793 auto entry = MakeJitcodeGlobalEntry<DummyEntry>(cx, code, code->raw(),
17794 code->rawEnd());
17795 if (!entry) {
17796 return false;
17797 }
17798
17799 // Add entry to the global table.
17800 JitcodeGlobalTable* globalTable =
17801 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
17802 if (!globalTable->addEntry(std::move(entry))) {
17803 return false;
17804 }
17805
17806 // Mark the jitcode as having a bytecode map.
17807 code->setHasBytecodeMap();
17808 }
17809
17810 ionScript->setMethod(code);
17811
17812 // If the Gecko Profiler is enabled, mark IonScript as having been
17813 // instrumented accordingly.
17814 if (isProfilerInstrumentationEnabled()) {
17815 ionScript->setHasProfilingInstrumentation();
17816 }
17817
17818 Assembler::PatchDataWithValueCheck(
17819 CodeLocationLabel(code, invalidateEpilogueData_), ImmPtr(ionScript),
17820 ImmPtr((void*)-1));
17821
17822 for (CodeOffset offset : ionScriptLabels_) {
17823 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, offset),
17824 ImmPtr(ionScript), ImmPtr((void*)-1));
17825 }
17826
17827 for (NurseryObjectLabel label : nurseryObjectLabels_) {
17828 void* entry = ionScript->addressOfNurseryObject(label.nurseryIndex);
17829 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset),
17830 ImmPtr(entry), ImmPtr((void*)-1));
17831 }
17832 for (NurseryValueLabel label : nurseryValueLabels_) {
17833 void* entry = &ionScript->getConstant(label.constantPoolIndex);
17834 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset),
17835 ImmPtr(entry), ImmPtr((void*)-1));
17836 }
17837
17838 // for generating inline caches during the execution.
17839 if (runtimeData_.length()) {
17840 ionScript->copyRuntimeData(&runtimeData_[0]);
17841 }
17842 if (icList_.length()) {
17843 ionScript->copyICEntries(&icList_[0]);
17844 }
17845
17846 for (size_t i = 0; i < icInfo_.length(); i++) {
17847 IonIC& ic = ionScript->getICFromIndex(i);
17848 Assembler::PatchDataWithValueCheck(
17849 CodeLocationLabel(code, icInfo_[i].icOffsetForJump),
17850 ImmPtr(ic.codeRawPtr()), ImmPtr((void*)-1));
17851 Assembler::PatchDataWithValueCheck(
17852 CodeLocationLabel(code, icInfo_[i].icOffsetForPush), ImmPtr(&ic),
17853 ImmPtr((void*)-1));
17854 }
17855
17856 JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)", (void*)ionScript,
17857 (void*)code->raw());
17858
17859 ionScript->setInvalidationEpilogueDataOffset(
17860 invalidateEpilogueData_.offset());
17861 if (jsbytecode* osrPc = gen->outerInfo().osrPc()) {
17862 ionScript->setOsrPc(osrPc);
17863 ionScript->setOsrEntryOffset(getOsrEntryOffset());
17864 }
17865 ionScript->setInvalidationEpilogueOffset(invalidate_.offset());
17866
17867 perfSpewer().saveJSProfile(cx, script, code);
17868
17869#ifdef MOZ_VTUNE1
17870 vtune::MarkScript(code, script, "ion");
17871#endif
17872
17873 // Set a Ion counter hint for this script.
17874 if (cx->runtime()->jitRuntime()->hasJitHintsMap()) {
17875 JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap();
17876 jitHints->recordIonCompilation(script);
17877 }
17878
17879 // for marking during GC.
17880 if (safepointIndices_.length()) {
17881 ionScript->copySafepointIndices(&safepointIndices_[0]);
17882 }
17883 if (safepoints_.size()) {
17884 ionScript->copySafepoints(&safepoints_);
17885 }
17886
17887 // for recovering from an Ion Frame.
17888 if (osiIndices_.length()) {
17889 ionScript->copyOsiIndices(&osiIndices_[0]);
17890 }
17891 if (snapshots_.listSize()) {
17892 ionScript->copySnapshots(&snapshots_);
17893 }
17894 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"
, 17894); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size()"
")"); do { MOZ_CrashSequence(__null, 17894); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
17895 if (recovers_.size()) {
17896 ionScript->copyRecovers(&recovers_);
17897 }
17898 if (graph.numConstants()) {
17899 const Value* vp = graph.constantPool();
17900 ionScript->copyConstants(vp);
17901 }
17902
17903 // Attach any generated script counts to the script.
17904 if (IonScriptCounts* counts = extractScriptCounts()) {
17905 script->addIonCounts(counts);
17906 }
17907 // WARNING: Code after this point must be infallible!
17908
17909 // Copy the list of nursery objects. Note that the store buffer can add
17910 // HeapPtr edges that must be cleared in IonScript::Destroy. See the
17911 // infallibility warning above.
17912 const auto& nurseryObjects = snapshot_->nurseryObjects();
17913 for (size_t i = 0; i < nurseryObjects.length(); i++) {
17914 ionScript->nurseryObjects()[i].init(nurseryObjects[i]);
17915 }
17916
17917 // Initialization fence for the IonScript.
17918 MemoryReleaseFence(script.get());
17919
17920 // Transfer ownership of the IonScript to the JitScript. At this point enough
17921 // of the IonScript must be initialized for IonScript::Destroy to work.
17922 freeIonScript.release();
17923 script->jitScript()->setIonScript(script, ionScript);
17924
17925 return true;
17926}
17927
17928void CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint* lir) {
17929 ValueOperand box = ToValue(lir->input());
17930 const LDefinition* result = lir->output();
17931
17932 // Out-of-line path to convert int32 to double or bailout
17933 // if this instruction is fallible.
17934 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
17935 ValueOperand value = ToValue(lir->input());
17936
17937 if (lir->mir()->fallible()) {
17938 Label bail;
17939 masm.branchTestInt32(Assembler::NotEqual, value, &bail);
17940 bailoutFrom(&bail, lir->snapshot());
17941 }
17942 masm.convertInt32ToDouble(value.payloadOrValueReg(),
17943 ToFloatRegister(lir->output()));
17944 masm.jump(ool.rejoin());
17945 });
17946 addOutOfLineCode(ool, lir->mir());
17947
17948 FloatRegister resultReg = ToFloatRegister(result);
17949 masm.branchTestDouble(Assembler::NotEqual, box, ool->entry());
17950 masm.unboxDouble(box, resultReg);
17951 masm.bind(ool->rejoin());
17952}
17953
17954void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) {
17955 Register obj = ToRegister(lir->object());
17956 ValueOperand idVal = ToValue(lir->index());
17957 ValueOperand value = ToValue(lir->value());
17958
17959 Register temp0 = ToRegister(lir->temp0());
17960 // See comment in LIROps.yaml (x86 is short on registers)
17961#ifndef JS_CODEGEN_X86
17962 Register temp1 = ToRegister(lir->temp1());
17963 Register temp2 = ToRegister(lir->temp2());
17964#endif
17965
17966 // The instruction is marked as call-instruction so only these registers are
17967 // live.
17968 LiveRegisterSet liveRegs;
17969 liveRegs.addUnchecked(obj);
17970 liveRegs.addUnchecked(idVal);
17971 liveRegs.addUnchecked(value);
17972 liveRegs.addUnchecked(temp0);
17973#ifndef JS_CODEGEN_X86
17974 liveRegs.addUnchecked(temp1);
17975 liveRegs.addUnchecked(temp2);
17976#endif
17977
17978 Label cacheHit, done;
17979#ifdef JS_CODEGEN_X86
17980 masm.emitMegamorphicCachedSetSlot(
17981 idVal, obj, temp0, value, liveRegs, &cacheHit,
17982 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
17983 EmitPreBarrier(masm, addr, mirType);
17984 });
17985#else
17986 masm.emitMegamorphicCachedSetSlot(
17987 idVal, obj, temp0, temp1, temp2, value, liveRegs, &cacheHit,
17988 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
17989 EmitPreBarrier(masm, addr, mirType);
17990 });
17991#endif
17992
17993 pushArg(Imm32(lir->mir()->strict()));
17994 pushArg(ToValue(lir->value()));
17995 pushArg(ToValue(lir->index()));
17996 pushArg(obj);
17997
17998 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
17999 callVM<Fn, js::jit::SetElementMegamorphic<true>>(lir);
18000
18001 masm.jump(&done);
18002 masm.bind(&cacheHit);
18003
18004 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
18005 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
18006
18007 // Note: because this is a call-instruction, no registers need to be saved.
18008 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", 18008); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { MOZ_CrashSequence
(__null, 18008); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
18009 emitPostWriteBarrier(obj);
18010
18011 masm.bind(&done);
18012}
18013
18014void CodeGenerator::visitLoadScriptedProxyHandler(
18015 LLoadScriptedProxyHandler* ins) {
18016 Register obj = ToRegister(ins->object());
18017 Register output = ToRegister(ins->output());
18018
18019 Label bail;
18020 Address handlerAddr(obj, ProxyObject::offsetOfReservedSlot(
18021 ScriptedProxyHandler::HANDLER_EXTRA));
18022 masm.fallibleUnboxObject(handlerAddr, output, &bail);
18023 bailoutFrom(&bail, ins->snapshot());
18024}
18025
18026#ifdef JS_PUNBOX641
18027void CodeGenerator::visitCheckScriptedProxyGetResult(
18028 LCheckScriptedProxyGetResult* ins) {
18029 ValueOperand target = ToValue(ins->target());
18030 ValueOperand value = ToValue(ins->value());
18031 ValueOperand id = ToValue(ins->id());
18032 Register scratch = ToRegister(ins->temp0());
18033 Register scratch2 = ToRegister(ins->temp1());
18034
18035 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue,
18036 MutableHandleValue);
18037 OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>(
18038 ins, ArgList(scratch, id, value), StoreValueTo(value));
18039
18040 masm.unboxObject(target, scratch);
18041 masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch,
18042 scratch2, ool->entry());
18043 masm.bind(ool->rejoin());
18044}
18045#endif
18046
18047void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) {
18048 ValueOperand id = ToValue(ins->idVal());
18049 ValueOperand output = ToOutValue(ins);
18050 Register scratch = ToRegister(ins->temp0());
18051
18052 masm.moveValue(id, output);
18053
18054 Label done, callVM;
18055 Label bail;
18056 {
18057 ScratchTagScope tag(masm, output);
18058 masm.splitTagForTest(output, tag);
18059 masm.branchTestString(Assembler::Equal, tag, &done);
18060 masm.branchTestSymbol(Assembler::Equal, tag, &done);
18061 masm.branchTestInt32(Assembler::NotEqual, tag, &bail);
18062 }
18063
18064 masm.unboxInt32(output, scratch);
18065
18066 using Fn = JSLinearString* (*)(JSContext*, int);
18067 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
18068 ins, ArgList(scratch), StoreRegisterTo(output.scratchReg()));
18069
18070 masm.lookupStaticIntString(scratch, output.scratchReg(),
18071 gen->runtime->staticStrings(), ool->entry());
18072
18073 masm.bind(ool->rejoin());
18074 masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
18075 masm.bind(&done);
18076
18077 bailoutFrom(&bail, ins->snapshot());
18078}
18079
18080void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) {
18081 const Register obj = ToRegister(ins->object());
18082 size_t slot = ins->mir()->slot();
18083 ValueOperand result = ToOutValue(ins);
18084
18085 masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result);
18086}
18087
18088void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) {
18089 const Register obj = ToRegister(ins->object());
18090 size_t slot = ins->mir()->slot();
18091 AnyRegister result = ToAnyRegister(ins->output());
18092 MIRType type = ins->mir()->type();
18093
18094 masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)),
18095 type, result);
18096}
18097
18098void CodeGenerator::visitLoadFixedSlotFromOffset(
18099 LLoadFixedSlotFromOffset* lir) {
18100 Register obj = ToRegister(lir->object());
18101 Register offset = ToRegister(lir->offset());
18102 ValueOperand out = ToOutValue(lir);
18103
18104 // obj[offset]
18105 masm.loadValue(BaseIndex(obj, offset, TimesOne), out);
18106}
18107
18108void CodeGenerator::visitStoreFixedSlotFromOffsetV(
18109 LStoreFixedSlotFromOffsetV* lir) {
18110 Register obj = ToRegister(lir->object());
18111 Register offset = ToRegister(lir->offset());
18112 ValueOperand value = ToValue(lir->value());
18113 Register temp = ToRegister(lir->temp0());
18114
18115 BaseIndex baseIndex(obj, offset, TimesOne);
18116 masm.computeEffectiveAddress(baseIndex, temp);
18117
18118 Address slot(temp, 0);
18119 if (lir->mir()->needsPreBarrier()) {
18120 emitPreBarrier(slot);
18121 }
18122
18123 // obj[offset]
18124 masm.storeValue(value, slot);
18125}
18126
18127void CodeGenerator::visitStoreFixedSlotFromOffsetT(
18128 LStoreFixedSlotFromOffsetT* lir) {
18129 Register obj = ToRegister(lir->object());
18130 Register offset = ToRegister(lir->offset());
18131 const LAllocation* value = lir->value();
18132 MIRType valueType = lir->mir()->value()->type();
18133 Register temp = ToRegister(lir->temp0());
18134
18135 BaseIndex baseIndex(obj, offset, TimesOne);
18136 masm.computeEffectiveAddress(baseIndex, temp);
18137
18138 Address slot(temp, 0);
18139 if (lir->mir()->needsPreBarrier()) {
18140 emitPreBarrier(slot);
18141 }
18142
18143 // obj[offset]
18144 ConstantOrRegister nvalue =
18145 value->isConstant()
18146 ? ConstantOrRegister(value->toConstant()->toJSValue())
18147 : TypedOrValueRegister(valueType, ToAnyRegister(value));
18148 masm.storeConstantOrRegister(nvalue, slot);
18149}
18150
18151template <typename T>
18152static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type,
18153 bool fallible, AnyRegister dest, Register64 temp,
18154 Label* fail) {
18155 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", 18155); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "temp != Register64::Invalid()" ")"); do { MOZ_CrashSequence
(__null, 18155); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
18156 if (type == MIRType::Double) {
18157 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", 18157); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dest.isFloat()" ")"); do { MOZ_CrashSequence
(__null, 18157); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
18158#if defined(JS_NUNBOX32)
18159 auto tempVal = ValueOperand(temp.high, temp.low);
18160#else
18161 auto tempVal = ValueOperand(temp.reg);
18162#endif
18163 masm.loadValue(src, tempVal);
18164 masm.ensureDouble(tempVal, dest.fpu(), fail);
18165 return;
18166 }
18167 if (fallible) {
18168 switch (type) {
18169 case MIRType::Int32:
18170 masm.fallibleUnboxInt32(src, dest.gpr(), fail);
18171 break;
18172 case MIRType::Boolean:
18173 masm.fallibleUnboxBoolean(src, dest.gpr(), fail);
18174 break;
18175 case MIRType::Object:
18176 masm.fallibleUnboxObject(src, dest.gpr(), fail);
18177 break;
18178 case MIRType::String:
18179 masm.fallibleUnboxString(src, dest.gpr(), fail);
18180 break;
18181 case MIRType::Symbol:
18182 masm.fallibleUnboxSymbol(src, dest.gpr(), fail);
18183 break;
18184 case MIRType::BigInt:
18185 masm.fallibleUnboxBigInt(src, dest.gpr(), fail);
18186 break;
18187 default:
18188 MOZ_CRASH("Unexpected MIRType")do { do { } while (false); MOZ_ReportCrash("" "Unexpected MIRType"
, "./../../../../js/src/jit/CodeGenerator.cpp", 18188); AnnotateMozCrashReason
("MOZ_CRASH(" "Unexpected MIRType" ")"); do { MOZ_CrashSequence
(__null, 18188); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
18189 }
18190 return;
18191 }
18192 masm.loadUnboxedValue(src, type, dest);
18193}
18194
18195void CodeGenerator::visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* ins) {
18196 const MLoadFixedSlotAndUnbox* mir = ins->mir();
18197 MIRType type = mir->type();
18198 Register input = ToRegister(ins->object());
18199 AnyRegister result = ToAnyRegister(ins->output());
18200 Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0());
18201 size_t slot = mir->slot();
18202
18203 Address address(input, NativeObject::getFixedSlotOffset(slot));
18204
18205 Label bail;
18206 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, maybeTemp,
18207 &bail);
18208 if (mir->fallible()) {
18209 bailoutFrom(&bail, ins->snapshot());
18210 }
18211}
18212
18213void CodeGenerator::visitLoadDynamicSlotAndUnbox(
18214 LLoadDynamicSlotAndUnbox* ins) {
18215 const MLoadDynamicSlotAndUnbox* mir = ins->mir();
18216 MIRType type = mir->type();
18217 Register input = ToRegister(ins->slots());
18218 AnyRegister result = ToAnyRegister(ins->output());
18219 Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0());
18220 size_t slot = mir->slot();
18221
18222 Address address(input, slot * sizeof(JS::Value));
18223
18224 Label bail;
18225 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, maybeTemp,
18226 &bail);
18227 if (mir->fallible()) {
18228 bailoutFrom(&bail, ins->snapshot());
18229 }
18230}
18231
18232void CodeGenerator::visitLoadElementAndUnbox(LLoadElementAndUnbox* ins) {
18233 const MLoadElementAndUnbox* mir = ins->mir();
18234 MIRType type = mir->type();
18235 Register elements = ToRegister(ins->elements());
18236 AnyRegister result = ToAnyRegister(ins->output());
18237 Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0());
18238
18239 auto source = ToAddressOrBaseObjectElementIndex(elements, ins->index());
18240
18241 Label bail;
18242 source.match([&](const auto& source) {
18243 EmitLoadAndUnbox(masm, source, type, mir->fallible(), result, maybeTemp,
18244 &bail);
18245 });
18246
18247 if (mir->fallible()) {
18248 bailoutFrom(&bail, ins->snapshot());
18249 }
18250}
18251
18252void CodeGenerator::emitMaybeAtomizeSlot(LInstruction* ins, Register stringReg,
18253 Address slotAddr,
18254 TypedOrValueRegister dest) {
18255 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
18256 // This code is called with a non-atomic string in |stringReg|.
18257 // When it returns, |stringReg| contains an unboxed pointer to an
18258 // atomized version of that string, and |slotAddr| contains a
18259 // StringValue pointing to that atom. If |dest| is a ValueOperand,
18260 // it contains the same StringValue; otherwise we assert that |dest|
18261 // is |stringReg|.
18262
18263 saveLive(ins);
18264 pushArg(stringReg);
18265
18266 using Fn = JSAtom* (*)(JSContext*, JSString*);
18267 callVM<Fn, js::AtomizeString>(ins);
18268 StoreRegisterTo(stringReg).generate(this);
18269 restoreLiveIgnore(ins, StoreRegisterTo(stringReg).clobbered());
18270
18271 if (dest.hasValue()) {
18272 masm.moveValue(
18273 TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)),
18274 dest.valueReg());
18275 } else {
18276 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"
, 18276); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg"
")"); do { MOZ_CrashSequence(__null, 18276); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18277 }
18278
18279 emitPreBarrier(slotAddr);
18280 masm.storeTypedOrValue(dest, slotAddr);
18281
18282 // We don't need a post-barrier because atoms aren't nursery-allocated.
18283#ifdef DEBUG1
18284 // We need a temp register for the nursery check. Spill something.
18285 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
18286 allRegs.take(stringReg);
18287 Register temp = allRegs.takeAny();
18288 masm.push(temp);
18289
18290 Label tenured;
18291 masm.branchPtrInNurseryChunk(Assembler::NotEqual, stringReg, temp,
18292 &tenured);
18293 masm.assumeUnreachable("AtomizeString returned a nursery pointer");
18294 masm.bind(&tenured);
18295
18296 masm.pop(temp);
18297#endif
18298
18299 masm.jump(ool.rejoin());
18300 });
18301 addOutOfLineCode(ool, ins->mirRaw()->toInstruction());
18302 masm.branchTest32(Assembler::NonZero,
18303 Address(stringReg, JSString::offsetOfFlags()),
18304 Imm32(StringFlags::ATOM_BIT), ool->rejoin());
18305
18306 masm.branchTest32(Assembler::Zero,
18307 Address(stringReg, JSString::offsetOfFlags()),
18308 Imm32(StringFlags::ATOM_REF_BIT), ool->entry());
18309 masm.loadPtr(Address(stringReg, JSAtomRefString::offsetOfAtom()), stringReg);
18310
18311 if (dest.hasValue()) {
18312 masm.moveValue(
18313 TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)),
18314 dest.valueReg());
18315 } else {
18316 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"
, 18316); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg"
")"); do { MOZ_CrashSequence(__null, 18316); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18317 }
18318
18319 emitPreBarrier(slotAddr);
18320 masm.storeTypedOrValue(dest, slotAddr);
18321
18322 masm.bind(ool->rejoin());
18323}
18324
18325void CodeGenerator::visitLoadFixedSlotAndAtomize(
18326 LLoadFixedSlotAndAtomize* ins) {
18327 Register obj = ToRegister(ins->object());
18328 Register temp = ToRegister(ins->temp0());
18329 size_t slot = ins->mir()->slot();
18330 ValueOperand result = ToOutValue(ins);
18331
18332 Address slotAddr(obj, NativeObject::getFixedSlotOffset(slot));
18333 masm.loadValue(slotAddr, result);
18334
18335 Label notString;
18336 masm.branchTestString(Assembler::NotEqual, result, &notString);
18337 masm.unboxString(result, temp);
18338 emitMaybeAtomizeSlot(ins, temp, slotAddr, result);
18339 masm.bind(&notString);
18340}
18341
18342void CodeGenerator::visitLoadDynamicSlotAndAtomize(
18343 LLoadDynamicSlotAndAtomize* ins) {
18344 ValueOperand result = ToOutValue(ins);
18345 Register temp = ToRegister(ins->temp0());
18346 Register base = ToRegister(ins->input());
18347 int32_t offset = ins->mir()->slot() * sizeof(js::Value);
18348
18349 Address slotAddr(base, offset);
18350 masm.loadValue(slotAddr, result);
18351
18352 Label notString;
18353 masm.branchTestString(Assembler::NotEqual, result, &notString);
18354 masm.unboxString(result, temp);
18355 emitMaybeAtomizeSlot(ins, temp, slotAddr, result);
18356 masm.bind(&notString);
18357}
18358
18359void CodeGenerator::visitLoadFixedSlotUnboxAndAtomize(
18360 LLoadFixedSlotUnboxAndAtomize* ins) {
18361 const MLoadFixedSlotAndUnbox* mir = ins->mir();
18362 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"
, 18362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 18362); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18363 Register input = ToRegister(ins->object());
18364 AnyRegister result = ToAnyRegister(ins->output());
18365 size_t slot = mir->slot();
18366
18367 Address slotAddr(input, NativeObject::getFixedSlotOffset(slot));
18368
18369 Label bail;
18370 EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result,
18371 Register64::Invalid(), &bail);
18372 emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr,
18373 TypedOrValueRegister(MIRType::String, result));
18374
18375 if (mir->fallible()) {
18376 bailoutFrom(&bail, ins->snapshot());
18377 }
18378}
18379
18380void CodeGenerator::visitLoadDynamicSlotUnboxAndAtomize(
18381 LLoadDynamicSlotUnboxAndAtomize* ins) {
18382 const MLoadDynamicSlotAndUnbox* mir = ins->mir();
18383 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"
, 18383); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String"
")"); do { MOZ_CrashSequence(__null, 18383); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18384 Register input = ToRegister(ins->slots());
18385 AnyRegister result = ToAnyRegister(ins->output());
18386 size_t slot = mir->slot();
18387
18388 Address slotAddr(input, slot * sizeof(JS::Value));
18389
18390 Label bail;
18391 EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result,
18392 Register64::Invalid(), &bail);
18393 emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr,
18394 TypedOrValueRegister(MIRType::String, result));
18395
18396 if (mir->fallible()) {
18397 bailoutFrom(&bail, ins->snapshot());
18398 }
18399}
18400
18401void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) {
18402 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"
, 18402); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->preserveWrapper()"
")"); do { MOZ_CrashSequence(__null, 18402); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18403
18404 Register obj = ToRegister(ins->object());
18405 ValueOperand value = ToValue(ins->value());
18406 Register maybeTemp = ToTempRegisterOrInvalid(ins->temp0());
18407
18408 Shape* shape = ins->mir()->shape();
18409 masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) {
18410 EmitPreBarrier(masm, addr, MIRType::Shape);
18411 });
18412
18413 // Perform the store. No pre-barrier required since this is a new
18414 // initialization.
18415
18416 uint32_t offset = ins->mir()->slotOffset();
18417 if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) {
18418 Address slot(obj, offset);
18419 masm.storeValue(value, slot);
18420 } else {
18421 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), maybeTemp);
18422 Address slot(maybeTemp, offset);
18423 masm.storeValue(value, slot);
18424 }
18425}
18426
18427void CodeGenerator::visitAddAndStoreSlotPreserveWrapper(
18428 LAddAndStoreSlotPreserveWrapper* ins) {
18429 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"
, 18429); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->preserveWrapper()"
")"); do { MOZ_CrashSequence(__null, 18429); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18430
18431 Register obj = ToRegister(ins->object());
18432 ValueOperand value = ToValue(ins->value());
18433 Register temp0 = ToTempRegisterOrInvalid(ins->temp0());
18434 Register temp1 = ToTempRegisterOrInvalid(ins->temp1());
18435
18436 LiveRegisterSet liveRegs = liveVolatileRegs(ins);
18437 liveRegs.takeUnchecked(temp0);
18438 liveRegs.takeUnchecked(temp1);
18439 masm.preserveWrapper(obj, temp0, temp1, liveRegs);
18440 bailoutIfFalseBool(temp0, ins->snapshot());
18441
18442 Shape* shape = ins->mir()->shape();
18443 masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) {
18444 EmitPreBarrier(masm, addr, MIRType::Shape);
18445 });
18446
18447 // Perform the store. No pre-barrier required since this is a new
18448 // initialization.
18449
18450 uint32_t offset = ins->mir()->slotOffset();
18451 if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) {
18452 Address slot(obj, offset);
18453 masm.storeValue(value, slot);
18454 } else {
18455 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0);
18456 Address slot(temp0, offset);
18457 masm.storeValue(value, slot);
18458 }
18459}
18460
18461void CodeGenerator::visitAllocateAndStoreSlot(LAllocateAndStoreSlot* ins) {
18462 Register obj = ToRegister(ins->object());
18463 ValueOperand value = ToValue(ins->value());
18464 Register temp0 = ToRegister(ins->temp0());
18465 Register temp1 = ToRegister(ins->temp1());
18466
18467 if (ins->mir()->preserveWrapper()) {
18468 LiveRegisterSet liveRegs;
18469 liveRegs.addUnchecked(obj);
18470 liveRegs.addUnchecked(value);
18471 masm.preserveWrapper(obj, temp0, temp1, liveRegs);
18472 bailoutIfFalseBool(temp0, ins->snapshot());
18473 }
18474
18475 masm.Push(obj);
18476 masm.Push(value);
18477
18478 using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount);
18479 masm.setupAlignedABICall();
18480 masm.loadJSContext(temp0);
18481 masm.passABIArg(temp0);
18482 masm.passABIArg(obj);
18483 masm.move32(Imm32(ins->mir()->numNewSlots()), temp1);
18484 masm.passABIArg(temp1);
18485 masm.callWithABI<Fn, NativeObject::growSlotsPure>();
18486 masm.storeCallPointerResult(temp0);
18487
18488 masm.Pop(value);
18489 masm.Pop(obj);
18490
18491 bailoutIfFalseBool(temp0, ins->snapshot());
18492
18493 masm.storeObjShape(ins->mir()->shape(), obj,
18494 [](MacroAssembler& masm, const Address& addr) {
18495 EmitPreBarrier(masm, addr, MIRType::Shape);
18496 });
18497
18498 // Perform the store. No pre-barrier required since this is a new
18499 // initialization.
18500 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0);
18501 Address slot(temp0, ins->mir()->slotOffset());
18502 masm.storeValue(value, slot);
18503}
18504
18505void CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV* ins) {
18506 Register obj = ToRegister(ins->obj());
18507 size_t slot = ins->mir()->slot();
18508
18509 ValueOperand value = ToValue(ins->value());
18510
18511 Address address(obj, NativeObject::getFixedSlotOffset(slot));
18512 if (ins->mir()->needsPreBarrier()) {
18513 emitPreBarrier(address);
18514 }
18515
18516 masm.storeValue(value, address);
18517}
18518
18519void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) {
18520 const Register obj = ToRegister(ins->obj());
18521 size_t slot = ins->mir()->slot();
18522
18523 const LAllocation* value = ins->value();
18524 MIRType valueType = ins->mir()->value()->type();
18525
18526 Address address(obj, NativeObject::getFixedSlotOffset(slot));
18527 if (ins->mir()->needsPreBarrier()) {
18528 emitPreBarrier(address);
18529 }
18530
18531 ConstantOrRegister nvalue =
18532 value->isConstant()
18533 ? ConstantOrRegister(value->toConstant()->toJSValue())
18534 : TypedOrValueRegister(valueType, ToAnyRegister(value));
18535 masm.storeConstantOrRegister(nvalue, address);
18536}
18537
18538void CodeGenerator::visitGetNameCache(LGetNameCache* ins) {
18539 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18540 Register envChain = ToRegister(ins->envObj());
18541 ValueOperand output = ToOutValue(ins);
18542 Register temp = ToRegister(ins->temp0());
18543
18544 IonGetNameIC ic(liveRegs, envChain, output, temp);
18545 addIC(ins, allocateIC(ic));
18546}
18547
18548static bool IsConstantNonIndexString(const ConstantOrRegister& id) {
18549 if (!id.constant() || !id.value().isString()) {
18550 return false;
18551 }
18552 return !id.value().toString()->asOffThreadAtom().isIndex();
18553}
18554
18555void CodeGenerator::addGetPropertyCache(LInstruction* ins,
18556 LiveRegisterSet liveRegs,
18557 TypedOrValueRegister value,
18558 const ConstantOrRegister& id,
18559 ValueOperand output) {
18560 CacheKind kind = CacheKind::GetElem;
18561 if (IsConstantNonIndexString(id)) {
18562 kind = CacheKind::GetProp;
18563 }
18564 IonGetPropertyIC cache(kind, liveRegs, value, id, output);
18565 addIC(ins, allocateIC(cache));
18566}
18567
18568void CodeGenerator::addSetPropertyCache(LInstruction* ins,
18569 LiveRegisterSet liveRegs,
18570 Register objReg, Register temp,
18571 const ConstantOrRegister& id,
18572 const ConstantOrRegister& value,
18573 bool strict) {
18574 CacheKind kind = CacheKind::SetElem;
18575 if (IsConstantNonIndexString(id)) {
18576 kind = CacheKind::SetProp;
18577 }
18578 IonSetPropertyIC cache(kind, liveRegs, objReg, temp, id, value, strict);
18579 addIC(ins, allocateIC(cache));
18580}
18581
18582ConstantOrRegister CodeGenerator::toConstantOrRegister(LInstruction* lir,
18583 size_t n, MIRType type) {
18584 if (type == MIRType::Value) {
18585 return TypedOrValueRegister(ToValue(lir->getBoxOperand(n)));
18586 }
18587
18588 const LAllocation* value = lir->getOperand(n);
18589 if (value->isConstant()) {
18590 return ConstantOrRegister(value->toConstant()->toJSValue());
18591 }
18592
18593 return TypedOrValueRegister(type, ToAnyRegister(value));
18594}
18595
18596void CodeGenerator::visitGetPropertyCache(LGetPropertyCache* ins) {
18597 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18598 TypedOrValueRegister value =
18599 toConstantOrRegister(ins, LGetPropertyCache::ValueIndex,
18600 ins->mir()->value()->type())
18601 .reg();
18602 ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCache::IdIndex,
18603 ins->mir()->idval()->type());
18604 ValueOperand output = ToOutValue(ins);
18605 addGetPropertyCache(ins, liveRegs, value, id, output);
18606}
18607
18608void CodeGenerator::visitGetPropSuperCache(LGetPropSuperCache* ins) {
18609 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18610 Register obj = ToRegister(ins->obj());
18611 TypedOrValueRegister receiver =
18612 toConstantOrRegister(ins, LGetPropSuperCache::ReceiverIndex,
18613 ins->mir()->receiver()->type())
18614 .reg();
18615 ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCache::IdIndex,
18616 ins->mir()->idval()->type());
18617 ValueOperand output = ToOutValue(ins);
18618
18619 CacheKind kind = CacheKind::GetElemSuper;
18620 if (IsConstantNonIndexString(id)) {
18621 kind = CacheKind::GetPropSuper;
18622 }
18623
18624 IonGetPropSuperIC cache(kind, liveRegs, obj, receiver, id, output);
18625 addIC(ins, allocateIC(cache));
18626}
18627
18628void CodeGenerator::visitBindNameCache(LBindNameCache* ins) {
18629 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18630 Register envChain = ToRegister(ins->environmentChain());
18631 Register output = ToRegister(ins->output());
18632 Register temp = ToRegister(ins->temp0());
18633
18634 IonBindNameIC ic(liveRegs, envChain, output, temp);
18635 addIC(ins, allocateIC(ic));
18636}
18637
18638void CodeGenerator::visitHasOwnCache(LHasOwnCache* ins) {
18639 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18640 TypedOrValueRegister value =
18641 toConstantOrRegister(ins, LHasOwnCache::ValueIndex,
18642 ins->mir()->value()->type())
18643 .reg();
18644 TypedOrValueRegister id = toConstantOrRegister(ins, LHasOwnCache::IdIndex,
18645 ins->mir()->idval()->type())
18646 .reg();
18647 Register output = ToRegister(ins->output());
18648
18649 IonHasOwnIC cache(liveRegs, value, id, output);
18650 addIC(ins, allocateIC(cache));
18651}
18652
18653void CodeGenerator::visitCheckPrivateFieldCache(LCheckPrivateFieldCache* ins) {
18654 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18655 TypedOrValueRegister value =
18656 toConstantOrRegister(ins, LCheckPrivateFieldCache::ValueIndex,
18657 ins->mir()->value()->type())
18658 .reg();
18659 TypedOrValueRegister id =
18660 toConstantOrRegister(ins, LCheckPrivateFieldCache::IdIndex,
18661 ins->mir()->idval()->type())
18662 .reg();
18663 Register output = ToRegister(ins->output());
18664
18665 IonCheckPrivateFieldIC cache(liveRegs, value, id, output);
18666 addIC(ins, allocateIC(cache));
18667}
18668
18669void CodeGenerator::visitNewPrivateName(LNewPrivateName* ins) {
18670 pushArg(ImmGCPtr(ins->mir()->name()));
18671
18672 using Fn = JS::Symbol* (*)(JSContext*, Handle<JSAtom*>);
18673 callVM<Fn, NewPrivateName>(ins);
18674}
18675
18676void CodeGenerator::visitDeleteProperty(LDeleteProperty* lir) {
18677 pushArg(ImmGCPtr(lir->mir()->name()));
18678 pushArg(ToValue(lir->value()));
18679
18680 using Fn = bool (*)(JSContext*, HandleValue, Handle<PropertyName*>, bool*);
18681 if (lir->mir()->strict()) {
18682 callVM<Fn, DelPropOperation<true>>(lir);
18683 } else {
18684 callVM<Fn, DelPropOperation<false>>(lir);
18685 }
18686}
18687
18688void CodeGenerator::visitDeleteElement(LDeleteElement* lir) {
18689 pushArg(ToValue(lir->index()));
18690 pushArg(ToValue(lir->value()));
18691
18692 using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*);
18693 if (lir->mir()->strict()) {
18694 callVM<Fn, DelElemOperation<true>>(lir);
18695 } else {
18696 callVM<Fn, DelElemOperation<false>>(lir);
18697 }
18698}
18699
18700void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) {
18701 Register obj = ToRegister(lir->object());
18702 Register iterObj = ToRegister(lir->output());
18703 Register temp = ToRegister(lir->temp0());
18704 Register temp2 = ToRegister(lir->temp1());
18705 Register temp3 = ToRegister(lir->temp2());
18706
18707 using Fn = PropertyIteratorObject* (*)(JSContext*, HandleObject);
18708 OutOfLineCode* ool = nullptr;
18709
18710 if (lir->mir()->skipRegistration()) {
18711 if (lir->mir()->wantsIndices()) {
18712 ool = oolCallVM<Fn, GetIteratorWithIndicesForObjectKeys>(
18713 lir, ArgList(obj), StoreRegisterTo(iterObj));
18714 } else {
18715 ool = oolCallVM<Fn, GetIteratorForObjectKeys>(lir, ArgList(obj),
18716 StoreRegisterTo(iterObj));
18717 }
18718 } else {
18719 if (lir->mir()->wantsIndices()) {
18720 ool = oolCallVM<Fn, GetIteratorWithIndices>(lir, ArgList(obj),
18721 StoreRegisterTo(iterObj));
18722 } else {
18723 ool = oolCallVM<Fn, GetIterator>(lir, ArgList(obj),
18724 StoreRegisterTo(iterObj));
18725 }
18726 }
18727
18728#ifdef DEBUG1
18729 if (!lir->mir()->getAliasSet().isStore()) {
18730 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"
, 18730); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->skipRegistration()"
")"); do { MOZ_CrashSequence(__null, 18730); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18731 Label done;
18732 masm.branchTestObjectIsProxy(false, obj, temp, &done);
18733 masm.assumeUnreachable("ObjectToIterator on a proxy must be a store.");
18734 masm.bind(&done);
18735 }
18736#endif
18737
18738 masm.maybeLoadIteratorFromShape(obj, iterObj, temp, temp2, temp3,
18739 ool->entry(),
18740 !lir->mir()->skipRegistration());
18741
18742 Register nativeIter = temp;
18743 masm.loadPrivate(
18744 Address(iterObj, PropertyIteratorObject::offsetOfIteratorSlot()),
18745 nativeIter);
18746
18747 Address iterFlagsAddr(nativeIter, NativeIterator::offsetOfFlags());
18748 if (lir->mir()->wantsIndices()) {
18749 // At least one consumer of the output of this iterator has been optimized
18750 // to use iterator indices. If the cached iterator doesn't include indices,
18751 // but it was marked to indicate that we can create them if needed, then we
18752 // do a VM call to replace the cached iterator with a fresh iterator
18753 // including indices.
18754 masm.branchTest32(Assembler::NonZero, iterFlagsAddr,
18755 Imm32(NativeIterator::Flags::IndicesSupported),
18756 ool->entry());
18757 }
18758
18759 if (!lir->mir()->skipRegistration()) {
18760 masm.storePtr(obj, Address(nativeIter,
18761 NativeIterator::offsetOfObjectBeingIterated()));
18762 masm.or32(Imm32(NativeIterator::Flags::Active), iterFlagsAddr);
18763
18764 Register enumeratorsAddr = temp2;
18765 masm.movePtr(ImmPtr(lir->mir()->enumeratorsAddr()), enumeratorsAddr);
18766 masm.registerIterator(enumeratorsAddr, nativeIter, temp3);
18767
18768 // Generate post-write barrier for storing to
18769 // |iterObj->objectBeingIterated_|. We already know that |iterObj| is
18770 // tenured, so we only have to check |obj|.
18771 Label skipBarrier;
18772 masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp2, &skipBarrier);
18773 {
18774 LiveRegisterSet save = liveVolatileRegs(lir);
18775 save.takeUnchecked(temp);
18776 save.takeUnchecked(temp2);
18777 save.takeUnchecked(temp3);
18778 if (iterObj.volatile_()) {
18779 save.addUnchecked(iterObj);
18780 }
18781
18782 masm.PushRegsInMask(save);
18783 emitPostWriteBarrier(iterObj);
18784 masm.PopRegsInMask(save);
18785 }
18786 masm.bind(&skipBarrier);
18787 }
18788
18789 masm.bind(ool->rejoin());
18790}
18791
18792void CodeGenerator::visitValueToIterator(LValueToIterator* lir) {
18793 pushArg(ToValue(lir->value()));
18794
18795 using Fn = PropertyIteratorObject* (*)(JSContext*, HandleValue);
18796 callVM<Fn, ValueToIterator>(lir);
18797}
18798
18799void CodeGenerator::emitIteratorHasIndicesAndBranch(Register iterator,
18800 Register object,
18801 Register temp,
18802 Register temp2,
18803 Label* ifFalse) {
18804 // Check that the iterator has indices available.
18805 Address nativeIterAddr(iterator,
18806 PropertyIteratorObject::offsetOfIteratorSlot());
18807 masm.loadPrivate(nativeIterAddr, temp);
18808 masm.branchTest32(Assembler::Zero,
18809 Address(temp, NativeIterator::offsetOfFlags()),
18810 Imm32(NativeIterator::Flags::IndicesAvailable), ifFalse);
18811
18812 // Guard that the first shape stored in the iterator matches the current
18813 // shape of the iterated object.
18814 Address objShapeAddr(temp, NativeIterator::offsetOfObjectShape());
18815 masm.loadPtr(objShapeAddr, temp);
18816 masm.branchTestObjShape(Assembler::NotEqual, object, temp, temp2, object,
18817 ifFalse);
18818}
18819
18820void CodeGenerator::visitIteratorHasIndicesAndBranch(
18821 LIteratorHasIndicesAndBranch* lir) {
18822 Register iterator = ToRegister(lir->iterator());
18823 Register object = ToRegister(lir->object());
18824 Register temp = ToRegister(lir->temp0());
18825 Register temp2 = ToRegister(lir->temp1());
18826 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
18827 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
18828
18829 emitIteratorHasIndicesAndBranch(iterator, object, temp, temp2, ifFalse);
18830
18831 if (!isNextBlock(lir->ifTrue()->lir())) {
18832 masm.jump(ifTrue);
18833 }
18834}
18835
18836void CodeGenerator::visitIteratorsMatchAndHaveIndicesAndBranch(
18837 LIteratorsMatchAndHaveIndicesAndBranch* lir) {
18838 Register iterator = ToRegister(lir->iterator());
18839 Register otherIterator = ToRegister(lir->otherIterator());
18840 Register object = ToRegister(lir->object());
18841 Register temp = ToRegister(lir->temp0());
18842 Register temp2 = ToRegister(lir->temp1());
18843 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
18844 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
18845
18846 // Check that the iterators match, and then we can use either iterator
18847 // as a basis as if this were visitIteratorHasIndicesAndBranch
18848 masm.branchPtr(Assembler::NotEqual, iterator, otherIterator, ifFalse);
18849
18850 emitIteratorHasIndicesAndBranch(iterator, object, temp, temp2, ifFalse);
18851
18852 if (!isNextBlock(lir->ifTrue()->lir())) {
18853 masm.jump(ifTrue);
18854 }
18855}
18856
18857void CodeGenerator::visitLoadSlotByIteratorIndexCommon(Register object,
18858 Register indexScratch,
18859 Register kindScratch,
18860 ValueOperand result) {
18861 Label notDynamicSlot, notFixedSlot, done;
18862 masm.branch32(Assembler::NotEqual, kindScratch,
18863 Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)),
18864 &notDynamicSlot);
18865 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), kindScratch);
18866 masm.loadValue(BaseValueIndex(kindScratch, indexScratch), result);
18867 masm.jump(&done);
18868
18869 masm.bind(&notDynamicSlot);
18870 masm.branch32(Assembler::NotEqual, kindScratch,
18871 Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot);
18872 // Fixed slot
18873 masm.loadValue(BaseValueIndex(object, indexScratch, sizeof(NativeObject)),
18874 result);
18875 masm.jump(&done);
18876 masm.bind(&notFixedSlot);
18877
18878#ifdef DEBUG1
18879 Label kindOkay;
18880 masm.branch32(Assembler::Equal, kindScratch,
18881 Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay);
18882 masm.assumeUnreachable("Invalid PropertyIndex::Kind");
18883 masm.bind(&kindOkay);
18884#endif
18885
18886 // Dense element
18887 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), kindScratch);
18888 Label indexOkay;
18889 Address initLength(kindScratch, ObjectElements::offsetOfInitializedLength());
18890 masm.branch32(Assembler::Above, initLength, indexScratch, &indexOkay);
18891 masm.assumeUnreachable("Dense element out of bounds");
18892 masm.bind(&indexOkay);
18893
18894 masm.loadValue(BaseObjectElementIndex(kindScratch, indexScratch), result);
18895 masm.branchTestMagicValue(Assembler::NotEqual, result, JS_ELEMENTS_HOLE,
18896 &done);
18897 masm.assumeUnreachable("Dense element is a hole");
18898 masm.bind(&done);
18899}
18900
18901void CodeGenerator::visitLoadSlotByIteratorIndex(
18902 LLoadSlotByIteratorIndex* lir) {
18903 Register object = ToRegister(lir->object());
18904 Register iterator = ToRegister(lir->iterator());
18905 Register indexScratch = ToRegister(lir->temp0());
18906 Register kindScratch = ToRegister(lir->temp1());
18907 ValueOperand result = ToOutValue(lir);
18908
18909 masm.extractCurrentIndexAndKindFromIterator(iterator, indexScratch,
18910 kindScratch);
18911
18912 visitLoadSlotByIteratorIndexCommon(object, indexScratch, kindScratch, result);
18913}
18914
18915void CodeGenerator::visitLoadSlotByIteratorIndexIndexed(
18916 LLoadSlotByIteratorIndexIndexed* lir) {
18917 Register object = ToRegister(lir->object());
18918 Register iterator = ToRegister(lir->iterator());
18919 Register index = ToRegister(lir->index());
18920 Register indexScratch = ToRegister(lir->temp0());
18921 Register kindScratch = ToRegister(lir->temp1());
18922 ValueOperand result = ToOutValue(lir);
18923
18924 masm.extractIndexAndKindFromIteratorByIterIndex(iterator, index, kindScratch,
18925 indexScratch);
18926
18927 visitLoadSlotByIteratorIndexCommon(object, indexScratch, kindScratch, result);
18928}
18929
18930void CodeGenerator::visitStoreSlotByIteratorIndexCommon(Register object,
18931 Register indexScratch,
18932 Register kindScratch,
18933 ValueOperand value) {
18934 Label notDynamicSlot, notFixedSlot, done, doStore;
18935 masm.branch32(Assembler::NotEqual, kindScratch,
18936 Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)),
18937 &notDynamicSlot);
18938 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), kindScratch);
18939 masm.computeEffectiveAddress(BaseValueIndex(kindScratch, indexScratch),
18940 indexScratch);
18941 masm.jump(&doStore);
18942
18943 masm.bind(&notDynamicSlot);
18944 masm.branch32(Assembler::NotEqual, kindScratch,
18945 Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot);
18946 // Fixed slot
18947 masm.computeEffectiveAddress(
18948 BaseValueIndex(object, indexScratch, sizeof(NativeObject)), indexScratch);
18949 masm.jump(&doStore);
18950 masm.bind(&notFixedSlot);
18951
18952#ifdef DEBUG1
18953 Label kindOkay;
18954 masm.branch32(Assembler::Equal, kindScratch,
18955 Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay);
18956 masm.assumeUnreachable("Invalid PropertyIndex::Kind");
18957 masm.bind(&kindOkay);
18958#endif
18959
18960 // Dense element
18961 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), kindScratch);
18962 Label indexOkay;
18963 Address initLength(kindScratch, ObjectElements::offsetOfInitializedLength());
18964 masm.branch32(Assembler::Above, initLength, indexScratch, &indexOkay);
18965 masm.assumeUnreachable("Dense element out of bounds");
18966 masm.bind(&indexOkay);
18967
18968 BaseObjectElementIndex elementAddress(kindScratch, indexScratch);
18969 masm.computeEffectiveAddress(elementAddress, indexScratch);
18970
18971 masm.bind(&doStore);
18972 Address storeAddress(indexScratch, 0);
18973 emitPreBarrier(storeAddress);
18974 masm.storeValue(value, storeAddress);
18975
18976 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, kindScratch, &done);
18977 masm.branchPtrInNurseryChunk(Assembler::Equal, object, kindScratch, &done);
18978
18979 saveVolatile(kindScratch);
18980 emitPostWriteBarrier(object);
18981 restoreVolatile(kindScratch);
18982
18983 masm.bind(&done);
18984}
18985
18986void CodeGenerator::visitStoreSlotByIteratorIndex(
18987 LStoreSlotByIteratorIndex* lir) {
18988 Register object = ToRegister(lir->object());
18989 Register iterator = ToRegister(lir->iterator());
18990 ValueOperand value = ToValue(lir->value());
18991 Register indexScratch = ToRegister(lir->temp0());
18992 Register kindScratch = ToRegister(lir->temp1());
18993
18994 masm.extractCurrentIndexAndKindFromIterator(iterator, indexScratch,
18995 kindScratch);
18996
18997 visitStoreSlotByIteratorIndexCommon(object, indexScratch, kindScratch, value);
18998}
18999
19000void CodeGenerator::visitStoreSlotByIteratorIndexIndexed(
19001 LStoreSlotByIteratorIndexIndexed* lir) {
19002 Register object = ToRegister(lir->object());
19003 Register iterator = ToRegister(lir->iterator());
19004 Register index = ToRegister(lir->index());
19005 ValueOperand value = ToValue(lir->value());
19006 Register indexScratch = ToRegister(lir->temp0());
19007 Register kindScratch = ToRegister(lir->temp1());
19008
19009 masm.extractIndexAndKindFromIteratorByIterIndex(iterator, index, kindScratch,
19010 indexScratch);
19011
19012 visitStoreSlotByIteratorIndexCommon(object, indexScratch, kindScratch, value);
19013}
19014
19015void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) {
19016 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
19017 Register objReg = ToRegister(ins->object());
19018 Register temp = ToRegister(ins->temp0());
19019
19020 ConstantOrRegister id = toConstantOrRegister(ins, LSetPropertyCache::IdIndex,
19021 ins->mir()->idval()->type());
19022 ConstantOrRegister value = toConstantOrRegister(
19023 ins, LSetPropertyCache::ValueIndex, ins->mir()->value()->type());
19024
19025 addSetPropertyCache(ins, liveRegs, objReg, temp, id, value,
19026 ins->mir()->strict());
19027}
19028
19029void CodeGenerator::visitThrow(LThrow* lir) {
19030 pushArg(ToValue(lir->value()));
19031
19032 using Fn = bool (*)(JSContext*, HandleValue);
19033 callVM<Fn, js::ThrowOperation>(lir);
19034}
19035
19036void CodeGenerator::visitThrowWithStack(LThrowWithStack* lir) {
19037 pushArg(ToValue(lir->stack()));
19038 pushArg(ToValue(lir->value()));
19039
19040 using Fn = bool (*)(JSContext*, HandleValue, HandleValue);
19041 callVM<Fn, js::ThrowWithStackOperation>(lir);
19042}
19043
19044void CodeGenerator::emitTypeOfJSType(JSValueType type, Register output) {
19045 switch (type) {
19046 case JSVAL_TYPE_OBJECT:
19047 masm.move32(Imm32(JSTYPE_OBJECT), output);
19048 break;
19049 case JSVAL_TYPE_DOUBLE:
19050 case JSVAL_TYPE_INT32:
19051 masm.move32(Imm32(JSTYPE_NUMBER), output);
19052 break;
19053 case JSVAL_TYPE_BOOLEAN:
19054 masm.move32(Imm32(JSTYPE_BOOLEAN), output);
19055 break;
19056 case JSVAL_TYPE_UNDEFINED:
19057 masm.move32(Imm32(JSTYPE_UNDEFINED), output);
19058 break;
19059 case JSVAL_TYPE_NULL:
19060 masm.move32(Imm32(JSTYPE_OBJECT), output);
19061 break;
19062 case JSVAL_TYPE_STRING:
19063 masm.move32(Imm32(JSTYPE_STRING), output);
19064 break;
19065 case JSVAL_TYPE_SYMBOL:
19066 masm.move32(Imm32(JSTYPE_SYMBOL), output);
19067 break;
19068 case JSVAL_TYPE_BIGINT:
19069 masm.move32(Imm32(JSTYPE_BIGINT), output);
19070 break;
19071 default:
19072 MOZ_CRASH("Unsupported JSValueType")do { do { } while (false); MOZ_ReportCrash("" "Unsupported JSValueType"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19072); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported JSValueType" ")"); do { MOZ_CrashSequence
(__null, 19072); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19073 }
19074}
19075
19076void CodeGenerator::emitTypeOfCheck(JSValueType type, Register tag,
19077 Register output, Label* done,
19078 Label* oolObject) {
19079 Label notMatch;
19080 switch (type) {
19081 case JSVAL_TYPE_OBJECT:
19082 // The input may be a callable object (result is "function") or
19083 // may emulate undefined (result is "undefined"). Use an OOL path.
19084 masm.branchTestObject(Assembler::Equal, tag, oolObject);
19085 return;
19086 case JSVAL_TYPE_DOUBLE:
19087 case JSVAL_TYPE_INT32:
19088 masm.branchTestNumber(Assembler::NotEqual, tag, &notMatch);
19089 break;
19090 default:
19091 masm.branchTestType(Assembler::NotEqual, tag, type, &notMatch);
19092 break;
19093 }
19094
19095 emitTypeOfJSType(type, output);
19096 masm.jump(done);
19097 masm.bind(&notMatch);
19098}
19099
19100void CodeGenerator::visitTypeOfV(LTypeOfV* lir) {
19101 ValueOperand value = ToValue(lir->input());
19102 Register output = ToRegister(lir->output());
19103 Register tag = masm.extractTag(value, output);
19104
19105 Label done;
19106
19107 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
19108 ValueOperand input = ToValue(lir->input());
19109 Register temp = ToTempUnboxRegister(lir->temp0());
19110 Register output = ToRegister(lir->output());
19111
19112 Register obj = masm.extractObject(input, temp);
19113 emitTypeOfObject(obj, output, ool.rejoin());
19114 masm.jump(ool.rejoin());
19115 });
19116 addOutOfLineCode(ool, lir->mir());
19117
19118 const std::initializer_list<JSValueType> defaultOrder = {
19119 JSVAL_TYPE_OBJECT, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_UNDEFINED,
19120 JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_STRING,
19121 JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
19122
19123 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
19124
19125 // Generate checks for previously observed types first.
19126 // The TypeDataList is sorted by descending frequency.
19127 for (auto& observed : lir->mir()->observedTypes()) {
19128 JSValueType type = observed.type();
19129
19130 // Unify number types.
19131 if (type == JSVAL_TYPE_INT32) {
19132 type = JSVAL_TYPE_DOUBLE;
19133 }
19134
19135 remaining -= type;
19136
19137 emitTypeOfCheck(type, tag, output, &done, ool->entry());
19138 }
19139
19140 // Generate checks for remaining types.
19141 for (auto type : defaultOrder) {
19142 if (!remaining.contains(type)) {
19143 continue;
19144 }
19145 remaining -= type;
19146
19147 if (remaining.isEmpty() && type != JSVAL_TYPE_OBJECT) {
19148 // We can skip the check for the last remaining type, unless the type is
19149 // JSVAL_TYPE_OBJECT, which may have to go through the OOL path.
19150#ifdef DEBUG1
19151 emitTypeOfCheck(type, tag, output, &done, ool->entry());
19152 masm.assumeUnreachable("Unexpected Value type in visitTypeOfV");
19153#else
19154 emitTypeOfJSType(type, output);
19155#endif
19156 } else {
19157 emitTypeOfCheck(type, tag, output, &done, ool->entry());
19158 }
19159 }
19160 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", 19160); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { MOZ_CrashSequence
(__null, 19160); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
19161
19162 masm.bind(&done);
19163 masm.bind(ool->rejoin());
19164}
19165
19166void CodeGenerator::emitTypeOfObject(Register obj, Register output,
19167 Label* done) {
19168 Label slowCheck, isObject, isCallable, isUndefined;
19169 masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable,
19170 &isUndefined);
19171
19172 masm.bind(&isCallable);
19173 masm.move32(Imm32(JSTYPE_FUNCTION), output);
19174 masm.jump(done);
19175
19176 masm.bind(&isUndefined);
19177 masm.move32(Imm32(JSTYPE_UNDEFINED), output);
19178 masm.jump(done);
19179
19180 masm.bind(&isObject);
19181 masm.move32(Imm32(JSTYPE_OBJECT), output);
19182 masm.jump(done);
19183
19184 masm.bind(&slowCheck);
19185
19186 saveVolatile(output);
19187 using Fn = JSType (*)(JSObject*);
19188 masm.setupAlignedABICall();
19189 masm.passABIArg(obj);
19190 masm.callWithABI<Fn, js::TypeOfObject>();
19191 masm.storeCallInt32Result(output);
19192 restoreVolatile(output);
19193}
19194
19195void CodeGenerator::visitTypeOfO(LTypeOfO* lir) {
19196 Register obj = ToRegister(lir->object());
19197 Register output = ToRegister(lir->output());
19198
19199 Label done;
19200 emitTypeOfObject(obj, output, &done);
19201 masm.bind(&done);
19202}
19203
19204void CodeGenerator::visitTypeOfName(LTypeOfName* lir) {
19205 Register input = ToRegister(lir->input());
19206 Register output = ToRegister(lir->output());
19207
19208#ifdef DEBUG1
19209 Label ok;
19210 masm.branch32(Assembler::Below, input, Imm32(JSTYPE_LIMIT), &ok);
19211 masm.assumeUnreachable("bad JSType");
19212 masm.bind(&ok);
19213#endif
19214
19215 static_assert(JSTYPE_UNDEFINED == 0);
19216
19217 masm.movePtr(ImmPtr(&gen->runtime->names().undefined), output);
19218 masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
19219}
19220
19221void CodeGenerator::emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj,
19222 Register output) {
19223 saveVolatile(output);
19224 using Fn = JSType (*)(JSObject*);
19225 masm.setupAlignedABICall();
19226 masm.passABIArg(obj);
19227 masm.callWithABI<Fn, js::TypeOfObject>();
19228 masm.storeCallInt32Result(output);
19229 restoreVolatile(output);
19230
19231 auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false);
19232 masm.cmp32Set(cond, output, Imm32(mir->jstype()), output);
19233}
19234
19235void CodeGenerator::emitTypeOfIsObject(MTypeOfIs* mir, Register obj,
19236 Register output, Label* success,
19237 Label* fail, Label* slowCheck) {
19238 Label* isObject = fail;
19239 Label* isFunction = fail;
19240 Label* isUndefined = fail;
19241
19242 switch (mir->jstype()) {
19243 case JSTYPE_UNDEFINED:
19244 isUndefined = success;
19245 break;
19246
19247 case JSTYPE_OBJECT:
19248 isObject = success;
19249 break;
19250
19251 case JSTYPE_FUNCTION:
19252 isFunction = success;
19253 break;
19254
19255 case JSTYPE_STRING:
19256 case JSTYPE_NUMBER:
19257 case JSTYPE_BOOLEAN:
19258 case JSTYPE_SYMBOL:
19259 case JSTYPE_BIGINT:
19260 case JSTYPE_LIMIT:
19261 MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19261); AnnotateMozCrashReason
("MOZ_CRASH(" "Primitive type" ")"); do { MOZ_CrashSequence(__null
, 19261); __attribute__((nomerge)) ::abort(); } while (false)
; } while (false)
;
19262 }
19263
19264 masm.typeOfObject(obj, output, slowCheck, isObject, isFunction, isUndefined);
19265
19266 auto op = mir->jsop();
19267
19268 Label done;
19269 masm.bind(fail);
19270 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
19271 masm.jump(&done);
19272 masm.bind(success);
19273 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
19274 masm.bind(&done);
19275}
19276
19277void CodeGenerator::visitTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* lir) {
19278 ValueOperand input = ToValue(lir->input());
19279 Register output = ToRegister(lir->output());
19280 Register temp = ToTempUnboxRegister(lir->temp0());
19281
19282 auto* mir = lir->mir();
19283
19284 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
19285 ValueOperand input = ToValue(lir->input());
19286 Register output = ToRegister(lir->output());
19287 Register temp = ToTempUnboxRegister(lir->temp0());
19288
19289 Register obj = masm.extractObject(input, temp);
19290
19291 emitTypeOfIsObjectOOL(lir->mir(), obj, output);
19292
19293 masm.jump(ool.rejoin());
19294 });
19295 addOutOfLineCode(ool, mir);
19296
19297 Label success, fail;
19298
19299 switch (mir->jstype()) {
19300 case JSTYPE_UNDEFINED: {
19301 ScratchTagScope tag(masm, input);
19302 masm.splitTagForTest(input, tag);
19303
19304 masm.branchTestUndefined(Assembler::Equal, tag, &success);
19305 masm.branchTestObject(Assembler::NotEqual, tag, &fail);
19306 break;
19307 }
19308
19309 case JSTYPE_OBJECT: {
19310 ScratchTagScope tag(masm, input);
19311 masm.splitTagForTest(input, tag);
19312
19313 masm.branchTestNull(Assembler::Equal, tag, &success);
19314 masm.branchTestObject(Assembler::NotEqual, tag, &fail);
19315 break;
19316 }
19317
19318 case JSTYPE_FUNCTION: {
19319 masm.branchTestObject(Assembler::NotEqual, input, &fail);
19320 break;
19321 }
19322
19323 case JSTYPE_STRING:
19324 case JSTYPE_NUMBER:
19325 case JSTYPE_BOOLEAN:
19326 case JSTYPE_SYMBOL:
19327 case JSTYPE_BIGINT:
19328 case JSTYPE_LIMIT:
19329 MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19329); AnnotateMozCrashReason
("MOZ_CRASH(" "Primitive type" ")"); do { MOZ_CrashSequence(__null
, 19329); __attribute__((nomerge)) ::abort(); } while (false)
; } while (false)
;
19330 }
19331
19332 Register obj = masm.extractObject(input, temp);
19333
19334 emitTypeOfIsObject(mir, obj, output, &success, &fail, ool->entry());
19335
19336 masm.bind(ool->rejoin());
19337}
19338
19339void CodeGenerator::visitTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* lir) {
19340 Register input = ToRegister(lir->input());
19341 Register output = ToRegister(lir->output());
19342
19343 auto* mir = lir->mir();
19344
19345 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
19346 Register input = ToRegister(lir->input());
19347 Register output = ToRegister(lir->output());
19348
19349 emitTypeOfIsObjectOOL(lir->mir(), input, output);
19350
19351 masm.jump(ool.rejoin());
19352 });
19353 addOutOfLineCode(ool, mir);
19354
19355 Label success, fail;
19356 emitTypeOfIsObject(mir, input, output, &success, &fail, ool->entry());
19357
19358 masm.bind(ool->rejoin());
19359}
19360
19361void CodeGenerator::visitTypeOfIsPrimitive(LTypeOfIsPrimitive* lir) {
19362 ValueOperand input = ToValue(lir->input());
19363 Register output = ToRegister(lir->output());
19364
19365 auto* mir = lir->mir();
19366 auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false);
19367
19368 switch (mir->jstype()) {
19369 case JSTYPE_STRING:
19370 masm.testStringSet(cond, input, output);
19371 break;
19372 case JSTYPE_NUMBER:
19373 masm.testNumberSet(cond, input, output);
19374 break;
19375 case JSTYPE_BOOLEAN:
19376 masm.testBooleanSet(cond, input, output);
19377 break;
19378 case JSTYPE_SYMBOL:
19379 masm.testSymbolSet(cond, input, output);
19380 break;
19381 case JSTYPE_BIGINT:
19382 masm.testBigIntSet(cond, input, output);
19383 break;
19384
19385 case JSTYPE_UNDEFINED:
19386 case JSTYPE_OBJECT:
19387 case JSTYPE_FUNCTION:
19388 case JSTYPE_LIMIT:
19389 MOZ_CRASH("Non-primitive type")do { do { } while (false); MOZ_ReportCrash("" "Non-primitive type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19389); AnnotateMozCrashReason
("MOZ_CRASH(" "Non-primitive type" ")"); do { MOZ_CrashSequence
(__null, 19389); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19390 }
19391}
19392
19393void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) {
19394 pushArg(ToValue(lir->nextMethod()));
19395 pushArg(ToRegister(lir->iterator()));
19396
19397 using Fn = JSObject* (*)(JSContext*, HandleObject, HandleValue);
19398 callVM<Fn, js::CreateAsyncFromSyncIterator>(lir);
19399}
19400
19401void CodeGenerator::visitToPropertyKeyCache(LToPropertyKeyCache* lir) {
19402 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
19403 ValueOperand input = ToValue(lir->input());
19404 ValueOperand output = ToOutValue(lir);
19405
19406 IonToPropertyKeyIC ic(liveRegs, input, output);
19407 addIC(lir, allocateIC(ic));
19408}
19409
19410void CodeGenerator::visitLoadElementV(LLoadElementV* load) {
19411 Register elements = ToRegister(load->elements());
19412 const ValueOperand out = ToOutValue(load);
19413
19414 auto source = ToAddressOrBaseObjectElementIndex(elements, load->index());
19415
19416 source.match([&](auto const& source) { masm.loadValue(source, out); });
19417
19418 if (load->mir()->needsHoleCheck()) {
19419 Label testMagic;
19420 masm.branchTestMagicValue(Assembler::Equal, out, JS_ELEMENTS_HOLE,
19421 &testMagic);
19422 bailoutFrom(&testMagic, load->snapshot());
19423 } else {
19424#ifdef DEBUG1
19425 Label ok;
19426 masm.branchTestMagicValue(Assembler::NotEqual, out, JS_ELEMENTS_HOLE, &ok);
19427 masm.assumeUnreachable("LoadElementV had incorrect needsHoleCheck");
19428 masm.bind(&ok);
19429#endif
19430 }
19431}
19432
19433void CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) {
19434 Register elements = ToRegister(lir->elements());
19435 Register index = ToRegister(lir->index());
19436 Register initLength = ToRegister(lir->initLength());
19437 const ValueOperand out = ToOutValue(lir);
19438
19439 const MLoadElementHole* mir = lir->mir();
19440
19441 // If the index is out of bounds, load |undefined|. Otherwise, load the
19442 // value.
19443 Label outOfBounds, done;
19444 masm.spectreBoundsCheck32(index, initLength, out.scratchReg(), &outOfBounds);
19445
19446 masm.loadValue(BaseObjectElementIndex(elements, index), out);
19447
19448 // If the value wasn't a hole, we're done. Otherwise, we'll load undefined.
19449 masm.branchTestMagicValue(Assembler::NotEqual, out, JS_ELEMENTS_HOLE, &done);
19450
19451 if (mir->needsNegativeIntCheck()) {
19452 Label loadUndefined;
19453 masm.jump(&loadUndefined);
19454
19455 masm.bind(&outOfBounds);
19456
19457 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
19458
19459 masm.bind(&loadUndefined);
19460 } else {
19461 masm.bind(&outOfBounds);
19462 }
19463 masm.moveValue(UndefinedValue(), out);
19464
19465 masm.bind(&done);
19466}
19467
19468CodeGenerator::AddressOrBaseIndex CodeGenerator::ToAddressOrBaseIndex(
19469 Register elements, const LAllocation* index, Scalar::Type type) {
19470 if (index->isConstant()) {
19471 return AddressOrBaseIndex(ToAddress(elements, index, type));
19472 }
19473 return AddressOrBaseIndex(
19474 BaseIndex(elements, ToRegister(index), ScaleFromScalarType(type)));
19475}
19476
19477void CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) {
19478 Register elements = ToRegister(lir->elements());
19479 Register temp0 = ToTempRegisterOrInvalid(lir->temp0());
19480 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
19481 AnyRegister out = ToAnyRegister(lir->output());
19482
19483 Scalar::Type storageType = lir->mir()->storageType();
19484
19485 LiveRegisterSet volatileRegs;
19486 if (MacroAssembler::LoadRequiresCall(storageType)) {
19487 volatileRegs = liveVolatileRegs(lir);
19488 }
19489
19490 auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType);
19491
19492 Label fail;
19493 source.match([&](const auto& source) {
19494 masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail,
19495 volatileRegs);
19496 });
19497
19498 if (fail.used()) {
19499 bailoutFrom(&fail, lir->snapshot());
19500 }
19501}
19502
19503void CodeGenerator::visitLoadUnboxedInt64(LLoadUnboxedInt64* lir) {
19504 Register elements = ToRegister(lir->elements());
19505 Register64 out = ToOutRegister64(lir);
19506
19507 Scalar::Type storageType = lir->mir()->storageType();
19508
19509 auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType);
19510
19511 source.match([&](const auto& source) { masm.load64(source, out); });
19512}
19513
19514static bool IsNativeEndian(const LAllocation* littleEndian) {
19515 constexpr bool isLittleEndian = std::endian::native == std::endian::little;
19516 return littleEndian->isConstant() &&
19517 ToBoolean(littleEndian) == isLittleEndian;
19518}
19519
19520static void BranchIfNativeEndian(MacroAssembler& masm,
19521 const LAllocation* littleEndian,
19522 Label* label) {
19523 if (!littleEndian->isConstant()) {
19524 if constexpr (std::endian::native == std::endian::little) {
19525 masm.branch32(Assembler::NotEqual, ToRegister(littleEndian), Imm32(0),
19526 label);
19527 } else {
19528 masm.branch32(Assembler::Equal, ToRegister(littleEndian), Imm32(0),
19529 label);
19530 }
19531 }
19532}
19533
19534void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) {
19535 Register elements = ToRegister(lir->elements());
19536 const LAllocation* littleEndian = lir->littleEndian();
19537 Register temp1 = ToTempRegisterOrInvalid(lir->temp0());
19538 Register temp2 = ToTempRegisterOrInvalid(lir->temp1());
19539 Register64 temp64 = ToTempRegister64OrInvalid(lir->temp2());
19540 AnyRegister out = ToAnyRegister(lir->output());
19541
19542 Scalar::Type storageType = lir->mir()->storageType();
19543
19544 LiveRegisterSet volatileRegs;
19545 if (MacroAssembler::LoadRequiresCall(storageType)) {
19546 volatileRegs = liveVolatileRegs(lir);
19547 }
19548
19549 auto source = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8);
19550
19551 bool noSwap = IsNativeEndian(littleEndian);
19552
19553 // Directly load if no byte swap is needed and the platform supports unaligned
19554 // accesses for the access. (Such support is assumed for integer types.)
19555 if (noSwap && (!Scalar::isFloatingType(storageType) ||
19556 MacroAssembler::SupportsFastUnalignedFPAccesses())) {
19557 Label fail;
19558 source.match([&](const auto& source) {
19559 masm.loadFromTypedArray(storageType, source, out, temp1, temp2, &fail,
19560 volatileRegs);
19561 });
19562
19563 if (fail.used()) {
19564 bailoutFrom(&fail, lir->snapshot());
19565 }
19566 return;
19567 }
19568
19569 // Load the value into a gpr register.
19570 source.match([&](const auto& source) {
19571 switch (storageType) {
19572 case Scalar::Int16:
19573 masm.load16UnalignedSignExtend(source, out.gpr());
19574 break;
19575 case Scalar::Uint16:
19576 masm.load16UnalignedZeroExtend(source, out.gpr());
19577 break;
19578 case Scalar::Int32:
19579 masm.load32Unaligned(source, out.gpr());
19580 break;
19581 case Scalar::Uint32:
19582 masm.load32Unaligned(source, out.isFloat() ? temp1 : out.gpr());
19583 break;
19584 case Scalar::Float16:
19585 masm.load16UnalignedZeroExtend(source, temp1);
19586 break;
19587 case Scalar::Float32:
19588 masm.load32Unaligned(source, temp1);
19589 break;
19590 case Scalar::Float64:
19591 masm.load64Unaligned(source, temp64);
19592 break;
19593 case Scalar::Int8:
19594 case Scalar::Uint8:
19595 case Scalar::Uint8Clamped:
19596 case Scalar::BigInt64:
19597 case Scalar::BigUint64:
19598 default:
19599 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19599); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19599); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19600 }
19601 });
19602
19603 if (!noSwap) {
19604 // Swap the bytes in the loaded value.
19605 Label skip;
19606 BranchIfNativeEndian(masm, littleEndian, &skip);
19607
19608 switch (storageType) {
19609 case Scalar::Int16:
19610 masm.byteSwap16SignExtend(out.gpr());
19611 break;
19612 case Scalar::Uint16:
19613 masm.byteSwap16ZeroExtend(out.gpr());
19614 break;
19615 case Scalar::Int32:
19616 masm.byteSwap32(out.gpr());
19617 break;
19618 case Scalar::Uint32:
19619 masm.byteSwap32(out.isFloat() ? temp1 : out.gpr());
19620 break;
19621 case Scalar::Float16:
19622 masm.byteSwap16ZeroExtend(temp1);
19623 break;
19624 case Scalar::Float32:
19625 masm.byteSwap32(temp1);
19626 break;
19627 case Scalar::Float64:
19628 masm.byteSwap64(temp64);
19629 break;
19630 case Scalar::Int8:
19631 case Scalar::Uint8:
19632 case Scalar::Uint8Clamped:
19633 case Scalar::BigInt64:
19634 case Scalar::BigUint64:
19635 default:
19636 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19636); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19636); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19637 }
19638
19639 if (skip.used()) {
19640 masm.bind(&skip);
19641 }
19642 }
19643
19644 // Move the value into the output register.
19645 switch (storageType) {
19646 case Scalar::Int16:
19647 case Scalar::Uint16:
19648 case Scalar::Int32:
19649 break;
19650 case Scalar::Uint32:
19651 if (out.isFloat()) {
19652 masm.convertUInt32ToDouble(temp1, out.fpu());
19653 } else {
19654 // Bail out if the value doesn't fit into a signed int32 value. This
19655 // is what allows MLoadDataViewElement to have a type() of
19656 // MIRType::Int32 for UInt32 array loads.
19657 bailoutTest32(Assembler::Signed, out.gpr(), out.gpr(), lir->snapshot());
19658 }
19659 break;
19660 case Scalar::Float16:
19661 masm.moveGPRToFloat16(temp1, out.fpu(), temp2, volatileRegs);
19662 break;
19663 case Scalar::Float32:
19664 masm.moveGPRToFloat32(temp1, out.fpu());
19665 break;
19666 case Scalar::Float64:
19667 masm.moveGPR64ToDouble(temp64, out.fpu());
19668 break;
19669 case Scalar::Int8:
19670 case Scalar::Uint8:
19671 case Scalar::Uint8Clamped:
19672 case Scalar::BigInt64:
19673 case Scalar::BigUint64:
19674 default:
19675 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19675); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19675); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19676 }
19677}
19678
19679void CodeGenerator::visitLoadDataViewElement64(LLoadDataViewElement64* lir) {
19680 Register elements = ToRegister(lir->elements());
19681 const LAllocation* littleEndian = lir->littleEndian();
19682 Register64 out = ToOutRegister64(lir);
19683
19684 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"
, 19684); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->storageType())"
")"); do { MOZ_CrashSequence(__null, 19684); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19685
19686 auto source = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8);
19687
19688 bool noSwap = IsNativeEndian(littleEndian);
19689
19690 // Load the value into a register.
19691 source.match([&](const auto& source) { masm.load64Unaligned(source, out); });
19692
19693 if (!noSwap) {
19694 // Swap the bytes in the loaded value.
19695 Label skip;
19696 BranchIfNativeEndian(masm, littleEndian, &skip);
19697
19698 masm.byteSwap64(out);
19699
19700 if (skip.used()) {
19701 masm.bind(&skip);
19702 }
19703 }
19704}
19705
19706void CodeGenerator::visitLoadTypedArrayElementHole(
19707 LLoadTypedArrayElementHole* lir) {
19708 Register elements = ToRegister(lir->elements());
19709 Register index = ToRegister(lir->index());
19710 Register length = ToRegister(lir->length());
19711 Register temp = ToTempRegisterOrInvalid(lir->temp0());
19712 const ValueOperand out = ToOutValue(lir);
19713
19714 Register scratch = out.scratchReg();
19715
19716 // Load undefined if index >= length.
19717 Label outOfBounds, done;
19718 masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds);
19719
19720 Scalar::Type arrayType = lir->mir()->arrayType();
19721
19722 LiveRegisterSet volatileRegs;
19723 if (MacroAssembler::LoadRequiresCall(arrayType)) {
19724 volatileRegs = liveVolatileRegs(lir);
19725 }
19726
19727 Label fail;
19728 BaseIndex source(elements, index, ScaleFromScalarType(arrayType));
19729 MacroAssembler::Uint32Mode uint32Mode =
19730 lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble
19731 : MacroAssembler::Uint32Mode::FailOnDouble;
19732 masm.loadFromTypedArray(arrayType, source, out, uint32Mode, temp, &fail,
19733 volatileRegs);
19734 masm.jump(&done);
19735
19736 masm.bind(&outOfBounds);
19737 masm.moveValue(UndefinedValue(), out);
19738
19739 if (fail.used()) {
19740 bailoutFrom(&fail, lir->snapshot());
19741 }
19742
19743 masm.bind(&done);
19744}
19745
19746void CodeGenerator::visitLoadTypedArrayElementHoleBigInt(
19747 LLoadTypedArrayElementHoleBigInt* lir) {
19748 Register elements = ToRegister(lir->elements());
19749 Register index = ToRegister(lir->index());
19750 Register length = ToRegister(lir->length());
19751 const ValueOperand out = ToOutValue(lir);
19752
19753 Register temp = ToRegister(lir->temp0());
19754
19755 // On x86 there are not enough registers. In that case reuse the output
19756 // registers as temporaries.
19757#ifdef JS_CODEGEN_X86
19758 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"
, 19758); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->temp1().isBogusTemp()"
")"); do { MOZ_CrashSequence(__null, 19758); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19759 Register64 temp64 = out.toRegister64();
19760#else
19761 Register64 temp64 = ToRegister64(lir->temp1());
19762#endif
19763
19764 // Load undefined if index >= length.
19765 Label outOfBounds, done;
19766 masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds);
19767
19768 Scalar::Type arrayType = lir->mir()->arrayType();
19769 BaseIndex source(elements, index, ScaleFromScalarType(arrayType));
19770 masm.load64(source, temp64);
19771
19772#ifdef JS_CODEGEN_X86
19773 Register bigInt = temp;
19774 Register maybeTemp = InvalidReg;
19775#else
19776 Register bigInt = out.scratchReg();
19777 Register maybeTemp = temp;
19778#endif
19779 emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp);
19780
19781 masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out);
19782 masm.jump(&done);
19783
19784 masm.bind(&outOfBounds);
19785 masm.moveValue(UndefinedValue(), out);
19786
19787 masm.bind(&done);
19788}
19789
19790template <typename T>
19791static inline void StoreToTypedArray(MacroAssembler& masm,
19792 Scalar::Type writeType,
19793 const LAllocation* value, const T& dest,
19794 Register temp,
19795 LiveRegisterSet volatileRegs) {
19796 if (Scalar::isFloatingType(writeType)) {
19797 masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, temp,
19798 volatileRegs);
19799 } else {
19800 if (value->isConstant()) {
19801 masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest);
19802 } else {
19803 masm.storeToTypedIntArray(writeType, ToRegister(value), dest);
19804 }
19805 }
19806}
19807
19808void CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) {
19809 Register elements = ToRegister(lir->elements());
19810 Register temp = ToTempRegisterOrInvalid(lir->temp0());
19811 const LAllocation* value = lir->value();
19812
19813 Scalar::Type writeType = lir->mir()->writeType();
19814
19815 LiveRegisterSet volatileRegs;
19816 if (MacroAssembler::StoreRequiresCall(writeType)) {
19817 volatileRegs = liveVolatileRegs(lir);
19818 }
19819
19820 auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType);
19821
19822 dest.match([&](const auto& dest) {
19823 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
19824 });
19825}
19826
19827template <typename T>
19828static inline void StoreToTypedBigIntArray(MacroAssembler& masm,
19829 const LInt64Allocation& value,
19830 const T& dest) {
19831 if (IsConstant(value)) {
19832 masm.storeToTypedBigIntArray(Imm64(ToInt64(value)), dest);
19833 } else {
19834 masm.storeToTypedBigIntArray(ToRegister64(value), dest);
19835 }
19836}
19837
19838void CodeGenerator::visitStoreUnboxedInt64(LStoreUnboxedInt64* lir) {
19839 Register elements = ToRegister(lir->elements());
19840 LInt64Allocation value = lir->value();
19841
19842 Scalar::Type writeType = lir->mir()->writeType();
19843 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"
, 19843); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(writeType)"
")"); do { MOZ_CrashSequence(__null, 19843); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19844
19845 auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType);
19846
19847 dest.match(
19848 [&](const auto& dest) { StoreToTypedBigIntArray(masm, value, dest); });
19849}
19850
19851void CodeGenerator::visitStoreDataViewElement(LStoreDataViewElement* lir) {
19852 Register elements = ToRegister(lir->elements());
19853 const LAllocation* value = lir->value();
19854 const LAllocation* littleEndian = lir->littleEndian();
19855 Register temp = ToTempRegisterOrInvalid(lir->temp0());
19856 Register64 temp64 = ToTempRegister64OrInvalid(lir->temp1());
19857
19858 Scalar::Type writeType = lir->mir()->writeType();
19859
19860 LiveRegisterSet volatileRegs;
19861 if (MacroAssembler::StoreRequiresCall(writeType)) {
19862 volatileRegs = liveVolatileRegs(lir);
19863 }
19864
19865 auto dest = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8);
19866
19867 bool noSwap = IsNativeEndian(littleEndian);
19868
19869 // Directly store if no byte swap is needed and the platform supports
19870 // unaligned accesses for the access. (Such support is assumed for integer
19871 // types.)
19872 if (noSwap && (!Scalar::isFloatingType(writeType) ||
19873 MacroAssembler::SupportsFastUnalignedFPAccesses())) {
19874 dest.match([&](const auto& dest) {
19875 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
19876 });
19877 return;
19878 }
19879
19880 // Load the value into a gpr register.
19881 switch (writeType) {
19882 case Scalar::Int16:
19883 case Scalar::Uint16:
19884 case Scalar::Int32:
19885 case Scalar::Uint32:
19886 if (value->isConstant()) {
19887 masm.move32(Imm32(ToInt32(value)), temp);
19888 } else {
19889 masm.move32(ToRegister(value), temp);
19890 }
19891 break;
19892 case Scalar::Float16: {
19893 FloatRegister fvalue = ToFloatRegister(value);
19894 masm.moveFloat16ToGPR(fvalue, temp, volatileRegs);
19895 break;
19896 }
19897 case Scalar::Float32: {
19898 FloatRegister fvalue = ToFloatRegister(value);
19899 masm.moveFloat32ToGPR(fvalue, temp);
19900 break;
19901 }
19902 case Scalar::Float64: {
19903 FloatRegister fvalue = ToFloatRegister(value);
19904 masm.moveDoubleToGPR64(fvalue, temp64);
19905 break;
19906 }
19907 case Scalar::Int8:
19908 case Scalar::Uint8:
19909 case Scalar::Uint8Clamped:
19910 case Scalar::BigInt64:
19911 case Scalar::BigUint64:
19912 default:
19913 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19913); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19913); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19914 }
19915
19916 if (!noSwap) {
19917 // Swap the bytes in the loaded value.
19918 Label skip;
19919 BranchIfNativeEndian(masm, littleEndian, &skip);
19920
19921 switch (writeType) {
19922 case Scalar::Int16:
19923 masm.byteSwap16SignExtend(temp);
19924 break;
19925 case Scalar::Uint16:
19926 case Scalar::Float16:
19927 masm.byteSwap16ZeroExtend(temp);
19928 break;
19929 case Scalar::Int32:
19930 case Scalar::Uint32:
19931 case Scalar::Float32:
19932 masm.byteSwap32(temp);
19933 break;
19934 case Scalar::Float64:
19935 masm.byteSwap64(temp64);
19936 break;
19937 case Scalar::Int8:
19938 case Scalar::Uint8:
19939 case Scalar::Uint8Clamped:
19940 case Scalar::BigInt64:
19941 case Scalar::BigUint64:
19942 default:
19943 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19943); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19943); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19944 }
19945
19946 if (skip.used()) {
19947 masm.bind(&skip);
19948 }
19949 }
19950
19951 // Store the value into the destination.
19952 dest.match([&](const auto& dest) {
19953 switch (writeType) {
19954 case Scalar::Int16:
19955 case Scalar::Uint16:
19956 case Scalar::Float16:
19957 masm.store16Unaligned(temp, dest);
19958 break;
19959 case Scalar::Int32:
19960 case Scalar::Uint32:
19961 case Scalar::Float32:
19962 masm.store32Unaligned(temp, dest);
19963 break;
19964 case Scalar::Float64:
19965 masm.store64Unaligned(temp64, dest);
19966 break;
19967 case Scalar::Int8:
19968 case Scalar::Uint8:
19969 case Scalar::Uint8Clamped:
19970 case Scalar::BigInt64:
19971 case Scalar::BigUint64:
19972 default:
19973 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 19973); AnnotateMozCrashReason
("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence
(__null, 19973); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
19974 }
19975 });
19976}
19977
19978void CodeGenerator::visitStoreDataViewElement64(LStoreDataViewElement64* lir) {
19979 Register elements = ToRegister(lir->elements());
19980 LInt64Allocation value = lir->value();
19981 const LAllocation* littleEndian = lir->littleEndian();
19982 Register64 temp = ToTempRegister64OrInvalid(lir->temp0());
19983
19984 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", 19984); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->writeType())"
")"); do { MOZ_CrashSequence(__null, 19984); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19985
19986 auto dest = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8);
19987
19988 bool noSwap = IsNativeEndian(littleEndian);
19989
19990 // Directly store if no byte swap is needed and the platform supports
19991 // unaligned accesses for the access. (Such support is assumed for integer
19992 // types.)
19993 if (noSwap) {
19994 dest.match(
19995 [&](const auto& dest) { StoreToTypedBigIntArray(masm, value, dest); });
19996 return;
19997 }
19998
19999 Register64 valueReg = Register64::Invalid();
20000 if (IsConstant(value)) {
20001 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"
, 20001); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp != Register64::Invalid()"
")"); do { MOZ_CrashSequence(__null, 20001); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20002 masm.move64(Imm64(ToInt64(value)), temp);
20003 } else {
20004 valueReg = ToRegister64(value);
20005
20006 // Preserve the input value.
20007 if (temp != Register64::Invalid()) {
20008 masm.move64(valueReg, temp);
20009 } else {
20010 masm.Push(valueReg);
20011 temp = valueReg;
20012 }
20013 }
20014
20015 // Swap the bytes in the loaded value.
20016 Label skip;
20017 BranchIfNativeEndian(masm, littleEndian, &skip);
20018
20019 masm.byteSwap64(temp);
20020
20021 if (skip.used()) {
20022 masm.bind(&skip);
20023 }
20024
20025 // Store the value into the destination.
20026 dest.match([&](const auto& dest) { masm.store64Unaligned(temp, dest); });
20027
20028 // Restore |value| if it was modified.
20029 if (valueReg == temp) {
20030 masm.Pop(valueReg);
20031 }
20032}
20033
20034void CodeGenerator::visitStoreTypedArrayElementHole(
20035 LStoreTypedArrayElementHole* lir) {
20036 Register elements = ToRegister(lir->elements());
20037 const LAllocation* value = lir->value();
20038
20039 Scalar::Type arrayType = lir->mir()->arrayType();
20040
20041 Register index = ToRegister(lir->index());
20042 const LAllocation* length = lir->length();
20043 Register temp = ToTempRegisterOrInvalid(lir->temp0());
20044
20045 LiveRegisterSet volatileRegs;
20046 if (MacroAssembler::StoreRequiresCall(arrayType)) {
20047 volatileRegs = liveVolatileRegs(lir);
20048 }
20049
20050 Label skip;
20051 if (length->isGeneralReg()) {
20052 masm.spectreBoundsCheckPtr(index, ToRegister(length), temp, &skip);
20053 } else {
20054 masm.spectreBoundsCheckPtr(index, ToAddress(length), temp, &skip);
20055 }
20056
20057 BaseIndex dest(elements, index, ScaleFromScalarType(arrayType));
20058 StoreToTypedArray(masm, arrayType, value, dest, temp, volatileRegs);
20059
20060 masm.bind(&skip);
20061}
20062
20063void CodeGenerator::visitStoreTypedArrayElementHoleInt64(
20064 LStoreTypedArrayElementHoleInt64* lir) {
20065 Register elements = ToRegister(lir->elements());
20066 LInt64Allocation value = lir->value();
20067
20068 Scalar::Type arrayType = lir->mir()->arrayType();
20069 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"
, 20069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(arrayType)"
")"); do { MOZ_CrashSequence(__null, 20069); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20070
20071 Register index = ToRegister(lir->index());
20072 const LAllocation* length = lir->length();
20073 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0());
20074
20075 Label skip;
20076 if (length->isGeneralReg()) {
20077 masm.spectreBoundsCheckPtr(index, ToRegister(length), spectreTemp, &skip);
20078 } else {
20079 masm.spectreBoundsCheckPtr(index, ToAddress(length), spectreTemp, &skip);
20080 }
20081
20082 BaseIndex dest(elements, index, ScaleFromScalarType(arrayType));
20083 StoreToTypedBigIntArray(masm, value, dest);
20084
20085 masm.bind(&skip);
20086}
20087
20088void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) {
20089 masm.memoryBarrier(ins->barrier());
20090}
20091
20092void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) {
20093 Register value = ToRegister(lir->value());
20094 Register output = ToRegister(lir->output());
20095
20096 masm.atomicIsLockFreeJS(value, output);
20097}
20098
20099void CodeGenerator::visitAtomicPause(LAtomicPause* lir) { masm.atomicPause(); }
20100
20101void CodeGenerator::visitClampIToUint8(LClampIToUint8* lir) {
20102 Register output = ToRegister(lir->output());
20103 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", 20103); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output == ToRegister(lir->input())" ")"
); do { MOZ_CrashSequence(__null, 20103); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
20104 masm.clampIntToUint8(output);
20105}
20106
20107void CodeGenerator::visitClampDToUint8(LClampDToUint8* lir) {
20108 FloatRegister input = ToFloatRegister(lir->input());
20109 Register output = ToRegister(lir->output());
20110 masm.clampDoubleToUint8(input, output);
20111}
20112
20113void CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) {
20114 ValueOperand operand = ToValue(lir->input());
20115 FloatRegister tempFloat = ToFloatRegister(lir->temp0());
20116 Register output = ToRegister(lir->output());
20117
20118 using Fn = bool (*)(JSContext*, JSString*, double*);
20119 OutOfLineCode* oolString = oolCallVM<Fn, StringToNumber>(
20120 lir, ArgList(output), StoreFloatRegisterTo(tempFloat));
20121 Label* stringEntry = oolString->entry();
20122 Label* stringRejoin = oolString->rejoin();
20123
20124 Label fails;
20125 masm.clampValueToUint8(operand, stringEntry, stringRejoin, output, tempFloat,
20126 output, &fails);
20127
20128 bailoutFrom(&fails, lir->snapshot());
20129}
20130
20131void CodeGenerator::visitInCache(LInCache* ins) {
20132 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
20133
20134 ConstantOrRegister key =
20135 toConstantOrRegister(ins, LInCache::LhsIndex, ins->mir()->key()->type());
20136 Register object = ToRegister(ins->rhs());
20137 Register output = ToRegister(ins->output());
20138 Register temp = ToRegister(ins->temp0());
20139
20140 IonInIC cache(liveRegs, key, object, output, temp);
20141 addIC(ins, allocateIC(cache));
20142}
20143
20144void CodeGenerator::visitInArray(LInArray* lir) {
20145 const MInArray* mir = lir->mir();
20146 Register elements = ToRegister(lir->elements());
20147 Register initLength = ToRegister(lir->initLength());
20148 Register output = ToRegister(lir->output());
20149
20150 Label falseBranch, done, trueBranch;
20151
20152 if (lir->index()->isConstant()) {
20153 int32_t index = ToInt32(lir->index());
20154
20155 if (index < 0) {
20156 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"
, 20156); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->needsNegativeIntCheck()"
")"); do { MOZ_CrashSequence(__null, 20156); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20157 bailout(lir->snapshot());
20158 return;
20159 }
20160
20161 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index),
20162 &falseBranch);
20163
20164 NativeObject::elementsSizeMustNotOverflow();
20165 Address address = Address(elements, index * sizeof(Value));
20166 masm.branchTestMagic(Assembler::Equal, address, JS_ELEMENTS_HOLE,
20167 &falseBranch);
20168 } else {
20169 Register index = ToRegister(lir->index());
20170
20171 Label negativeIntCheck;
20172 Label* failedInitLength = &falseBranch;
20173 if (mir->needsNegativeIntCheck()) {
20174 failedInitLength = &negativeIntCheck;
20175 }
20176
20177 masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
20178
20179 BaseObjectElementIndex address(elements, index);
20180 masm.branchTestMagic(Assembler::Equal, address, JS_ELEMENTS_HOLE,
20181 &falseBranch);
20182
20183 if (mir->needsNegativeIntCheck()) {
20184 masm.jump(&trueBranch);
20185 masm.bind(&negativeIntCheck);
20186
20187 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
20188
20189 masm.jump(&falseBranch);
20190 }
20191 }
20192
20193 masm.bind(&trueBranch);
20194 masm.move32(Imm32(1), output);
20195 masm.jump(&done);
20196
20197 masm.bind(&falseBranch);
20198 masm.move32(Imm32(0), output);
20199 masm.bind(&done);
20200}
20201
20202void CodeGenerator::visitGuardElementNotHole(LGuardElementNotHole* lir) {
20203 Register elements = ToRegister(lir->elements());
20204 const LAllocation* index = lir->index();
20205
20206 auto source = ToAddressOrBaseObjectElementIndex(elements, index);
20207
20208 Label testMagic;
20209 source.match([&](const auto& source) {
20210 masm.branchTestMagic(Assembler::Equal, source, JS_ELEMENTS_HOLE,
20211 &testMagic);
20212 });
20213 bailoutFrom(&testMagic, lir->snapshot());
20214}
20215
20216void CodeGenerator::visitInstanceOfO(LInstanceOfO* ins) {
20217 Register protoReg = ToRegister(ins->rhs());
20218 emitInstanceOf(ins, protoReg);
20219}
20220
20221void CodeGenerator::visitInstanceOfV(LInstanceOfV* ins) {
20222 Register protoReg = ToRegister(ins->rhs());
20223 emitInstanceOf(ins, protoReg);
20224}
20225
20226void CodeGenerator::emitInstanceOf(LInstruction* ins, Register protoReg) {
20227 // This path implements fun_hasInstance when the function's prototype is
20228 // known to be the object in protoReg
20229
20230 Label done;
20231 Register output = ToRegister(ins->getDef(0));
20232
20233 // If the lhs is a primitive, the result is false.
20234 Register objReg;
20235 if (ins->isInstanceOfV()) {
20236 Label isObject;
20237 ValueOperand lhsValue = ToValue(ins->toInstanceOfV()->lhs());
20238 masm.branchTestObject(Assembler::Equal, lhsValue, &isObject);
20239 masm.mov(ImmWord(0), output);
20240 masm.jump(&done);
20241 masm.bind(&isObject);
20242 objReg = masm.extractObject(lhsValue, output);
20243 } else {
20244 objReg = ToRegister(ins->toInstanceOfO()->lhs());
20245 }
20246
20247 // Crawl the lhs's prototype chain in a loop to search for prototypeObject.
20248 // This follows the main loop of js::IsPrototypeOf, though additionally breaks
20249 // out of the loop on Proxy::LazyProto.
20250
20251 // Load the lhs's prototype.
20252 masm.loadObjProto(objReg, output);
20253
20254 Label testLazy;
20255 {
20256 Label loopPrototypeChain;
20257 masm.bind(&loopPrototypeChain);
20258
20259 // Test for the target prototype object.
20260 Label notPrototypeObject;
20261 masm.branchPtr(Assembler::NotEqual, output, protoReg, &notPrototypeObject);
20262 masm.mov(ImmWord(1), output);
20263 masm.jump(&done);
20264 masm.bind(&notPrototypeObject);
20265
20266 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", 20266); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 20266); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
20267
20268 // Test for nullptr or Proxy::LazyProto
20269 masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy);
20270
20271 // Load the current object's prototype.
20272 masm.loadObjProto(output, output);
20273
20274 masm.jump(&loopPrototypeChain);
20275 }
20276
20277 // Make a VM call if an object with a lazy proto was found on the prototype
20278 // chain. This currently occurs only for cross compartment wrappers, which
20279 // we do not expect to be compared with non-wrapper functions from this
20280 // compartment. Otherwise, we stopped on a nullptr prototype and the output
20281 // register is already correct.
20282
20283 using Fn = bool (*)(JSContext*, HandleObject, JSObject*, bool*);
20284 auto* ool = oolCallVM<Fn, IsPrototypeOf>(ins, ArgList(protoReg, objReg),
20285 StoreRegisterTo(output));
20286
20287 // Regenerate the original lhs object for the VM call.
20288 Label regenerate, *lazyEntry;
20289 if (objReg != output) {
20290 lazyEntry = ool->entry();
20291 } else {
20292 masm.bind(&regenerate);
20293 lazyEntry = &regenerate;
20294 if (ins->isInstanceOfV()) {
20295 ValueOperand lhsValue = ToValue(ins->toInstanceOfV()->lhs());
20296 objReg = masm.extractObject(lhsValue, output);
20297 } else {
20298 objReg = ToRegister(ins->toInstanceOfO()->lhs());
20299 }
20300 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", 20300); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "objReg == output" ")"); do { MOZ_CrashSequence
(__null, 20300); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
20301 masm.jump(ool->entry());
20302 }
20303
20304 masm.bind(&testLazy);
20305 masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry);
20306
20307 masm.bind(&done);
20308 masm.bind(ool->rejoin());
20309}
20310
20311void CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins) {
20312 // The Lowering ensures that RHS is an object, and that LHS is a value.
20313 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
20314 TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(ins->obj()));
20315 Register rhs = ToRegister(ins->proto());
20316 Register output = ToRegister(ins->output());
20317
20318 IonInstanceOfIC ic(liveRegs, lhs, rhs, output);
20319 addIC(ins, allocateIC(ic));
20320}
20321
20322void CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) {
20323 const Register JSContextReg = ToRegister(ins->temp0());
20324 const Register ObjectReg = ToRegister(ins->object());
20325 const Register PrivateReg = ToRegister(ins->temp1());
20326 const Register ValueReg = ToRegister(ins->temp2());
20327
20328 Label haveValue;
20329 if (ins->mir()->valueMayBeInSlot()) {
20330 size_t slot = ins->mir()->domMemberSlotIndex();
20331 // It's a bit annoying to redo these slot calculations, which duplcate
20332 // LSlots and a few other things like that, but I'm not sure there's a
20333 // way to reuse those here.
20334 //
20335 // If this ever gets fixed to work with proxies (by not assuming that
20336 // reserved slot indices, which is what domMemberSlotIndex() returns,
20337 // match fixed slot indices), we can reenable MGetDOMProperty for
20338 // proxies in IonBuilder.
20339 if (slot < NativeObject::MAX_FIXED_SLOTS) {
20340 masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)),
20341 JSReturnOperand);
20342 } else {
20343 // It's a dynamic slot.
20344 slot -= NativeObject::MAX_FIXED_SLOTS;
20345 // Use PrivateReg as a scratch register for the slots pointer.
20346 masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()),
20347 PrivateReg);
20348 masm.loadValue(Address(PrivateReg, slot * sizeof(js::Value)),
20349 JSReturnOperand);
20350 }
20351 masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue);
20352 }
20353
20354 DebugOnly<uint32_t> initialStack = masm.framePushed();
20355
20356 masm.checkStackAlignment();
20357
20358 // Make space for the outparam. Pre-initialize it to UndefinedValue so we
20359 // can trace it at GC time.
20360 masm.Push(UndefinedValue());
20361 // We pass the pointer to our out param as an instance of
20362 // JSJitGetterCallArgs, since on the binary level it's the same thing.
20363 static_assert(sizeof(JSJitGetterCallArgs) == sizeof(Value*));
20364 masm.moveStackPtrTo(ValueReg);
20365
20366 masm.Push(ObjectReg);
20367
20368 LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
20369
20370 // Rooting will happen at GC time.
20371 masm.moveStackPtrTo(ObjectReg);
20372
20373 Realm* getterRealm = ins->mir()->getterRealm();
20374 if (gen->realm->realmPtr() != getterRealm) {
20375 // We use JSContextReg as scratch register here.
20376 masm.switchToRealm(getterRealm, JSContextReg);
20377 }
20378
20379 uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
20380 masm.loadJSContext(JSContextReg);
20381 masm.enterFakeExitFrame(JSContextReg, JSContextReg,
20382 ExitFrameType::IonDOMGetter);
20383
20384 markSafepointAt(safepointOffset, ins);
20385
20386 maybeEmitSetInPureCall(ins->mir(), true, /*scratch =*/JSContextReg);
20387
20388 masm.setupAlignedABICall();
20389 masm.loadJSContext(JSContextReg);
20390 masm.passABIArg(JSContextReg);
20391 masm.passABIArg(ObjectReg);
20392 masm.passABIArg(PrivateReg);
20393 masm.passABIArg(ValueReg);
20394 ensureOsiSpace();
20395 masm.callWithABI(DynamicFunction<JSJitGetterOp>(ins->mir()->fun()),
20396 ABIType::General,
20397 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
20398
20399 Register notReturnReg = JSContextReg == ReturnReg ? ObjectReg : JSContextReg;
20400 maybeEmitSetInPureCall(ins->mir(), false, /*scratch =*/notReturnReg);
20401
20402 if (ins->mir()->isInfallible()) {
20403 masm.loadValue(Address(masm.getStackPointer(),
20404 IonDOMExitFrameLayout::offsetOfResult()),
20405 JSReturnOperand);
20406 } else {
20407 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
20408
20409 masm.loadValue(Address(masm.getStackPointer(),
20410 IonDOMExitFrameLayout::offsetOfResult()),
20411 JSReturnOperand);
20412 }
20413
20414 // Switch back to the current realm if needed. Note: if the getter threw an
20415 // exception, the exception handler will do this.
20416 if (gen->realm->realmPtr() != getterRealm) {
20417 static_assert(!JSReturnOperand.aliases(ReturnReg),
20418 "Clobbering ReturnReg should not affect the return value");
20419 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
20420 }
20421
20422 // Until C++ code is instrumented against Spectre, prevent speculative
20423 // execution from returning any private data.
20424 if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) {
20425 masm.speculationBarrier();
20426 }
20427
20428 masm.adjustStack(IonDOMExitFrameLayout::Size());
20429
20430 masm.bind(&haveValue);
20431
20432 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"
, 20432); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { MOZ_CrashSequence(__null, 20432); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20433}
20434
20435void CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins) {
20436 // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to
20437 // use an LLoadFixedSlotV or some subclass of it for this case: that would
20438 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
20439 // we'd have to duplicate a bunch of stuff we now get for free from
20440 // MGetDOMProperty.
20441 //
20442 // If this ever gets fixed to work with proxies (by not assuming that
20443 // reserved slot indices, which is what domMemberSlotIndex() returns,
20444 // match fixed slot indices), we can reenable MGetDOMMember for
20445 // proxies in IonBuilder.
20446 Register object = ToRegister(ins->object());
20447 size_t slot = ins->mir()->domMemberSlotIndex();
20448 ValueOperand result = ToOutValue(ins);
20449
20450 masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)),
20451 result);
20452}
20453
20454void CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT* ins) {
20455 // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to
20456 // use an LLoadFixedSlotT or some subclass of it for this case: that would
20457 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
20458 // we'd have to duplicate a bunch of stuff we now get for free from
20459 // MGetDOMProperty.
20460 //
20461 // If this ever gets fixed to work with proxies (by not assuming that
20462 // reserved slot indices, which is what domMemberSlotIndex() returns,
20463 // match fixed slot indices), we can reenable MGetDOMMember for
20464 // proxies in IonBuilder.
20465 Register object = ToRegister(ins->object());
20466 size_t slot = ins->mir()->domMemberSlotIndex();
20467 AnyRegister result = ToAnyRegister(ins->output());
20468 MIRType type = ins->mir()->type();
20469
20470 masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)),
20471 type, result);
20472}
20473
20474void CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) {
20475 const Register JSContextReg = ToRegister(ins->temp0());
20476 const Register ObjectReg = ToRegister(ins->object());
20477 const Register PrivateReg = ToRegister(ins->temp1());
20478 const Register ValueReg = ToRegister(ins->temp2());
20479
20480 DebugOnly<uint32_t> initialStack = masm.framePushed();
20481
20482 masm.checkStackAlignment();
20483
20484 // Push the argument. Rooting will happen at GC time.
20485 ValueOperand argVal = ToValue(ins->value());
20486 masm.Push(argVal);
20487 // We pass the pointer to our out param as an instance of
20488 // JSJitGetterCallArgs, since on the binary level it's the same thing.
20489 static_assert(sizeof(JSJitSetterCallArgs) == sizeof(Value*));
20490 masm.moveStackPtrTo(ValueReg);
20491
20492 masm.Push(ObjectReg);
20493
20494 LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
20495
20496 // Rooting will happen at GC time.
20497 masm.moveStackPtrTo(ObjectReg);
20498
20499 Realm* setterRealm = ins->mir()->setterRealm();
20500 if (gen->realm->realmPtr() != setterRealm) {
20501 // We use JSContextReg as scratch register here.
20502 masm.switchToRealm(setterRealm, JSContextReg);
20503 }
20504
20505 uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
20506 masm.loadJSContext(JSContextReg);
20507 masm.enterFakeExitFrame(JSContextReg, JSContextReg,
20508 ExitFrameType::IonDOMSetter);
20509
20510 markSafepointAt(safepointOffset, ins);
20511
20512 masm.setupAlignedABICall();
20513 masm.loadJSContext(JSContextReg);
20514 masm.passABIArg(JSContextReg);
20515 masm.passABIArg(ObjectReg);
20516 masm.passABIArg(PrivateReg);
20517 masm.passABIArg(ValueReg);
20518 ensureOsiSpace();
20519 masm.callWithABI(DynamicFunction<JSJitSetterOp>(ins->mir()->fun()),
20520 ABIType::General,
20521 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
20522
20523 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
20524
20525 // Switch back to the current realm if needed. Note: if the setter threw an
20526 // exception, the exception handler will do this.
20527 if (gen->realm->realmPtr() != setterRealm) {
20528 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
20529 }
20530
20531 masm.adjustStack(IonDOMExitFrameLayout::Size());
20532
20533 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"
, 20533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { MOZ_CrashSequence(__null, 20533); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20534}
20535
20536void CodeGenerator::visitLoadDOMExpandoValue(LLoadDOMExpandoValue* ins) {
20537 Register proxy = ToRegister(ins->proxy());
20538 ValueOperand out = ToOutValue(ins);
20539
20540 masm.loadValue(Address(proxy, ProxyObject::offsetOfPrivateSlot()), out);
20541}
20542
20543void CodeGenerator::visitLoadDOMExpandoValueGuardGeneration(
20544 LLoadDOMExpandoValueGuardGeneration* ins) {
20545 Register proxy = ToRegister(ins->proxy());
20546 ValueOperand out = ToOutValue(ins);
20547
20548 Label bail;
20549 masm.loadDOMExpandoValueGuardGeneration(proxy, out,
20550 ins->mir()->expandoAndGeneration(),
20551 ins->mir()->generation(), &bail);
20552 bailoutFrom(&bail, ins->snapshot());
20553}
20554
20555void CodeGenerator::visitLoadDOMExpandoValueIgnoreGeneration(
20556 LLoadDOMExpandoValueIgnoreGeneration* ins) {
20557 Register proxy = ToRegister(ins->proxy());
20558 ValueOperand out = ToOutValue(ins);
20559
20560 // Load the ExpandoAndGeneration* from the PrivateValue.
20561 masm.loadPrivate(Address(proxy, ProxyObject::offsetOfPrivateSlot()),
20562 out.scratchReg());
20563
20564 // Load expandoAndGeneration->expando into the output Value register.
20565 masm.loadValue(
20566 Address(out.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), out);
20567}
20568
20569void CodeGenerator::visitGuardDOMExpandoMissingOrGuardShape(
20570 LGuardDOMExpandoMissingOrGuardShape* ins) {
20571 Register temp = ToRegister(ins->temp0());
20572 ValueOperand input = ToValue(ins->expando());
20573
20574 Label done;
20575 masm.branchTestUndefined(Assembler::Equal, input, &done);
20576
20577 masm.debugAssertIsObject(input);
20578 masm.unboxObject(input, temp);
20579 // The expando object is not used in this case, so we don't need Spectre
20580 // mitigations.
20581 Label bail;
20582 masm.branchTestObjShapeNoSpectreMitigations(Assembler::NotEqual, temp,
20583 ins->mir()->shape(), &bail);
20584 bailoutFrom(&bail, ins->snapshot());
20585
20586 masm.bind(&done);
20587}
20588
20589void CodeGenerator::emitIsCallableOOL(Register object, Register output) {
20590 saveVolatile(output);
20591 using Fn = bool (*)(JSObject* obj);
20592 masm.setupAlignedABICall();
20593 masm.passABIArg(object);
20594 masm.callWithABI<Fn, ObjectIsCallable>();
20595 masm.storeCallBoolResult(output);
20596 restoreVolatile(output);
20597}
20598
20599void CodeGenerator::visitIsCallableO(LIsCallableO* ins) {
20600 Register object = ToRegister(ins->object());
20601 Register output = ToRegister(ins->output());
20602
20603 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
20604 emitIsCallableOOL(object, output);
20605 masm.jump(ool.rejoin());
20606 });
20607 addOutOfLineCode(ool, ins->mir());
20608
20609 masm.isCallable(object, output, ool->entry());
20610
20611 masm.bind(ool->rejoin());
20612}
20613
20614void CodeGenerator::visitIsCallableV(LIsCallableV* ins) {
20615 ValueOperand val = ToValue(ins->object());
20616 Register output = ToRegister(ins->output());
20617 Register temp = ToRegister(ins->temp0());
20618
20619 Label notObject;
20620 masm.fallibleUnboxObject(val, temp, &notObject);
20621
20622 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
20623 emitIsCallableOOL(temp, output);
20624 masm.jump(ool.rejoin());
20625 });
20626 addOutOfLineCode(ool, ins->mir());
20627
20628 masm.isCallable(temp, output, ool->entry());
20629 masm.jump(ool->rejoin());
20630
20631 masm.bind(&notObject);
20632 masm.move32(Imm32(0), output);
20633
20634 masm.bind(ool->rejoin());
20635}
20636
20637void CodeGenerator::visitIsConstructor(LIsConstructor* ins) {
20638 Register object = ToRegister(ins->object());
20639 Register output = ToRegister(ins->output());
20640
20641 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
20642 saveVolatile(output);
20643 using Fn = bool (*)(JSObject* obj);
20644 masm.setupAlignedABICall();
20645 masm.passABIArg(object);
20646 masm.callWithABI<Fn, ObjectIsConstructor>();
20647 masm.storeCallBoolResult(output);
20648 restoreVolatile(output);
20649 masm.jump(ool.rejoin());
20650 });
20651 addOutOfLineCode(ool, ins->mir());
20652
20653 masm.isConstructor(object, output, ool->entry());
20654
20655 masm.bind(ool->rejoin());
20656}
20657
20658void CodeGenerator::visitIsCrossRealmArrayConstructor(
20659 LIsCrossRealmArrayConstructor* ins) {
20660 Register object = ToRegister(ins->object());
20661 Register output = ToRegister(ins->output());
20662
20663 masm.setIsCrossRealmArrayConstructor(object, output);
20664}
20665
20666static void EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool,
20667 Register obj, Register output,
20668 Label* notArray = nullptr) {
20669 masm.loadObjClassUnsafe(obj, output);
20670
20671 Label isArray;
20672 masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_),
20673 &isArray);
20674
20675 // Branch to OOL path if it's a proxy.
20676 masm.branchTestClassIsProxy(true, output, ool->entry());
20677
20678 if (notArray) {
20679 masm.bind(notArray);
20680 }
20681 masm.move32(Imm32(0), output);
20682 masm.jump(ool->rejoin());
20683
20684 masm.bind(&isArray);
20685 masm.move32(Imm32(1), output);
20686
20687 masm.bind(ool->rejoin());
20688}
20689
20690void CodeGenerator::visitIsArrayO(LIsArrayO* lir) {
20691 Register object = ToRegister(lir->object());
20692 Register output = ToRegister(lir->output());
20693
20694 using Fn = bool (*)(JSContext*, HandleObject, bool*);
20695 OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>(
20696 lir, ArgList(object), StoreRegisterTo(output));
20697 EmitObjectIsArray(masm, ool, object, output);
20698}
20699
20700void CodeGenerator::visitIsArrayV(LIsArrayV* lir) {
20701 ValueOperand val = ToValue(lir->value());
20702 Register output = ToRegister(lir->output());
20703 Register temp = ToRegister(lir->temp0());
20704
20705 Label notArray;
20706 masm.fallibleUnboxObject(val, temp, &notArray);
20707
20708 using Fn = bool (*)(JSContext*, HandleObject, bool*);
20709 OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>(
20710 lir, ArgList(temp), StoreRegisterTo(output));
20711 EmitObjectIsArray(masm, ool, temp, output, &notArray);
20712}
20713
20714void CodeGenerator::visitIsTypedArray(LIsTypedArray* lir) {
20715 Register object = ToRegister(lir->object());
20716 Register output = ToRegister(lir->output());
20717
20718 OutOfLineCode* ool = nullptr;
20719 if (lir->mir()->isPossiblyWrapped()) {
20720 using Fn = bool (*)(JSContext*, JSObject*, bool*);
20721 ool = oolCallVM<Fn, jit::IsPossiblyWrappedTypedArray>(
20722 lir, ArgList(object), StoreRegisterTo(output));
20723 }
20724
20725 Label notTypedArray;
20726 Label done;
20727
20728 masm.loadObjClassUnsafe(object, output);
20729 masm.branchIfClassIsNotTypedArray(output, &notTypedArray);
20730
20731 masm.move32(Imm32(1), output);
20732 masm.jump(&done);
20733 masm.bind(&notTypedArray);
20734 if (ool) {
20735 Label notProxy;
20736 masm.branchTestClassIsProxy(false, output, &notProxy);
20737 masm.branchTestProxyHandlerFamily(Assembler::Equal, object, output,
20738 &Wrapper::family, ool->entry());
20739 masm.bind(&notProxy);
20740 }
20741 masm.move32(Imm32(0), output);
20742 masm.bind(&done);
20743 if (ool) {
20744 masm.bind(ool->rejoin());
20745 }
20746}
20747
20748void CodeGenerator::visitIsObject(LIsObject* ins) {
20749 Register output = ToRegister(ins->output());
20750 ValueOperand value = ToValue(ins->object());
20751 masm.testObjectSet(Assembler::Equal, value, output);
20752}
20753
20754void CodeGenerator::visitIsSuspendedGenerator(LIsSuspendedGenerator* lir) {
20755 Register obj = ToRegister(lir->object());
20756 Register output = ToRegister(lir->output());
20757 Register scratch = ToRegister(lir->temp0());
20758
20759 Label returnFalse, done;
20760 masm.branchIfNotSuspendedGenerator(obj, scratch, obj, &returnFalse);
20761
20762 masm.move32(Imm32(1), output);
20763 masm.jump(&done);
20764
20765 masm.bind(&returnFalse);
20766 masm.move32(Imm32(0), output);
20767
20768 masm.bind(&done);
20769}
20770
20771void CodeGenerator::visitIsSuspendedGeneratorAndBranch(
20772 LIsSuspendedGeneratorAndBranch* lir) {
20773 Register obj = ToRegister(lir->object());
20774 Register scratch = ToRegister(lir->temp0());
20775
20776 MBasicBlock* ifTrue = lir->ifTrue();
20777 MBasicBlock* ifFalse = lir->ifFalse();
20778
20779 masm.branchIfNotSuspendedGenerator(obj, scratch, obj,
20780 getJumpLabelForBranch(ifFalse));
20781 jumpToBlock(ifTrue);
20782}
20783
20784void CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins) {
20785 ValueOperand value = ToValue(ins->input());
20786
20787 MBasicBlock* ifTrue = ins->ifTrue();
20788 MBasicBlock* ifFalse = ins->ifFalse();
20789
20790 if (isNextBlock(ifFalse->lir())) {
20791 masm.branchTestObject(Assembler::Equal, value,
20792 getJumpLabelForBranch(ifTrue));
20793 } else {
20794 masm.branchTestObject(Assembler::NotEqual, value,
20795 getJumpLabelForBranch(ifFalse));
20796 jumpToBlock(ifTrue);
20797 }
20798}
20799
20800void CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins) {
20801 Register output = ToRegister(ins->output());
20802 ValueOperand value = ToValue(ins->value());
20803
20804 Label isNotNull, done;
20805 masm.branchTestNull(Assembler::NotEqual, value, &isNotNull);
20806
20807 masm.move32(Imm32(1), output);
20808 masm.jump(&done);
20809
20810 masm.bind(&isNotNull);
20811 masm.testUndefinedSet(Assembler::Equal, value, output);
20812
20813 masm.bind(&done);
20814}
20815
20816void CodeGenerator::visitIsNullOrUndefinedAndBranch(
20817 LIsNullOrUndefinedAndBranch* ins) {
20818 Label* ifTrue = getJumpLabelForBranch(ins->ifTrue());
20819 Label* ifFalse = getJumpLabelForBranch(ins->ifFalse());
20820 ValueOperand value = ToValue(ins->input());
20821
20822 ScratchTagScope tag(masm, value);
20823 masm.splitTagForTest(value, tag);
20824
20825 masm.branchTestNull(Assembler::Equal, tag, ifTrue);
20826 masm.branchTestUndefined(Assembler::Equal, tag, ifTrue);
20827
20828 if (!isNextBlock(ins->ifFalse()->lir())) {
20829 masm.jump(ifFalse);
20830 }
20831}
20832
20833void CodeGenerator::visitHasClass(LHasClass* ins) {
20834 Register lhs = ToRegister(ins->lhs());
20835 Register output = ToRegister(ins->output());
20836
20837 masm.loadObjClassUnsafe(lhs, output);
20838 masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()),
20839 output);
20840}
20841
20842void CodeGenerator::visitHasShape(LHasShape* ins) {
20843 Register obj = ToRegister(ins->object());
20844 Register output = ToRegister(ins->output());
20845
20846 // Note: no Spectre mitigations are needed here because this shape check only
20847 // affects correctness.
20848 masm.loadObjShapeUnsafe(obj, output);
20849 masm.cmpPtrSet(Assembler::Equal, output, ImmGCPtr(ins->mir()->shape()),
20850 output);
20851}
20852
20853void CodeGenerator::visitGuardToClass(LGuardToClass* ins) {
20854 Register lhs = ToRegister(ins->lhs());
20855 Register temp = ToRegister(ins->temp0());
20856
20857 // branchTestObjClass may zero the object register on speculative paths
20858 // (we should have a defineReuseInput allocation in this case).
20859 Register spectreRegToZero = lhs;
20860
20861 Label notEqual;
20862
20863 masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(),
20864 temp, spectreRegToZero, &notEqual);
20865
20866 // Can't return null-return here, so bail.
20867 bailoutFrom(&notEqual, ins->snapshot());
20868}
20869
20870void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) {
20871 Register lhs = ToRegister(ins->lhs());
20872 Register temp = ToRegister(ins->temp0());
20873
20874 // branchTestObjClass may zero the object register on speculative paths
20875 // (we should have a defineReuseInput allocation in this case).
20876 Register spectreRegToZero = lhs;
20877
20878 Label notEqual;
20879
20880 masm.branchTestObjIsFunction(Assembler::NotEqual, lhs, temp, spectreRegToZero,
20881 &notEqual);
20882
20883 // Can't return null-return here, so bail.
20884 bailoutFrom(&notEqual, ins->snapshot());
20885}
20886
20887void CodeGenerator::visitObjectClassToString(LObjectClassToString* lir) {
20888 Register obj = ToRegister(lir->object());
20889 Register temp = ToRegister(lir->temp0());
20890
20891 using Fn = JSString* (*)(JSContext*, JSObject*);
20892 masm.setupAlignedABICall();
20893 masm.loadJSContext(temp);
20894 masm.passABIArg(temp);
20895 masm.passABIArg(obj);
20896 masm.callWithABI<Fn, js::ObjectClassToString>();
20897
20898 bailoutCmpPtr(Assembler::Equal, ReturnReg, ImmWord(0), lir->snapshot());
20899}
20900
20901void CodeGenerator::visitWasmParameter(LWasmParameter* lir) {}
20902
20903void CodeGenerator::visitWasmParameterI64(LWasmParameterI64* lir) {}
20904
20905void CodeGenerator::visitWasmReturn(LWasmReturn* lir) {
20906 // Don't emit a jump to the return label if this is the last block.
20907 if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) {
20908 masm.jump(&returnLabel_);
20909 }
20910}
20911
20912void CodeGenerator::visitWasmReturnI64(LWasmReturnI64* lir) {
20913 // Don't emit a jump to the return label if this is the last block.
20914 if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) {
20915 masm.jump(&returnLabel_);
20916 }
20917}
20918
20919void CodeGenerator::visitWasmReturnVoid(LWasmReturnVoid* lir) {
20920 // Don't emit a jump to the return label if this is the last block.
20921 if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) {
20922 masm.jump(&returnLabel_);
20923 }
20924}
20925
20926void CodeGenerator::emitAssertRangeI(MIRType type, const Range* r,
20927 Register input) {
20928 // Check the lower bound.
20929 if (r->hasInt32LowerBound() && r->lower() > INT32_MIN(-2147483647-1)) {
20930 Label success;
20931 if (type == MIRType::Int32 || type == MIRType::Boolean) {
20932 masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()),
20933 &success);
20934 } else {
20935 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", 20935); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 20935); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
20936 masm.branchPtr(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()),
20937 &success);
20938 }
20939 masm.assumeUnreachable(
20940 "Integer input should be equal or higher than Lowerbound.");
20941 masm.bind(&success);
20942 }
20943
20944 // Check the upper bound.
20945 if (r->hasInt32UpperBound() && r->upper() < INT32_MAX(2147483647)) {
20946 Label success;
20947 if (type == MIRType::Int32 || type == MIRType::Boolean) {
20948 masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()),
20949 &success);
20950 } else {
20951 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", 20951); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence
(__null, 20951); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
20952 masm.branchPtr(Assembler::LessThanOrEqual, input, Imm32(r->upper()),
20953 &success);
20954 }
20955 masm.assumeUnreachable(
20956 "Integer input should be lower or equal than Upperbound.");
20957 masm.bind(&success);
20958 }
20959
20960 // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and
20961 // r->exponent(), there's nothing to check, because if we ended up in the
20962 // integer range checking code, the value is already in an integer register
20963 // in the integer range.
20964}
20965
20966void CodeGenerator::emitAssertRangeD(const Range* r, FloatRegister input,
20967 FloatRegister temp) {
20968 // Check the lower bound.
20969 if (r->hasInt32LowerBound()) {
20970 Label success;
20971 masm.loadConstantDouble(r->lower(), temp);
20972 if (r->canBeNaN()) {
20973 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
20974 }
20975 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp,
20976 &success);
20977 masm.assumeUnreachable(
20978 "Double input should be equal or higher than Lowerbound.");
20979 masm.bind(&success);
20980 }
20981 // Check the upper bound.
20982 if (r->hasInt32UpperBound()) {
20983 Label success;
20984 masm.loadConstantDouble(r->upper(), temp);
20985 if (r->canBeNaN()) {
20986 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
20987 }
20988 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
20989 masm.assumeUnreachable(
20990 "Double input should be lower or equal than Upperbound.");
20991 masm.bind(&success);
20992 }
20993
20994 // This code does not yet check r->canHaveFractionalPart(). This would require
20995 // new assembler interfaces to make rounding instructions available.
20996
20997 if (!r->canBeNegativeZero()) {
20998 Label success;
20999
21000 // First, test for being equal to 0.0, which also includes -0.0.
21001 masm.loadConstantDouble(0.0, temp);
21002 masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp,
21003 &success);
21004
21005 // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is
21006 // -Infinity instead of Infinity.
21007 masm.loadConstantDouble(1.0, temp);
21008 masm.divDouble(input, temp);
21009 masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success);
21010
21011 masm.assumeUnreachable("Input shouldn't be negative zero.");
21012
21013 masm.bind(&success);
21014 }
21015
21016 if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() &&
21017 r->exponent() < FloatingPoint<double>::kExponentBias) {
21018 // Check the bounds implied by the maximum exponent.
21019 Label exponentLoOk;
21020 masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp);
21021 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
21022 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp,
21023 &exponentLoOk);
21024 masm.assumeUnreachable("Check for exponent failed.");
21025 masm.bind(&exponentLoOk);
21026
21027 Label exponentHiOk;
21028 masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp);
21029 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk);
21030 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp,
21031 &exponentHiOk);
21032 masm.assumeUnreachable("Check for exponent failed.");
21033 masm.bind(&exponentHiOk);
21034 } else if (!r->hasInt32Bounds() && !r->canBeNaN()) {
21035 // If we think the value can't be NaN, check that it isn't.
21036 Label notnan;
21037 masm.branchDouble(Assembler::DoubleOrdered, input, input, &notnan);
21038 masm.assumeUnreachable("Input shouldn't be NaN.");
21039 masm.bind(&notnan);
21040
21041 // If we think the value also can't be an infinity, check that it isn't.
21042 if (!r->canBeInfiniteOrNaN()) {
21043 Label notposinf;
21044 masm.loadConstantDouble(PositiveInfinity<double>(), temp);
21045 masm.branchDouble(Assembler::DoubleLessThan, input, temp, &notposinf);
21046 masm.assumeUnreachable("Input shouldn't be +Inf.");
21047 masm.bind(&notposinf);
21048
21049 Label notneginf;
21050 masm.loadConstantDouble(NegativeInfinity<double>(), temp);
21051 masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, &notneginf);
21052 masm.assumeUnreachable("Input shouldn't be -Inf.");
21053 masm.bind(&notneginf);
21054 }
21055 }
21056}
21057
21058void CodeGenerator::visitAssertClass(LAssertClass* ins) {
21059 Register obj = ToRegister(ins->input());
21060 Register temp = ToRegister(ins->temp0());
21061
21062 Label success;
21063 if (ins->mir()->getClass() == &FunctionClass) {
21064 // Allow both possible function classes here.
21065 masm.branchTestObjIsFunctionNoSpectreMitigations(Assembler::Equal, obj,
21066 temp, &success);
21067 } else {
21068 masm.branchTestObjClassNoSpectreMitigations(
21069 Assembler::Equal, obj, ins->mir()->getClass(), temp, &success);
21070 }
21071 masm.assumeUnreachable("Wrong KnownClass during run-time");
21072 masm.bind(&success);
21073}
21074
21075void CodeGenerator::visitAssertShape(LAssertShape* ins) {
21076 Register obj = ToRegister(ins->object());
21077
21078 Label success;
21079 masm.branchTestObjShapeNoSpectreMitigations(Assembler::Equal, obj,
21080 ins->mir()->shape(), &success);
21081 masm.assumeUnreachable("Wrong Shape during run-time");
21082 masm.bind(&success);
21083}
21084
21085void CodeGenerator::visitAssertRangeI(LAssertRangeI* ins) {
21086 Register input = ToRegister(ins->input());
21087 const Range* r = ins->mir()->assertedRange();
21088
21089 emitAssertRangeI(ins->mir()->input()->type(), r, input);
21090}
21091
21092void CodeGenerator::visitAssertRangeD(LAssertRangeD* ins) {
21093 FloatRegister input = ToFloatRegister(ins->input());
21094 FloatRegister temp = ToFloatRegister(ins->temp0());
21095 const Range* r = ins->mir()->assertedRange();
21096
21097 emitAssertRangeD(r, input, temp);
21098}
21099
21100void CodeGenerator::visitAssertRangeF(LAssertRangeF* ins) {
21101 FloatRegister input = ToFloatRegister(ins->input());
21102 FloatRegister temp = ToFloatRegister(ins->temp0());
21103 FloatRegister temp2 = ToFloatRegister(ins->temp1());
21104
21105 const Range* r = ins->mir()->assertedRange();
21106
21107 masm.convertFloat32ToDouble(input, temp);
21108 emitAssertRangeD(r, temp, temp2);
21109}
21110
21111void CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) {
21112 const Range* r = ins->mir()->assertedRange();
21113 ValueOperand value = ToValue(ins->input());
21114 Label done;
21115
21116 {
21117 ScratchTagScope tag(masm, value);
21118 masm.splitTagForTest(value, tag);
21119
21120 {
21121 Label isNotInt32;
21122 masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32);
21123 {
21124 ScratchTagScopeRelease _(&tag);
21125 Register unboxInt32 = ToTempUnboxRegister(ins->temp0());
21126 Register input = masm.extractInt32(value, unboxInt32);
21127 emitAssertRangeI(MIRType::Int32, r, input);
21128 masm.jump(&done);
21129 }
21130 masm.bind(&isNotInt32);
21131 }
21132
21133 {
21134 Label isNotDouble;
21135 masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble);
21136 {
21137 ScratchTagScopeRelease _(&tag);
21138 FloatRegister input = ToFloatRegister(ins->temp1());
21139 FloatRegister temp = ToFloatRegister(ins->temp2());
21140 masm.unboxDouble(value, input);
21141 emitAssertRangeD(r, input, temp);
21142 masm.jump(&done);
21143 }
21144 masm.bind(&isNotDouble);
21145 }
21146 }
21147
21148 masm.assumeUnreachable("Incorrect range for Value.");
21149 masm.bind(&done);
21150}
21151
21152void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) {
21153 using Fn = bool (*)(JSContext*);
21154 OutOfLineCode* ool =
21155 oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing());
21156
21157 const void* interruptAddr = gen->runtime->addressOfInterruptBits();
21158 masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0),
21159 ool->entry());
21160 masm.bind(ool->rejoin());
21161}
21162
21163void CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir) {
21164 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", 21164); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21164); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21165
21166 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21167 emitResumableWasmTrapOOL(lir, masm.framePushed(),
21168 lir->mir()->trapSiteDesc(),
21169 wasm::Trap::CheckInterrupt);
21170 masm.jump(ool.rejoin());
21171 });
21172 addOutOfLineCode(ool, lir->mir());
21173 masm.branch32(
21174 Assembler::NotEqual,
21175 Address(ToRegister(lir->instance()), wasm::Instance::offsetOfInterrupt()),
21176 Imm32(0), ool->entry());
21177 masm.bind(ool->rejoin());
21178}
21179
21180void CodeGenerator::visitWasmTrap(LWasmTrap* lir) {
21181 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", 21181); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21181); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21182 const MWasmTrap* mir = lir->mir();
21183
21184 masm.wasmTrap(mir->trap(), mir->trapSiteDesc());
21185}
21186
21187void CodeGenerator::visitWasmRefAsNonNull(LWasmRefAsNonNull* lir) {
21188 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", 21188); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21188); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21189 const MWasmRefAsNonNull* mir = lir->mir();
21190 Label nonNull;
21191 Register ref = ToRegister(lir->ref());
21192
21193 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21194 masm.wasmTrap(wasm::Trap::NullPointerDereference, mir->trapSiteDesc());
21195 });
21196 addOutOfLineCode(ool, mir);
21197 masm.branchWasmAnyRefIsNull(true, ref, ool->entry());
21198}
21199
21200void CodeGenerator::visitWasmRefTestAbstract(LWasmRefTestAbstract* ins) {
21201 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", 21201); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21201); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21202
21203 const MWasmRefTestAbstract* mir = ins->mir();
21204 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"
, 21204); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()"
")"); do { MOZ_CrashSequence(__null, 21204); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21205
21206 Register ref = ToRegister(ins->ref());
21207 Register superSTV = Register::Invalid();
21208 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
21209 Register scratch2 = Register::Invalid();
21210 Register result = ToRegister(ins->output());
21211 Label onSuccess;
21212 Label onFail;
21213 Label join;
21214 masm.branchWasmRefIsSubtype(ref, mir->ref()->wasmRefType(), mir->destType(),
21215 &onSuccess,
21216 /*onSuccess=*/true, /*signalNullChecks=*/false,
21217 superSTV, scratch1, scratch2);
21218 masm.bind(&onFail);
21219 masm.xor32(result, result);
21220 masm.jump(&join);
21221 masm.bind(&onSuccess);
21222 masm.move32(Imm32(1), result);
21223 masm.bind(&join);
21224}
21225
21226void CodeGenerator::visitWasmRefTestConcrete(LWasmRefTestConcrete* ins) {
21227 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", 21227); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21227); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21228
21229 const MWasmRefTestConcrete* mir = ins->mir();
21230 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"
, 21230); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()"
")"); do { MOZ_CrashSequence(__null, 21230); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21231
21232 Register ref = ToRegister(ins->ref());
21233 Register superSTV = ToRegister(ins->superSTV());
21234 Register scratch1 = ToRegister(ins->temp0());
21235 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
21236 Register result = ToRegister(ins->output());
21237 Label onSuccess;
21238 Label join;
21239 masm.branchWasmRefIsSubtype(ref, mir->ref()->wasmRefType(), mir->destType(),
21240 &onSuccess,
21241 /*onSuccess=*/true, /*signalNullChecks=*/false,
21242 superSTV, scratch1, scratch2);
21243 masm.move32(Imm32(0), result);
21244 masm.jump(&join);
21245 masm.bind(&onSuccess);
21246 masm.move32(Imm32(1), result);
21247 masm.bind(&join);
21248}
21249
21250void CodeGenerator::visitWasmRefTestAbstractAndBranch(
21251 LWasmRefTestAbstractAndBranch* ins) {
21252 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", 21252); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21252); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21253 Register ref = ToRegister(ins->ref());
21254 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
21255 Label* onSuccess = getJumpLabelForBranch(ins->ifTrue());
21256 Label* onFail = getJumpLabelForBranch(ins->ifFalse());
21257 masm.branchWasmRefIsSubtype(ref, ins->sourceType(), ins->destType(),
21258 onSuccess, /*onSuccess=*/true,
21259 /*signalNullChecks=*/false, Register::Invalid(),
21260 scratch1, Register::Invalid());
21261 masm.jump(onFail);
21262}
21263
21264void CodeGenerator::visitWasmRefTestConcreteAndBranch(
21265 LWasmRefTestConcreteAndBranch* ins) {
21266 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", 21266); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21266); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21267 Register ref = ToRegister(ins->ref());
21268 Register superSTV = ToRegister(ins->superSTV());
21269 Register scratch1 = ToRegister(ins->temp0());
21270 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
21271 Label* onSuccess = getJumpLabelForBranch(ins->ifTrue());
21272 Label* onFail = getJumpLabelForBranch(ins->ifFalse());
21273 masm.branchWasmRefIsSubtype(
21274 ref, ins->sourceType(), ins->destType(), onSuccess, /*onSuccess=*/true,
21275 /*signalNullChecks=*/false, superSTV, scratch1, scratch2);
21276 masm.jump(onFail);
21277}
21278
21279void CodeGenerator::visitWasmRefCastAbstract(LWasmRefCastAbstract* ins) {
21280 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", 21280); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21280); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21281
21282 const MWasmRefCastAbstract* mir = ins->mir();
21283 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"
, 21283); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()"
")"); do { MOZ_CrashSequence(__null, 21283); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21284
21285 Register ref = ToRegister(ins->ref());
21286 Register superSTV = Register::Invalid();
21287 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
21288 Register scratch2 = Register::Invalid();
21289 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"
, 21289); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref == ToRegister(ins->output())"
")"); do { MOZ_CrashSequence(__null, 21289); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21290 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21291 masm.wasmTrap(wasm::Trap::BadCast, mir->trapSiteDesc());
21292 });
21293 addOutOfLineCode(ool, ins->mir());
21294 FaultingCodeRange fcr = masm.branchWasmRefIsSubtype(
21295 ref, mir->ref()->wasmRefType(), mir->destType(), ool->entry(),
21296 /*onSuccess=*/false, /*signalNullChecks=*/true, superSTV, scratch1,
21297 scratch2);
21298 if (fcr.isValid()) {
21299 masm.appendAndVerify(wasm::Trap::BadCast,
21300 wasm::TrapMachineInsnForLoadWord(), fcr,
21301 mir->trapSiteDesc());
21302 }
21303}
21304
21305void CodeGenerator::visitWasmRefCastConcrete(LWasmRefCastConcrete* ins) {
21306 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", 21306); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21306); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21307
21308 const MWasmRefCastConcrete* mir = ins->mir();
21309 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"
, 21309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()"
")"); do { MOZ_CrashSequence(__null, 21309); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21310
21311 Register ref = ToRegister(ins->ref());
21312 Register superSTV = ToRegister(ins->superSTV());
21313 Register scratch1 = ToRegister(ins->temp0());
21314 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
21315 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"
, 21315); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref == ToRegister(ins->output())"
")"); do { MOZ_CrashSequence(__null, 21315); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21316 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21317 masm.wasmTrap(wasm::Trap::BadCast, mir->trapSiteDesc());
21318 });
21319 addOutOfLineCode(ool, ins->mir());
21320 FaultingCodeRange fcr = masm.branchWasmRefIsSubtype(
21321 ref, mir->ref()->wasmRefType(), mir->destType(), ool->entry(),
21322 /*onSuccess=*/false, /*signalNullChecks=*/true, superSTV, scratch1,
21323 scratch2);
21324 if (fcr.isValid()) {
21325 masm.appendAndVerify(wasm::Trap::BadCast,
21326 wasm::TrapMachineInsnForLoadWord(), fcr,
21327 mir->trapSiteDesc());
21328 }
21329}
21330
21331void CodeGenerator::callWasmStructAllocFun(
21332 LInstruction* lir, wasm::SymbolicAddress fun, Register typeDefIndex,
21333 Register allocSite, Register output,
21334 const wasm::TrapSiteDesc& trapSiteDesc) {
21335 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", 21338); 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, 21338); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21336 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", 21338); 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, 21338); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21337 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", 21338); 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, 21338); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21338 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", 21338); 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, 21338); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21339 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", 21340); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21340); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21340 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", 21340); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21340); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21341 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", 21342); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21342); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21342 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", 21342); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21342); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21343 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", 21344); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_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::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", 21344); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21344); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21345 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", 21346); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_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::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", 21346); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_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(typeDefIndex);
21355 masm.passABIArg(allocSite);
21356 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
21357 CodeOffset offset =
21358 masm.callWithABI(trapSiteDesc.bytecodeOffset, fun,
21359 mozilla::Some(instanceOffset), ABIType::General);
21360 masm.storeCallPointerResult(output);
21361
21362 markSafepointAt(offset.offset(), lir);
21363 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance);
21364 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall);
21365
21366 restoreLive(lir);
21367 masm.Pop(InstanceReg);
21368#if JS_CODEGEN_ARM64
21369 masm.syncStackPtr();
21370#endif
21371
21372 masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr,
21373 wasm::Trap::ThrowReported, trapSiteDesc);
21374}
21375
21376void CodeGenerator::visitWasmNewStructObject(LWasmNewStructObject* lir) {
21377 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", 21377); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21377); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21378
21379 MWasmNewStructObject* mir = lir->mir();
21380 uint32_t typeDefIndex = wasmCodeMeta()->types->indexOf(mir->typeDef());
21381
21382 Register allocSite = ToRegister(lir->allocSite());
21383 Register output = ToRegister(lir->output());
21384 Register temp = ToRegister(lir->temp0());
21385
21386 if (mir->isOutline()) {
21387 wasm::SymbolicAddress fun = mir->zeroFields()
21388 ? wasm::SymbolicAddress::StructNewOOL_true
21389 : wasm::SymbolicAddress::StructNewOOL_false;
21390
21391 masm.move32(Imm32(typeDefIndex), temp);
21392 callWasmStructAllocFun(lir, fun, temp, allocSite, output,
21393 mir->trapSiteDesc());
21394 } else {
21395 wasm::SymbolicAddress fun = mir->zeroFields()
21396 ? wasm::SymbolicAddress::StructNewIL_true
21397 : wasm::SymbolicAddress::StructNewIL_false;
21398
21399 Register instance = ToRegister(lir->instance());
21400 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", 21400); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence
(__null, 21400); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21401
21402 auto* ool =
21403 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21404 masm.move32(Imm32(typeDefIndex), temp);
21405 callWasmStructAllocFun(lir, fun, temp, allocSite, output,
21406 mir->trapSiteDesc());
21407 masm.jump(ool.rejoin());
21408 });
21409 addOutOfLineCode(ool, lir->mir());
21410
21411 size_t offsetOfTypeDefData = wasm::Instance::offsetInData(
21412 wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex));
21413 masm.wasmNewStructObject(instance, output, allocSite, temp,
21414 offsetOfTypeDefData, ool->entry(),
21415 mir->allocKind(), mir->zeroFields());
21416
21417 masm.bind(ool->rejoin());
21418 }
21419}
21420
21421void CodeGenerator::callWasmArrayAllocFun(
21422 LInstruction* lir, wasm::SymbolicAddress fun, Register numElements,
21423 Register typeDefIndex, Register allocSite, Register output,
21424 const wasm::TrapSiteDesc& trapSiteDesc) {
21425 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", 21426); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
")"); do { MOZ_CrashSequence(__null, 21426); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21426 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", 21426); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
")"); do { MOZ_CrashSequence(__null, 21426); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21427 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", 21428); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21428); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21428 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", 21428); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21428); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21429 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", 21430); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21430); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
21430 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", 21430); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { MOZ_CrashSequence(__null, 21430); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21431
21432 masm.Push(InstanceReg);
21433 int32_t framePushedAfterInstance = masm.framePushed();
21434 saveLive(lir);
21435
21436 masm.setupWasmABICall(fun);
21437 masm.passABIArg(InstanceReg);
21438 masm.passABIArg(numElements);
21439 masm.passABIArg(typeDefIndex);
21440 masm.passABIArg(allocSite);
21441 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
21442 CodeOffset offset =
21443 masm.callWithABI(trapSiteDesc.bytecodeOffset, fun,
21444 mozilla::Some(instanceOffset), ABIType::General);
21445 masm.storeCallPointerResult(output);
21446
21447 markSafepointAt(offset.offset(), lir);
21448 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance);
21449 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall);
21450
21451 restoreLive(lir);
21452 masm.Pop(InstanceReg);
21453#if JS_CODEGEN_ARM64
21454 masm.syncStackPtr();
21455#endif
21456
21457 masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr,
21458 wasm::Trap::ThrowReported, trapSiteDesc);
21459}
21460
21461void CodeGenerator::visitWasmNewArrayObject(LWasmNewArrayObject* lir) {
21462 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", 21462); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 21462); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21463
21464 MWasmNewArrayObject* mir = lir->mir();
21465 uint32_t typeDefIndex = wasmCodeMeta()->types->indexOf(mir->typeDef());
21466
21467 Register allocSite = ToRegister(lir->allocSite());
21468 Register output = ToRegister(lir->output());
21469 Register temp0 = ToRegister(lir->temp0());
21470 Register temp1 = ToRegister(lir->temp1());
21471
21472 wasm::SymbolicAddress fun = mir->zeroFields()
21473 ? wasm::SymbolicAddress::ArrayNew_true
21474 : wasm::SymbolicAddress::ArrayNew_false;
21475
21476 if (lir->numElements()->isConstant()) {
21477 // numElements is constant, so we can do optimized code generation.
21478 uint32_t numElements = lir->numElements()->toConstant()->toInt32();
21479 CheckedUint32 arrayDataBytes = WasmArrayObject::calcArrayDataBytesChecked(
21480 mir->elemSize(), numElements);
21481 if (!arrayDataBytes.isValid() ||
21482 arrayDataBytes.value() > WasmArrayObject_MaxInlineBytes) {
21483 // Too much array data to store inline. Immediately perform an instance
21484 // call to handle the out-of-line storage (or the trap).
21485 masm.move32(Imm32(typeDefIndex), temp0);
21486 masm.move32(Imm32(numElements), temp1);
21487 callWasmArrayAllocFun(lir, fun, temp1, temp0, allocSite, output,
21488 mir->trapSiteDesc());
21489 } else {
21490 // arrayDataBytes is small enough to be stored inline in WasmArrayObject.
21491 // Attempt a nursery allocation and fall back to an instance call if it
21492 // fails.
21493 Register instance = ToRegister(lir->instance());
21494 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", 21494); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence
(__null, 21494); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21495
21496 auto* ool =
21497 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21498 masm.move32(Imm32(typeDefIndex), temp0);
21499 masm.move32(Imm32(numElements), temp1);
21500 callWasmArrayAllocFun(lir, fun, temp1, temp0, allocSite, output,
21501 mir->trapSiteDesc());
21502 masm.jump(ool.rejoin());
21503 });
21504 addOutOfLineCode(ool, lir->mir());
21505
21506 size_t offsetOfTypeDefData = wasm::Instance::offsetInData(
21507 wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex));
21508 masm.wasmNewArrayObjectFixed(
21509 instance, output, allocSite, temp0, temp1, offsetOfTypeDefData,
21510 ool->entry(), numElements, arrayDataBytes.value(), mir->zeroFields());
21511
21512 masm.bind(ool->rejoin());
21513 }
21514 } else {
21515 // numElements is dynamic. Attempt a dynamic inline-storage nursery
21516 // allocation and fall back to an instance call if it fails.
21517 Register instance = ToRegister(lir->instance());
21518 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", 21518); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence
(__null, 21518); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
21519 Register numElements = ToRegister(lir->numElements());
21520
21521 auto* ool =
21522 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21523 masm.move32(Imm32(typeDefIndex), temp0);
21524 callWasmArrayAllocFun(lir, fun, numElements, temp0, allocSite, output,
21525 mir->trapSiteDesc());
21526 masm.jump(ool.rejoin());
21527 });
21528 addOutOfLineCode(ool, lir->mir());
21529
21530 size_t offsetOfTypeDefData = wasm::Instance::offsetInData(
21531 wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex));
21532 masm.wasmNewArrayObject(instance, output, numElements, allocSite, temp1,
21533 offsetOfTypeDefData, ool->entry(), mir->elemSize(),
21534 mir->zeroFields());
21535
21536 masm.bind(ool->rejoin());
21537 }
21538}
21539
21540void CodeGenerator::visitWasmHeapReg(LWasmHeapReg* ins) {
21541#ifdef WASM_HAS_HEAPREG1
21542 masm.movePtr(HeapReg, ToRegister(ins->output()));
21543#else
21544 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp"
, 21544); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence
(__null, 21544); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
21545#endif
21546}
21547
21548void CodeGenerator::emitResumableWasmTrapOOL(
21549 LInstruction* lir, size_t framePushed,
21550 const wasm::TrapSiteDesc& trapSiteDesc, wasm::Trap trap) {
21551 FaultingCodeRange fcr = masm.wasmTrap(trap, trapSiteDesc);
21552 if (!fcr.isValid()) {
21553 return;
21554 }
21555
21556 markSafepointAt(fcr.resumeOffset(), lir);
21557
21558 // Note that masm.framePushed() doesn't include the register dump area.
21559 // That will be taken into account when the StackMap is created from the
21560 // LSafepoint.
21561 lir->safepoint()->setFramePushedAtStackMapBase(framePushed);
21562 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::Trap);
21563}
21564
21565void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) {
21566 const MWasmBoundsCheck* mir = ins->mir();
21567
21568 Register ptr = ToRegister(ins->ptr());
21569 if (ins->boundsCheckLimit()->isConstant()) {
21570 auto* ool =
21571 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21572 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21573 });
21574 addOutOfLineCode(ool, mir);
21575 masm.branch32(Assembler::AboveOrEqual, ptr,
21576 Imm32(ins->boundsCheckLimit()->toConstant()->toInt32()),
21577 ool->entry());
21578 return;
21579 }
21580
21581 Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit());
21582 // When there are no spectre mitigations in place, branching out-of-line to
21583 // the trap is a big performance win, but with mitigations it's trickier. See
21584 // bug 1680243.
21585 if (JitOptions.spectreIndexMasking) {
21586 Label ok;
21587 masm.wasmBoundsCheck32(Assembler::Below, ptr, boundsCheckLimit, &ok);
21588 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21589 masm.bind(&ok);
21590 } else {
21591 auto* ool =
21592 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21593 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21594 });
21595 addOutOfLineCode(ool, mir);
21596 masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
21597 ool->entry());
21598 }
21599}
21600
21601void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) {
21602 const MWasmBoundsCheck* mir = ins->mir();
21603
21604 Register64 ptr = ToRegister64(ins->ptr());
21605 if (IsConstant(ins->boundsCheckLimit())) {
21606 auto* ool =
21607 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21608 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21609 });
21610 addOutOfLineCode(ool, mir);
21611 masm.branch64(Assembler::AboveOrEqual, ptr,
21612 Imm64(ToInt64(ins->boundsCheckLimit())), ool->entry());
21613 return;
21614 }
21615
21616 Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit());
21617 // See above.
21618 if (JitOptions.spectreIndexMasking) {
21619 Label ok;
21620 masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok);
21621 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21622 masm.bind(&ok);
21623 } else {
21624 auto* ool =
21625 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21626 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21627 });
21628 addOutOfLineCode(ool, mir);
21629 masm.wasmBoundsCheck64(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
21630 ool->entry());
21631 }
21632}
21633
21634void CodeGenerator::visitWasmBoundsCheckInstanceField(
21635 LWasmBoundsCheckInstanceField* ins) {
21636 const MWasmBoundsCheck* mir = ins->mir();
21637 Register ptr = ToRegister(ins->ptr());
21638 Register instance = ToRegister(ins->instance());
21639 // See above.
21640 if (JitOptions.spectreIndexMasking) {
21641 Label ok;
21642 masm.wasmBoundsCheck32(Assembler::Condition::Below, ptr,
21643 Address(instance, ins->offset()), &ok);
21644 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21645 masm.bind(&ok);
21646 } else {
21647 auto* ool =
21648 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21649 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21650 });
21651 addOutOfLineCode(ool, mir);
21652 masm.wasmBoundsCheck32(Assembler::Condition::AboveOrEqual, ptr,
21653 Address(instance, ins->offset()), ool->entry());
21654 }
21655}
21656
21657void CodeGenerator::visitWasmBoundsCheckInstanceField64(
21658 LWasmBoundsCheckInstanceField64* ins) {
21659 const MWasmBoundsCheck* mir = ins->mir();
21660 Register64 ptr = ToRegister64(ins->ptr());
21661 Register instance = ToRegister(ins->instance());
21662 // See above.
21663 if (JitOptions.spectreIndexMasking) {
21664 Label ok;
21665 masm.wasmBoundsCheck64(Assembler::Condition::Below, ptr,
21666 Address(instance, ins->offset()), &ok);
21667 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21668 masm.bind(&ok);
21669 } else {
21670 auto* ool =
21671 new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21672 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
21673 });
21674 addOutOfLineCode(ool, mir);
21675 masm.wasmBoundsCheck64(Assembler::Condition::AboveOrEqual, ptr,
21676 Address(instance, ins->offset()), ool->entry());
21677 }
21678}
21679
21680void CodeGenerator::visitWasmBoundsCheckRange32(LWasmBoundsCheckRange32* ins) {
21681 const MWasmBoundsCheckRange32* mir = ins->mir();
21682 Register index = ToRegister(ins->index());
21683 Register length = ToRegister(ins->length());
21684 Register limit = ToRegister(ins->limit());
21685 Register tmp = ToRegister(ins->temp0());
21686
21687 masm.wasmBoundsCheckRange32(index, length, limit, tmp, mir->trapSiteDesc());
21688}
21689
21690void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) {
21691 const MWasmAlignmentCheck* mir = ins->mir();
21692 Register ptr = ToRegister(ins->ptr());
21693 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21694 masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->trapSiteDesc());
21695 });
21696 addOutOfLineCode(ool, mir);
21697 masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1),
21698 ool->entry());
21699}
21700
21701void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) {
21702 const MWasmAlignmentCheck* mir = ins->mir();
21703 Register64 ptr = ToRegister64(ins->ptr());
21704#ifdef JS_64BIT1
21705 Register r = ptr.reg;
21706#else
21707 Register r = ptr.low;
21708#endif
21709 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
21710 masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->trapSiteDesc());
21711 });
21712 addOutOfLineCode(ool, mir);
21713 masm.branchTestPtr(Assembler::NonZero, r, Imm32(mir->byteSize() - 1),
21714 ool->entry());
21715}
21716
21717void CodeGenerator::visitWasmLoadInstance(LWasmLoadInstance* ins) {
21718 switch (ins->mir()->type()) {
21719 case MIRType::WasmAnyRef:
21720 case MIRType::Pointer:
21721 masm.loadPtr(Address(ToRegister(ins->instance()), ins->mir()->offset()),
21722 ToRegister(ins->output()));
21723 break;
21724 case MIRType::Int32:
21725 masm.load32(Address(ToRegister(ins->instance()), ins->mir()->offset()),
21726 ToRegister(ins->output()));
21727 break;
21728 default:
21729 MOZ_CRASH("MIRType not supported in WasmLoadInstance")do { do { } while (false); MOZ_ReportCrash("" "MIRType not supported in WasmLoadInstance"
, "./../../../../js/src/jit/CodeGenerator.cpp", 21729); AnnotateMozCrashReason
("MOZ_CRASH(" "MIRType not supported in WasmLoadInstance" ")"
); do { MOZ_CrashSequence(__null, 21729); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
21730 }
21731}
21732
21733void CodeGenerator::visitWasmLoadInstance64(LWasmLoadInstance64* ins) {
21734 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", 21734); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ins->mir()->type() == MIRType::Int64"
")"); do { MOZ_CrashSequence(__null, 21734); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21735 masm.load64(Address(ToRegister(ins->instance()), ins->mir()->offset()),
21736 ToOutRegister64(ins));
21737}
21738
21739void CodeGenerator::incrementWarmUpCounter(AbsoluteAddress warmUpCount,
21740 JSScript* script, Register tmp) {
21741 // The code depends on the JitScript* not being discarded without also
21742 // invalidating Ion code. Assert this.
21743#ifdef DEBUG1
21744 Label ok;
21745 masm.movePtr(ImmGCPtr(script), tmp);
21746 masm.loadJitScript(tmp, tmp);
21747 masm.branchPtr(Assembler::Equal, tmp, ImmPtr(script->jitScript()), &ok);
21748 masm.assumeUnreachable("Didn't find JitScript?");
21749 masm.bind(&ok);
21750#endif
21751
21752 masm.load32(warmUpCount, tmp);
21753 masm.add32(Imm32(1), tmp);
21754 masm.store32(tmp, warmUpCount);
21755}
21756
21757void CodeGenerator::visitIncrementWarmUpCounter(LIncrementWarmUpCounter* ins) {
21758 Register tmp = ToRegister(ins->temp0());
21759
21760 AbsoluteAddress warmUpCount =
21761 AbsoluteAddress(ins->mir()->script()->jitScript())
21762 .offset(JitScript::offsetOfWarmUpCount());
21763 incrementWarmUpCounter(warmUpCount, ins->mir()->script(), tmp);
21764}
21765
21766void CodeGenerator::visitLexicalCheck(LLexicalCheck* ins) {
21767 ValueOperand inputValue = ToValue(ins->input());
21768 Label bail;
21769 masm.branchTestMagicValue(Assembler::Equal, inputValue,
21770 JS_UNINITIALIZED_LEXICAL, &bail);
21771 bailoutFrom(&bail, ins->snapshot());
21772}
21773
21774void CodeGenerator::visitThrowRuntimeLexicalError(
21775 LThrowRuntimeLexicalError* ins) {
21776 pushArg(Imm32(ins->mir()->errorNumber()));
21777
21778 using Fn = bool (*)(JSContext*, unsigned);
21779 callVM<Fn, jit::ThrowRuntimeLexicalError>(ins);
21780}
21781
21782void CodeGenerator::visitThrowMsg(LThrowMsg* ins) {
21783 pushArg(Imm32(static_cast<int32_t>(ins->mir()->throwMsgKind())));
21784
21785 using Fn = bool (*)(JSContext*, unsigned);
21786 callVM<Fn, js::ThrowMsgOperation>(ins);
21787}
21788
21789void CodeGenerator::visitGlobalDeclInstantiation(
21790 LGlobalDeclInstantiation* ins) {
21791 pushArg(ImmPtr(ins->mir()->resumePoint()->pc()));
21792 pushArg(ImmGCPtr(ins->mir()->block()->info().script()));
21793
21794 using Fn = bool (*)(JSContext*, HandleScript, const jsbytecode*);
21795 callVM<Fn, GlobalDeclInstantiationFromIon>(ins);
21796}
21797
21798void CodeGenerator::visitDebugger(LDebugger* ins) {
21799 Register cx = ToRegister(ins->temp0());
21800
21801 masm.loadJSContext(cx);
21802 using Fn = bool (*)(JSContext* cx);
21803 masm.setupAlignedABICall();
21804 masm.passABIArg(cx);
21805 masm.callWithABI<Fn, GlobalHasLiveOnDebuggerStatement>();
21806
21807 Label bail;
21808 masm.branchIfTrueBool(ReturnReg, &bail);
21809 bailoutFrom(&bail, ins->snapshot());
21810}
21811
21812void CodeGenerator::visitNewTarget(LNewTarget* ins) {
21813 ValueOperand output = ToOutValue(ins);
21814
21815 // if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)]
21816 Label notConstructing, done;
21817 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
21818 masm.branchTestPtr(Assembler::Zero, calleeToken,
21819 Imm32(CalleeToken_FunctionConstructing), &notConstructing);
21820
21821 Register argvLen = output.scratchReg();
21822 masm.loadNumActualArgs(FramePointer, argvLen);
21823
21824 Label useNFormals;
21825
21826 size_t numFormalArgs = ins->mir()->block()->info().nargs();
21827 masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs), &useNFormals);
21828
21829 size_t argsOffset = JitFrameLayout::offsetOfActualArgs();
21830 {
21831 BaseValueIndex newTarget(FramePointer, argvLen, argsOffset);
21832 masm.loadValue(newTarget, output);
21833 masm.jump(&done);
21834 }
21835
21836 masm.bind(&useNFormals);
21837
21838 {
21839 Address newTarget(FramePointer,
21840 argsOffset + (numFormalArgs * sizeof(Value)));
21841 masm.loadValue(newTarget, output);
21842 masm.jump(&done);
21843 }
21844
21845 // else output = undefined
21846 masm.bind(&notConstructing);
21847 masm.moveValue(UndefinedValue(), output);
21848 masm.bind(&done);
21849}
21850
21851void CodeGenerator::visitCheckReturn(LCheckReturn* ins) {
21852 ValueOperand returnValue = ToValue(ins->returnValue());
21853 ValueOperand thisValue = ToValue(ins->thisValue());
21854 ValueOperand output = ToOutValue(ins);
21855
21856 using Fn = bool (*)(JSContext*, HandleValue);
21857 OutOfLineCode* ool = oolCallVM<Fn, ThrowBadDerivedReturnOrUninitializedThis>(
21858 ins, ArgList(returnValue), StoreNothing());
21859
21860 Label noChecks;
21861 masm.branchTestObject(Assembler::Equal, returnValue, &noChecks);
21862 masm.branchTestUndefined(Assembler::NotEqual, returnValue, ool->entry());
21863 masm.branchTestMagicValue(Assembler::Equal, thisValue,
21864 JS_UNINITIALIZED_LEXICAL, ool->entry());
21865 masm.moveValue(thisValue, output);
21866 masm.jump(ool->rejoin());
21867 masm.bind(&noChecks);
21868 masm.moveValue(returnValue, output);
21869 masm.bind(ool->rejoin());
21870}
21871
21872void CodeGenerator::visitCheckIsObj(LCheckIsObj* ins) {
21873 ValueOperand value = ToValue(ins->value());
21874 Register output = ToRegister(ins->output());
21875
21876 using Fn = bool (*)(JSContext*, CheckIsObjectKind);
21877 OutOfLineCode* ool = oolCallVM<Fn, ThrowCheckIsObject>(
21878 ins, ArgList(Imm32(ins->mir()->checkKind())), StoreNothing());
21879
21880 masm.fallibleUnboxObject(value, output, ool->entry());
21881 masm.bind(ool->rejoin());
21882}
21883
21884void CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) {
21885 ValueOperand checkValue = ToValue(ins->checkValue());
21886
21887 using Fn = bool (*)(JSContext*, HandleValue);
21888 OutOfLineCode* ool = oolCallVM<Fn, ThrowObjectCoercible>(
21889 ins, ArgList(checkValue), StoreNothing());
21890 masm.branchTestNull(Assembler::Equal, checkValue, ool->entry());
21891 masm.branchTestUndefined(Assembler::Equal, checkValue, ool->entry());
21892 masm.bind(ool->rejoin());
21893}
21894
21895void CodeGenerator::visitCheckClassHeritage(LCheckClassHeritage* ins) {
21896 ValueOperand heritage = ToValue(ins->heritage());
21897 Register temp0 = ToRegister(ins->temp0());
21898 Register temp1 = ToRegister(ins->temp1());
21899
21900 using Fn = bool (*)(JSContext*, HandleValue);
21901 OutOfLineCode* ool = oolCallVM<Fn, CheckClassHeritageOperation>(
21902 ins, ArgList(heritage), StoreNothing());
21903
21904 masm.branchTestNull(Assembler::Equal, heritage, ool->rejoin());
21905 masm.fallibleUnboxObject(heritage, temp0, ool->entry());
21906
21907 masm.isConstructor(temp0, temp1, ool->entry());
21908 masm.branchTest32(Assembler::Zero, temp1, temp1, ool->entry());
21909
21910 masm.bind(ool->rejoin());
21911}
21912
21913void CodeGenerator::visitCheckThis(LCheckThis* ins) {
21914 ValueOperand thisValue = ToValue(ins->thisValue());
21915
21916 using Fn = bool (*)(JSContext*);
21917 OutOfLineCode* ool =
21918 oolCallVM<Fn, ThrowUninitializedThis>(ins, ArgList(), StoreNothing());
21919 masm.branchTestMagicValue(Assembler::Equal, thisValue,
21920 JS_UNINITIALIZED_LEXICAL, ool->entry());
21921 masm.bind(ool->rejoin());
21922}
21923
21924void CodeGenerator::visitCheckThisReinit(LCheckThisReinit* ins) {
21925 ValueOperand thisValue = ToValue(ins->thisValue());
21926
21927 using Fn = bool (*)(JSContext*);
21928 OutOfLineCode* ool =
21929 oolCallVM<Fn, ThrowInitializedThis>(ins, ArgList(), StoreNothing());
21930 masm.branchTestMagicValue(Assembler::NotEqual, thisValue,
21931 JS_UNINITIALIZED_LEXICAL, ool->entry());
21932 masm.bind(ool->rejoin());
21933}
21934
21935void CodeGenerator::visitGenerator(LGenerator* lir) {
21936 Register callee = ToRegister(lir->callee());
21937 Register environmentChain = ToRegister(lir->environmentChain());
21938 Register argsObject = ToRegister(lir->argsObject());
21939
21940 pushArg(argsObject);
21941 pushArg(environmentChain);
21942 pushArg(ImmGCPtr(current->mir()->info().script()));
21943 pushArg(callee);
21944
21945 using Fn = JSObject* (*)(JSContext * cx, HandleFunction, HandleScript,
21946 HandleObject, HandleObject);
21947 callVM<Fn, CreateGenerator>(lir);
21948}
21949
21950void CodeGenerator::visitAsyncResolve(LAsyncResolve* lir) {
21951 Register generator = ToRegister(lir->generator());
21952 ValueOperand value = ToValue(lir->value());
21953
21954 pushArg(value);
21955 pushArg(generator);
21956
21957 using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>,
21958 HandleValue);
21959 callVM<Fn, js::AsyncFunctionResolve>(lir);
21960}
21961
21962void CodeGenerator::visitAsyncAwait(LAsyncAwait* lir) {
21963 ValueOperand value = ToValue(lir->value());
21964 Register generator = ToRegister(lir->generator());
21965
21966 pushArg(value);
21967 pushArg(generator);
21968
21969 using Fn = JSObject* (*)(JSContext * cx,
21970 Handle<AsyncFunctionGeneratorObject*> genObj,
21971 HandleValue value);
21972 callVM<Fn, js::AsyncFunctionAwait>(lir);
21973}
21974
21975void CodeGenerator::visitGeneratorResume(LGeneratorResume* lir) {
21976 Register genObj = ToRegister(lir->generator());
21977 ValueOperand value = ToValue(lir->value());
21978 int32_t resumeKind = lir->mir()->resumeKind();
21979
21980 Register callee = ToRegister(lir->temp0());
21981 Register scratch = ToRegister(lir->temp1());
21982
21983 // Load callee. Note that JSOp::Resume is never used for modules.
21984 masm.unboxObject(
21985 Address(genObj, AbstractGeneratorObject::offsetOfCalleeOrModuleSlot()),
21986 callee);
21987
21988 // The pushed argument Values are the formals (all |undefined|) and the resume
21989 // args stored after the formals.
21990 masm.loadFunctionArgCount(callee, scratch);
21991 masm.alignJitStackBasedOnNArgs(scratch, /* countIncludesThis = */ false,
21992 /* extraArgs = */ ResumeFrameArgs::NumSlots);
21993
21994 // Unlike the Baseline version, we don't have to zero the alignment padding
21995 // here: Ion frames are traced with the safepoint's slot map, not as a range.
21996
21997 // Push the resume args and the |undefined| formals.
21998 Address resumeIndexSlot(genObj,
21999 AbstractGeneratorObject::offsetOfResumeIndexSlot());
22000 masm.pushGeneratorResumeArgsAndFormals(
22001 resumeIndexSlot, Int32Value(resumeKind), genObj, value, scratch);
22002
22003 masm.PushCalleeToken(callee, /* constructing = */ false);
22004 masm.push(FrameDescriptor(FrameType::IonJS, /* argc = */ 0,
22005 /* hasInlined = */ false,
22006 /* isResumingGenerator = */ true));
22007
22008 // Load the code to call. Throw currently always resumes in Baseline.
22009 // See MaybeEnterJit.
22010 Register code = callee;
22011 if (resumeKind != int32_t(GeneratorResumeKind::Throw)) {
22012 masm.loadJitCodeRaw(callee, code);
22013 } else {
22014 masm.loadJitCodeRawNoIon(callee, code, scratch);
22015 }
22016
22017 masm.switchToObjectRealm(genObj, scratch);
22018
22019 // Mark the generator as running.
22020 masm.storeValue(Int32Value(AbstractGeneratorObject::RESUME_INDEX_RUNNING),
22021 resumeIndexSlot);
22022
22023 // Call the callee's JIT code. Its prologue sees the descriptor bit and
22024 // dispatches to the resume point.
22025 ensureOsiSpace();
22026 uint32_t callOffset = masm.callJit(code);
22027 markSafepointAt(callOffset, lir);
22028
22029 static_assert(!JSReturnOperand.aliases(ReturnReg),
22030 "ReturnReg must not alias return value in JSReturnOperand");
22031 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
22032
22033 masm.setFramePushed(frameSize());
22034 emitRestoreStackPointerFromFP();
22035}
22036
22037// The offset of a ResumeFrameArgs slot from the frame pointer.
22038static size_t OffsetOfResumeFrameArg(MIRGenerator* gen, uint32_t slot) {
22039 JSScript* script = gen->outerInfo().script();
22040 size_t base =
22041 script->isFunction()
22042 ? JitFrameLayout::offsetOfActualArg(script->function()->nargs())
22043 : JitFrameLayout::offsetOfModuleResumeArgs();
22044 return base + ResumeFrameArgs::offsetOfSlot(slot);
22045}
22046
22047static Address AddressOfFrameDescriptor() {
22048 return Address(FramePointer, CommonFrameLayout::offsetOfDescriptor());
22049}
22050
22051void CodeGenerator::visitResumeFrameArg(LResumeFrameArg* lir) {
22052#ifdef DEBUG1
22053 // The slots are only valid and traced on GC while this frame is mid-resume.
22054 Label ok;
22055 masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(),
22056 Imm32(FrameDescriptor::IsResumingGenerator), &ok);
22057 masm.assumeUnreachable("ResumeFrameArgs read outside a generator resume");
22058 masm.bind(&ok);
22059#endif
22060 ValueOperand output = ToOutValue(lir);
22061 masm.loadValue(
22062 Address(FramePointer, OffsetOfResumeFrameArg(gen, lir->mir()->slot())),
22063 output);
22064}
22065
22066void CodeGenerator::visitIsResumingGenerator(LIsResumingGenerator* lir) {
22067 Register output = ToRegister(lir->output());
22068 Label isResuming, done;
22069 masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(),
22070 Imm32(FrameDescriptor::IsResumingGenerator), &isResuming);
22071 masm.move32(Imm32(0), output);
22072 masm.jump(&done);
22073 masm.bind(&isResuming);
22074 masm.move32(Imm32(1), output);
22075 masm.bind(&done);
22076}
22077
22078void CodeGenerator::visitIsResumingGeneratorAndBranch(
22079 LIsResumingGeneratorAndBranch* lir) {
22080 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
22081 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
22082 masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(),
22083 Imm32(FrameDescriptor::IsResumingGenerator), ifTrue);
22084 if (!isNextBlock(lir->ifFalse()->lir())) {
22085 masm.jump(ifFalse);
22086 }
22087}
22088
22089void CodeGenerator::visitClearResumingGeneratorFlag(
22090 LClearResumingGeneratorFlag* lir) {
22091 masm.andPtr(Imm32(~int32_t(FrameDescriptor::IsResumingGenerator)),
22092 AddressOfFrameDescriptor());
22093}
22094
22095void CodeGenerator::visitCanSkipAwait(LCanSkipAwait* lir) {
22096 ValueOperand value = ToValue(lir->value());
22097 Register scratch = ToRegister(lir->temp0());
22098
22099 // The await can only be skipped when this is the first frame of its
22100 // activation. See js::CanSkipAwait.
22101 Label notEntryFrame, done;
22102 masm.branchIfNotActivationEntryFrame(scratch, &notEntryFrame);
22103
22104 pushArg(value);
22105
22106 using Fn = bool (*)(JSContext*, HandleValue, bool* canSkip);
22107 callVM<Fn, js::CanSkipAwait>(lir);
22108 masm.jump(&done);
22109
22110 masm.bind(&notEntryFrame);
22111 masm.move32(Imm32(0), ReturnReg);
22112
22113 masm.bind(&done);
22114}
22115
22116void CodeGenerator::visitMaybeExtractAwaitValue(LMaybeExtractAwaitValue* lir) {
22117 ValueOperand value = ToValue(lir->value());
22118 ValueOperand output = ToOutValue(lir);
22119 Register canSkip = ToRegister(lir->canSkip());
22120
22121 Label cantExtract, finished;
22122 masm.branchIfFalseBool(canSkip, &cantExtract);
22123
22124 pushArg(value);
22125
22126 using Fn = bool (*)(JSContext*, HandleValue, MutableHandleValue);
22127 callVM<Fn, js::ExtractAwaitValue>(lir);
22128 masm.jump(&finished);
22129 masm.bind(&cantExtract);
22130
22131 masm.moveValue(value, output);
22132
22133 masm.bind(&finished);
22134}
22135
22136void CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) {
22137 ValueOperand checkValue = ToValue(ins->checkValue());
22138 pushArg(checkValue);
22139 using Fn = bool (*)(JSContext*, HandleValue);
22140 callVM<Fn, js::Debug_CheckSelfHosted>(ins);
22141}
22142
22143void CodeGenerator::visitRandom(LRandom* ins) {
22144 using mozilla::non_crypto::XorShift128PlusRNG;
22145
22146 FloatRegister output = ToFloatRegister(ins->output());
22147 Register rngReg = ToRegister(ins->temp0());
22148
22149 Register64 temp1 = ToRegister64(ins->temp1());
22150 Register64 temp2 = ToRegister64(ins->temp2());
22151
22152 const XorShift128PlusRNG* rng = gen->realm->addressOfRandomNumberGenerator();
22153 masm.movePtr(ImmPtr(rng), rngReg);
22154
22155 masm.randomDouble(rngReg, output, temp1, temp2);
22156 if (js::SupportDifferentialTesting()) {
22157 masm.loadConstantDouble(0.0, output);
22158 }
22159}
22160
22161void CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins) {
22162 Register input = ToRegister(ins->input());
22163 Register output = ToRegister(ins->output());
22164
22165 switch (ins->mir()->mode()) {
22166 case MSignExtendInt32::Byte:
22167 masm.move8SignExtend(input, output);
22168 break;
22169 case MSignExtendInt32::Half:
22170 masm.move16SignExtend(input, output);
22171 break;
22172 }
22173}
22174
22175void CodeGenerator::visitSignExtendIntPtr(LSignExtendIntPtr* ins) {
22176 Register input = ToRegister(ins->input());
22177 Register output = ToRegister(ins->output());
22178
22179 switch (ins->mir()->mode()) {
22180 case MSignExtendIntPtr::Byte:
22181 masm.move8SignExtendToPtr(input, output);
22182 break;
22183 case MSignExtendIntPtr::Half:
22184 masm.move16SignExtendToPtr(input, output);
22185 break;
22186 case MSignExtendIntPtr::Word:
22187 masm.move32SignExtendToPtr(input, output);
22188 break;
22189 }
22190}
22191
22192void CodeGenerator::visitRotate(LRotate* ins) {
22193 MRotate* mir = ins->mir();
22194 Register input = ToRegister(ins->input());
22195 Register dest = ToRegister(ins->output());
22196
22197 const LAllocation* count = ins->count();
22198 if (count->isConstant()) {
22199 int32_t c = ToInt32(count) & 0x1F;
22200 if (mir->isLeftRotate()) {
22201 masm.rotateLeft(Imm32(c), input, dest);
22202 } else {
22203 masm.rotateRight(Imm32(c), input, dest);
22204 }
22205 } else {
22206 Register creg = ToRegister(count);
22207 if (mir->isLeftRotate()) {
22208 masm.rotateLeft(creg, input, dest);
22209 } else {
22210 masm.rotateRight(creg, input, dest);
22211 }
22212 }
22213}
22214
22215void CodeGenerator::visitRotateI64(LRotateI64* lir) {
22216 MRotate* mir = lir->mir();
22217 const LAllocation* count = lir->count();
22218
22219 Register64 input = ToRegister64(lir->input());
22220 Register64 output = ToOutRegister64(lir);
22221 Register temp = ToTempRegisterOrInvalid(lir->temp0());
22222
22223 if (count->isConstant()) {
22224 int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F);
22225 if (!c) {
22226 if (input != output) {
22227 masm.move64(input, output);
22228 }
22229 return;
22230 }
22231 if (mir->isLeftRotate()) {
22232 masm.rotateLeft64(Imm32(c), input, output, temp);
22233 } else {
22234 masm.rotateRight64(Imm32(c), input, output, temp);
22235 }
22236 } else {
22237 if (mir->isLeftRotate()) {
22238 masm.rotateLeft64(ToRegister(count), input, output, temp);
22239 } else {
22240 masm.rotateRight64(ToRegister(count), input, output, temp);
22241 }
22242 }
22243}
22244
22245void CodeGenerator::visitReinterpretCast(LReinterpretCast* lir) {
22246 MReinterpretCast* ins = lir->mir();
22247
22248 MIRType to = ins->type();
22249 mozilla::DebugOnly<MIRType> from = ins->input()->type();
22250
22251 switch (to) {
22252 case MIRType::Int32:
22253 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", 22253); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "from == MIRType::Float32" ")"); do { MOZ_CrashSequence
(__null, 22253); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
22254 masm.moveFloat32ToGPR(ToFloatRegister(lir->input()),
22255 ToRegister(lir->output()));
22256 break;
22257 case MIRType::Float32:
22258 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", 22258); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "from == MIRType::Int32" ")"); do { MOZ_CrashSequence
(__null, 22258); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
22259 masm.moveGPRToFloat32(ToRegister(lir->input()),
22260 ToFloatRegister(lir->output()));
22261 break;
22262 case MIRType::Double:
22263 case MIRType::Int64:
22264 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", 22264); AnnotateMozCrashReason
("MOZ_CRASH(" "not handled by this LIR opcode" ")"); do { MOZ_CrashSequence
(__null, 22264); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
22265 default:
22266 MOZ_CRASH("unexpected ReinterpretCast")do { do { } while (false); MOZ_ReportCrash("" "unexpected ReinterpretCast"
, "./../../../../js/src/jit/CodeGenerator.cpp", 22266); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected ReinterpretCast" ")"); do { MOZ_CrashSequence
(__null, 22266); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
22267 }
22268}
22269
22270void CodeGenerator::visitReinterpretCastFromI64(LReinterpretCastFromI64* lir) {
22271 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", 22271); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 22271); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22272 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"
, 22272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Int64"
")"); do { MOZ_CrashSequence(__null, 22272); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22273 masm.moveGPR64ToDouble(ToRegister64(lir->input()),
22274 ToFloatRegister(lir->output()));
22275}
22276
22277void CodeGenerator::visitReinterpretCastToI64(LReinterpretCastToI64* lir) {
22278 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", 22278); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64"
")"); do { MOZ_CrashSequence(__null, 22278); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22279 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"
, 22279); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 22279); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22280 masm.moveDoubleToGPR64(ToFloatRegister(lir->input()), ToOutRegister64(lir));
22281}
22282
22283void CodeGenerator::visitNaNToZero(LNaNToZero* lir) {
22284 FloatRegister input = ToFloatRegister(lir->input());
22285
22286 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
22287 FloatRegister output = ToFloatRegister(lir->output());
22288 masm.loadConstantDouble(0.0, output);
22289 masm.jump(ool.rejoin());
22290 });
22291 addOutOfLineCode(ool, lir->mir());
22292
22293 if (lir->mir()->operandIsNeverNegativeZero()) {
22294 masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry());
22295 } else {
22296 FloatRegister scratch = ToFloatRegister(lir->temp0());
22297 masm.loadConstantDouble(0.0, scratch);
22298 masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch,
22299 ool->entry());
22300 }
22301 masm.bind(ool->rejoin());
22302}
22303
22304void CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) {
22305 Register obj = ToRegister(lir->object());
22306 Register output = ToRegister(lir->output());
22307 Register temp = ToRegister(lir->temp0());
22308
22309 masm.setIsPackedArray(obj, output, temp);
22310}
22311
22312void CodeGenerator::visitGuardArrayIsPacked(LGuardArrayIsPacked* lir) {
22313 Register array = ToRegister(lir->array());
22314 Register temp0 = ToRegister(lir->temp0());
22315 Register temp1 = ToRegister(lir->temp1());
22316
22317 Label bail;
22318 masm.branchArrayIsNotPacked(array, temp0, temp1, &bail);
22319 bailoutFrom(&bail, lir->snapshot());
22320}
22321
22322void CodeGenerator::visitGuardElementsArePacked(LGuardElementsArePacked* lir) {
22323 Register elements = ToRegister(lir->elements());
22324
22325 Label bail;
22326 Address flags(elements, ObjectElements::offsetOfFlags());
22327 masm.branchTest32(Assembler::NonZero, flags,
22328 Imm32(ObjectElements::NON_PACKED), &bail);
22329 bailoutFrom(&bail, lir->snapshot());
22330}
22331
22332void CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) {
22333 Register target = ToRegister(lir->target());
22334 ValueOperand out = ToOutValue(lir);
22335 Register scratch = out.scratchReg();
22336
22337 using Fn = bool (*)(JSContext*, HandleObject, MutableHandleValue);
22338 OutOfLineCode* ool = oolCallVM<Fn, jit::GetPrototypeOf>(lir, ArgList(target),
22339 StoreValueTo(out));
22340
22341 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", 22341); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 22341); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
22342
22343 masm.loadObjProto(target, scratch);
22344
22345 Label hasProto;
22346 masm.branchPtr(Assembler::Above, scratch, ImmWord(1), &hasProto);
22347
22348 // Call into the VM for lazy prototypes.
22349 masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), ool->entry());
22350
22351 masm.moveValue(NullValue(), out);
22352 masm.jump(ool->rejoin());
22353
22354 masm.bind(&hasProto);
22355 masm.tagValue(JSVAL_TYPE_OBJECT, scratch, out);
22356
22357 masm.bind(ool->rejoin());
22358}
22359
22360void CodeGenerator::visitObjectWithProto(LObjectWithProto* lir) {
22361 pushArg(ToValue(lir->prototype()));
22362
22363 using Fn = PlainObject* (*)(JSContext*, HandleValue);
22364 callVM<Fn, js::ObjectWithProtoOperation>(lir);
22365}
22366
22367void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) {
22368 Register obj = ToRegister(lir->object());
22369 Register output = ToRegister(lir->output());
22370
22371 masm.loadObjProto(obj, output);
22372
22373#ifdef DEBUG1
22374 // We shouldn't encounter a null or lazy proto.
22375 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", 22375); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 22375); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
22376
22377 Label done;
22378 masm.branchPtr(Assembler::Above, output, ImmWord(1), &done);
22379 masm.assumeUnreachable("Unexpected null or lazy proto in MObjectStaticProto");
22380 masm.bind(&done);
22381#endif
22382}
22383
22384void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) {
22385 pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind())));
22386
22387 using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind);
22388 callVM<Fn, js::BuiltinObjectOperation>(lir);
22389}
22390
22391static void EmitLoadSuperFunction(MacroAssembler& masm, Register callee,
22392 Register dest) {
22393#ifdef DEBUG1
22394 Label classCheckDone;
22395 masm.branchTestObjIsFunction(Assembler::Equal, callee, dest, callee,
22396 &classCheckDone);
22397 masm.assumeUnreachable("Unexpected non-JSFunction callee in JSOp::SuperFun");
22398 masm.bind(&classCheckDone);
22399#endif
22400
22401 // Load prototype of callee
22402 masm.loadObjProto(callee, dest);
22403
22404#ifdef DEBUG1
22405 // We won't encounter a lazy proto, because |callee| is guaranteed to be a
22406 // JSFunction and only proxy objects can have a lazy proto.
22407 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", 22407); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"
); do { MOZ_CrashSequence(__null, 22407); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
22408
22409 Label proxyCheckDone;
22410 masm.branchPtr(Assembler::NotEqual, dest, ImmWord(1), &proxyCheckDone);
22411 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperFun");
22412 masm.bind(&proxyCheckDone);
22413#endif
22414}
22415
22416void CodeGenerator::visitSuperFunction(LSuperFunction* lir) {
22417 Register callee = ToRegister(lir->callee());
22418 ValueOperand out = ToOutValue(lir);
22419 Register temp = out.scratchReg();
22420
22421 EmitLoadSuperFunction(masm, callee, temp);
22422
22423 Label nullProto, done;
22424 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
22425
22426 // Box prototype and return
22427 masm.tagValue(JSVAL_TYPE_OBJECT, temp, out);
22428 masm.jump(&done);
22429
22430 masm.bind(&nullProto);
22431 masm.moveValue(NullValue(), out);
22432
22433 masm.bind(&done);
22434}
22435
22436void CodeGenerator::visitSuperFunctionAndUnbox(LSuperFunctionAndUnbox* lir) {
22437 Register callee = ToRegister(lir->callee());
22438 Register output = ToRegister(lir->output());
22439
22440 EmitLoadSuperFunction(masm, callee, output);
22441
22442 bailoutCmpPtr(Assembler::Equal, output, ImmWord(0), lir->snapshot());
22443}
22444
22445void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) {
22446 Register func = ToRegister(lir->function());
22447 ValueOperand homeObject = ToValue(lir->homeObject());
22448
22449 masm.assertFunctionIsExtended(func);
22450
22451 Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
22452
22453 emitPreBarrier(addr);
22454 masm.storeValue(homeObject, addr);
22455}
22456
22457void CodeGenerator::visitIsTypedArrayConstructor(
22458 LIsTypedArrayConstructor* lir) {
22459 Register object = ToRegister(lir->object());
22460 Register output = ToRegister(lir->output());
22461
22462 masm.setIsDefinitelyTypedArrayConstructor(object, output);
22463}
22464
22465void CodeGenerator::visitLoadValueTag(LLoadValueTag* lir) {
22466 ValueOperand value = ToValue(lir->value());
22467 Register output = ToRegister(lir->output());
22468
22469 Register tag = masm.extractTag(value, output);
22470 if (tag != output) {
22471 masm.mov(tag, output);
22472 }
22473}
22474
22475void CodeGenerator::visitGuardTagNotEqual(LGuardTagNotEqual* lir) {
22476 Register lhs = ToRegister(lir->lhs());
22477 Register rhs = ToRegister(lir->rhs());
22478
22479 bailoutCmp32(Assembler::Equal, lhs, rhs, lir->snapshot());
22480
22481 // If both lhs and rhs are numbers, can't use tag comparison to do inequality
22482 // comparison
22483 Label done;
22484 masm.branchTestNumber(Assembler::NotEqual, lhs, &done);
22485 masm.branchTestNumber(Assembler::NotEqual, rhs, &done);
22486 bailout(lir->snapshot());
22487
22488 masm.bind(&done);
22489}
22490
22491void CodeGenerator::visitLoadWrapperTarget(LLoadWrapperTarget* lir) {
22492 Register object = ToRegister(lir->object());
22493 Register output = ToRegister(lir->output());
22494
22495 // Bail for revoked proxies.
22496 Label bail;
22497 Address targetAddr(object, ProxyObject::offsetOfPrivateSlot());
22498 if (lir->mir()->fallible()) {
22499 masm.fallibleUnboxObject(targetAddr, output, &bail);
22500 bailoutFrom(&bail, lir->snapshot());
22501 } else {
22502 masm.unboxObject(targetAddr, output);
22503 }
22504}
22505
22506void CodeGenerator::visitLoadGetterSetterFunction(
22507 LLoadGetterSetterFunction* lir) {
22508 ValueOperand getterSetter = ToValue(lir->getterSetter());
22509 Register output = ToRegister(lir->output());
22510
22511 masm.unboxNonDouble(getterSetter, output, JSVAL_TYPE_PRIVATE_GCTHING);
22512
22513 size_t offset = lir->mir()->isGetter() ? GetterSetter::offsetOfGetter()
22514 : GetterSetter::offsetOfSetter();
22515 masm.loadPtr(Address(output, offset), output);
22516
22517 Label bail;
22518 masm.branchTestPtr(Assembler::Zero, output, output, &bail);
22519 if (lir->mir()->needsClassGuard()) {
22520 Register temp = ToRegister(lir->temp0());
22521 masm.branchTestObjIsFunction(Assembler::NotEqual, output, temp, output,
22522 &bail);
22523 }
22524
22525 bailoutFrom(&bail, lir->snapshot());
22526}
22527
22528void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) {
22529 Register object = ToRegister(lir->object());
22530 Register temp0 = ToRegister(lir->temp0());
22531 Register temp1 = ToRegister(lir->temp1());
22532 Register temp2 = ToRegister(lir->temp2());
22533
22534 masm.movePropertyKey(lir->mir()->propId(), temp1);
22535
22536 auto getterSetterVal = lir->mir()->getterSetterValue();
22537 if (getterSetterVal.isValue()) {
22538 auto* gs = getterSetterVal.toValue().toGCThing()->as<GetterSetter>();
22539 masm.movePtr(ImmGCPtr(gs), temp2);
22540 } else {
22541 // Load the GetterSetter* from the Value stored in the IonScript.
22542 Address valueAddr = getNurseryValueAddress(getterSetterVal, temp2);
22543 masm.unboxNonDouble(valueAddr, temp2, JSVAL_TYPE_PRIVATE_GCTHING);
22544 }
22545
22546 using Fn = bool (*)(JSContext* cx, JSObject* obj, jsid id,
22547 GetterSetter* getterSetter);
22548 masm.setupAlignedABICall();
22549 masm.loadJSContext(temp0);
22550 masm.passABIArg(temp0);
22551 masm.passABIArg(object);
22552 masm.passABIArg(temp1);
22553 masm.passABIArg(temp2);
22554 masm.callWithABI<Fn, ObjectHasGetterSetterPure>();
22555
22556 bailoutIfFalseBool(ReturnReg, lir->snapshot());
22557}
22558
22559void CodeGenerator::visitGuardIsExtensible(LGuardIsExtensible* lir) {
22560 Register object = ToRegister(lir->object());
22561 Register temp = ToRegister(lir->temp0());
22562
22563 Label bail;
22564 masm.branchIfObjectNotExtensible(object, temp, &bail);
22565 bailoutFrom(&bail, lir->snapshot());
22566}
22567
22568void CodeGenerator::visitGuardInt32IsNonNegative(
22569 LGuardInt32IsNonNegative* lir) {
22570 Register index = ToRegister(lir->index());
22571
22572 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
22573}
22574
22575void CodeGenerator::visitGuardIntPtrIsNonNegative(
22576 LGuardIntPtrIsNonNegative* lir) {
22577 Register index = ToRegister(lir->index());
22578
22579 bailoutCmpPtr(Assembler::LessThan, index, ImmWord(0), lir->snapshot());
22580}
22581
22582void CodeGenerator::visitGuardInt32Range(LGuardInt32Range* lir) {
22583 Register input = ToRegister(lir->input());
22584
22585 bailoutCmp32(Assembler::LessThan, input, Imm32(lir->mir()->minimum()),
22586 lir->snapshot());
22587 bailoutCmp32(Assembler::GreaterThan, input, Imm32(lir->mir()->maximum()),
22588 lir->snapshot());
22589}
22590
22591void CodeGenerator::visitGuardIndexIsNotDenseElement(
22592 LGuardIndexIsNotDenseElement* lir) {
22593 Register object = ToRegister(lir->object());
22594 Register index = ToRegister(lir->index());
22595 Register temp = ToRegister(lir->temp0());
22596 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
22597
22598 // Load obj->elements.
22599 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
22600
22601 // Ensure index >= initLength or the element is a hole.
22602 Label notDense;
22603 Address capacity(temp, ObjectElements::offsetOfInitializedLength());
22604 masm.spectreBoundsCheck32(index, capacity, spectreTemp, &notDense);
22605
22606 BaseObjectElementIndex element(temp, index);
22607 masm.branchTestMagic(Assembler::Equal, element, JS_ELEMENTS_HOLE, &notDense);
22608
22609 bailout(lir->snapshot());
22610
22611 masm.bind(&notDense);
22612}
22613
22614void CodeGenerator::visitGuardIndexIsValidUpdateOrAdd(
22615 LGuardIndexIsValidUpdateOrAdd* lir) {
22616 Register object = ToRegister(lir->object());
22617 Register index = ToRegister(lir->index());
22618 Register temp = ToRegister(lir->temp0());
22619 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
22620
22621 // Load obj->elements.
22622 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
22623
22624 Label success;
22625
22626 // If length is writable, branch to &success. All indices are writable.
22627 Address flags(temp, ObjectElements::offsetOfFlags());
22628 masm.branchTest32(Assembler::Zero, flags,
22629 Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH),
22630 &success);
22631
22632 // Otherwise, ensure index is in bounds.
22633 Label bail;
22634 Address length(temp, ObjectElements::offsetOfLength());
22635 masm.spectreBoundsCheck32(index, length, spectreTemp, &bail);
22636 masm.bind(&success);
22637
22638 bailoutFrom(&bail, lir->snapshot());
22639}
22640
22641void CodeGenerator::visitCallAddOrUpdateSparseElement(
22642 LCallAddOrUpdateSparseElement* lir) {
22643 Register object = ToRegister(lir->object());
22644 Register index = ToRegister(lir->index());
22645 ValueOperand value = ToValue(lir->value());
22646
22647 pushArg(Imm32(lir->mir()->strict()));
22648 pushArg(value);
22649 pushArg(index);
22650 pushArg(object);
22651
22652 using Fn =
22653 bool (*)(JSContext*, Handle<NativeObject*>, int32_t, HandleValue, bool);
22654 callVM<Fn, js::AddOrUpdateSparseElementHelper>(lir);
22655}
22656
22657void CodeGenerator::visitCallGetSparseElement(LCallGetSparseElement* lir) {
22658 Register object = ToRegister(lir->object());
22659 Register index = ToRegister(lir->index());
22660
22661 pushArg(index);
22662 pushArg(object);
22663
22664 using Fn =
22665 bool (*)(JSContext*, Handle<NativeObject*>, int32_t, MutableHandleValue);
22666 callVM<Fn, js::GetSparseElementHelper>(lir);
22667}
22668
22669void CodeGenerator::visitCallNativeGetElement(LCallNativeGetElement* lir) {
22670 Register object = ToRegister(lir->object());
22671 Register index = ToRegister(lir->index());
22672
22673 pushArg(index);
22674 pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
22675 pushArg(object);
22676
22677 using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t,
22678 MutableHandleValue);
22679 callVM<Fn, js::NativeGetElement>(lir);
22680}
22681
22682void CodeGenerator::visitCallNativeGetElementSuper(
22683 LCallNativeGetElementSuper* lir) {
22684 Register object = ToRegister(lir->object());
22685 Register index = ToRegister(lir->index());
22686 ValueOperand receiver = ToValue(lir->receiver());
22687
22688 pushArg(index);
22689 pushArg(receiver);
22690 pushArg(object);
22691
22692 using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t,
22693 MutableHandleValue);
22694 callVM<Fn, js::NativeGetElement>(lir);
22695}
22696
22697void CodeGenerator::visitCallObjectHasSparseElement(
22698 LCallObjectHasSparseElement* lir) {
22699 Register object = ToRegister(lir->object());
22700 Register index = ToRegister(lir->index());
22701 Register temp0 = ToRegister(lir->temp0());
22702 Register temp1 = ToRegister(lir->temp1());
22703 Register output = ToRegister(lir->output());
22704
22705 masm.reserveStack(sizeof(Value));
22706 masm.moveStackPtrTo(temp1);
22707
22708 using Fn = bool (*)(JSContext*, NativeObject*, int32_t, Value*);
22709 masm.setupAlignedABICall();
22710 masm.loadJSContext(temp0);
22711 masm.passABIArg(temp0);
22712 masm.passABIArg(object);
22713 masm.passABIArg(index);
22714 masm.passABIArg(temp1);
22715 masm.callWithABI<Fn, HasNativeElementPure>();
22716 masm.storeCallPointerResult(temp0);
22717
22718 Label bail, ok;
22719 uint32_t framePushed = masm.framePushed();
22720 masm.branchIfTrueBool(temp0, &ok);
22721 masm.adjustStack(sizeof(Value));
22722 masm.jump(&bail);
22723
22724 masm.bind(&ok);
22725 masm.setFramePushed(framePushed);
22726 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
22727 masm.adjustStack(sizeof(Value));
22728
22729 bailoutFrom(&bail, lir->snapshot());
22730}
22731
22732void CodeGenerator::visitBigIntAsIntN(LBigIntAsIntN* ins) {
22733 Register bits = ToRegister(ins->bits());
22734 Register input = ToRegister(ins->input());
22735
22736 pushArg(bits);
22737 pushArg(input);
22738
22739 using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t);
22740 callVM<Fn, jit::BigIntAsIntN>(ins);
22741}
22742
22743void CodeGenerator::visitBigIntAsUintN(LBigIntAsUintN* ins) {
22744 Register bits = ToRegister(ins->bits());
22745 Register input = ToRegister(ins->input());
22746
22747 pushArg(bits);
22748 pushArg(input);
22749
22750 using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t);
22751 callVM<Fn, jit::BigIntAsUintN>(ins);
22752}
22753
22754void CodeGenerator::visitGuardNonGCThing(LGuardNonGCThing* ins) {
22755 ValueOperand input = ToValue(ins->input());
22756
22757 Label bail;
22758 masm.branchTestGCThing(Assembler::Equal, input, &bail);
22759 bailoutFrom(&bail, ins->snapshot());
22760}
22761
22762void CodeGenerator::visitToHashableNonGCThing(LToHashableNonGCThing* ins) {
22763 ValueOperand input = ToValue(ins->input());
22764 FloatRegister tempFloat = ToFloatRegister(ins->temp0());
22765 ValueOperand output = ToOutValue(ins);
22766
22767 masm.toHashableNonGCThing(input, output, tempFloat);
22768}
22769
22770void CodeGenerator::visitToHashableString(LToHashableString* ins) {
22771 Register input = ToRegister(ins->input());
22772 Register output = ToRegister(ins->output());
22773
22774 using Fn = JSAtom* (*)(JSContext*, JSString*);
22775 auto* ool = oolCallVM<Fn, js::AtomizeString>(ins, ArgList(input),
22776 StoreRegisterTo(output));
22777
22778 Label isAtom;
22779 masm.branchTest32(Assembler::NonZero,
22780 Address(input, JSString::offsetOfFlags()),
22781 Imm32(StringFlags::ATOM_BIT), &isAtom);
22782
22783 masm.tryFastAtomize(input, output, output, ool->entry());
22784 masm.jump(ool->rejoin());
22785 masm.bind(&isAtom);
22786 masm.movePtr(input, output);
22787 masm.bind(ool->rejoin());
22788}
22789
22790void CodeGenerator::visitToHashableValue(LToHashableValue* ins) {
22791 ValueOperand input = ToValue(ins->input());
22792 FloatRegister tempFloat = ToFloatRegister(ins->temp0());
22793 ValueOperand output = ToOutValue(ins);
22794
22795 Register str = output.scratchReg();
22796
22797 using Fn = JSAtom* (*)(JSContext*, JSString*);
22798 auto* ool =
22799 oolCallVM<Fn, js::AtomizeString>(ins, ArgList(str), StoreRegisterTo(str));
22800
22801 masm.toHashableValue(input, output, tempFloat, ool->entry(), ool->rejoin());
22802}
22803
22804void CodeGenerator::visitHashNonGCThing(LHashNonGCThing* ins) {
22805 ValueOperand input = ToValue(ins->input());
22806 Register temp = ToRegister(ins->temp0());
22807 Register output = ToRegister(ins->output());
22808
22809 masm.prepareHashNonGCThing(input, output, temp);
22810}
22811
22812void CodeGenerator::visitHashString(LHashString* ins) {
22813 Register input = ToRegister(ins->input());
22814 Register temp = ToRegister(ins->temp0());
22815 Register output = ToRegister(ins->output());
22816
22817 masm.prepareHashString(input, output, temp);
22818}
22819
22820void CodeGenerator::visitHashSymbol(LHashSymbol* ins) {
22821 Register input = ToRegister(ins->input());
22822 Register output = ToRegister(ins->output());
22823
22824 masm.prepareHashSymbol(input, output);
22825}
22826
22827void CodeGenerator::visitHashBigInt(LHashBigInt* ins) {
22828 Register input = ToRegister(ins->input());
22829 Register temp0 = ToRegister(ins->temp0());
22830 Register temp1 = ToRegister(ins->temp1());
22831 Register temp2 = ToRegister(ins->temp2());
22832 Register output = ToRegister(ins->output());
22833
22834 masm.prepareHashBigInt(input, output, temp0, temp1, temp2);
22835}
22836
22837void CodeGenerator::visitHashObject(LHashObject* ins) {
22838 Register setObj = ToRegister(ins->setObject());
22839 ValueOperand input = ToValue(ins->input());
22840 Register temp0 = ToRegister(ins->temp0());
22841 Register temp1 = ToRegister(ins->temp1());
22842 Register temp2 = ToRegister(ins->temp2());
22843 Register temp3 = ToRegister(ins->temp3());
22844 Register output = ToRegister(ins->output());
22845
22846 masm.prepareHashObject(setObj, input, output, temp0, temp1, temp2, temp3);
22847}
22848
22849void CodeGenerator::visitHashValue(LHashValue* ins) {
22850 Register setObj = ToRegister(ins->setObject());
22851 ValueOperand input = ToValue(ins->input());
22852 Register temp0 = ToRegister(ins->temp0());
22853 Register temp1 = ToRegister(ins->temp1());
22854 Register temp2 = ToRegister(ins->temp2());
22855 Register temp3 = ToRegister(ins->temp3());
22856 Register output = ToRegister(ins->output());
22857
22858 masm.prepareHashValue(setObj, input, output, temp0, temp1, temp2, temp3);
22859}
22860
22861void CodeGenerator::visitSetObjectHasNonBigInt(LSetObjectHasNonBigInt* ins) {
22862 Register setObj = ToRegister(ins->setObject());
22863 ValueOperand input = ToValue(ins->value());
22864 Register hash = ToRegister(ins->hash());
22865 Register temp0 = ToRegister(ins->temp0());
22866 Register temp1 = ToRegister(ins->temp1());
22867 Register output = ToRegister(ins->output());
22868
22869 masm.setObjectHasNonBigInt(setObj, input, hash, output, temp0, temp1);
22870}
22871
22872void CodeGenerator::visitSetObjectHasBigInt(LSetObjectHasBigInt* ins) {
22873 Register setObj = ToRegister(ins->setObject());
22874 ValueOperand input = ToValue(ins->value());
22875 Register hash = ToRegister(ins->hash());
22876 Register temp0 = ToRegister(ins->temp0());
22877 Register temp1 = ToRegister(ins->temp1());
22878 Register temp2 = ToRegister(ins->temp2());
22879 Register temp3 = ToRegister(ins->temp3());
22880 Register output = ToRegister(ins->output());
22881
22882 masm.setObjectHasBigInt(setObj, input, hash, output, temp0, temp1, temp2,
22883 temp3);
22884}
22885
22886void CodeGenerator::visitSetObjectHasValue(LSetObjectHasValue* ins) {
22887 Register setObj = ToRegister(ins->setObject());
22888 ValueOperand input = ToValue(ins->value());
22889 Register hash = ToRegister(ins->hash());
22890 Register temp0 = ToRegister(ins->temp0());
22891 Register temp1 = ToRegister(ins->temp1());
22892 Register temp2 = ToRegister(ins->temp2());
22893 Register temp3 = ToRegister(ins->temp3());
22894 Register output = ToRegister(ins->output());
22895
22896 masm.setObjectHasValue(setObj, input, hash, output, temp0, temp1, temp2,
22897 temp3);
22898}
22899
22900void CodeGenerator::visitSetObjectHasValueVMCall(
22901 LSetObjectHasValueVMCall* ins) {
22902 pushArg(ToValue(ins->value()));
22903 pushArg(ToRegister(ins->setObject()));
22904
22905 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*);
22906 callVM<Fn, jit::SetObjectHas>(ins);
22907}
22908
22909void CodeGenerator::visitSetObjectDelete(LSetObjectDelete* ins) {
22910 pushArg(ToValue(ins->key()));
22911 pushArg(ToRegister(ins->setObject()));
22912 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*);
22913 callVM<Fn, jit::SetObjectDelete>(ins);
22914}
22915
22916void CodeGenerator::visitSetObjectAdd(LSetObjectAdd* ins) {
22917 pushArg(ToValue(ins->key()));
22918 pushArg(ToRegister(ins->setObject()));
22919 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue);
22920 callVM<Fn, jit::SetObjectAdd>(ins);
22921}
22922
22923void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) {
22924 Register setObj = ToRegister(ins->setObject());
22925 Register output = ToRegister(ins->output());
22926
22927 masm.loadSetObjectSize(setObj, output);
22928}
22929
22930void CodeGenerator::visitMapObjectHasNonBigInt(LMapObjectHasNonBigInt* ins) {
22931 Register mapObj = ToRegister(ins->mapObject());
22932 ValueOperand input = ToValue(ins->value());
22933 Register hash = ToRegister(ins->hash());
22934 Register temp0 = ToRegister(ins->temp0());
22935 Register temp1 = ToRegister(ins->temp1());
22936 Register output = ToRegister(ins->output());
22937
22938 masm.mapObjectHasNonBigInt(mapObj, input, hash, output, temp0, temp1);
22939}
22940
22941void CodeGenerator::visitMapObjectHasBigInt(LMapObjectHasBigInt* ins) {
22942 Register mapObj = ToRegister(ins->mapObject());
22943 ValueOperand input = ToValue(ins->value());
22944 Register hash = ToRegister(ins->hash());
22945 Register temp0 = ToRegister(ins->temp0());
22946 Register temp1 = ToRegister(ins->temp1());
22947 Register temp2 = ToRegister(ins->temp2());
22948 Register temp3 = ToRegister(ins->temp3());
22949 Register output = ToRegister(ins->output());
22950
22951 masm.mapObjectHasBigInt(mapObj, input, hash, output, temp0, temp1, temp2,
22952 temp3);
22953}
22954
22955void CodeGenerator::visitMapObjectHasValue(LMapObjectHasValue* ins) {
22956 Register mapObj = ToRegister(ins->mapObject());
22957 ValueOperand input = ToValue(ins->value());
22958 Register hash = ToRegister(ins->hash());
22959 Register temp0 = ToRegister(ins->temp0());
22960 Register temp1 = ToRegister(ins->temp1());
22961 Register temp2 = ToRegister(ins->temp2());
22962 Register temp3 = ToRegister(ins->temp3());
22963 Register output = ToRegister(ins->output());
22964
22965 masm.mapObjectHasValue(mapObj, input, hash, output, temp0, temp1, temp2,
22966 temp3);
22967}
22968
22969void CodeGenerator::visitMapObjectHasValueVMCall(
22970 LMapObjectHasValueVMCall* ins) {
22971 pushArg(ToValue(ins->value()));
22972 pushArg(ToRegister(ins->mapObject()));
22973
22974 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*);
22975 callVM<Fn, jit::MapObjectHas>(ins);
22976}
22977
22978void CodeGenerator::visitMapObjectGetNonBigInt(LMapObjectGetNonBigInt* ins) {
22979 Register mapObj = ToRegister(ins->mapObject());
22980 ValueOperand input = ToValue(ins->value());
22981 Register hash = ToRegister(ins->hash());
22982 Register temp0 = ToRegister(ins->temp0());
22983 Register temp1 = ToRegister(ins->temp1());
22984 ValueOperand output = ToOutValue(ins);
22985
22986 masm.mapObjectGetNonBigInt(mapObj, input, hash, output, temp0, temp1,
22987 output.scratchReg());
22988}
22989
22990void CodeGenerator::visitMapObjectGetBigInt(LMapObjectGetBigInt* ins) {
22991 Register mapObj = ToRegister(ins->mapObject());
22992 ValueOperand input = ToValue(ins->value());
22993 Register hash = ToRegister(ins->hash());
22994 Register temp0 = ToRegister(ins->temp0());
22995 Register temp1 = ToRegister(ins->temp1());
22996 Register temp2 = ToRegister(ins->temp2());
22997 Register temp3 = ToRegister(ins->temp3());
22998 ValueOperand output = ToOutValue(ins);
22999
23000 masm.mapObjectGetBigInt(mapObj, input, hash, output, temp0, temp1, temp2,
23001 temp3, output.scratchReg());
23002}
23003
23004void CodeGenerator::visitMapObjectGetValue(LMapObjectGetValue* ins) {
23005 Register mapObj = ToRegister(ins->mapObject());
23006 ValueOperand input = ToValue(ins->value());
23007 Register hash = ToRegister(ins->hash());
23008 Register temp0 = ToRegister(ins->temp0());
23009 Register temp1 = ToRegister(ins->temp1());
23010 Register temp2 = ToRegister(ins->temp2());
23011 Register temp3 = ToRegister(ins->temp3());
23012 ValueOperand output = ToOutValue(ins);
23013
23014 masm.mapObjectGetValue(mapObj, input, hash, output, temp0, temp1, temp2,
23015 temp3, output.scratchReg());
23016}
23017
23018void CodeGenerator::visitMapObjectGetValueVMCall(
23019 LMapObjectGetValueVMCall* ins) {
23020 pushArg(ToValue(ins->value()));
23021 pushArg(ToRegister(ins->mapObject()));
23022
23023 using Fn =
23024 bool (*)(JSContext*, Handle<MapObject*>, HandleValue, MutableHandleValue);
23025 callVM<Fn, jit::MapObjectGet>(ins);
23026}
23027
23028void CodeGenerator::visitMapObjectDelete(LMapObjectDelete* ins) {
23029 pushArg(ToValue(ins->key()));
23030 pushArg(ToRegister(ins->mapObject()));
23031 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*);
23032 callVM<Fn, jit::MapObjectDelete>(ins);
23033}
23034
23035void CodeGenerator::visitMapObjectSet(LMapObjectSet* ins) {
23036 pushArg(ToValue(ins->value()));
23037 pushArg(ToValue(ins->key()));
23038 pushArg(ToRegister(ins->mapObject()));
23039 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, HandleValue);
23040 callVM<Fn, jit::MapObjectSet>(ins);
23041}
23042
23043void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) {
23044 Register mapObj = ToRegister(ins->mapObject());
23045 Register output = ToRegister(ins->output());
23046
23047 masm.loadMapObjectSize(mapObj, output);
23048}
23049
23050void CodeGenerator::emitWeakMapLookupObject(
23051 Register weakMap, Register obj, Register hashTable, Register hashCode,
23052 Register scratch, Register scratch2, Register scratch3, Register scratch4,
23053 Register scratch5, Label* found, Label* missing) {
23054 // Load hash map if it exists. If not, jump to missing.
23055 Address mapAddr(weakMap,
23056 NativeObject::getFixedSlotOffset(WeakMapObject::DataSlot));
23057 masm.branchTestUndefined(Assembler::Equal, mapAddr, missing);
23058 masm.loadPrivate(mapAddr, hashTable);
23059
23060 // Hash and scramble address of object.
23061#ifdef JS_PUNBOX641
23062 ValueOperand boxedObj(scratch);
23063#else
23064 ValueOperand boxedObj(scratch, obj);
23065#endif
23066 masm.tagValue(JSVAL_TYPE_OBJECT, obj, boxedObj);
23067 masm.hashAndScrambleValue(boxedObj, hashCode, scratch2);
23068 masm.prepareHashMFBT(hashCode, /*alreadyScrambled*/ true);
23069
23070 using Entry = WeakMapObject::Map::Entry;
23071 auto matchEntry = [&]() {
23072 Register entry = scratch;
23073 Label noMatch;
23074 masm.fallibleUnboxObject(Address(entry, Entry::offsetOfKey()), scratch2,
23075 &noMatch);
23076 masm.branchPtr(Assembler::Equal, obj, scratch2, found);
23077 masm.bind(&noMatch);
23078 };
23079 masm.lookupMFBT<WeakMapObject::Map>(hashTable, hashCode, scratch, scratch2,
23080 scratch3, scratch4, scratch5, missing,
23081 matchEntry);
23082}
23083
23084void CodeGenerator::visitWeakMapGetObject(LWeakMapGetObject* ins) {
23085#ifndef JS_CODEGEN_X86
23086 Register weakMap = ToRegister(ins->weakMap());
23087 Register obj = ToRegister(ins->object());
23088 Register hashTable = ToRegister(ins->temp0());
23089 Register hashCode = ToRegister(ins->temp1());
23090 Register scratch = ToRegister(ins->temp2());
23091 Register scratch2 = ToRegister(ins->temp3());
23092 Register scratch3 = ToRegister(ins->temp4());
23093 Register scratch4 = ToRegister(ins->temp5());
23094 Register scratch5 = ToRegister(ins->temp6());
23095 ValueOperand output = ToOutValue(ins);
23096
23097 Label found, missing;
23098
23099 emitWeakMapLookupObject(weakMap, obj, hashTable, hashCode, scratch, scratch2,
23100 scratch3, scratch4, scratch5, &found, &missing);
23101
23102 masm.bind(&found);
23103
23104 using Entry = WeakMapObject::Map::Entry;
23105 masm.loadValue(Address(scratch, Entry::offsetOfValue()), output);
23106
23107 auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) {
23108 // Unboxed, tenured GC cell that needs to be barriered is in scratch.
23109
23110 LiveRegisterSet regsToSave(RegisterSet::Volatile());
23111 regsToSave.takeUnchecked(hashTable);
23112 regsToSave.takeUnchecked(hashCode);
23113 regsToSave.takeUnchecked(scratch);
23114 regsToSave.takeUnchecked(scratch2);
23115 regsToSave.takeUnchecked(scratch3);
23116 regsToSave.takeUnchecked(scratch4);
23117 regsToSave.takeUnchecked(scratch5);
23118 masm.PushRegsInMask(regsToSave);
23119
23120 masm.movePtr(ImmPtr(mirGen().realm->zone()->addressOfZone()), scratch2);
23121
23122 using Fn = void (*)(js::gc::TenuredCell*, Zone*);
23123 masm.setupAlignedABICall();
23124 masm.passABIArg(scratch);
23125 masm.passABIArg(scratch2);
23126 masm.callWithABI<Fn, js::jit::WeakMapValueReadBarrier>();
23127
23128 masm.PopRegsInMask(regsToSave);
23129
23130 masm.jump(ool.rejoin());
23131 });
23132 addOutOfLineCode(ool, ins->mir());
23133
23134 masm.emitWeakMapBarrierFastPath(output, scratch, scratch2, scratch3, scratch4,
23135 scratch5, ool->entry());
23136 masm.jump(ool->rejoin());
23137
23138 masm.bind(&missing);
23139 masm.moveValue(UndefinedValue(), output);
23140
23141 masm.bind(ool->rejoin());
23142#else
23143 // x86 doesn't have enough registers, so we call into the VM.
23144 Register weakMap = ToRegister(ins->weakMap());
23145 Register obj = ToRegister(ins->object());
23146 Register temp = ToRegister(ins->temp0());
23147 ValueOperand output = ToOutValue(ins);
23148
23149 // The result Value will be stored on the stack.
23150 masm.reserveStack(sizeof(Value));
23151 masm.moveStackPtrTo(temp);
23152
23153 using Fn = void (*)(WeakMapObject*, JSObject*, Value*);
23154 masm.setupAlignedABICall();
23155 masm.passABIArg(weakMap);
23156 masm.passABIArg(obj);
23157 masm.passABIArg(temp);
23158 masm.callWithABI<Fn, js::WeakMapObject::getObject>();
23159
23160 masm.Pop(output);
23161#endif
23162}
23163
23164void CodeGenerator::visitWeakMapHasObject(LWeakMapHasObject* ins) {
23165#ifndef JS_CODEGEN_X86
23166 Register weakMap = ToRegister(ins->weakMap());
23167 Register obj = ToRegister(ins->object());
23168 Register hashTable = ToRegister(ins->temp0());
23169 Register hashCode = ToRegister(ins->temp1());
23170 Register scratch = ToRegister(ins->temp2());
23171 Register scratch2 = ToRegister(ins->temp3());
23172 Register scratch3 = ToRegister(ins->temp4());
23173 Register scratch4 = ToRegister(ins->temp5());
23174 Register scratch5 = ToRegister(ins->temp6());
23175 Register output = ToRegister(ins->output());
23176
23177 Label found, missing, done;
23178
23179 emitWeakMapLookupObject(weakMap, obj, hashTable, hashCode, scratch, scratch2,
23180 scratch3, scratch4, scratch5, &found, &missing);
23181
23182 masm.bind(&found);
23183 masm.move32(Imm32(1), output);
23184 masm.jump(&done);
23185
23186 masm.bind(&missing);
23187 masm.move32(Imm32(0), output);
23188 masm.bind(&done);
23189#else
23190 // x86 doesn't have enough registers, so we call into the VM.
23191 Register weakMap = ToRegister(ins->weakMap());
23192 Register obj = ToRegister(ins->object());
23193 Register output = ToRegister(ins->output());
23194
23195 using Fn = bool (*)(WeakMapObject*, JSObject*);
23196 masm.setupAlignedABICall();
23197 masm.passABIArg(weakMap);
23198 masm.passABIArg(obj);
23199 masm.callWithABI<Fn, js::WeakMapObject::hasObject>();
23200 masm.storeCallBoolResult(output);
23201#endif
23202}
23203
23204void CodeGenerator::visitWeakSetHasObject(LWeakSetHasObject* ins) {
23205 Register weakSet = ToRegister(ins->weakSet());
23206 Register obj = ToRegister(ins->object());
23207 Register output = ToRegister(ins->output());
23208
23209 using Fn = bool (*)(WeakSetObject*, JSObject*);
23210 masm.setupAlignedABICall();
23211 masm.passABIArg(weakSet);
23212 masm.passABIArg(obj);
23213 masm.callWithABI<Fn, js::WeakSetObject::hasObject>();
23214 masm.storeCallBoolResult(output);
23215}
23216
23217void CodeGenerator::visitDateFillLocalTimeSlots(LDateFillLocalTimeSlots* ins) {
23218 Register date = ToRegister(ins->date());
23219 Register temp = ToRegister(ins->temp0());
23220
23221 masm.dateFillLocalTimeSlots(date, temp, liveVolatileRegs(ins));
23222}
23223
23224void CodeGenerator::visitDateHoursFromSecondsIntoYear(
23225 LDateHoursFromSecondsIntoYear* ins) {
23226 auto secondsIntoYear = ToValue(ins->secondsIntoYear());
23227 auto output = ToOutValue(ins);
23228 Register temp0 = ToRegister(ins->temp0());
23229 Register temp1 = ToRegister(ins->temp1());
23230
23231 masm.dateHoursFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
23232}
23233
23234void CodeGenerator::visitDateMinutesFromSecondsIntoYear(
23235 LDateMinutesFromSecondsIntoYear* ins) {
23236 auto secondsIntoYear = ToValue(ins->secondsIntoYear());
23237 auto output = ToOutValue(ins);
23238 Register temp0 = ToRegister(ins->temp0());
23239 Register temp1 = ToRegister(ins->temp1());
23240
23241 masm.dateMinutesFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
23242}
23243
23244void CodeGenerator::visitDateSecondsFromSecondsIntoYear(
23245 LDateSecondsFromSecondsIntoYear* ins) {
23246 auto secondsIntoYear = ToValue(ins->secondsIntoYear());
23247 auto output = ToOutValue(ins);
23248 Register temp0 = ToRegister(ins->temp0());
23249 Register temp1 = ToRegister(ins->temp1());
23250
23251 masm.dateSecondsFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
23252}
23253
23254void CodeGenerator::visitDateNow(LDateNow* ins) {
23255 Register temp0 = ToRegister(ins->temp0());
23256 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"
, 23256); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 23256); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23257
23258 using Fn = double (*)(JSContext*);
23259 masm.setupAlignedABICall();
23260 masm.loadJSContext(temp0);
23261 masm.passABIArg(temp0);
23262 masm.callWithABI<Fn, jit::DateNow>(ABIType::Float64);
23263}
23264
23265void CodeGenerator::visitDateParse(LDateParse* ins) {
23266 Register string = ToRegister(ins->string());
23267 Register temp0 = ToRegister(ins->temp0());
23268 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"
, 23268); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 23268); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23269
23270 using Fn = double (*)(JSContext*, const JSString*);
23271 masm.setupAlignedABICall();
23272 masm.loadJSContext(temp0);
23273 masm.passABIArg(temp0);
23274 masm.passABIArg(string);
23275 masm.callWithABI<Fn, jit::DateParse>(ABIType::Float64);
23276}
23277
23278void CodeGenerator::visitTimeClip(LTimeClip* ins) {
23279 auto time = ToFloatRegister(ins->time());
23280 auto output = ToFloatRegister(ins->output());
23281
23282 masm.timeClip(time, output);
23283}
23284
23285void CodeGenerator::visitTimeClipCall(LTimeClipCall* ins) {
23286 auto time = ToFloatRegister(ins->time());
23287 auto output = ToFloatRegister(ins->output());
23288 auto temp = ToRegister(ins->temp0());
23289
23290 masm.timeClip(time, output, temp, liveVolatileRegs(ins));
23291}
23292
23293void CodeGenerator::visitLocalTimeToUTC(LLocalTimeToUTC* ins) {
23294 Register64 localTime = ToRegister64(ins->localTime());
23295 Register temp0 = ToRegister(ins->temp0());
23296 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"
, 23296); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 23296); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23297
23298 using Fn = double (*)(JSContext*, int64_t);
23299 masm.setupAlignedABICall();
23300 masm.loadJSContext(temp0);
23301 masm.passABIArg(temp0);
23302 masm.passABIArg(localTime);
23303 masm.callWithABI<Fn, jit::DateLocalTimeToUTC>(ABIType::Float64);
23304}
23305
23306void CodeGenerator::visitYearFromTime(LYearFromTime* ins) {
23307 FloatRegister utcTime = ToFloatRegister(ins->utcTime());
23308 Register temp0 = ToRegister(ins->temp0());
23309 Register temp1 = ToRegister(ins->temp1());
23310 ValueOperand output = ToOutValue(ins);
23311
23312 masm.reserveStack(sizeof(JS::Value));
23313 masm.moveStackPtrTo(temp1);
23314
23315 using Fn = void (*)(JSContext*, double, JS::Value*);
23316 masm.setupAlignedABICall();
23317 masm.loadJSContext(temp0);
23318 masm.passABIArg(temp0);
23319 masm.passABIArg(utcTime, ABIType::Float64);
23320 masm.passABIArg(temp1);
23321 masm.callWithABI<Fn, jit::DateYearFromTime>();
23322
23323 masm.Pop(output);
23324}
23325
23326void CodeGenerator::visitMonthFromTime(LMonthFromTime* ins) {
23327 FloatRegister utcTime = ToFloatRegister(ins->utcTime());
23328 Register temp0 = ToRegister(ins->temp0());
23329 Register temp1 = ToRegister(ins->temp1());
23330 ValueOperand output = ToOutValue(ins);
23331
23332 masm.reserveStack(sizeof(JS::Value));
23333 masm.moveStackPtrTo(temp1);
23334
23335 using Fn = void (*)(JSContext*, double, JS::Value*);
23336 masm.setupAlignedABICall();
23337 masm.loadJSContext(temp0);
23338 masm.passABIArg(temp0);
23339 masm.passABIArg(utcTime, ABIType::Float64);
23340 masm.passABIArg(temp1);
23341 masm.callWithABI<Fn, jit::DateMonthFromTime>();
23342
23343 masm.Pop(output);
23344}
23345
23346void CodeGenerator::visitDateFromTime(LDateFromTime* ins) {
23347 FloatRegister utcTime = ToFloatRegister(ins->utcTime());
23348 Register temp0 = ToRegister(ins->temp0());
23349 Register temp1 = ToRegister(ins->temp1());
23350 ValueOperand output = ToOutValue(ins);
23351
23352 masm.reserveStack(sizeof(JS::Value));
23353 masm.moveStackPtrTo(temp1);
23354
23355 using Fn = void (*)(JSContext*, double, JS::Value*);
23356 masm.setupAlignedABICall();
23357 masm.loadJSContext(temp0);
23358 masm.passABIArg(temp0);
23359 masm.passABIArg(utcTime, ABIType::Float64);
23360 masm.passABIArg(temp1);
23361 masm.callWithABI<Fn, jit::DateDateFromTime>();
23362
23363 masm.Pop(output);
23364}
23365
23366void CodeGenerator::visitNewDateObject(LNewDateObject* lir) {
23367 FloatRegister utcTime = ToFloatRegister(lir->utcTime());
23368 Register output = ToRegister(lir->output());
23369 Register temp = ToRegister(lir->temp0());
23370
23371 JSObject* templateObj = lir->mir()->templateObject();
23372
23373 using Fn = JSObject* (*)(JSContext*, double);
23374 auto* ool = oolCallVM<Fn, jit::NewDateObject>(lir, ArgList(utcTime),
23375 StoreRegisterTo(output));
23376
23377 TemplateObject templateObject(templateObj);
23378 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
23379 ool->entry());
23380 masm.boxDouble(utcTime, Address(output, DateObject::offsetOfUTCTimeSlot()));
23381
23382 masm.bind(ool->rejoin());
23383}
23384
23385void CodeGenerator::visitUnpackTime(LUnpackTime* lir) {
23386 ValueOperand packedVal = ToValue(lir->packedVal());
23387 Register output = ToRegister(lir->output());
23388 Register temp = ToTempRegisterOrInvalid(lir->temp0());
23389
23390 auto* mir = lir->mir();
23391
23392 masm.unpackTime(packedVal, output, temp, mir->shiftImm(), mir->maskImm());
23393}
23394
23395void CodeGenerator::visitEpochMilliseconds(LEpochMilliseconds* lir) {
23396 FloatRegister seconds = ToFloatRegister(lir->seconds());
23397 Register nanoseconds = ToRegister(lir->nanoseconds());
23398 FloatRegister output = ToFloatRegister(lir->output());
23399 Register temp = ToRegister(lir->temp0());
23400
23401 masm.epochMilliseconds(seconds, nanoseconds, output, temp);
23402}
23403
23404void CodeGenerator::visitCanonicalizeNaND(LCanonicalizeNaND* ins) {
23405 auto output = ToFloatRegister(ins->output());
23406 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", 23406); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output == ToFloatRegister(ins->input())"
")"); do { MOZ_CrashSequence(__null, 23406); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23407
23408 masm.canonicalizeDoubleNaN(output);
23409}
23410
23411void CodeGenerator::visitCanonicalizeNaNF(LCanonicalizeNaNF* ins) {
23412 auto output = ToFloatRegister(ins->output());
23413 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", 23413); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output == ToFloatRegister(ins->input())"
")"); do { MOZ_CrashSequence(__null, 23413); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23414
23415 masm.canonicalizeFloatNaN(output);
23416}
23417
23418template <size_t NumDefs>
23419void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) {
23420 wasm::JitCallStackArgVector stackArgs;
23421 masm.propagateOOM(stackArgs.reserve(lir->numOperands()));
23422 if (masm.oom()) {
23423 return;
23424 }
23425
23426 MIonToWasmCall* mir = lir->mir();
23427 const wasm::FuncExport& funcExport = mir->funcExport();
23428 const wasm::FuncType& sig =
23429 mir->instance()->code().codeMeta().getFuncType(funcExport.funcIndex());
23430
23431 ABIArgGenerator abi(ABIKind::Wasm);
23432 for (size_t i = 0; i < lir->numOperands(); i++) {
23433 MIRType argMir;
23434 switch (sig.args()[i].kind()) {
23435 case wasm::ValType::I32:
23436 case wasm::ValType::I64:
23437 case wasm::ValType::F32:
23438 case wasm::ValType::F64:
23439 argMir = sig.args()[i].toMIRType();
23440 break;
23441 case wasm::ValType::V128:
23442 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", 23442); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected argument type when calling from ion to wasm"
")"); do { MOZ_CrashSequence(__null, 23442); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
23443 case wasm::ValType::Ref:
23444 // temporarilyUnsupportedReftypeForEntry() restricts args to externref
23445 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"
, 23445); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "sig.args()[i].refType().isExtern()"
")"); do { MOZ_CrashSequence(__null, 23445); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23446 // Argument is boxed on the JS side to an anyref, so passed as a
23447 // pointer here.
23448 argMir = sig.args()[i].toMIRType();
23449 break;
23450 }
23451
23452 ABIArg arg = abi.next(argMir);
23453 switch (arg.kind()) {
23454 case ABIArg::GPR:
23455 case ABIArg::FPU: {
23456 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", 23456); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToAnyRegister(lir->getOperand(i)) == arg.reg()"
")"); do { MOZ_CrashSequence(__null, 23456); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23457 stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg());
23458 break;
23459 }
23460 case ABIArg::Stack: {
23461 const LAllocation* larg = lir->getOperand(i);
23462 if (larg->isConstant()) {
23463 stackArgs.infallibleEmplaceBack(ToInt32(larg));
23464 } else if (larg->isGeneralReg()) {
23465 stackArgs.infallibleEmplaceBack(ToRegister(larg));
23466 } else if (larg->isFloatReg()) {
23467 stackArgs.infallibleEmplaceBack(ToFloatRegister(larg));
23468 } else {
23469 // Always use the stack pointer here because GenerateDirectCallFromJit
23470 // depends on this.
23471 Address addr = ToAddress<BaseRegForAddress::SP>(larg);
23472 stackArgs.infallibleEmplaceBack(addr);
23473 }
23474 break;
23475 }
23476#ifdef JS_CODEGEN_REGISTER_PAIR
23477 case ABIArg::GPR_PAIR: {
23478 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", 23479); AnnotateMozCrashReason
("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls"
")"); do { MOZ_CrashSequence(__null, 23479); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
23479 "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", 23479); AnnotateMozCrashReason
("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls"
")"); do { MOZ_CrashSequence(__null, 23479); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
23480 }
23481#endif
23482 case ABIArg::Uninitialized: {
23483 MOZ_CRASH("Uninitialized ABIArg kind")do { do { } while (false); MOZ_ReportCrash("" "Uninitialized ABIArg kind"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23483); AnnotateMozCrashReason
("MOZ_CRASH(" "Uninitialized ABIArg kind" ")"); do { MOZ_CrashSequence
(__null, 23483); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
23484 }
23485 }
23486 }
23487
23488 const wasm::ValTypeVector& results = sig.results();
23489 if (results.length() == 0) {
23490 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", 23490); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value"
")"); do { MOZ_CrashSequence(__null, 23490); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23491 } else {
23492 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"
, 23492); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 1"
") (" "multi-value return unimplemented" ")"); do { MOZ_CrashSequence
(__null, 23492); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
23493 switch (results[0].kind()) {
23494 case wasm::ValType::I32:
23495 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", 23495); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int32"
")"); do { MOZ_CrashSequence(__null, 23495); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23496 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", 23496); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { MOZ_CrashSequence(__null, 23496); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23497 break;
23498 case wasm::ValType::I64:
23499 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", 23499); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64"
")"); do { MOZ_CrashSequence(__null, 23499); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23500 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"
, 23500); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutRegister64(lir) == ReturnReg64"
")"); do { MOZ_CrashSequence(__null, 23500); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23501 break;
23502 case wasm::ValType::F32:
23503 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", 23503); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32"
")"); do { MOZ_CrashSequence(__null, 23503); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23504 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"
, 23504); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg"
")"); do { MOZ_CrashSequence(__null, 23504); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23505 break;
23506 case wasm::ValType::F64:
23507 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", 23507); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double"
")"); do { MOZ_CrashSequence(__null, 23507); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23508 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"
, 23508); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { MOZ_CrashSequence(__null, 23508); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23509 break;
23510 case wasm::ValType::V128:
23511 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", 23511); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected return type when calling from ion to wasm"
")"); do { MOZ_CrashSequence(__null, 23511); __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
23512 case wasm::ValType::Ref:
23513 // The wasm stubs layer unboxes anything that needs to be unboxed
23514 // and leaves it in a Value. A FuncRef/EqRef we could in principle
23515 // leave it as a raw object pointer but for now it complicates the
23516 // API to do so.
23517 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", 23517); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value"
")"); do { MOZ_CrashSequence(__null, 23517); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23518 break;
23519 }
23520 }
23521
23522 WasmInstanceObject* instObj = lir->mir()->instanceObject();
23523
23524 Register scratch = ToRegister(lir->temp());
23525
23526 uint32_t callOffset;
23527 ensureOsiSpace();
23528 GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs,
23529 scratch, &callOffset);
23530
23531 // Add the instance object to the constant pool, so it is transferred to
23532 // the owning IonScript and so that it gets traced as long as the IonScript
23533 // lives.
23534
23535 uint32_t unused;
23536 masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused));
23537
23538 markSafepointAt(callOffset, lir);
23539}
23540
23541void CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) {
23542 emitIonToWasmCallBase(lir);
23543}
23544void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) {
23545 emitIonToWasmCallBase(lir);
23546}
23547void CodeGenerator::visitIonToWasmCallI64(LIonToWasmCallI64* lir) {
23548 emitIonToWasmCallBase(lir);
23549}
23550
23551void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) {
23552 masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output()));
23553}
23554
23555void CodeGenerator::visitWasmFence(LWasmFence* lir) {
23556 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", 23556); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence
(__null, 23556); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
23557 masm.memoryBarrier(MemoryBarrier::Full());
23558}
23559
23560void CodeGenerator::visitWasmAnyRefFromJSValue(LWasmAnyRefFromJSValue* lir) {
23561 ValueOperand input = ToValue(lir->def());
23562 ValueOperand temp = ToValue(lir->temp1());
23563 Register output = ToRegister(lir->output());
23564 FloatRegister tempFloat = ToFloatRegister(lir->temp0());
23565
23566 using Fn = JSObject* (*)(JSContext * cx, HandleValue value);
23567 OutOfLineCode* oolBoxValue = oolCallVM<Fn, wasm::AnyRef::boxValue>(
23568 lir, ArgList(temp), StoreRegisterTo(output));
23569
23570 // If the value is a double that is a negative denormal and denormals
23571 // are disabled, then `convertValueToWasmAnyRef` will view
23572 // it as '-0' (which must be boxed in the OOL path). However, the
23573 // AnyRef boxing code uses `mozilla::NumberIsInt32` which does not
23574 // properly handle the CPU DAZ/FTZ flags and asserts that the value doesn't
23575 // actually need to be boxed.
23576 //
23577 // Making `mozilla::NumberIsInt32` handle the CPU DAZ/FTZ flags would
23578 // add a significant cost to many hot-paths. We instead just
23579 // eagerly canonicalize denormals to +-0.0 here to avoid inconsistent
23580 // results (see Bug 1971519).
23581 masm.moveValue(input, temp);
23582 masm.canonicalizeValueZero(temp, tempFloat);
23583
23584 masm.convertValueToWasmAnyRef(temp, output, tempFloat, oolBoxValue->entry());
23585 masm.bind(oolBoxValue->rejoin());
23586}
23587
23588void CodeGenerator::visitWasmAnyRefFromJSObject(LWasmAnyRefFromJSObject* lir) {
23589 Register input = ToRegister(lir->def());
23590 Register output = ToRegister(lir->output());
23591 masm.convertObjectToWasmAnyRef(input, output);
23592}
23593
23594void CodeGenerator::visitWasmAnyRefFromJSString(LWasmAnyRefFromJSString* lir) {
23595 Register input = ToRegister(lir->def());
23596 Register output = ToRegister(lir->output());
23597 masm.convertStringToWasmAnyRef(input, output);
23598}
23599
23600void CodeGenerator::visitWasmAnyRefIsJSString(LWasmAnyRefIsJSString* lir) {
23601 Register input = ToRegister(lir->input());
23602 Register output = ToRegister(lir->output());
23603 Register temp = ToRegister(lir->temp0());
23604 Label fallthrough;
23605 Label isJSString;
23606 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
23607 masm.move32(Imm32(0), output);
23608 masm.jump(&fallthrough);
23609 masm.bind(&isJSString);
23610 masm.move32(Imm32(1), output);
23611 masm.bind(&fallthrough);
23612}
23613
23614void CodeGenerator::visitWasmTrapIfAnyRefIsNotJSString(
23615 LWasmTrapIfAnyRefIsNotJSString* lir) {
23616 Register input = ToRegister(lir->input());
23617 Register temp = ToRegister(lir->temp0());
23618 Label isJSString;
23619 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
23620 masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc());
23621 masm.bind(&isJSString);
23622}
23623
23624void CodeGenerator::visitWasmAnyRefJSStringLength(
23625 LWasmAnyRefJSStringLength* lir) {
23626 Register input = ToRegister(lir->input());
23627 Register output = ToRegister(lir->output());
23628 Register temp = ToRegister(lir->temp0());
23629 Label isJSString;
23630 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
23631 masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc());
23632 masm.bind(&isJSString);
23633 masm.untagWasmAnyRef(input, temp, wasm::AnyRefTag::String);
23634 masm.loadStringLength(temp, output);
23635}
23636
23637void CodeGenerator::visitWasmNewI31Ref(LWasmNewI31Ref* lir) {
23638 if (lir->value()->isConstant()) {
23639 // i31ref are often created with constants. If that's the case we will
23640 // do the operation statically here. This is similar to what is done
23641 // in masm.truncate32ToWasmI31Ref.
23642 Register output = ToRegister(lir->output());
23643 uint32_t value =
23644 static_cast<uint32_t>(lir->value()->toConstant()->toInt32());
23645 uintptr_t ptr = wasm::AnyRef::fromUint32Truncate(value).rawValue();
23646 masm.movePtr(ImmWord(ptr), output);
23647 } else {
23648 Register value = ToRegister(lir->value());
23649 Register output = ToRegister(lir->output());
23650 masm.truncate32ToWasmI31Ref(value, output);
23651 }
23652}
23653
23654void CodeGenerator::visitWasmI31RefGet(LWasmI31RefGet* lir) {
23655 Register value = ToRegister(lir->input());
23656 Register output = ToRegister(lir->output());
23657 if (lir->mir()->wideningOp() == wasm::FieldWideningOp::Signed) {
23658 masm.convertWasmI31RefTo32Signed(value, output);
23659 } else {
23660 masm.convertWasmI31RefTo32Unsigned(value, output);
23661 }
23662}
23663
23664#ifdef JS_64BIT1
23665void CodeGenerator::visitWasmAddSubI128HI64(LWasmAddSubI128HI64* lir) {
23666 Register lhsLo = ToRegister(lir->lhsLo());
23667 Register lhsHi = ToRegister(lir->lhsHi());
23668 Register rhsLo = ToRegister(lir->rhsLo());
23669 Register rhsHi = ToRegister(lir->rhsHi());
23670 Register output = ToRegister(lir->output());
23671 // This holds because all 4 operands are non-AtStart variants.
23672 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", 23673); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi"
")"); do { MOZ_CrashSequence(__null, 23673); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
23673 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", 23673); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi"
")"); do { MOZ_CrashSequence(__null, 23673); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
23674 masm.wasmAddSubI128HI64(lhsLo, lhsHi, rhsLo, rhsHi, output, lir->isAdd());
23675}
23676#endif
23677
23678#ifndef JS_64BIT1
23679void CodeGenerator::visitWasmLoadInstanceScratch2xI32(
23680 LWasmLoadInstanceScratch2xI32* lir) {
23681 Register64 output = ToOutRegister64(lir);
23682 Register instance = ToRegister(lir->instance());
23683 uint32_t offset =
23684 wasm::Instance::offsetofBaselineScratchWords() + lir->byteOffset();
23685 masm.loadPtr(Address(instance, offset + 0), output.low);
23686 masm.loadPtr(Address(instance, offset + 4), output.high);
23687}
23688
23689void CodeGenerator::visitWasmStoreInstanceScratch2xI32(
23690 LWasmStoreInstanceScratch2xI32* lir) {
23691 Register64 value = ToRegister64(lir->value());
23692 Register instance = ToRegister(lir->instance());
23693 uint32_t offset =
23694 wasm::Instance::offsetofBaselineScratchWords() + lir->byteOffset();
23695 masm.storePtr(value.low, Address(instance, offset + 0));
23696 masm.storePtr(value.high, Address(instance, offset + 4));
23697}
23698#endif
23699
23700void CodeGenerator::visitUint32ToDouble(LUint32ToDouble* lir) {
23701 masm.convertUInt32ToDouble(ToRegister(lir->input()),
23702 ToFloatRegister(lir->output()));
23703}
23704
23705void CodeGenerator::visitUint32ToFloat32(LUint32ToFloat32* lir) {
23706 masm.convertUInt32ToFloat32(ToRegister(lir->input()),
23707 ToFloatRegister(lir->output()));
23708}
23709
23710void CodeGenerator::visitAddDisposableResource(LAddDisposableResource* lir) {
23711 Register environment = ToRegister(lir->environment());
23712 ValueOperand resource = ToValue(lir->resource());
23713 ValueOperand method = ToValue(lir->method());
23714 Register needsClosure = ToRegister(lir->needsClosure());
23715 uint8_t hint = lir->mir()->hint();
23716
23717 pushArg(Imm32(hint));
23718 pushArg(needsClosure);
23719 pushArg(method);
23720 pushArg(resource);
23721 pushArg(environment);
23722
23723 using Fn = bool (*)(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>,
23724 JS::Handle<JS::Value>, bool, UsingHint);
23725 callVM<Fn, js::AddDisposableResourceToCapability>(lir);
23726}
23727
23728void CodeGenerator::visitTakeDisposeCapability(LTakeDisposeCapability* lir) {
23729 Register environment = ToRegister(lir->environment());
23730 ValueOperand output = ToOutValue(lir);
23731
23732 Address capabilityAddr(
23733 environment, DisposableEnvironmentObject::offsetOfDisposeCapability());
23734 emitPreBarrier(capabilityAddr);
23735 masm.loadValue(capabilityAddr, output);
23736 masm.storeValue(JS::UndefinedValue(), capabilityAddr);
23737}
23738
23739#ifdef FUZZING_JS_FUZZILLI
23740void CodeGenerator::emitFuzzilliHashObject(LInstruction* lir, Register obj,
23741 Register output) {
23742 using Fn = void (*)(JSContext* cx, JSObject* obj, uint32_t* out);
23743 OutOfLineCode* ool = oolCallVM<Fn, FuzzilliHashObjectInl>(
23744 lir, ArgList(obj), StoreRegisterTo(output));
23745
23746 masm.jump(ool->entry());
23747 masm.bind(ool->rejoin());
23748}
23749
23750void CodeGenerator::emitFuzzilliHashBigInt(LInstruction* lir, Register bigInt,
23751 Register output) {
23752 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
23753 volatileRegs.takeUnchecked(output);
23754
23755 masm.PushRegsInMask(volatileRegs);
23756
23757 using Fn = uint32_t (*)(BigInt* bigInt);
23758 masm.setupUnalignedABICall(output);
23759 masm.passABIArg(bigInt);
23760 masm.callWithABI<Fn, js::FuzzilliHashBigInt>();
23761 masm.storeCallInt32Result(output);
23762
23763 masm.PopRegsInMask(volatileRegs);
23764}
23765
23766void CodeGenerator::visitFuzzilliHashV(LFuzzilliHashV* ins) {
23767 ValueOperand value = ToValue(ins->value());
23768
23769 FloatRegister scratchFloat = ToFloatRegister(ins->temp1());
23770 Register scratch = ToRegister(ins->temp0());
23771 Register output = ToRegister(ins->output());
23772 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", 23772); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scratch != output" ")"); do { MOZ_CrashSequence
(__null, 23772); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
23773
23774 Label hashDouble, done;
23775
23776 Label isInt32, isDouble, isNull, isUndefined, isBoolean, isBigInt, isObject;
23777 {
23778 ScratchTagScope tag(masm, value);
23779 masm.splitTagForTest(value, tag);
23780
23781 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
23782 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
23783 masm.branchTestNull(Assembler::Equal, tag, &isNull);
23784 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
23785 masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean);
23786 masm.branchTestBigInt(Assembler::Equal, tag, &isBigInt);
23787 masm.branchTestObject(Assembler::Equal, tag, &isObject);
23788
23789 // Symbol or String.
23790 masm.move32(Imm32(0), output);
23791 masm.jump(&done);
23792 }
23793
23794 masm.bind(&isInt32);
23795 {
23796 masm.unboxInt32(value, scratch);
23797 masm.convertInt32ToDouble(scratch, scratchFloat);
23798 masm.jump(&hashDouble);
23799 }
23800
23801 masm.bind(&isDouble);
23802 {
23803 masm.unboxDouble(value, scratchFloat);
23804 masm.jump(&hashDouble);
23805 }
23806
23807 masm.bind(&isNull);
23808 {
23809 masm.loadConstantDouble(1.0, scratchFloat);
23810 masm.jump(&hashDouble);
23811 }
23812
23813 masm.bind(&isUndefined);
23814 {
23815 masm.loadConstantDouble(2.0, scratchFloat);
23816 masm.jump(&hashDouble);
23817 }
23818
23819 masm.bind(&isBoolean);
23820 {
23821 masm.unboxBoolean(value, scratch);
23822 masm.add32(Imm32(3), scratch);
23823 masm.convertInt32ToDouble(scratch, scratchFloat);
23824 masm.jump(&hashDouble);
23825 }
23826
23827 masm.bind(&isBigInt);
23828 {
23829 masm.unboxBigInt(value, scratch);
23830 emitFuzzilliHashBigInt(ins, scratch, output);
23831 masm.jump(&done);
23832 }
23833
23834 masm.bind(&isObject);
23835 {
23836 masm.unboxObject(value, scratch);
23837 emitFuzzilliHashObject(ins, scratch, output);
23838 masm.jump(&done);
23839 }
23840
23841 masm.bind(&hashDouble);
23842 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23843
23844 masm.bind(&done);
23845}
23846
23847void CodeGenerator::visitFuzzilliHashT(LFuzzilliHashT* ins) {
23848 const LAllocation* value = ins->value();
23849 MIRType mirType = ins->mir()->getOperand(0)->type();
23850
23851 Register scratch = ToTempRegisterOrInvalid(ins->temp0());
23852 FloatRegister scratchFloat = ToTempFloatRegisterOrInvalid(ins->temp1());
23853
23854 Register output = ToRegister(ins->output());
23855 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", 23855); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scratch != output" ")"); do { MOZ_CrashSequence
(__null, 23855); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
23856
23857 switch (mirType) {
23858 case MIRType::Undefined: {
23859 masm.loadConstantDouble(2.0, scratchFloat);
23860 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23861 break;
23862 }
23863
23864 case MIRType::Null: {
23865 masm.loadConstantDouble(1.0, scratchFloat);
23866 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23867 break;
23868 }
23869
23870 case MIRType::Int32: {
23871 masm.move32(ToRegister(value), scratch);
23872 masm.convertInt32ToDouble(scratch, scratchFloat);
23873 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23874 break;
23875 }
23876
23877 case MIRType::Double: {
23878 masm.moveDouble(ToFloatRegister(value), scratchFloat);
23879 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23880 break;
23881 }
23882
23883 case MIRType::Float32: {
23884 masm.convertFloat32ToDouble(ToFloatRegister(value), scratchFloat);
23885 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23886 break;
23887 }
23888
23889 case MIRType::Boolean: {
23890 masm.add32(Imm32(3), ToRegister(value), scratch);
23891 masm.convertInt32ToDouble(scratch, scratchFloat);
23892 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
23893 break;
23894 }
23895
23896 case MIRType::BigInt: {
23897 emitFuzzilliHashBigInt(ins, ToRegister(value), output);
23898 break;
23899 }
23900
23901 case MIRType::Object: {
23902 emitFuzzilliHashObject(ins, ToRegister(value), output);
23903 break;
23904 }
23905
23906 default:
23907 MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type"
, "./../../../../js/src/jit/CodeGenerator.cpp", 23907); AnnotateMozCrashReason
("MOZ_CRASH(" "unexpected type" ")"); do { MOZ_CrashSequence(
__null, 23907); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
23908 }
23909}
23910
23911void CodeGenerator::visitFuzzilliHashStore(LFuzzilliHashStore* ins) {
23912 Register value = ToRegister(ins->value());
23913 Register temp0 = ToRegister(ins->temp0());
23914 Register temp1 = ToRegister(ins->temp1());
23915
23916 masm.fuzzilliStoreHash(value, temp0, temp1);
23917}
23918#endif
23919
23920static_assert(!std::is_polymorphic_v<CodeGenerator>,
23921 "CodeGenerator should not have any virtual methods");
23922
23923} // namespace jit
23924} // namespace js