Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp
Warning:line 12469, 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 -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=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/jit -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/jit -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /var/lib/jenkins/workspace/firefox-scan-build/js/src/jit -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/jit -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src -I /var/lib/jenkins/workspace/firefox-scan-build/js/src -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-07-27-022226-2793976-1 -x c++ Unified_cpp_js_src_jit3.cpp
1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7#include "jit/CodeGenerator.h"
8
9#include "mozilla/Assertions.h"
10#include "mozilla/Casting.h"
11#include "mozilla/DebugOnly.h"
12#include "mozilla/EndianUtils.h"
13#include "mozilla/EnumeratedArray.h"
14#include "mozilla/EnumeratedRange.h"
15#include "mozilla/EnumSet.h"
16#include "mozilla/IntegerTypeTraits.h"
17#include "mozilla/Latin1.h"
18#include "mozilla/MathAlgorithms.h"
19#include "mozilla/ScopeExit.h"
20#include "mozilla/SIMD.h"
21
22#include <limits>
23#include <type_traits>
24#include <utility>
25
26#include "jslibmath.h"
27#include "jsmath.h"
28#include "jsnum.h"
29
30#include "builtin/MapObject.h"
31#include "builtin/RegExp.h"
32#include "builtin/String.h"
33#include "irregexp/RegExpTypes.h"
34#include "jit/ABIArgGenerator.h"
35#include "jit/CompileInfo.h"
36#include "jit/InlineScriptTree.h"
37#include "jit/Invalidation.h"
38#include "jit/IonGenericCallStub.h"
39#include "jit/IonIC.h"
40#include "jit/IonScript.h"
41#include "jit/JitcodeMap.h"
42#include "jit/JitFrames.h"
43#include "jit/JitRuntime.h"
44#include "jit/JitSpewer.h"
45#include "jit/JitZone.h"
46#include "jit/Linker.h"
47#include "jit/MIRGenerator.h"
48#include "jit/MoveEmitter.h"
49#include "jit/RangeAnalysis.h"
50#include "jit/RegExpStubConstants.h"
51#include "jit/SafepointIndex.h"
52#include "jit/SharedICHelpers.h"
53#include "jit/SharedICRegisters.h"
54#include "jit/VMFunctions.h"
55#include "jit/WarpSnapshot.h"
56#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
57#include "js/experimental/JitInfo.h" // JSJit{Getter,Setter}CallArgs, JSJitMethodCallArgsTraits, JSJitInfo
58#include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
59#include "js/RegExpFlags.h" // JS::RegExpFlag
60#include "js/ScalarType.h" // js::Scalar::Type
61#include "proxy/DOMProxy.h"
62#include "proxy/ScriptedProxyHandler.h"
63#include "util/CheckedArithmetic.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/FunctionFlags.h" // js::FunctionFlags
70#include "vm/Interpreter.h"
71#include "vm/JSAtomUtils.h" // AtomizeString
72#include "vm/MatchPairs.h"
73#include "vm/RegExpObject.h"
74#include "vm/RegExpStatics.h"
75#include "vm/StaticStrings.h"
76#include "vm/StringObject.h"
77#include "vm/StringType.h"
78#include "vm/TypedArrayObject.h"
79#include "wasm/WasmCodegenConstants.h"
80#include "wasm/WasmPI.h"
81#include "wasm/WasmValType.h"
82#ifdef MOZ_VTUNE1
83# include "vtune/VTuneWrapper.h"
84#endif
85#include "wasm/WasmBinary.h"
86#include "wasm/WasmGC.h"
87#include "wasm/WasmGcObject.h"
88#include "wasm/WasmStubs.h"
89
90#include "builtin/Boolean-inl.h"
91#include "jit/MacroAssembler-inl.h"
92#include "jit/shared/CodeGenerator-shared-inl.h"
93#include "jit/TemplateObject-inl.h"
94#include "jit/VMFunctionList-inl.h"
95#include "vm/JSScript-inl.h"
96#include "wasm/WasmInstance-inl.h"
97
98using namespace js;
99using namespace js::jit;
100
101using JS::GenericNaN;
102using mozilla::AssertedCast;
103using mozilla::CheckedUint32;
104using mozilla::DebugOnly;
105using mozilla::FloatingPoint;
106using mozilla::Maybe;
107using mozilla::NegativeInfinity;
108using mozilla::PositiveInfinity;
109
110using JS::ExpandoAndGeneration;
111
112namespace js {
113namespace jit {
114
115#ifdef CHECK_OSIPOINT_REGISTERS1
116template <class Op>
117static void HandleRegisterDump(Op op, MacroAssembler& masm,
118 LiveRegisterSet liveRegs, Register activation,
119 Register scratch) {
120 const size_t baseOffset = JitActivation::offsetOfRegs();
121
122 // Handle live GPRs.
123 for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) {
124 Register reg = *iter;
125 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
126
127 if (reg == activation) {
128 // To use the original value of the activation register (that's
129 // now on top of the stack), we need the scratch register.
130 masm.push(scratch);
131 masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch);
132 op(scratch, dump);
133 masm.pop(scratch);
134 } else {
135 op(reg, dump);
136 }
137 }
138
139 // Handle live FPRs.
140 for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) {
141 FloatRegister reg = *iter;
142 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
143 op(reg, dump);
144 }
145}
146
147class StoreOp {
148 MacroAssembler& masm;
149
150 public:
151 explicit StoreOp(MacroAssembler& masm) : masm(masm) {}
152
153 void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); }
154 void operator()(FloatRegister reg, Address dump) {
155 if (reg.isDouble()) {
156 masm.storeDouble(reg, dump);
157 } else if (reg.isSingle()) {
158 masm.storeFloat32(reg, dump);
159 } else if (reg.isSimd128()) {
160 MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 160); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD"
")"); do { *((volatile int*)__null) = 160; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
161 } else {
162 MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 162); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type."
")"); do { *((volatile int*)__null) = 162; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
163 }
164 }
165};
166
167class VerifyOp {
168 MacroAssembler& masm;
169 Label* failure_;
170
171 public:
172 VerifyOp(MacroAssembler& masm, Label* failure)
173 : masm(masm), failure_(failure) {}
174
175 void operator()(Register reg, Address dump) {
176 masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
177 }
178 void operator()(FloatRegister reg, Address dump) {
179 if (reg.isDouble()) {
180 ScratchDoubleScope scratch(masm);
181 masm.loadDouble(dump, scratch);
182 masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_);
183 } else if (reg.isSingle()) {
184 ScratchFloat32Scope scratch(masm);
185 masm.loadFloat32(dump, scratch);
186 masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_);
187 } else if (reg.isSimd128()) {
188 MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 188); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD"
")"); do { *((volatile int*)__null) = 188; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
189 } else {
190 MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 190); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type."
")"); do { *((volatile int*)__null) = 190; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
191 }
192 }
193};
194
195void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) {
196 // Ensure the live registers stored by callVM did not change between
197 // the call and this OsiPoint. Try-catch relies on this invariant.
198
199 // Load pointer to the JitActivation in a scratch register.
200 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
201 Register scratch = allRegs.takeAny();
202 masm.push(scratch);
203 masm.loadJitActivation(scratch);
204
205 // If we should not check registers (because the instruction did not call
206 // into the VM, or a GC happened), we're done.
207 Label failure, done;
208 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
209 masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
210
211 // Having more than one VM function call made in one visit function at
212 // runtime is a sec-ciritcal error, because if we conservatively assume that
213 // one of the function call can re-enter Ion, then the invalidation process
214 // will potentially add a call at a random location, by patching the code
215 // before the return address.
216 masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
217
218 // Set checkRegs to 0, so that we don't try to verify registers after we
219 // return from this script to the caller.
220 masm.store32(Imm32(0), checkRegs);
221
222 // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
223 // temps after calling into the VM. This is fine because no other
224 // instructions (including this OsiPoint) will depend on them. Also
225 // backtracking can also use the same register for an input and an output.
226 // These are marked as clobbered and shouldn't get checked.
227 LiveRegisterSet liveRegs;
228 liveRegs.set() = RegisterSet::Intersect(
229 safepoint->liveRegs().set(),
230 RegisterSet::Not(safepoint->clobberedRegs().set()));
231
232 VerifyOp op(masm, &failure);
233 HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
234
235 masm.jump(&done);
236
237 // Do not profile the callWithABI that occurs below. This is to avoid a
238 // rare corner case that occurs when profiling interacts with itself:
239 //
240 // When slow profiling assertions are turned on, FunctionBoundary ops
241 // (which update the profiler pseudo-stack) may emit a callVM, which
242 // forces them to have an osi point associated with them. The
243 // FunctionBoundary for inline function entry is added to the caller's
244 // graph with a PC from the caller's code, but during codegen it modifies
245 // Gecko Profiler instrumentation to add the callee as the current top-most
246 // script. When codegen gets to the OSIPoint, and the callWithABI below is
247 // emitted, the codegen thinks that the current frame is the callee, but
248 // the PC it's using from the OSIPoint refers to the caller. This causes
249 // the profiler instrumentation of the callWithABI below to ASSERT, since
250 // the script and pc are mismatched. To avoid this, we simply omit
251 // instrumentation for these callWithABIs.
252
253 // Any live register captured by a safepoint (other than temp registers)
254 // must remain unchanged between the call and the OsiPoint instruction.
255 masm.bind(&failure);
256 masm.assumeUnreachable("Modified registers between VM call and OsiPoint");
257
258 masm.bind(&done);
259 masm.pop(scratch);
260}
261
262bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) {
263 if (!checkOsiPointRegisters) {
264 return false;
265 }
266
267 if (safepoint->liveRegs().emptyGeneral() &&
268 safepoint->liveRegs().emptyFloat()) {
269 return false; // No registers to check.
270 }
271
272 return true;
273}
274
275void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) {
276 if (!shouldVerifyOsiPointRegs(safepoint)) {
277 return;
278 }
279
280 // Set checkRegs to 0. If we perform a VM call, the instruction
281 // will set it to 1.
282 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
283 Register scratch = allRegs.takeAny();
284 masm.push(scratch);
285 masm.loadJitActivation(scratch);
286 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
287 masm.store32(Imm32(0), checkRegs);
288 masm.pop(scratch);
289}
290
291static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) {
292 // Store a copy of all live registers before performing the call.
293 // When we reach the OsiPoint, we can use this to check nothing
294 // modified them in the meantime.
295
296 // Load pointer to the JitActivation in a scratch register.
297 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
298 Register scratch = allRegs.takeAny();
299 masm.push(scratch);
300 masm.loadJitActivation(scratch);
301
302 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
303 masm.add32(Imm32(1), checkRegs);
304
305 StoreOp op(masm);
306 HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
307
308 masm.pop(scratch);
309}
310#endif // CHECK_OSIPOINT_REGISTERS
311
312// Before doing any call to Cpp, you should ensure that volatile
313// registers are evicted by the register allocator.
314void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) {
315 TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id);
316 const VMFunctionData& fun = GetVMFunction(id);
317
318 // Stack is:
319 // ... frame ...
320 // [args]
321#ifdef DEBUG1
322 MOZ_ASSERT(pushedArgs_ == fun.explicitArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pushedArgs_ == fun.explicitArgs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pushedArgs_ == fun.explicitArgs
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pushedArgs_ == fun.explicitArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 322); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs"
")"); do { *((volatile int*)__null) = 322; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
323 pushedArgs_ = 0;
324#endif
325
326#ifdef CHECK_OSIPOINT_REGISTERS1
327 if (shouldVerifyOsiPointRegs(ins->safepoint())) {
328 StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
329 }
330#endif
331
332#ifdef DEBUG1
333 if (ins->mirRaw()) {
334 MOZ_ASSERT(ins->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mirRaw()->isInstruction())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mirRaw()->isInstruction
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ins->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()"
")"); do { *((volatile int*)__null) = 334; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
335 MInstruction* mir = ins->mirRaw()->toInstruction();
336 MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint())do { if (mir->needsResumePoint()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(mir->resumePoint
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mir->resumePoint()))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("mir->resumePoint()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 336); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()"
")"); do { *((volatile int*)__null) = 336; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
337
338 // If this MIR instruction has an overridden AliasSet, set the JitRuntime's
339 // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call
340 // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because
341 // interrupt callbacks can call JS (chrome JS or shell testing functions).
342 bool isWhitelisted = mir->isInterruptCheck() || mir->isCheckOverRecursed();
343 if (!mir->hasDefaultAliasSet() && !isWhitelisted) {
344 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
345 masm.move32(Imm32(1), ReturnReg);
346 masm.store32(ReturnReg, AbsoluteAddress(addr));
347 }
348 }
349#endif
350
351 // Push an exit frame descriptor.
352 masm.PushFrameDescriptor(FrameType::IonJS);
353
354 // Call the wrapper function. The wrapper is in charge to unwind the stack
355 // when returning from the call. Failures are handled with exceptions based
356 // on the return value of the C functions. To guard the outcome of the
357 // returned value, use another LIR instruction.
358 ensureOsiSpace();
359 uint32_t callOffset = masm.callJit(code);
360 markSafepointAt(callOffset, ins);
361
362#ifdef DEBUG1
363 // Reset the disallowArbitraryCode flag after the call.
364 {
365 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
366 masm.push(ReturnReg);
367 masm.move32(Imm32(0), ReturnReg);
368 masm.store32(ReturnReg, AbsoluteAddress(addr));
369 masm.pop(ReturnReg);
370 }
371#endif
372
373 // Pop rest of the exit frame and the arguments left on the stack.
374 int framePop =
375 sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall();
376 masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop);
377
378 // Stack is:
379 // ... frame ...
380}
381
382template <typename Fn, Fn fn>
383void CodeGenerator::callVM(LInstruction* ins) {
384 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
385 callVMInternal(id, ins);
386}
387
388// ArgSeq store arguments for OutOfLineCallVM.
389//
390// OutOfLineCallVM are created with "oolCallVM" function. The third argument of
391// this function is an instance of a class which provides a "generate" in charge
392// of pushing the argument, with "pushArg", for a VMFunction.
393//
394// Such list of arguments can be created by using the "ArgList" function which
395// creates one instance of "ArgSeq", where the type of the arguments are
396// inferred from the type of the arguments.
397//
398// The list of arguments must be written in the same order as if you were
399// calling the function in C++.
400//
401// Example:
402// ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs()))
403
404template <typename... ArgTypes>
405class ArgSeq {
406 std::tuple<std::remove_reference_t<ArgTypes>...> args_;
407
408 template <std::size_t... ISeq>
409 inline void generate(CodeGenerator* codegen,
410 std::index_sequence<ISeq...>) const {
411 // Arguments are pushed in reverse order, from last argument to first
412 // argument.
413 (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...);
414 }
415
416 public:
417 explicit ArgSeq(ArgTypes&&... args)
418 : args_(std::forward<ArgTypes>(args)...) {}
419
420 inline void generate(CodeGenerator* codegen) const {
421 generate(codegen, std::index_sequence_for<ArgTypes...>{});
422 }
423
424#ifdef DEBUG1
425 static constexpr size_t numArgs = sizeof...(ArgTypes);
426#endif
427};
428
429template <typename... ArgTypes>
430inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) {
431 return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...);
432}
433
434// Store wrappers, to generate the right move of data after the VM call.
435
436struct StoreNothing {
437 inline void generate(CodeGenerator* codegen) const {}
438 inline LiveRegisterSet clobbered() const {
439 return LiveRegisterSet(); // No register gets clobbered
440 }
441};
442
443class StoreRegisterTo {
444 private:
445 Register out_;
446
447 public:
448 explicit StoreRegisterTo(Register out) : out_(out) {}
449
450 inline void generate(CodeGenerator* codegen) const {
451 // It's okay to use storePointerResultTo here - the VMFunction wrapper
452 // ensures the upper bytes are zero for bool/int32 return values.
453 codegen->storePointerResultTo(out_);
454 }
455 inline LiveRegisterSet clobbered() const {
456 LiveRegisterSet set;
457 set.add(out_);
458 return set;
459 }
460};
461
462class StoreFloatRegisterTo {
463 private:
464 FloatRegister out_;
465
466 public:
467 explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {}
468
469 inline void generate(CodeGenerator* codegen) const {
470 codegen->storeFloatResultTo(out_);
471 }
472 inline LiveRegisterSet clobbered() const {
473 LiveRegisterSet set;
474 set.add(out_);
475 return set;
476 }
477};
478
479template <typename Output>
480class StoreValueTo_ {
481 private:
482 Output out_;
483
484 public:
485 explicit StoreValueTo_(const Output& out) : out_(out) {}
486
487 inline void generate(CodeGenerator* codegen) const {
488 codegen->storeResultValueTo(out_);
489 }
490 inline LiveRegisterSet clobbered() const {
491 LiveRegisterSet set;
492 set.add(out_);
493 return set;
494 }
495};
496
497template <typename Output>
498StoreValueTo_<Output> StoreValueTo(const Output& out) {
499 return StoreValueTo_<Output>(out);
500}
501
502template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
503class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> {
504 private:
505 LInstruction* lir_;
506 ArgSeq args_;
507 StoreOutputTo out_;
508
509 public:
510 OutOfLineCallVM(LInstruction* lir, const ArgSeq& args,
511 const StoreOutputTo& out)
512 : lir_(lir), args_(args), out_(out) {}
513
514 void accept(CodeGenerator* codegen) override {
515 codegen->visitOutOfLineCallVM(this);
516 }
517
518 LInstruction* lir() const { return lir_; }
519 const ArgSeq& args() const { return args_; }
520 const StoreOutputTo& out() const { return out_; }
521};
522
523template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
524OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args,
525 const StoreOutputTo& out) {
526 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 526); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()"
")"); do { *((volatile int*)__null) = 526; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
527 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 527); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()"
")"); do { *((volatile int*)__null) = 527; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
528
529#ifdef DEBUG1
530 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
531 const VMFunctionData& fun = GetVMFunction(id);
532 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs"
")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
533 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>)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { *((volatile int*)__null) = 534; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
534 (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>)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { *((volatile int*)__null) = 534; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
535#endif
536
537 OutOfLineCode* ool = new (alloc())
538 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out);
539 addOutOfLineCode(ool, lir->mirRaw()->toInstruction());
540 return ool;
541}
542
543template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
544void CodeGenerator::visitOutOfLineCallVM(
545 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) {
546 LInstruction* lir = ool->lir();
547
548#ifdef JS_JITSPEW1
549 JitSpewStart(JitSpew_Codegen, " # LIR=%s",
550 lir->opName());
551 if (const char* extra = lir->getExtraName()) {
552 JitSpewCont(JitSpew_Codegen, ":%s", extra);
553 }
554 JitSpewFin(JitSpew_Codegen);
555#endif
556 perfSpewer_.recordInstruction(masm, lir);
557 saveLive(lir);
558 ool->args().generate(this);
559 callVM<Fn, fn>(lir);
560 ool->out().generate(this);
561 restoreLiveIgnore(lir, ool->out().clobbered());
562 masm.jump(ool->rejoin());
563}
564
565class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> {
566 private:
567 LInstruction* lir_;
568 size_t cacheIndex_;
569 size_t cacheInfoIndex_;
570
571 public:
572 OutOfLineICFallback(LInstruction* lir, size_t cacheIndex,
573 size_t cacheInfoIndex)
574 : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {}
575
576 void bind(MacroAssembler* masm) override {
577 // The binding of the initial jump is done in
578 // CodeGenerator::visitOutOfLineICFallback.
579 }
580
581 size_t cacheIndex() const { return cacheIndex_; }
582 size_t cacheInfoIndex() const { return cacheInfoIndex_; }
583 LInstruction* lir() const { return lir_; }
584
585 void accept(CodeGenerator* codegen) override {
586 codegen->visitOutOfLineICFallback(this);
587 }
588};
589
590void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) {
591 if (cacheIndex == SIZE_MAX(18446744073709551615UL)) {
592 masm.setOOM();
593 return;
594 }
595
596 DataPtr<IonIC> cache(this, cacheIndex);
597 MInstruction* mir = lir->mirRaw()->toInstruction();
598 cache->setScriptedLocation(mir->block()->info().script(),
599 mir->resumePoint()->pc());
600
601 Register temp = cache->scratchRegisterForEntryJump();
602 icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp);
603 masm.jump(Address(temp, 0));
604
605 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!icInfo_.empty()"
")"); do { *((volatile int*)__null) = 605; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
606
607 OutOfLineICFallback* ool =
608 new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1);
609 addOutOfLineCode(ool, mir);
610
611 masm.bind(ool->rejoin());
612 cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset()));
613}
614
615void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) {
616 LInstruction* lir = ool->lir();
617 size_t cacheIndex = ool->cacheIndex();
618 size_t cacheInfoIndex = ool->cacheInfoIndex();
619
620 DataPtr<IonIC> ic(this, cacheIndex);
621
622 // Register the location of the OOL path in the IC.
623 ic->setFallbackOffset(CodeOffset(masm.currentOffset()));
624
625 switch (ic->kind()) {
626 case CacheKind::GetProp:
627 case CacheKind::GetElem: {
628 IonGetPropertyIC* getPropIC = ic->asGetPropertyIC();
629
630 saveLive(lir);
631
632 pushArg(getPropIC->id());
633 pushArg(getPropIC->value());
634 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
635 pushArg(ImmGCPtr(gen->outerInfo().script()));
636
637 using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*,
638 HandleValue, HandleValue, MutableHandleValue);
639 callVM<Fn, IonGetPropertyIC::update>(lir);
640
641 StoreValueTo(getPropIC->output()).generate(this);
642 restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
643
644 masm.jump(ool->rejoin());
645 return;
646 }
647 case CacheKind::GetPropSuper:
648 case CacheKind::GetElemSuper: {
649 IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC();
650
651 saveLive(lir);
652
653 pushArg(getPropSuperIC->id());
654 pushArg(getPropSuperIC->receiver());
655 pushArg(getPropSuperIC->object());
656 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
657 pushArg(ImmGCPtr(gen->outerInfo().script()));
658
659 using Fn =
660 bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject,
661 HandleValue, HandleValue, MutableHandleValue);
662 callVM<Fn, IonGetPropSuperIC::update>(lir);
663
664 StoreValueTo(getPropSuperIC->output()).generate(this);
665 restoreLiveIgnore(lir,
666 StoreValueTo(getPropSuperIC->output()).clobbered());
667
668 masm.jump(ool->rejoin());
669 return;
670 }
671 case CacheKind::SetProp:
672 case CacheKind::SetElem: {
673 IonSetPropertyIC* setPropIC = ic->asSetPropertyIC();
674
675 saveLive(lir);
676
677 pushArg(setPropIC->rhs());
678 pushArg(setPropIC->id());
679 pushArg(setPropIC->object());
680 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
681 pushArg(ImmGCPtr(gen->outerInfo().script()));
682
683 using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*,
684 HandleObject, HandleValue, HandleValue);
685 callVM<Fn, IonSetPropertyIC::update>(lir);
686
687 restoreLive(lir);
688
689 masm.jump(ool->rejoin());
690 return;
691 }
692 case CacheKind::GetName: {
693 IonGetNameIC* getNameIC = ic->asGetNameIC();
694
695 saveLive(lir);
696
697 pushArg(getNameIC->environment());
698 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
699 pushArg(ImmGCPtr(gen->outerInfo().script()));
700
701 using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject,
702 MutableHandleValue);
703 callVM<Fn, IonGetNameIC::update>(lir);
704
705 StoreValueTo(getNameIC->output()).generate(this);
706 restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered());
707
708 masm.jump(ool->rejoin());
709 return;
710 }
711 case CacheKind::BindName: {
712 IonBindNameIC* bindNameIC = ic->asBindNameIC();
713
714 saveLive(lir);
715
716 pushArg(bindNameIC->environment());
717 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
718 pushArg(ImmGCPtr(gen->outerInfo().script()));
719
720 using Fn =
721 JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject);
722 callVM<Fn, IonBindNameIC::update>(lir);
723
724 StoreRegisterTo(bindNameIC->output()).generate(this);
725 restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered());
726
727 masm.jump(ool->rejoin());
728 return;
729 }
730 case CacheKind::GetIterator: {
731 IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC();
732
733 saveLive(lir);
734
735 pushArg(getIteratorIC->value());
736 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
737 pushArg(ImmGCPtr(gen->outerInfo().script()));
738
739 using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*,
740 HandleValue);
741 callVM<Fn, IonGetIteratorIC::update>(lir);
742
743 StoreRegisterTo(getIteratorIC->output()).generate(this);
744 restoreLiveIgnore(lir,
745 StoreRegisterTo(getIteratorIC->output()).clobbered());
746
747 masm.jump(ool->rejoin());
748 return;
749 }
750 case CacheKind::OptimizeSpreadCall: {
751 auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC();
752
753 saveLive(lir);
754
755 pushArg(optimizeSpreadCallIC->value());
756 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
757 pushArg(ImmGCPtr(gen->outerInfo().script()));
758
759 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*,
760 HandleValue, MutableHandleValue);
761 callVM<Fn, IonOptimizeSpreadCallIC::update>(lir);
762
763 StoreValueTo(optimizeSpreadCallIC->output()).generate(this);
764 restoreLiveIgnore(
765 lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered());
766
767 masm.jump(ool->rejoin());
768 return;
769 }
770 case CacheKind::In: {
771 IonInIC* inIC = ic->asInIC();
772
773 saveLive(lir);
774
775 pushArg(inIC->object());
776 pushArg(inIC->key());
777 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
778 pushArg(ImmGCPtr(gen->outerInfo().script()));
779
780 using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue,
781 HandleObject, bool*);
782 callVM<Fn, IonInIC::update>(lir);
783
784 StoreRegisterTo(inIC->output()).generate(this);
785 restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered());
786
787 masm.jump(ool->rejoin());
788 return;
789 }
790 case CacheKind::HasOwn: {
791 IonHasOwnIC* hasOwnIC = ic->asHasOwnIC();
792
793 saveLive(lir);
794
795 pushArg(hasOwnIC->id());
796 pushArg(hasOwnIC->value());
797 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
798 pushArg(ImmGCPtr(gen->outerInfo().script()));
799
800 using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue,
801 HandleValue, int32_t*);
802 callVM<Fn, IonHasOwnIC::update>(lir);
803
804 StoreRegisterTo(hasOwnIC->output()).generate(this);
805 restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered());
806
807 masm.jump(ool->rejoin());
808 return;
809 }
810 case CacheKind::CheckPrivateField: {
811 IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC();
812
813 saveLive(lir);
814
815 pushArg(checkPrivateFieldIC->id());
816 pushArg(checkPrivateFieldIC->value());
817
818 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
819 pushArg(ImmGCPtr(gen->outerInfo().script()));
820
821 using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*,
822 HandleValue, HandleValue, bool*);
823 callVM<Fn, IonCheckPrivateFieldIC::update>(lir);
824
825 StoreRegisterTo(checkPrivateFieldIC->output()).generate(this);
826 restoreLiveIgnore(
827 lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered());
828
829 masm.jump(ool->rejoin());
830 return;
831 }
832 case CacheKind::InstanceOf: {
833 IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC();
834
835 saveLive(lir);
836
837 pushArg(hasInstanceOfIC->rhs());
838 pushArg(hasInstanceOfIC->lhs());
839 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
840 pushArg(ImmGCPtr(gen->outerInfo().script()));
841
842 using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*,
843 HandleValue lhs, HandleObject rhs, bool* res);
844 callVM<Fn, IonInstanceOfIC::update>(lir);
845
846 StoreRegisterTo(hasInstanceOfIC->output()).generate(this);
847 restoreLiveIgnore(lir,
848 StoreRegisterTo(hasInstanceOfIC->output()).clobbered());
849
850 masm.jump(ool->rejoin());
851 return;
852 }
853 case CacheKind::UnaryArith: {
854 IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC();
855
856 saveLive(lir);
857
858 pushArg(unaryArithIC->input());
859 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
860 pushArg(ImmGCPtr(gen->outerInfo().script()));
861
862 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
863 IonUnaryArithIC* stub, HandleValue val,
864 MutableHandleValue res);
865 callVM<Fn, IonUnaryArithIC::update>(lir);
866
867 StoreValueTo(unaryArithIC->output()).generate(this);
868 restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered());
869
870 masm.jump(ool->rejoin());
871 return;
872 }
873 case CacheKind::ToPropertyKey: {
874 IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC();
875
876 saveLive(lir);
877
878 pushArg(toPropertyKeyIC->input());
879 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
880 pushArg(ImmGCPtr(gen->outerInfo().script()));
881
882 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
883 IonToPropertyKeyIC* ic, HandleValue val,
884 MutableHandleValue res);
885 callVM<Fn, IonToPropertyKeyIC::update>(lir);
886
887 StoreValueTo(toPropertyKeyIC->output()).generate(this);
888 restoreLiveIgnore(lir,
889 StoreValueTo(toPropertyKeyIC->output()).clobbered());
890
891 masm.jump(ool->rejoin());
892 return;
893 }
894 case CacheKind::BinaryArith: {
895 IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC();
896
897 saveLive(lir);
898
899 pushArg(binaryArithIC->rhs());
900 pushArg(binaryArithIC->lhs());
901 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
902 pushArg(ImmGCPtr(gen->outerInfo().script()));
903
904 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
905 IonBinaryArithIC* stub, HandleValue lhs,
906 HandleValue rhs, MutableHandleValue res);
907 callVM<Fn, IonBinaryArithIC::update>(lir);
908
909 StoreValueTo(binaryArithIC->output()).generate(this);
910 restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered());
911
912 masm.jump(ool->rejoin());
913 return;
914 }
915 case CacheKind::Compare: {
916 IonCompareIC* compareIC = ic->asCompareIC();
917
918 saveLive(lir);
919
920 pushArg(compareIC->rhs());
921 pushArg(compareIC->lhs());
922 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
923 pushArg(ImmGCPtr(gen->outerInfo().script()));
924
925 using Fn =
926 bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
927 HandleValue lhs, HandleValue rhs, bool* res);
928 callVM<Fn, IonCompareIC::update>(lir);
929
930 StoreRegisterTo(compareIC->output()).generate(this);
931 restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered());
932
933 masm.jump(ool->rejoin());
934 return;
935 }
936 case CacheKind::CloseIter: {
937 IonCloseIterIC* closeIterIC = ic->asCloseIterIC();
938
939 saveLive(lir);
940
941 pushArg(closeIterIC->iter());
942 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
943 pushArg(ImmGCPtr(gen->outerInfo().script()));
944
945 using Fn =
946 bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject);
947 callVM<Fn, IonCloseIterIC::update>(lir);
948
949 restoreLive(lir);
950
951 masm.jump(ool->rejoin());
952 return;
953 }
954 case CacheKind::OptimizeGetIterator: {
955 auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC();
956
957 saveLive(lir);
958
959 pushArg(optimizeGetIteratorIC->value());
960 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
961 pushArg(ImmGCPtr(gen->outerInfo().script()));
962
963 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*,
964 HandleValue, bool* res);
965 callVM<Fn, IonOptimizeGetIteratorIC::update>(lir);
966
967 StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this);
968 restoreLiveIgnore(
969 lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered());
970
971 masm.jump(ool->rejoin());
972 return;
973 }
974 case CacheKind::Call:
975 case CacheKind::TypeOf:
976 case CacheKind::TypeOfEq:
977 case CacheKind::ToBool:
978 case CacheKind::GetIntrinsic:
979 case CacheKind::NewArray:
980 case CacheKind::NewObject:
981 MOZ_CRASH("Unsupported IC")do { do { } while (false); MOZ_ReportCrash("" "Unsupported IC"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 981); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported IC" ")"
); do { *((volatile int*)__null) = 981; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
982 }
983 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 983); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 983; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
984}
985
986StringObject* MNewStringObject::templateObj() const {
987 return &templateObj_->as<StringObject>();
988}
989
990CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph,
991 MacroAssembler* masm)
992 : CodeGeneratorSpecific(gen, graph, masm),
993 ionScriptLabels_(gen->alloc()),
994 ionNurseryObjectLabels_(gen->alloc()),
995 scriptCounts_(nullptr),
996 zoneStubsToReadBarrier_(0) {}
997
998CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); }
999
1000void CodeGenerator::visitValueToInt32(LValueToInt32* lir) {
1001 ValueOperand operand = ToValue(lir, LValueToInt32::Input);
1002 Register output = ToRegister(lir->output());
1003 FloatRegister temp = ToFloatRegister(lir->tempFloat());
1004
1005 Label fails;
1006 if (lir->mode() == LValueToInt32::TRUNCATE) {
1007 OutOfLineCode* oolDouble = oolTruncateDouble(temp, output, lir->mir());
1008
1009 // We can only handle strings in truncation contexts, like bitwise
1010 // operations.
1011 Register stringReg = ToRegister(lir->temp());
1012 using Fn = bool (*)(JSContext*, JSString*, double*);
1013 auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg),
1014 StoreFloatRegisterTo(temp));
1015 Label* stringEntry = oolString->entry();
1016 Label* stringRejoin = oolString->rejoin();
1017
1018 masm.truncateValueToInt32(operand, stringEntry, stringRejoin,
1019 oolDouble->entry(), stringReg, temp, output,
1020 &fails);
1021 masm.bind(oolDouble->rejoin());
1022 } else {
1023 MOZ_ASSERT(lir->mode() == LValueToInt32::NORMAL)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mode() == LValueToInt32::NORMAL)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mode() == LValueToInt32::NORMAL))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("lir->mode() == LValueToInt32::NORMAL"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1023); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mode() == LValueToInt32::NORMAL"
")"); do { *((volatile int*)__null) = 1023; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1024 masm.convertValueToInt32(operand, temp, output, &fails,
1025 lir->mirNormal()->needsNegativeZeroCheck(),
1026 lir->mirNormal()->conversion());
1027 }
1028
1029 bailoutFrom(&fails, lir->snapshot());
1030}
1031
1032void CodeGenerator::visitValueToDouble(LValueToDouble* lir) {
1033 ValueOperand operand = ToValue(lir, LValueToDouble::InputIndex);
1034 FloatRegister output = ToFloatRegister(lir->output());
1035
1036 Label fail;
1037 masm.convertValueToDouble(operand, output, &fail);
1038 bailoutFrom(&fail, lir->snapshot());
1039}
1040
1041void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) {
1042 ValueOperand operand = ToValue(lir, LValueToFloat32::InputIndex);
1043 FloatRegister output = ToFloatRegister(lir->output());
1044
1045 Label fail;
1046 masm.convertValueToFloat32(operand, output, &fail);
1047 bailoutFrom(&fail, lir->snapshot());
1048}
1049
1050void CodeGenerator::visitValueToFloat16(LValueToFloat16* lir) {
1051 ValueOperand operand = ToValue(lir, LValueToFloat16::InputIndex);
1052 Register temp = ToTempRegisterOrInvalid(lir->temp0());
1053 FloatRegister output = ToFloatRegister(lir->output());
1054
1055 LiveRegisterSet volatileRegs;
1056 if (!MacroAssembler::SupportsFloat64To16()) {
1057 volatileRegs = liveVolatileRegs(lir);
1058 }
1059
1060 Label fail;
1061 masm.convertValueToFloat16(operand, output, temp, volatileRegs, &fail);
1062 bailoutFrom(&fail, lir->snapshot());
1063}
1064
1065void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) {
1066 ValueOperand operand = ToValue(lir, LValueToBigInt::InputIndex);
1067 Register output = ToRegister(lir->output());
1068
1069 using Fn = BigInt* (*)(JSContext*, HandleValue);
1070 auto* ool =
1071 oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output));
1072
1073 Register tag = masm.extractTag(operand, output);
1074
1075 Label notBigInt, done;
1076 masm.branchTestBigInt(Assembler::NotEqual, tag, &notBigInt);
1077 masm.unboxBigInt(operand, output);
1078 masm.jump(&done);
1079 masm.bind(&notBigInt);
1080
1081 masm.branchTestBoolean(Assembler::Equal, tag, ool->entry());
1082 masm.branchTestString(Assembler::Equal, tag, ool->entry());
1083
1084 // ToBigInt(object) can have side-effects; all other types throw a TypeError.
1085 bailout(lir->snapshot());
1086
1087 masm.bind(ool->rejoin());
1088 masm.bind(&done);
1089}
1090
1091void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) {
1092 masm.convertInt32ToDouble(ToRegister(lir->input()),
1093 ToFloatRegister(lir->output()));
1094}
1095
1096void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) {
1097 masm.convertFloat32ToDouble(ToFloatRegister(lir->input()),
1098 ToFloatRegister(lir->output()));
1099}
1100
1101void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) {
1102 masm.convertDoubleToFloat32(ToFloatRegister(lir->input()),
1103 ToFloatRegister(lir->output()));
1104}
1105
1106void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) {
1107 masm.convertInt32ToFloat32(ToRegister(lir->input()),
1108 ToFloatRegister(lir->output()));
1109}
1110
1111void CodeGenerator::visitDoubleToFloat16(LDoubleToFloat16* lir) {
1112 LiveRegisterSet volatileRegs;
1113 if (!MacroAssembler::SupportsFloat64To16()) {
1114 volatileRegs = liveVolatileRegs(lir);
1115 }
1116 masm.convertDoubleToFloat16(
1117 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1118 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1119}
1120
1121void CodeGenerator::visitFloat32ToFloat16(LFloat32ToFloat16* lir) {
1122 LiveRegisterSet volatileRegs;
1123 if (!MacroAssembler::SupportsFloat32To16()) {
1124 volatileRegs = liveVolatileRegs(lir);
1125 }
1126 masm.convertFloat32ToFloat16(
1127 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1128 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1129}
1130
1131void CodeGenerator::visitInt32ToFloat16(LInt32ToFloat16* lir) {
1132 LiveRegisterSet volatileRegs;
1133 if (!MacroAssembler::SupportsFloat32To16()) {
1134 volatileRegs = liveVolatileRegs(lir);
1135 }
1136 masm.convertInt32ToFloat16(
1137 ToRegister(lir->input()), ToFloatRegister(lir->output()),
1138 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1139}
1140
1141void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) {
1142 Label fail;
1143 FloatRegister input = ToFloatRegister(lir->input());
1144 Register output = ToRegister(lir->output());
1145 masm.convertDoubleToInt32(input, output, &fail,
1146 lir->mir()->needsNegativeZeroCheck());
1147 bailoutFrom(&fail, lir->snapshot());
1148}
1149
1150void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) {
1151 Label fail;
1152 FloatRegister input = ToFloatRegister(lir->input());
1153 Register output = ToRegister(lir->output());
1154 masm.convertFloat32ToInt32(input, output, &fail,
1155 lir->mir()->needsNegativeZeroCheck());
1156 bailoutFrom(&fail, lir->snapshot());
1157}
1158
1159void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) {
1160#ifdef JS_64BIT1
1161 // This LIR instruction is only used if the input can be negative.
1162 MOZ_ASSERT(lir->mir()->canBeNegative())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->canBeNegative())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mir()->canBeNegative
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->mir()->canBeNegative()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1162); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->canBeNegative()"
")"); do { *((volatile int*)__null) = 1162; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1163
1164 Register output = ToRegister(lir->output());
1165 const LAllocation* input = lir->input();
1166 if (input->isRegister()) {
1167 masm.move32SignExtendToPtr(ToRegister(input), output);
1168 } else {
1169 masm.load32SignExtendToPtr(ToAddress(input), output);
1170 }
1171#else
1172 MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1172); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms"
")"); do { *((volatile int*)__null) = 1172; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1173#endif
1174}
1175
1176void CodeGenerator::visitNonNegativeIntPtrToInt32(
1177 LNonNegativeIntPtrToInt32* lir) {
1178#ifdef JS_64BIT1
1179 Register output = ToRegister(lir->output());
1180 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1180); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output"
")"); do { *((volatile int*)__null) = 1180; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1181
1182 Label bail;
1183 masm.guardNonNegativeIntPtrToInt32(output, &bail);
1184 bailoutFrom(&bail, lir->snapshot());
1185#else
1186 MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1186); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms"
")"); do { *((volatile int*)__null) = 1186; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1187#endif
1188}
1189
1190void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) {
1191 Register input = ToRegister(lir->input());
1192 FloatRegister output = ToFloatRegister(lir->output());
1193 masm.convertIntPtrToDouble(input, output);
1194}
1195
1196void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) {
1197 Register output = ToRegister(lir->output());
1198 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output"
")"); do { *((volatile int*)__null) = 1198; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1199
1200 uint32_t byteSize = lir->mir()->byteSize();
1201
1202#ifdef DEBUG1
1203 Label ok;
1204 masm.branchTestPtr(Assembler::NotSigned, output, output, &ok);
1205 masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength");
1206 masm.bind(&ok);
1207#endif
1208
1209 Label bail;
1210 masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail);
1211 bailoutFrom(&bail, lir->snapshot());
1212}
1213
1214void CodeGenerator::emitOOLTestObject(Register objreg,
1215 Label* ifEmulatesUndefined,
1216 Label* ifDoesntEmulateUndefined,
1217 Register scratch) {
1218 saveVolatile(scratch);
1219#if defined(DEBUG1) || defined(FUZZING)
1220 masm.loadPtr(AbsoluteAddress(
1221 gen->runtime->addressOfHasSeenObjectEmulateUndefinedFuse()),
1222 scratch);
1223 using Fn = bool (*)(JSObject* obj, size_t fuseValue);
1224 masm.setupAlignedABICall();
1225 masm.passABIArg(objreg);
1226 masm.passABIArg(scratch);
1227 masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>();
1228#else
1229 using Fn = bool (*)(JSObject* obj);
1230 masm.setupAlignedABICall();
1231 masm.passABIArg(objreg);
1232 masm.callWithABI<Fn, js::EmulatesUndefined>();
1233#endif
1234 masm.storeCallPointerResult(scratch);
1235 restoreVolatile(scratch);
1236
1237 masm.branchIfTrueBool(scratch, ifEmulatesUndefined);
1238 masm.jump(ifDoesntEmulateUndefined);
1239}
1240
1241// Base out-of-line code generator for all tests of the truthiness of an
1242// object, where the object might not be truthy. (Recall that per spec all
1243// objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class
1244// flag to permit objects to look like |undefined| in certain contexts,
1245// including in object truthiness testing.) We check truthiness inline except
1246// when we're testing it on a proxy, in which case out-of-line code will call
1247// EmulatesUndefined for a conclusive answer.
1248class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> {
1249 Register objreg_;
1250 Register scratch_;
1251
1252 Label* ifEmulatesUndefined_;
1253 Label* ifDoesntEmulateUndefined_;
1254
1255#ifdef DEBUG1
1256 bool initialized() { return ifEmulatesUndefined_ != nullptr; }
1257#endif
1258
1259 public:
1260 OutOfLineTestObject()
1261 : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {}
1262
1263 void accept(CodeGenerator* codegen) final {
1264 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()",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1264); AnnotateMozCrashReason("MOZ_ASSERT" "(" "initialized()"
")"); do { *((volatile int*)__null) = 1264; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1265 codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_,
1266 ifDoesntEmulateUndefined_, scratch_);
1267 }
1268
1269 // Specify the register where the object to be tested is found, labels to
1270 // jump to if the object is truthy or falsy, and a scratch register for
1271 // use in the out-of-line path.
1272 void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined,
1273 Label* ifDoesntEmulateUndefined, Register scratch) {
1274 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1274); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!initialized()"
")"); do { *((volatile int*)__null) = 1274; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1275 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ifEmulatesUndefined"
")"); do { *((volatile int*)__null) = 1275; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1276 objreg_ = objreg;
1277 scratch_ = scratch;
1278 ifEmulatesUndefined_ = ifEmulatesUndefined;
1279 ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined;
1280 }
1281};
1282
1283// A subclass of OutOfLineTestObject containing two extra labels, for use when
1284// the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line
1285// code. The user should bind these labels in inline code, and specify them as
1286// targets via setInputAndTargets, as appropriate.
1287class OutOfLineTestObjectWithLabels : public OutOfLineTestObject {
1288 Label label1_;
1289 Label label2_;
1290
1291 public:
1292 OutOfLineTestObjectWithLabels() = default;
1293
1294 Label* label1() { return &label1_; }
1295 Label* label2() { return &label2_; }
1296};
1297
1298void CodeGenerator::testObjectEmulatesUndefinedKernel(
1299 Register objreg, Label* ifEmulatesUndefined,
1300 Label* ifDoesntEmulateUndefined, Register scratch,
1301 OutOfLineTestObject* ool) {
1302 ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
1303 scratch);
1304
1305 // Perform a fast-path check of the object's class flags if the object's
1306 // not a proxy. Let out-of-line code handle the slow cases that require
1307 // saving registers, making a function call, and restoring registers.
1308 masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(),
1309 ifEmulatesUndefined);
1310}
1311
1312void CodeGenerator::branchTestObjectEmulatesUndefined(
1313 Register objreg, Label* ifEmulatesUndefined,
1314 Label* ifDoesntEmulateUndefined, Register scratch,
1315 OutOfLineTestObject* ool) {
1316 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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()"
") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { *((volatile int*)__null) = 1317; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1317 "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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()"
") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { *((volatile int*)__null) = 1317; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1318
1319 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1320 ifDoesntEmulateUndefined, scratch, ool);
1321 masm.bind(ifDoesntEmulateUndefined);
1322}
1323
1324void CodeGenerator::testObjectEmulatesUndefined(Register objreg,
1325 Label* ifEmulatesUndefined,
1326 Label* ifDoesntEmulateUndefined,
1327 Register scratch,
1328 OutOfLineTestObject* ool) {
1329 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1330 ifDoesntEmulateUndefined, scratch, ool);
1331 masm.jump(ifDoesntEmulateUndefined);
1332}
1333
1334void CodeGenerator::testValueTruthyForType(
1335 JSValueType type, ScratchTagScope& tag, const ValueOperand& value,
1336 Register tempToUnbox, Register temp, FloatRegister floatTemp,
1337 Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool,
1338 bool skipTypeTest) {
1339#ifdef DEBUG1
1340 if (skipTypeTest) {
1341 Label expected;
1342 masm.branchTestType(Assembler::Equal, tag, type, &expected);
1343 masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType");
1344 masm.bind(&expected);
1345 }
1346#endif
1347
1348 // Handle irregular types first.
1349 switch (type) {
1350 case JSVAL_TYPE_UNDEFINED:
1351 case JSVAL_TYPE_NULL:
1352 // Undefined and null are falsy.
1353 if (!skipTypeTest) {
1354 masm.branchTestType(Assembler::Equal, tag, type, ifFalsy);
1355 } else {
1356 masm.jump(ifFalsy);
1357 }
1358 return;
1359 case JSVAL_TYPE_SYMBOL:
1360 // Symbols are truthy.
1361 if (!skipTypeTest) {
1362 masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy);
1363 } else {
1364 masm.jump(ifTruthy);
1365 }
1366 return;
1367 case JSVAL_TYPE_OBJECT: {
1368 Label notObject;
1369 if (!skipTypeTest) {
1370 masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
1371 }
1372 ScratchTagScopeRelease _(&tag);
1373 Register objreg = masm.extractObject(value, tempToUnbox);
1374 testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool);
1375 masm.bind(&notObject);
1376 return;
1377 }
1378 default:
1379 break;
1380 }
1381
1382 // Check the type of the value (unless this is the last possible type).
1383 Label differentType;
1384 if (!skipTypeTest) {
1385 masm.branchTestType(Assembler::NotEqual, tag, type, &differentType);
1386 }
1387
1388 // Branch if the value is falsy.
1389 ScratchTagScopeRelease _(&tag);
1390 switch (type) {
1391 case JSVAL_TYPE_BOOLEAN: {
1392 masm.branchTestBooleanTruthy(false, value, ifFalsy);
1393 break;
1394 }
1395 case JSVAL_TYPE_INT32: {
1396 masm.branchTestInt32Truthy(false, value, ifFalsy);
1397 break;
1398 }
1399 case JSVAL_TYPE_STRING: {
1400 masm.branchTestStringTruthy(false, value, ifFalsy);
1401 break;
1402 }
1403 case JSVAL_TYPE_BIGINT: {
1404 masm.branchTestBigIntTruthy(false, value, ifFalsy);
1405 break;
1406 }
1407 case JSVAL_TYPE_DOUBLE: {
1408 masm.unboxDouble(value, floatTemp);
1409 masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy);
1410 break;
1411 }
1412 default:
1413 MOZ_CRASH("Unexpected value type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected value type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1413); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected value type"
")"); do { *((volatile int*)__null) = 1413; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1414 }
1415
1416 // If we reach this point, the value is truthy. We fall through for
1417 // truthy on the last test; otherwise, branch.
1418 if (!skipTypeTest) {
1419 masm.jump(ifTruthy);
1420 }
1421
1422 masm.bind(&differentType);
1423}
1424
1425void CodeGenerator::testValueTruthy(const ValueOperand& value,
1426 Register tempToUnbox, Register temp,
1427 FloatRegister floatTemp,
1428 const TypeDataList& observedTypes,
1429 Label* ifTruthy, Label* ifFalsy,
1430 OutOfLineTestObject* ool) {
1431 ScratchTagScope tag(masm, value);
1432 masm.splitTagForTest(value, tag);
1433
1434 const std::initializer_list<JSValueType> defaultOrder = {
1435 JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN,
1436 JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING,
1437 JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
1438
1439 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
1440
1441 // Generate tests for previously observed types first.
1442 // The TypeDataList is sorted by descending frequency.
1443 for (auto& observed : observedTypes) {
1444 JSValueType type = observed.type();
1445 remaining -= type;
1446
1447 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1448 ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false);
1449 }
1450
1451 // Generate tests for remaining types.
1452 for (auto type : defaultOrder) {
1453 if (!remaining.contains(type)) {
1454 continue;
1455 }
1456 remaining -= type;
1457
1458 // We don't need a type test for the last possible type.
1459 bool skipTypeTest = remaining.isEmpty();
1460 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1461 ifTruthy, ifFalsy, ool, skipTypeTest);
1462 }
1463 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1463); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()"
")"); do { *((volatile int*)__null) = 1463; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1464
1465 // We fall through if the final test is truthy.
1466}
1467
1468void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) {
1469 Label* ifTrueLabel = getJumpLabelForBranch(lir->ifTrue());
1470 Label* ifFalseLabel = getJumpLabelForBranch(lir->ifFalse());
1471 Register input = ToRegister(lir->input());
1472
1473 if (isNextBlock(lir->ifFalse()->lir())) {
1474 masm.branchIfBigIntIsNonZero(input, ifTrueLabel);
1475 } else if (isNextBlock(lir->ifTrue()->lir())) {
1476 masm.branchIfBigIntIsZero(input, ifFalseLabel);
1477 } else {
1478 masm.branchIfBigIntIsZero(input, ifFalseLabel);
1479 jumpToBlock(lir->ifTrue());
1480 }
1481}
1482
1483void CodeGenerator::assertObjectDoesNotEmulateUndefined(
1484 Register input, Register temp, const MInstruction* mir) {
1485#if defined(DEBUG1) || defined(FUZZING)
1486 // Validate that the object indeed doesn't have the emulates undefined flag.
1487 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
1488 addOutOfLineCode(ool, mir);
1489
1490 Label* doesNotEmulateUndefined = ool->label1();
1491 Label* emulatesUndefined = ool->label2();
1492
1493 testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined,
1494 temp, ool);
1495 masm.bind(emulatesUndefined);
1496 masm.assumeUnreachable(
1497 "Found an object emulating undefined while the fuse is intact");
1498 masm.bind(doesNotEmulateUndefined);
1499#endif
1500}
1501
1502void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) {
1503 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1504 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1505 Register input = ToRegister(lir->input());
1506
1507 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
1508 if (intact) {
1509 assertObjectDoesNotEmulateUndefined(input, ToRegister(lir->temp()),
1510 lir->mir());
1511 // Bug 1874905: It would be fantastic if this could be optimized out
1512 masm.jump(truthy);
1513 } else {
1514 auto* ool = new (alloc()) OutOfLineTestObject();
1515 addOutOfLineCode(ool, lir->mir());
1516
1517 testObjectEmulatesUndefined(input, falsy, truthy, ToRegister(lir->temp()),
1518 ool);
1519 }
1520}
1521
1522void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) {
1523 auto* ool = new (alloc()) OutOfLineTestObject();
1524 addOutOfLineCode(ool, lir->mir());
1525
1526 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1527 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1528
1529 ValueOperand input = ToValue(lir, LTestVAndBranch::Input);
1530 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
1531 Register temp = ToRegister(lir->temp2());
1532 FloatRegister floatTemp = ToFloatRegister(lir->tempFloat());
1533 const TypeDataList& observedTypes = lir->mir()->observedTypes();
1534
1535 testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy,
1536 falsy, ool);
1537 masm.jump(truthy);
1538}
1539
1540void CodeGenerator::visitBooleanToString(LBooleanToString* lir) {
1541 Register input = ToRegister(lir->input());
1542 Register output = ToRegister(lir->output());
1543 const JSAtomState& names = gen->runtime->names();
1544 Label true_, done;
1545
1546 masm.branchTest32(Assembler::NonZero, input, input, &true_);
1547 masm.movePtr(ImmGCPtr(names.false_), output);
1548 masm.jump(&done);
1549
1550 masm.bind(&true_);
1551 masm.movePtr(ImmGCPtr(names.true_), output);
1552
1553 masm.bind(&done);
1554}
1555
1556void CodeGenerator::visitIntToString(LIntToString* lir) {
1557 Register input = ToRegister(lir->input());
1558 Register output = ToRegister(lir->output());
1559
1560 using Fn = JSLinearString* (*)(JSContext*, int);
1561 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
1562 lir, ArgList(input), StoreRegisterTo(output));
1563
1564 masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(),
1565 ool->entry());
1566
1567 masm.bind(ool->rejoin());
1568}
1569
1570void CodeGenerator::visitDoubleToString(LDoubleToString* lir) {
1571 FloatRegister input = ToFloatRegister(lir->input());
1572 Register temp = ToRegister(lir->temp0());
1573 Register output = ToRegister(lir->output());
1574
1575 using Fn = JSString* (*)(JSContext*, double);
1576 OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>(
1577 lir, ArgList(input), StoreRegisterTo(output));
1578
1579 // Try double to integer conversion and run integer to string code.
1580 masm.convertDoubleToInt32(input, temp, ool->entry(), false);
1581 masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(),
1582 ool->entry());
1583
1584 masm.bind(ool->rejoin());
1585}
1586
1587void CodeGenerator::visitValueToString(LValueToString* lir) {
1588 ValueOperand input = ToValue(lir, LValueToString::InputIndex);
1589 Register output = ToRegister(lir->output());
1590
1591 using Fn = JSString* (*)(JSContext*, HandleValue);
1592 OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>(
1593 lir, ArgList(input), StoreRegisterTo(output));
1594
1595 Label done;
1596 Register tag = masm.extractTag(input, output);
1597 const JSAtomState& names = gen->runtime->names();
1598
1599 // String
1600 {
1601 Label notString;
1602 masm.branchTestString(Assembler::NotEqual, tag, &notString);
1603 masm.unboxString(input, output);
1604 masm.jump(&done);
1605 masm.bind(&notString);
1606 }
1607
1608 // Integer
1609 {
1610 Label notInteger;
1611 masm.branchTestInt32(Assembler::NotEqual, tag, &notInteger);
1612 Register unboxed = ToTempUnboxRegister(lir->temp0());
1613 unboxed = masm.extractInt32(input, unboxed);
1614 masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(),
1615 ool->entry());
1616 masm.jump(&done);
1617 masm.bind(&notInteger);
1618 }
1619
1620 // Double
1621 {
1622 // Note: no fastpath. Need two extra registers and can only convert doubles
1623 // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT.
1624 masm.branchTestDouble(Assembler::Equal, tag, ool->entry());
1625 }
1626
1627 // Undefined
1628 {
1629 Label notUndefined;
1630 masm.branchTestUndefined(Assembler::NotEqual, tag, &notUndefined);
1631 masm.movePtr(ImmGCPtr(names.undefined), output);
1632 masm.jump(&done);
1633 masm.bind(&notUndefined);
1634 }
1635
1636 // Null
1637 {
1638 Label notNull;
1639 masm.branchTestNull(Assembler::NotEqual, tag, &notNull);
1640 masm.movePtr(ImmGCPtr(names.null), output);
1641 masm.jump(&done);
1642 masm.bind(&notNull);
1643 }
1644
1645 // Boolean
1646 {
1647 Label notBoolean, true_;
1648 masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
1649 masm.branchTestBooleanTruthy(true, input, &true_);
1650 masm.movePtr(ImmGCPtr(names.false_), output);
1651 masm.jump(&done);
1652 masm.bind(&true_);
1653 masm.movePtr(ImmGCPtr(names.true_), output);
1654 masm.jump(&done);
1655 masm.bind(&notBoolean);
1656 }
1657
1658 // Objects/symbols are only possible when |mir->mightHaveSideEffects()|.
1659 if (lir->mir()->mightHaveSideEffects()) {
1660 // Object
1661 if (lir->mir()->supportSideEffects()) {
1662 masm.branchTestObject(Assembler::Equal, tag, ool->entry());
1663 } else {
1664 // Bail.
1665 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1665); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { *((volatile int*)__null) = 1665; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1666 Label bail;
1667 masm.branchTestObject(Assembler::Equal, tag, &bail);
1668 bailoutFrom(&bail, lir->snapshot());
1669 }
1670
1671 // Symbol
1672 if (lir->mir()->supportSideEffects()) {
1673 masm.branchTestSymbol(Assembler::Equal, tag, ool->entry());
1674 } else {
1675 // Bail.
1676 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1676); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { *((volatile int*)__null) = 1676; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1677 Label bail;
1678 masm.branchTestSymbol(Assembler::Equal, tag, &bail);
1679 bailoutFrom(&bail, lir->snapshot());
1680 }
1681 }
1682
1683 // BigInt
1684 {
1685 // No fastpath currently implemented.
1686 masm.branchTestBigInt(Assembler::Equal, tag, ool->entry());
1687 }
1688
1689 masm.assumeUnreachable("Unexpected type for LValueToString.");
1690
1691 masm.bind(&done);
1692 masm.bind(ool->rejoin());
1693}
1694
1695using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**);
1696
1697static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder,
1698 size_t offset, Register buffer,
1699 LiveGeneralRegisterSet& liveVolatiles,
1700 StoreBufferMutationFn fun) {
1701 Label callVM;
1702 Label exit;
1703
1704 // Call into the VM to barrier the write. The only registers that need to
1705 // be preserved are those in liveVolatiles, so once they are saved on the
1706 // stack all volatile registers are available for use.
1707 masm.bind(&callVM);
1708 masm.PushRegsInMask(liveVolatiles);
1709
1710 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
1711 regs.takeUnchecked(buffer);
1712 regs.takeUnchecked(holder);
1713 Register addrReg = regs.takeAny();
1714
1715 masm.computeEffectiveAddress(Address(holder, offset), addrReg);
1716
1717 bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>();
1718 if (needExtraReg) {
1719 masm.push(holder);
1720 masm.setupUnalignedABICall(holder);
1721 } else {
1722 masm.setupUnalignedABICall(regs.takeAny());
1723 }
1724 masm.passABIArg(buffer);
1725 masm.passABIArg(addrReg);
1726 masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun),
1727 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
1728
1729 if (needExtraReg) {
1730 masm.pop(holder);
1731 }
1732 masm.PopRegsInMask(liveVolatiles);
1733 masm.bind(&exit);
1734}
1735
1736// Warning: this function modifies prev and next.
1737static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder,
1738 size_t offset, Register prev, Register next,
1739 LiveGeneralRegisterSet& liveVolatiles) {
1740 Label exit;
1741 Label checkRemove, putCell;
1742
1743 // if (next && (buffer = next->storeBuffer()))
1744 // but we never pass in nullptr for next.
1745 Register storebuffer = next;
1746 masm.loadStoreBuffer(next, storebuffer);
1747 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove);
1748
1749 // if (prev && prev->storeBuffer())
1750 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell);
1751 masm.loadStoreBuffer(prev, prev);
1752 masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit);
1753
1754 // buffer->putCell(cellp)
1755 masm.bind(&putCell);
1756 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
1757 JSString::addCellAddressToStoreBuffer);
1758 masm.jump(&exit);
1759
1760 // if (prev && (buffer = prev->storeBuffer()))
1761 masm.bind(&checkRemove);
1762 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit);
1763 masm.loadStoreBuffer(prev, storebuffer);
1764 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit);
1765 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
1766 JSString::removeCellAddressFromStoreBuffer);
1767
1768 masm.bind(&exit);
1769}
1770
1771void CodeGenerator::visitRegExp(LRegExp* lir) {
1772 Register output = ToRegister(lir->output());
1773 Register temp = ToRegister(lir->temp0());
1774 JSObject* source = lir->mir()->source();
1775
1776 using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>);
1777 OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>(
1778 lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output));
1779 if (lir->mir()->hasShared()) {
1780 TemplateObject templateObject(source);
1781 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
1782 ool->entry());
1783 } else {
1784 masm.jump(ool->entry());
1785 }
1786 masm.bind(ool->rejoin());
1787}
1788
1789static constexpr int32_t RegExpPairsVectorStartOffset(
1790 int32_t inputOutputDataStartOffset) {
1791 return inputOutputDataStartOffset + int32_t(InputOutputDataSize) +
1792 int32_t(sizeof(MatchPairs));
1793}
1794
1795static Address RegExpPairCountAddress(MacroAssembler& masm,
1796 int32_t inputOutputDataStartOffset) {
1797 return Address(FramePointer, inputOutputDataStartOffset +
1798 int32_t(InputOutputDataSize) +
1799 MatchPairs::offsetOfPairCount());
1800}
1801
1802static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp,
1803 Register input, Register lastIndex,
1804 Register staticsReg, Register temp1,
1805 Register temp2, gc::Heap initialStringHeap,
1806 LiveGeneralRegisterSet& volatileRegs) {
1807 Address pendingInputAddress(staticsReg,
1808 RegExpStatics::offsetOfPendingInput());
1809 Address matchesInputAddress(staticsReg,
1810 RegExpStatics::offsetOfMatchesInput());
1811 Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource());
1812 Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex());
1813
1814 masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String);
1815 masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String);
1816 masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String);
1817
1818 if (initialStringHeap == gc::Heap::Default) {
1819 // Writing into RegExpStatics tenured memory; must post-barrier.
1820 if (staticsReg.volatile_()) {
1821 volatileRegs.add(staticsReg);
1822 }
1823
1824 masm.loadPtr(pendingInputAddress, temp1);
1825 masm.storePtr(input, pendingInputAddress);
1826 masm.movePtr(input, temp2);
1827 EmitPostWriteBarrierS(masm, staticsReg,
1828 RegExpStatics::offsetOfPendingInput(),
1829 temp1 /* prev */, temp2 /* next */, volatileRegs);
1830
1831 masm.loadPtr(matchesInputAddress, temp1);
1832 masm.storePtr(input, matchesInputAddress);
1833 masm.movePtr(input, temp2);
1834 EmitPostWriteBarrierS(masm, staticsReg,
1835 RegExpStatics::offsetOfMatchesInput(),
1836 temp1 /* prev */, temp2 /* next */, volatileRegs);
1837 } else {
1838 masm.debugAssertGCThingIsTenured(input, temp1);
1839 masm.storePtr(input, pendingInputAddress);
1840 masm.storePtr(input, matchesInputAddress);
1841 }
1842
1843 masm.storePtr(lastIndex,
1844 Address(staticsReg, RegExpStatics::offsetOfLazyIndex()));
1845 masm.store32(
1846 Imm32(1),
1847 Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation()));
1848
1849 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset(
1850 RegExpObject::SHARED_SLOT)),
1851 temp1, JSVAL_TYPE_PRIVATE_GCTHING);
1852 masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2);
1853 masm.storePtr(temp2, lazySourceAddress);
1854 static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size");
1855 masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2);
1856 masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags()));
1857}
1858
1859// Prepare an InputOutputData and optional MatchPairs which space has been
1860// allocated for on the stack, and try to execute a RegExp on a string input.
1861// If the RegExp was successfully executed and matched the input, fallthrough.
1862// Otherwise, jump to notFound or failure.
1863//
1864// inputOutputDataStartOffset is the offset relative to the frame pointer
1865// register. This offset is negative for the RegExpExecTest stub.
1866static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp,
1867 Register input, Register lastIndex,
1868 Register temp1, Register temp2,
1869 Register temp3,
1870 int32_t inputOutputDataStartOffset,
1871 gc::Heap initialStringHeap, Label* notFound,
1872 Label* failure) {
1873 JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp");
1874
1875 using irregexp::InputOutputData;
1876
1877 /*
1878 * [SMDOC] Stack layout for PrepareAndExecuteRegExp
1879 *
1880 * Before this function is called, the caller is responsible for
1881 * allocating enough stack space for the following data:
1882 *
1883 * inputOutputDataStartOffset +-----> +---------------+
1884 * |InputOutputData|
1885 * inputStartAddress +----------> inputStart|
1886 * inputEndAddress +----------> inputEnd|
1887 * startIndexAddress +----------> startIndex|
1888 * matchesAddress +----------> matches|-----+
1889 * +---------------+ |
1890 * matchPairs(Address|Offset) +-----> +---------------+ <--+
1891 * | MatchPairs |
1892 * pairCountAddress +----------> count |
1893 * pairsPointerAddress +----------> pairs |-----+
1894 * +---------------+ |
1895 * pairsArray(Address|Offset) +-----> +---------------+ <--+
1896 * | MatchPair |
1897 * firstMatchStartAddress +----------> start | <--+
1898 * | limit | |
1899 * +---------------+ |
1900 * . |
1901 * . Reserved space for
1902 * . RegExpObject::MaxPairCount
1903 * . MatchPair objects
1904 * . |
1905 * +---------------+ |
1906 * | MatchPair | |
1907 * | start | |
1908 * | limit | <--+
1909 * +---------------+
1910 */
1911
1912 int32_t ioOffset = inputOutputDataStartOffset;
1913 int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData));
1914 int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs));
1915
1916 Address inputStartAddress(FramePointer,
1917 ioOffset + InputOutputData::offsetOfInputStart());
1918 Address inputEndAddress(FramePointer,
1919 ioOffset + InputOutputData::offsetOfInputEnd());
1920 Address startIndexAddress(FramePointer,
1921 ioOffset + InputOutputData::offsetOfStartIndex());
1922 Address matchesAddress(FramePointer,
1923 ioOffset + InputOutputData::offsetOfMatches());
1924
1925 Address matchPairsAddress(FramePointer, matchPairsOffset);
1926 Address pairCountAddress(FramePointer,
1927 matchPairsOffset + MatchPairs::offsetOfPairCount());
1928 Address pairsPointerAddress(FramePointer,
1929 matchPairsOffset + MatchPairs::offsetOfPairs());
1930
1931 Address pairsArrayAddress(FramePointer, pairsArrayOffset);
1932 Address firstMatchStartAddress(FramePointer,
1933 pairsArrayOffset + MatchPair::offsetOfStart());
1934
1935 // First, fill in a skeletal MatchPairs instance on the stack. This will be
1936 // passed to the OOL stub in the caller if we aren't able to execute the
1937 // RegExp inline, and that stub needs to be able to determine whether the
1938 // execution finished successfully.
1939
1940 // Initialize MatchPairs::pairCount to 1. The correct value can only
1941 // be determined after loading the RegExpShared. If the RegExpShared
1942 // has Kind::Atom, this is the correct pairCount.
1943 masm.store32(Imm32(1), pairCountAddress);
1944
1945 // Initialize MatchPairs::pairs pointer
1946 masm.computeEffectiveAddress(pairsArrayAddress, temp1);
1947 masm.storePtr(temp1, pairsPointerAddress);
1948
1949 // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch
1950 masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress);
1951
1952 // Determine the set of volatile inputs to save when calling into C++ or
1953 // regexp code.
1954 LiveGeneralRegisterSet volatileRegs;
1955 if (lastIndex.volatile_()) {
1956 volatileRegs.add(lastIndex);
1957 }
1958 if (input.volatile_()) {
1959 volatileRegs.add(input);
1960 }
1961 if (regexp.volatile_()) {
1962 volatileRegs.add(regexp);
1963 }
1964
1965 // Ensure the input string is not a rope.
1966 Label isLinear;
1967 masm.branchIfNotRope(input, &isLinear);
1968 {
1969 masm.PushRegsInMask(volatileRegs);
1970
1971 using Fn = JSLinearString* (*)(JSString*);
1972 masm.setupUnalignedABICall(temp1);
1973 masm.passABIArg(input);
1974 masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>();
1975
1976 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)"
")"); do { *((volatile int*)__null) = 1976; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1977 masm.storeCallPointerResult(temp1);
1978 masm.PopRegsInMask(volatileRegs);
1979
1980 masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure);
1981 }
1982 masm.bind(&isLinear);
1983
1984 // Load the RegExpShared.
1985 Register regexpReg = temp1;
1986 Address sharedSlot = Address(
1987 regexp, NativeObject::getFixedSlotOffset(RegExpObject::SHARED_SLOT));
1988 masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure);
1989 masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING);
1990
1991 // Handle Atom matches
1992 Label notAtom, checkSuccess;
1993 masm.branchPtr(Assembler::Equal,
1994 Address(regexpReg, RegExpShared::offsetOfPatternAtom()),
1995 ImmWord(0), &notAtom);
1996 {
1997 masm.computeEffectiveAddress(matchPairsAddress, temp3);
1998
1999 masm.PushRegsInMask(volatileRegs);
2000 using Fn =
2001 RegExpRunStatus (*)(RegExpShared* re, const JSLinearString* input,
2002 size_t start, MatchPairs* matchPairs);
2003 masm.setupUnalignedABICall(temp2);
2004 masm.passABIArg(regexpReg);
2005 masm.passABIArg(input);
2006 masm.passABIArg(lastIndex);
2007 masm.passABIArg(temp3);
2008 masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>();
2009
2010 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2010); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)"
")"); do { *((volatile int*)__null) = 2010; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2011 masm.storeCallInt32Result(temp1);
2012 masm.PopRegsInMask(volatileRegs);
2013
2014 masm.jump(&checkSuccess);
2015 }
2016 masm.bind(&notAtom);
2017
2018 // Don't handle regexps with too many capture pairs.
2019 masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2);
2020 masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount),
2021 failure);
2022
2023 // Fill in the pair count in the MatchPairs on the stack.
2024 masm.store32(temp2, pairCountAddress);
2025
2026 // Load code pointer and length of input (in bytes).
2027 // Store the input start in the InputOutputData.
2028 Register codePointer = temp1; // Note: temp1 was previously regexpReg.
2029 Register byteLength = temp3;
2030 {
2031 Label isLatin1, done;
2032 masm.loadStringLength(input, byteLength);
2033
2034 masm.branchLatin1String(input, &isLatin1);
2035
2036 // Two-byte input
2037 masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
2038 masm.storePtr(temp2, inputStartAddress);
2039 masm.loadPtr(
2040 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)),
2041 codePointer);
2042 masm.lshiftPtr(Imm32(1), byteLength);
2043 masm.jump(&done);
2044
2045 // Latin1 input
2046 masm.bind(&isLatin1);
2047 masm.loadStringChars(input, temp2, CharEncoding::Latin1);
2048 masm.storePtr(temp2, inputStartAddress);
2049 masm.loadPtr(
2050 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)),
2051 codePointer);
2052
2053 masm.bind(&done);
2054
2055 // Store end pointer
2056 masm.addPtr(byteLength, temp2);
2057 masm.storePtr(temp2, inputEndAddress);
2058 }
2059
2060 // Guard that the RegExpShared has been compiled for this type of input.
2061 // If it has not been compiled, we fall back to the OOL case, which will
2062 // do a VM call into the interpreter.
2063 // TODO: add an interpreter trampoline?
2064 masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure);
2065 masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
2066
2067 // Finish filling in the InputOutputData instance on the stack
2068 masm.computeEffectiveAddress(matchPairsAddress, temp2);
2069 masm.storePtr(temp2, matchesAddress);
2070 masm.storePtr(lastIndex, startIndexAddress);
2071
2072 // Execute the RegExp.
2073 masm.computeEffectiveAddress(
2074 Address(FramePointer, inputOutputDataStartOffset), temp2);
2075 masm.PushRegsInMask(volatileRegs);
2076 masm.setupUnalignedABICall(temp3);
2077 masm.passABIArg(temp2);
2078 masm.callWithABI(codePointer);
2079 masm.storeCallInt32Result(temp1);
2080 masm.PopRegsInMask(volatileRegs);
2081
2082 masm.bind(&checkSuccess);
2083 masm.branch32(Assembler::Equal, temp1,
2084 Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound);
2085 masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)),
2086 failure);
2087
2088 // Lazily update the RegExpStatics.
2089 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2090 RegExpRealm::offsetOfRegExpStatics();
2091 masm.loadGlobalObjectData(temp1);
2092 masm.loadPtr(Address(temp1, offset), temp1);
2093 UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3,
2094 initialStringHeap, volatileRegs);
2095
2096 return true;
2097}
2098
2099static void EmitInitDependentStringBase(MacroAssembler& masm,
2100 Register dependent, Register base,
2101 Register temp1, Register temp2,
2102 bool needsPostBarrier) {
2103 // Determine the base string to use and store it in temp2.
2104 Label notDependent, markedDependedOn;
2105 masm.load32(Address(base, JSString::offsetOfFlags()), temp1);
2106 masm.branchTest32(Assembler::Zero, temp1, Imm32(JSString::DEPENDENT_BIT),
2107 &notDependent);
2108 {
2109 // The base is also a dependent string. Load its base to prevent chains of
2110 // dependent strings in most cases. This must either be an atom or already
2111 // have the DEPENDED_ON_BIT set.
2112 masm.loadDependentStringBase(base, temp2);
2113 masm.jump(&markedDependedOn);
2114 }
2115 masm.bind(&notDependent);
2116 {
2117 // The base is not a dependent string. Set the DEPENDED_ON_BIT if it's not
2118 // an atom.
2119 masm.movePtr(base, temp2);
2120 masm.branchTest32(Assembler::NonZero, temp1, Imm32(JSString::ATOM_BIT),
2121 &markedDependedOn);
2122 masm.or32(Imm32(JSString::DEPENDED_ON_BIT), temp1);
2123 masm.store32(temp1, Address(temp2, JSString::offsetOfFlags()));
2124 }
2125 masm.bind(&markedDependedOn);
2126
2127#ifdef DEBUG1
2128 // Assert the base has the DEPENDED_ON_BIT set or is an atom.
2129 Label isAppropriatelyMarked;
2130 masm.branchTest32(Assembler::NonZero,
2131 Address(temp2, JSString::offsetOfFlags()),
2132 Imm32(JSString::ATOM_BIT | JSString::DEPENDED_ON_BIT),
2133 &isAppropriatelyMarked);
2134 masm.assumeUnreachable("Base string is missing DEPENDED_ON_BIT");
2135 masm.bind(&isAppropriatelyMarked);
2136#endif
2137 masm.storeDependentStringBase(temp2, dependent);
2138
2139 // Post-barrier the base store. The base is still in temp2.
2140 if (needsPostBarrier) {
2141 Label done;
2142 masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done);
2143 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done);
2144
2145 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2146 regsToSave.takeUnchecked(temp1);
2147 regsToSave.takeUnchecked(temp2);
2148
2149 masm.PushRegsInMask(regsToSave);
2150
2151 masm.mov(ImmPtr(masm.runtime()), temp1);
2152
2153 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell);
2154 masm.setupUnalignedABICall(temp2);
2155 masm.passABIArg(temp1);
2156 masm.passABIArg(dependent);
2157 masm.callWithABI<Fn, PostWriteBarrier>();
2158
2159 masm.PopRegsInMask(regsToSave);
2160
2161 masm.bind(&done);
2162 } else {
2163#ifdef DEBUG1
2164 Label done;
2165 masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done);
2166 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done);
2167 masm.assumeUnreachable("Missing post barrier for dependent string base");
2168 masm.bind(&done);
2169#endif
2170 }
2171}
2172
2173static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
2174 Register len, Register byteOpScratch,
2175 CharEncoding encoding,
2176 size_t maximumLength = SIZE_MAX(18446744073709551615UL));
2177
2178class CreateDependentString {
2179 CharEncoding encoding_;
2180 Register string_;
2181 Register temp1_;
2182 Register temp2_;
2183 Label* failure_;
2184
2185 enum class FallbackKind : uint8_t {
2186 InlineString,
2187 FatInlineString,
2188 NotInlineString,
2189 Count
2190 };
2191 mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)>
2192 fallbacks_, joins_;
2193
2194 public:
2195 CreateDependentString(CharEncoding encoding, Register string, Register temp1,
2196 Register temp2, Label* failure)
2197 : encoding_(encoding),
2198 string_(string),
2199 temp1_(temp1),
2200 temp2_(temp2),
2201 failure_(failure) {}
2202
2203 Register string() const { return string_; }
2204 CharEncoding encoding() const { return encoding_; }
2205
2206 // Generate code that creates DependentString.
2207 // Caller should call generateFallback after masm.ret(), to generate
2208 // fallback path.
2209 void generate(MacroAssembler& masm, const JSAtomState& names,
2210 CompileRuntime* runtime, Register base,
2211 BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
2212 gc::Heap initialStringHeap);
2213
2214 // Generate fallback path for creating DependentString.
2215 void generateFallback(MacroAssembler& masm);
2216};
2217
2218void CreateDependentString::generate(MacroAssembler& masm,
2219 const JSAtomState& names,
2220 CompileRuntime* runtime, Register base,
2221 BaseIndex startIndexAddress,
2222 BaseIndex limitIndexAddress,
2223 gc::Heap initialStringHeap) {
2224 JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)",
2225 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2226
2227 auto newGCString = [&](FallbackKind kind) {
2228 uint32_t flags = kind == FallbackKind::InlineString
2229 ? JSString::INIT_THIN_INLINE_FLAGS
2230 : kind == FallbackKind::FatInlineString
2231 ? JSString::INIT_FAT_INLINE_FLAGS
2232 : JSString::INIT_DEPENDENT_FLAGS;
2233 if (encoding_ == CharEncoding::Latin1) {
2234 flags |= JSString::LATIN1_CHARS_BIT;
2235 }
2236
2237 if (kind != FallbackKind::FatInlineString) {
2238 masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]);
2239 } else {
2240 masm.newGCFatInlineString(string_, temp2_, initialStringHeap,
2241 &fallbacks_[kind]);
2242 }
2243 masm.bind(&joins_[kind]);
2244 masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags()));
2245 };
2246
2247 // Compute the string length.
2248 masm.load32(startIndexAddress, temp2_);
2249 masm.load32(limitIndexAddress, temp1_);
2250 masm.sub32(temp2_, temp1_);
2251
2252 Label done, nonEmpty;
2253
2254 // Zero length matches use the empty string.
2255 masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty);
2256 masm.movePtr(ImmGCPtr(names.empty_), string_);
2257 masm.jump(&done);
2258
2259 masm.bind(&nonEmpty);
2260
2261 // Complete matches use the base string.
2262 Label nonBaseStringMatch;
2263 masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch);
2264 masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()),
2265 temp1_, &nonBaseStringMatch);
2266 masm.movePtr(base, string_);
2267 masm.jump(&done);
2268
2269 masm.bind(&nonBaseStringMatch);
2270
2271 Label notInline;
2272
2273 int32_t maxInlineLength = encoding_ == CharEncoding::Latin1
2274 ? JSFatInlineString::MAX_LENGTH_LATIN1
2275 : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
2276 masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), &notInline);
2277 {
2278 // Make a thin or fat inline string.
2279 Label stringAllocated, fatInline;
2280
2281 int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1
2282 ? JSThinInlineString::MAX_LENGTH_LATIN1
2283 : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
2284 masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength),
2285 &fatInline);
2286 if (encoding_ == CharEncoding::Latin1) {
2287 // One character Latin-1 strings can be loaded directly from the
2288 // static strings table.
2289 Label thinInline;
2290 masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline);
2291 {
2292 static_assert(
2293 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
2294 "Latin-1 strings can be loaded from static strings");
2295
2296 masm.loadStringChars(base, temp1_, encoding_);
2297 masm.loadChar(temp1_, temp2_, temp1_, encoding_);
2298
2299 masm.lookupStaticString(temp1_, string_, runtime->staticStrings());
2300
2301 masm.jump(&done);
2302 }
2303 masm.bind(&thinInline);
2304 }
2305 {
2306 newGCString(FallbackKind::InlineString);
2307 masm.jump(&stringAllocated);
2308 }
2309 masm.bind(&fatInline);
2310 { newGCString(FallbackKind::FatInlineString); }
2311 masm.bind(&stringAllocated);
2312
2313 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2314
2315 masm.push(string_);
2316 masm.push(base);
2317
2318 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" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2319); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer"
") (" "startIndexAddress is still valid after stack pushes" ")"
); do { *((volatile int*)__null) = 2319; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2319 "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" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2319); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer"
") (" "startIndexAddress is still valid after stack pushes" ")"
); do { *((volatile int*)__null) = 2319; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2320
2321 // Load chars pointer for the new string.
2322 masm.loadInlineStringCharsForStore(string_, string_);
2323
2324 // Load the source characters pointer.
2325 masm.loadStringChars(base, temp2_, encoding_);
2326 masm.load32(startIndexAddress, base);
2327 masm.addToCharPtr(temp2_, base, encoding_);
2328
2329 CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_);
2330
2331 masm.pop(base);
2332 masm.pop(string_);
2333
2334 masm.jump(&done);
2335 }
2336
2337 masm.bind(&notInline);
2338
2339 {
2340 // Make a dependent string.
2341 // Warning: string may be tenured (if the fallback case is hit), so
2342 // stores into it must be post barriered.
2343 newGCString(FallbackKind::NotInlineString);
2344
2345 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2346
2347 masm.loadNonInlineStringChars(base, temp1_, encoding_);
2348 masm.load32(startIndexAddress, temp2_);
2349 masm.addToCharPtr(temp1_, temp2_, encoding_);
2350 masm.storeNonInlineStringChars(temp1_, string_);
2351
2352 EmitInitDependentStringBase(masm, string_, base, temp1_, temp2_,
2353 /* needsPostBarrier = */ true);
2354 }
2355
2356 masm.bind(&done);
2357}
2358
2359void CreateDependentString::generateFallback(MacroAssembler& masm) {
2360 JitSpew(JitSpew_Codegen,
2361 "# Emitting CreateDependentString fallback (encoding=%s)",
2362 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2363
2364 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2365 regsToSave.takeUnchecked(string_);
2366 regsToSave.takeUnchecked(temp2_);
2367
2368 for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) {
2369 masm.bind(&fallbacks_[kind]);
2370
2371 masm.PushRegsInMask(regsToSave);
2372
2373 using Fn = void* (*)(JSContext* cx);
2374 masm.setupUnalignedABICall(string_);
2375 masm.loadJSContext(string_);
2376 masm.passABIArg(string_);
2377 if (kind == FallbackKind::FatInlineString) {
2378 masm.callWithABI<Fn, AllocateFatInlineString>();
2379 } else {
2380 masm.callWithABI<Fn, AllocateDependentString>();
2381 }
2382 masm.storeCallPointerResult(string_);
2383
2384 masm.PopRegsInMask(regsToSave);
2385
2386 masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_);
2387
2388 masm.jump(&joins_[kind]);
2389 }
2390}
2391
2392// Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar,
2393// but RegExpExecMatch also has to load and update .lastIndex for global/sticky
2394// regular expressions.
2395static JitCode* GenerateRegExpMatchStubShared(JSContext* cx,
2396 gc::Heap initialStringHeap,
2397 bool isExecMatch) {
2398 if (isExecMatch) {
2399 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub");
2400 } else {
2401 JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub");
2402 }
2403
2404 // |initialStringHeap| could be stale after a GC.
2405 JS::AutoCheckCannotGC nogc(cx);
2406
2407 Register regexp = RegExpMatcherRegExpReg;
2408 Register input = RegExpMatcherStringReg;
2409 Register lastIndex = RegExpMatcherLastIndexReg;
2410 ValueOperand result = JSReturnOperand;
2411
2412 // We are free to clobber all registers, as LRegExpMatcher is a call
2413 // instruction.
2414 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2415 regs.take(input);
2416 regs.take(regexp);
2417 regs.take(lastIndex);
2418
2419 Register temp1 = regs.takeAny();
2420 Register temp2 = regs.takeAny();
2421 Register temp3 = regs.takeAny();
2422 Register maybeTemp4 = InvalidReg;
2423 if (!regs.empty()) {
2424 // There are not enough registers on x86.
2425 maybeTemp4 = regs.takeAny();
2426 }
2427 Register maybeTemp5 = InvalidReg;
2428 if (!regs.empty()) {
2429 // There are not enough registers on x86.
2430 maybeTemp5 = regs.takeAny();
2431 }
2432
2433 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
2434 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
2435
2436 TempAllocator temp(&cx->tempLifoAlloc());
2437 JitContext jcx(cx);
2438 StackMacroAssembler masm(cx, temp);
2439 AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared");
2440
2441#ifdef JS_USE_LINK_REGISTER
2442 masm.pushReturnAddress();
2443#endif
2444 masm.push(FramePointer);
2445 masm.moveStackPtrTo(FramePointer);
2446
2447 Label notFoundZeroLastIndex;
2448 if (isExecMatch) {
2449 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
2450 }
2451
2452 // The InputOutputData is placed above the frame pointer and return address on
2453 // the stack.
2454 int32_t inputOutputDataStartOffset = 2 * sizeof(void*);
2455
2456 Label notFound, oolEntry;
2457 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
2458 temp3, inputOutputDataStartOffset,
2459 initialStringHeap, &notFound, &oolEntry)) {
2460 return nullptr;
2461 }
2462
2463 // If a regexp has named captures, fall back to the OOL stub, which
2464 // will end up calling CreateRegExpMatchResults.
2465 Register shared = temp2;
2466 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset(
2467 RegExpObject::SHARED_SLOT)),
2468 shared, JSVAL_TYPE_PRIVATE_GCTHING);
2469 masm.branchPtr(Assembler::NotEqual,
2470 Address(shared, RegExpShared::offsetOfGroupsTemplate()),
2471 ImmWord(0), &oolEntry);
2472
2473 // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub.
2474 masm.branchTest32(Assembler::NonZero,
2475 Address(shared, RegExpShared::offsetOfFlags()),
2476 Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry);
2477
2478 Address pairCountAddress =
2479 RegExpPairCountAddress(masm, inputOutputDataStartOffset);
2480
2481 // Construct the result.
2482 Register object = temp1;
2483 {
2484 // In most cases, the array will have just 1-2 elements, so we optimize for
2485 // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots
2486 // because two slots are used for the elements header).
2487
2488 // Load the array length in temp2 and the shape in temp3.
2489 Label allocated;
2490 masm.load32(pairCountAddress, temp2);
2491 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2492 RegExpRealm::offsetOfNormalMatchResultShape();
2493 masm.loadGlobalObjectData(temp3);
2494 masm.loadPtr(Address(temp3, offset), temp3);
2495
2496 auto emitAllocObject = [&](size_t elementCapacity) {
2497 gc::AllocKind kind = GuessArrayGCKind(elementCapacity);
2498 MOZ_ASSERT(CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(CanChangeToBackgroundAllocKind(kind, &ArrayObject
::class_))>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(kind
, &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2498); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)"
")"); do { *((volatile int*)__null) = 2498; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2499 kind = ForegroundToBackgroundAllocKind(kind);
2500
2501#ifdef DEBUG1
2502 // Assert all of the available slots are used for |elementCapacity|
2503 // elements.
2504 size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity;
2505 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)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)"
")"); do { *((volatile int*)__null) = 2505; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2506#endif
2507
2508 constexpr size_t numUsedDynamicSlots =
2509 RegExpRealm::MatchResultObjectSlotSpan;
2510 constexpr size_t numDynamicSlots =
2511 RegExpRealm::MatchResultObjectNumDynamicSlots;
2512 constexpr size_t arrayLength = 1;
2513 masm.createArrayWithFixedElements(object, temp3, temp2, temp3,
2514 arrayLength, elementCapacity,
2515 numUsedDynamicSlots, numDynamicSlots,
2516 kind, gc::Heap::Default, &oolEntry);
2517 };
2518
2519 Label moreThan2;
2520 masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2);
2521 emitAllocObject(2);
2522 masm.jump(&allocated);
2523
2524 Label moreThan6;
2525 masm.bind(&moreThan2);
2526 masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6);
2527 emitAllocObject(6);
2528 masm.jump(&allocated);
2529
2530 masm.bind(&moreThan6);
2531 static_assert(RegExpObject::MaxPairCount == 14);
2532 emitAllocObject(RegExpObject::MaxPairCount);
2533
2534 masm.bind(&allocated);
2535 }
2536
2537 // clang-format off
2538 /*
2539 * [SMDOC] Stack layout for the RegExpMatcher stub
2540 *
2541 * +---------------+
2542 * FramePointer +-----> |Caller-FramePtr|
2543 * +---------------+
2544 * |Return-Address |
2545 * +---------------+
2546 * inputOutputDataStartOffset +-----> +---------------+
2547 * |InputOutputData|
2548 * +---------------+
2549 * +---------------+
2550 * | MatchPairs |
2551 * pairsCountAddress +-----------> count |
2552 * | pairs |
2553 * | |
2554 * +---------------+
2555 * pairsVectorStartOffset +-----> +---------------+
2556 * | MatchPair |
2557 * matchPairStart +------------> start | <-------+
2558 * matchPairLimit +------------> limit | | Reserved space for
2559 * +---------------+ | `RegExpObject::MaxPairCount`
2560 * . | MatchPair objects.
2561 * . |
2562 * . | `count` objects will be
2563 * +---------------+ | initialized and can be
2564 * | MatchPair | | accessed below.
2565 * | start | <-------+
2566 * | limit |
2567 * +---------------+
2568 */
2569 // clang-format on
2570
2571 static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t),
2572 "MatchPair consists of two int32 values representing the start"
2573 "and the end offset of the match");
2574
2575 int32_t pairsVectorStartOffset =
2576 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
2577
2578 // Incremented by one below for each match pair.
2579 Register matchIndex = temp2;
2580 masm.move32(Imm32(0), matchIndex);
2581
2582 // The element in which to store the result of the current match.
2583 size_t elementsOffset = NativeObject::offsetOfFixedElements();
2584 BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset);
2585
2586 // The current match pair's "start" and "limit" member.
2587 BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight,
2588 pairsVectorStartOffset + MatchPair::offsetOfStart());
2589 BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight,
2590 pairsVectorStartOffset + MatchPair::offsetOfLimit());
2591
2592 Label* depStrFailure = &oolEntry;
2593 Label restoreRegExpAndLastIndex;
2594
2595 Register temp4;
2596 if (maybeTemp4 == InvalidReg) {
2597 depStrFailure = &restoreRegExpAndLastIndex;
2598
2599 // We don't have enough registers for a fourth temporary. Reuse |regexp|
2600 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
2601 masm.push(regexp);
2602 temp4 = regexp;
2603 } else {
2604 temp4 = maybeTemp4;
2605 }
2606
2607 Register temp5;
2608 if (maybeTemp5 == InvalidReg) {
2609 depStrFailure = &restoreRegExpAndLastIndex;
2610
2611 // We don't have enough registers for a fifth temporary. Reuse |lastIndex|
2612 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
2613 masm.push(lastIndex);
2614 temp5 = lastIndex;
2615 } else {
2616 temp5 = maybeTemp5;
2617 }
2618
2619 auto maybeRestoreRegExpAndLastIndex = [&]() {
2620 if (maybeTemp5 == InvalidReg) {
2621 masm.pop(lastIndex);
2622 }
2623 if (maybeTemp4 == InvalidReg) {
2624 masm.pop(regexp);
2625 }
2626 };
2627
2628 // Loop to construct the match strings. There are two different loops,
2629 // depending on whether the input is a Two-Byte or a Latin-1 string.
2630 CreateDependentString depStrs[]{
2631 {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure},
2632 {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure},
2633 };
2634
2635 {
2636 Label isLatin1, done;
2637 masm.branchLatin1String(input, &isLatin1);
2638
2639 for (auto& depStr : depStrs) {
2640 if (depStr.encoding() == CharEncoding::Latin1) {
2641 masm.bind(&isLatin1);
2642 }
2643
2644 Label matchLoop;
2645 masm.bind(&matchLoop);
2646
2647 static_assert(MatchPair::NoMatch == -1,
2648 "MatchPair::start is negative if no match was found");
2649
2650 Label isUndefined, storeDone;
2651 masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0),
2652 &isUndefined);
2653 {
2654 depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()),
2655 input, matchPairStart, matchPairLimit,
2656 initialStringHeap);
2657
2658 // Storing into nursery-allocated results object's elements; no post
2659 // barrier.
2660 masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement);
2661 masm.jump(&storeDone);
2662 }
2663 masm.bind(&isUndefined);
2664 { masm.storeValue(UndefinedValue(), objectMatchElement); }
2665 masm.bind(&storeDone);
2666
2667 masm.add32(Imm32(1), matchIndex);
2668 masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex,
2669 &done);
2670 masm.jump(&matchLoop);
2671 }
2672
2673#ifdef DEBUG1
2674 masm.assumeUnreachable("The match string loop doesn't fall through.");
2675#endif
2676
2677 masm.bind(&done);
2678 }
2679
2680 maybeRestoreRegExpAndLastIndex();
2681
2682 // Fill in the rest of the output object.
2683 masm.store32(
2684 matchIndex,
2685 Address(object,
2686 elementsOffset + ObjectElements::offsetOfInitializedLength()));
2687 masm.store32(
2688 matchIndex,
2689 Address(object, elementsOffset + ObjectElements::offsetOfLength()));
2690
2691 Address firstMatchPairStartAddress(
2692 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart());
2693 Address firstMatchPairLimitAddress(
2694 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit());
2695
2696 static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0,
2697 "First slot holds the 'index' property");
2698 static_assert(RegExpRealm::MatchResultObjectInputSlot == 1,
2699 "Second slot holds the 'input' property");
2700
2701 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
2702
2703 masm.load32(firstMatchPairStartAddress, temp3);
2704 masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
2705
2706 // No post barrier needed (address is within nursery object.)
2707 masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
2708
2709 // For the ExecMatch stub, if the regular expression is global or sticky, we
2710 // have to update its .lastIndex slot.
2711 if (isExecMatch) {
2712 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2712); AnnotateMozCrashReason("MOZ_ASSERT" "(" "object != lastIndex"
")"); do { *((volatile int*)__null) = 2712; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2713 Label notGlobalOrSticky;
2714 masm.branchTest32(Assembler::Zero, flagsSlot,
2715 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
2716 &notGlobalOrSticky);
2717 masm.load32(firstMatchPairLimitAddress, lastIndex);
2718 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
2719 masm.bind(&notGlobalOrSticky);
2720 }
2721
2722 // All done!
2723 masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
2724 masm.pop(FramePointer);
2725 masm.ret();
2726
2727 masm.bind(&notFound);
2728 if (isExecMatch) {
2729 Label notGlobalOrSticky;
2730 masm.branchTest32(Assembler::Zero, flagsSlot,
2731 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
2732 &notGlobalOrSticky);
2733 masm.bind(&notFoundZeroLastIndex);
2734 masm.storeValue(Int32Value(0), lastIndexSlot);
2735 masm.bind(&notGlobalOrSticky);
2736 }
2737 masm.moveValue(NullValue(), result);
2738 masm.pop(FramePointer);
2739 masm.ret();
2740
2741 // Fallback paths for CreateDependentString.
2742 for (auto& depStr : depStrs) {
2743 depStr.generateFallback(masm);
2744 }
2745
2746 // Fall-through to the ool entry after restoring the registers.
2747 masm.bind(&restoreRegExpAndLastIndex);
2748 maybeRestoreRegExpAndLastIndex();
2749
2750 // Use an undefined value to signal to the caller that the OOL stub needs to
2751 // be called.
2752 masm.bind(&oolEntry);
2753 masm.moveValue(UndefinedValue(), result);
2754 masm.pop(FramePointer);
2755 masm.ret();
2756
2757 Linker linker(masm);
2758 JitCode* code = linker.newCode(cx, CodeKind::Other);
2759 if (!code) {
2760 return nullptr;
2761 }
2762
2763 const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub";
2764 CollectPerfSpewerJitCodeProfile(code, name);
2765#ifdef MOZ_VTUNE1
2766 vtune::MarkStub(code, name);
2767#endif
2768
2769 return code;
2770}
2771
2772JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) {
2773 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
2774 /* isExecMatch = */ false);
2775}
2776
2777JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) {
2778 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
2779 /* isExecMatch = */ true);
2780}
2781
2782class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator> {
2783 LRegExpMatcher* lir_;
2784
2785 public:
2786 explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir) : lir_(lir) {}
2787
2788 void accept(CodeGenerator* codegen) override {
2789 codegen->visitOutOfLineRegExpMatcher(this);
2790 }
2791
2792 LRegExpMatcher* lir() const { return lir_; }
2793};
2794
2795void CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool) {
2796 LRegExpMatcher* lir = ool->lir();
2797 Register lastIndex = ToRegister(lir->lastIndex());
2798 Register input = ToRegister(lir->string());
2799 Register regexp = ToRegister(lir->regexp());
2800
2801 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2802 regs.take(lastIndex);
2803 regs.take(input);
2804 regs.take(regexp);
2805 Register temp = regs.takeAny();
2806
2807 masm.computeEffectiveAddress(
2808 Address(masm.getStackPointer(), InputOutputDataSize), temp);
2809
2810 pushArg(temp);
2811 pushArg(lastIndex);
2812 pushArg(input);
2813 pushArg(regexp);
2814
2815 // We are not using oolCallVM because we are in a Call, and that live
2816 // registers are already saved by the the register allocator.
2817 using Fn =
2818 bool (*)(JSContext*, HandleObject regexp, HandleString input,
2819 int32_t lastIndex, MatchPairs* pairs, MutableHandleValue output);
2820 callVM<Fn, RegExpMatcherRaw>(lir);
2821
2822 masm.jump(ool->rejoin());
2823}
2824
2825void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) {
2826 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2826); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { *((volatile int*)__null) = 2826; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2827 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2827); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { *((volatile int*)__null) = 2827; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2828 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2828); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg"
")"); do { *((volatile int*)__null) = 2828; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2829 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2829); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { *((volatile int*)__null) = 2829; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2830
2831#if defined(JS_NUNBOX32)
2832 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
2833 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
2834 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
2835 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
2836 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type);
2837 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data);
2838#elif defined(JS_PUNBOX641)
2839 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
2840 static_assert(RegExpMatcherStringReg != JSReturnReg);
2841 static_assert(RegExpMatcherLastIndexReg != JSReturnReg);
2842#endif
2843
2844 masm.reserveStack(RegExpReservedStack);
2845
2846 OutOfLineRegExpMatcher* ool = new (alloc()) OutOfLineRegExpMatcher(lir);
2847 addOutOfLineCode(ool, lir->mir());
2848
2849 const JitZone* jitZone = gen->realm->zone()->jitZone();
2850 JitCode* regExpMatcherStub =
2851 jitZone->regExpMatcherStubNoBarrier(&zoneStubsToReadBarrier_);
2852 masm.call(regExpMatcherStub);
2853 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
2854 masm.bind(ool->rejoin());
2855
2856 masm.freeStack(RegExpReservedStack);
2857}
2858
2859class OutOfLineRegExpExecMatch : public OutOfLineCodeBase<CodeGenerator> {
2860 LRegExpExecMatch* lir_;
2861
2862 public:
2863 explicit OutOfLineRegExpExecMatch(LRegExpExecMatch* lir) : lir_(lir) {}
2864
2865 void accept(CodeGenerator* codegen) override {
2866 codegen->visitOutOfLineRegExpExecMatch(this);
2867 }
2868
2869 LRegExpExecMatch* lir() const { return lir_; }
2870};
2871
2872void CodeGenerator::visitOutOfLineRegExpExecMatch(
2873 OutOfLineRegExpExecMatch* ool) {
2874 LRegExpExecMatch* lir = ool->lir();
2875 Register input = ToRegister(lir->string());
2876 Register regexp = ToRegister(lir->regexp());
2877
2878 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2879 regs.take(input);
2880 regs.take(regexp);
2881 Register temp = regs.takeAny();
2882
2883 masm.computeEffectiveAddress(
2884 Address(masm.getStackPointer(), InputOutputDataSize), temp);
2885
2886 pushArg(temp);
2887 pushArg(input);
2888 pushArg(regexp);
2889
2890 // We are not using oolCallVM because we are in a Call and live registers have
2891 // already been saved by the register allocator.
2892 using Fn =
2893 bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input,
2894 MatchPairs* pairs, MutableHandleValue output);
2895 callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir);
2896 masm.jump(ool->rejoin());
2897}
2898
2899void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) {
2900 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2900); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { *((volatile int*)__null) = 2900; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2901 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2901); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { *((volatile int*)__null) = 2901; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2902 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 2902); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { *((volatile int*)__null) = 2902; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2903
2904#if defined(JS_NUNBOX32)
2905 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
2906 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
2907 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
2908 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
2909#elif defined(JS_PUNBOX641)
2910 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
2911 static_assert(RegExpMatcherStringReg != JSReturnReg);
2912#endif
2913
2914 masm.reserveStack(RegExpReservedStack);
2915
2916 auto* ool = new (alloc()) OutOfLineRegExpExecMatch(lir);
2917 addOutOfLineCode(ool, lir->mir());
2918
2919 const JitZone* jitZone = gen->realm->zone()->jitZone();
2920 JitCode* regExpExecMatchStub =
2921 jitZone->regExpExecMatchStubNoBarrier(&zoneStubsToReadBarrier_);
2922 masm.call(regExpExecMatchStub);
2923 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
2924
2925 masm.bind(ool->rejoin());
2926 masm.freeStack(RegExpReservedStack);
2927}
2928
2929JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) {
2930 JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub");
2931
2932 Register regexp = RegExpSearcherRegExpReg;
2933 Register input = RegExpSearcherStringReg;
2934 Register lastIndex = RegExpSearcherLastIndexReg;
2935 Register result = ReturnReg;
2936
2937 // We are free to clobber all registers, as LRegExpSearcher is a call
2938 // instruction.
2939 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2940 regs.take(input);
2941 regs.take(regexp);
2942 regs.take(lastIndex);
2943
2944 Register temp1 = regs.takeAny();
2945 Register temp2 = regs.takeAny();
2946 Register temp3 = regs.takeAny();
2947
2948 TempAllocator temp(&cx->tempLifoAlloc());
2949 JitContext jcx(cx);
2950 StackMacroAssembler masm(cx, temp);
2951 AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub");
2952
2953#ifdef JS_USE_LINK_REGISTER
2954 masm.pushReturnAddress();
2955#endif
2956 masm.push(FramePointer);
2957 masm.moveStackPtrTo(FramePointer);
2958
2959#ifdef DEBUG1
2960 // Store sentinel value to cx->regExpSearcherLastLimit.
2961 // See comment in RegExpSearcherImpl.
2962 masm.loadJSContext(temp1);
2963 masm.store32(Imm32(RegExpSearcherLastLimitSentinel),
2964 Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit()));
2965#endif
2966
2967 // The InputOutputData is placed above the frame pointer and return address on
2968 // the stack.
2969 int32_t inputOutputDataStartOffset = 2 * sizeof(void*);
2970
2971 Label notFound, oolEntry;
2972 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
2973 temp3, inputOutputDataStartOffset,
2974 initialStringHeap, &notFound, &oolEntry)) {
2975 return nullptr;
2976 }
2977
2978 // clang-format off
2979 /*
2980 * [SMDOC] Stack layout for the RegExpSearcher stub
2981 *
2982 * +---------------+
2983 * FramePointer +-----> |Caller-FramePtr|
2984 * +---------------+
2985 * |Return-Address |
2986 * +---------------+
2987 * inputOutputDataStartOffset +-----> +---------------+
2988 * |InputOutputData|
2989 * +---------------+
2990 * +---------------+
2991 * | MatchPairs |
2992 * | count |
2993 * | pairs |
2994 * | |
2995 * +---------------+
2996 * pairsVectorStartOffset +-----> +---------------+
2997 * | MatchPair |
2998 * matchPairStart +------------> start | <-------+
2999 * matchPairLimit +------------> limit | | Reserved space for
3000 * +---------------+ | `RegExpObject::MaxPairCount`
3001 * . | MatchPair objects.
3002 * . |
3003 * . | Only a single object will
3004 * +---------------+ | be initialized and can be
3005 * | MatchPair | | accessed below.
3006 * | start | <-------+
3007 * | limit |
3008 * +---------------+
3009 */
3010 // clang-format on
3011
3012 int32_t pairsVectorStartOffset =
3013 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
3014 Address matchPairStart(FramePointer,
3015 pairsVectorStartOffset + MatchPair::offsetOfStart());
3016 Address matchPairLimit(FramePointer,
3017 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3018
3019 // Store match limit to cx->regExpSearcherLastLimit and return the index.
3020 masm.load32(matchPairLimit, result);
3021 masm.loadJSContext(input);
3022 masm.store32(result,
3023 Address(input, JSContext::offsetOfRegExpSearcherLastLimit()));
3024 masm.load32(matchPairStart, result);
3025 masm.pop(FramePointer);
3026 masm.ret();
3027
3028 masm.bind(&notFound);
3029 masm.move32(Imm32(RegExpSearcherResultNotFound), result);
3030 masm.pop(FramePointer);
3031 masm.ret();
3032
3033 masm.bind(&oolEntry);
3034 masm.move32(Imm32(RegExpSearcherResultFailed), result);
3035 masm.pop(FramePointer);
3036 masm.ret();
3037
3038 Linker linker(masm);
3039 JitCode* code = linker.newCode(cx, CodeKind::Other);
3040 if (!code) {
3041 return nullptr;
3042 }
3043
3044 CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub");
3045#ifdef MOZ_VTUNE1
3046 vtune::MarkStub(code, "RegExpSearcherStub");
3047#endif
3048
3049 return code;
3050}
3051
3052class OutOfLineRegExpSearcher : public OutOfLineCodeBase<CodeGenerator> {
3053 LRegExpSearcher* lir_;
3054
3055 public:
3056 explicit OutOfLineRegExpSearcher(LRegExpSearcher* lir) : lir_(lir) {}
3057
3058 void accept(CodeGenerator* codegen) override {
3059 codegen->visitOutOfLineRegExpSearcher(this);
3060 }
3061
3062 LRegExpSearcher* lir() const { return lir_; }
3063};
3064
3065void CodeGenerator::visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool) {
3066 LRegExpSearcher* lir = ool->lir();
3067 Register lastIndex = ToRegister(lir->lastIndex());
3068 Register input = ToRegister(lir->string());
3069 Register regexp = ToRegister(lir->regexp());
3070
3071 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3072 regs.take(lastIndex);
3073 regs.take(input);
3074 regs.take(regexp);
3075 Register temp = regs.takeAny();
3076
3077 masm.computeEffectiveAddress(
3078 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3079
3080 pushArg(temp);
3081 pushArg(lastIndex);
3082 pushArg(input);
3083 pushArg(regexp);
3084
3085 // We are not using oolCallVM because we are in a Call, and that live
3086 // registers are already saved by the the register allocator.
3087 using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input,
3088 int32_t lastIndex, MatchPairs* pairs, int32_t* result);
3089 callVM<Fn, RegExpSearcherRaw>(lir);
3090
3091 masm.jump(ool->rejoin());
3092}
3093
3094void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) {
3095 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3095); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg"
")"); do { *((volatile int*)__null) = 3095; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3096 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg"
")"); do { *((volatile int*)__null) = 3096; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3097 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3097); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg"
")"); do { *((volatile int*)__null) = 3097; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3098 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3098); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { *((volatile int*)__null) = 3098; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3099
3100 static_assert(RegExpSearcherRegExpReg != ReturnReg);
3101 static_assert(RegExpSearcherStringReg != ReturnReg);
3102 static_assert(RegExpSearcherLastIndexReg != ReturnReg);
3103
3104 masm.reserveStack(RegExpReservedStack);
3105
3106 OutOfLineRegExpSearcher* ool = new (alloc()) OutOfLineRegExpSearcher(lir);
3107 addOutOfLineCode(ool, lir->mir());
3108
3109 const JitZone* jitZone = gen->realm->zone()->jitZone();
3110 JitCode* regExpSearcherStub =
3111 jitZone->regExpSearcherStubNoBarrier(&zoneStubsToReadBarrier_);
3112 masm.call(regExpSearcherStub);
3113 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed),
3114 ool->entry());
3115 masm.bind(ool->rejoin());
3116
3117 masm.freeStack(RegExpReservedStack);
3118}
3119
3120void CodeGenerator::visitRegExpSearcherLastLimit(
3121 LRegExpSearcherLastLimit* lir) {
3122 Register result = ToRegister(lir->output());
3123 Register scratch = ToRegister(lir->temp0());
3124
3125 masm.loadAndClearRegExpSearcherLastLimit(result, scratch);
3126}
3127
3128JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) {
3129 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub");
3130
3131 Register regexp = RegExpExecTestRegExpReg;
3132 Register input = RegExpExecTestStringReg;
3133 Register result = ReturnReg;
3134
3135 TempAllocator temp(&cx->tempLifoAlloc());
3136 JitContext jcx(cx);
3137 StackMacroAssembler masm(cx, temp);
3138 AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub");
3139
3140#ifdef JS_USE_LINK_REGISTER
3141 masm.pushReturnAddress();
3142#endif
3143 masm.push(FramePointer);
3144 masm.moveStackPtrTo(FramePointer);
3145
3146 // We are free to clobber all registers, as LRegExpExecTest is a call
3147 // instruction.
3148 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3149 regs.take(input);
3150 regs.take(regexp);
3151
3152 // Ensure lastIndex != result.
3153 regs.take(result);
3154 Register lastIndex = regs.takeAny();
3155 regs.add(result);
3156 Register temp1 = regs.takeAny();
3157 Register temp2 = regs.takeAny();
3158 Register temp3 = regs.takeAny();
3159
3160 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
3161 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
3162
3163 masm.reserveStack(RegExpReservedStack);
3164
3165 // Load lastIndex and skip RegExp execution if needed.
3166 Label notFoundZeroLastIndex;
3167 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
3168
3169 // In visitRegExpMatcher and visitRegExpSearcher, we reserve stack space
3170 // before calling the stub. For RegExpExecTest we call the stub before
3171 // reserving stack space, so the offset of the InputOutputData relative to the
3172 // frame pointer is negative.
3173 constexpr int32_t inputOutputDataStartOffset = -int32_t(RegExpReservedStack);
3174
3175 // On ARM64, load/store instructions can encode an immediate offset in the
3176 // range [-256, 4095]. If we ever fail this assertion, it would be more
3177 // efficient to store the data above the frame pointer similar to
3178 // RegExpMatcher and RegExpSearcher.
3179 static_assert(inputOutputDataStartOffset >= -256);
3180
3181 Label notFound, oolEntry;
3182 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3183 temp3, inputOutputDataStartOffset,
3184 initialStringHeap, &notFound, &oolEntry)) {
3185 return nullptr;
3186 }
3187
3188 // Set `result` to true/false to indicate found/not-found, or to
3189 // RegExpExecTestResultFailed if we have to retry in C++. If the regular
3190 // expression is global or sticky, we also have to update its .lastIndex slot.
3191
3192 Label done;
3193 int32_t pairsVectorStartOffset =
3194 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
3195 Address matchPairLimit(FramePointer,
3196 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3197
3198 masm.move32(Imm32(1), result);
3199 masm.branchTest32(Assembler::Zero, flagsSlot,
3200 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3201 &done);
3202 masm.load32(matchPairLimit, lastIndex);
3203 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
3204 masm.jump(&done);
3205
3206 masm.bind(&notFound);
3207 masm.move32(Imm32(0), result);
3208 masm.branchTest32(Assembler::Zero, flagsSlot,
3209 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3210 &done);
3211 masm.storeValue(Int32Value(0), lastIndexSlot);
3212 masm.jump(&done);
3213
3214 masm.bind(&notFoundZeroLastIndex);
3215 masm.move32(Imm32(0), result);
3216 masm.storeValue(Int32Value(0), lastIndexSlot);
3217 masm.jump(&done);
3218
3219 masm.bind(&oolEntry);
3220 masm.move32(Imm32(RegExpExecTestResultFailed), result);
3221
3222 masm.bind(&done);
3223 masm.freeStack(RegExpReservedStack);
3224 masm.pop(FramePointer);
3225 masm.ret();
3226
3227 Linker linker(masm);
3228 JitCode* code = linker.newCode(cx, CodeKind::Other);
3229 if (!code) {
3230 return nullptr;
3231 }
3232
3233 CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub");
3234#ifdef MOZ_VTUNE1
3235 vtune::MarkStub(code, "RegExpExecTestStub");
3236#endif
3237
3238 return code;
3239}
3240
3241class OutOfLineRegExpExecTest : public OutOfLineCodeBase<CodeGenerator> {
3242 LRegExpExecTest* lir_;
3243
3244 public:
3245 explicit OutOfLineRegExpExecTest(LRegExpExecTest* lir) : lir_(lir) {}
3246
3247 void accept(CodeGenerator* codegen) override {
3248 codegen->visitOutOfLineRegExpExecTest(this);
3249 }
3250
3251 LRegExpExecTest* lir() const { return lir_; }
3252};
3253
3254void CodeGenerator::visitOutOfLineRegExpExecTest(OutOfLineRegExpExecTest* ool) {
3255 LRegExpExecTest* lir = ool->lir();
3256 Register input = ToRegister(lir->string());
3257 Register regexp = ToRegister(lir->regexp());
3258
3259 pushArg(input);
3260 pushArg(regexp);
3261
3262 // We are not using oolCallVM because we are in a Call and live registers have
3263 // already been saved by the register allocator.
3264 using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp,
3265 HandleString input, bool* result);
3266 callVM<Fn, RegExpBuiltinExecTestFromJit>(lir);
3267
3268 masm.jump(ool->rejoin());
3269}
3270
3271void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) {
3272 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg"
")"); do { *((volatile int*)__null) = 3272; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3273 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3273); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg"
")"); do { *((volatile int*)__null) = 3273; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3274 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3274); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { *((volatile int*)__null) = 3274; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3275
3276 static_assert(RegExpExecTestRegExpReg != ReturnReg);
3277 static_assert(RegExpExecTestStringReg != ReturnReg);
3278
3279 auto* ool = new (alloc()) OutOfLineRegExpExecTest(lir);
3280 addOutOfLineCode(ool, lir->mir());
3281
3282 const JitZone* jitZone = gen->realm->zone()->jitZone();
3283 JitCode* regExpExecTestStub =
3284 jitZone->regExpExecTestStubNoBarrier(&zoneStubsToReadBarrier_);
3285 masm.call(regExpExecTestStub);
3286
3287 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed),
3288 ool->entry());
3289
3290 masm.bind(ool->rejoin());
3291}
3292
3293void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) {
3294 Register regexp = ToRegister(ins->regexp());
3295 Register input = ToRegister(ins->input());
3296 Register output = ToRegister(ins->output());
3297
3298 using Fn =
3299 bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*);
3300 auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>(
3301 ins, ArgList(regexp, input), StoreRegisterTo(output));
3302
3303 // Load RegExpShared in |output|.
3304 Label vmCall;
3305 masm.loadParsedRegExpShared(regexp, output, ool->entry());
3306
3307 // Return true iff pairCount > 1.
3308 Label returnTrue;
3309 masm.branch32(Assembler::Above,
3310 Address(output, RegExpShared::offsetOfPairCount()), Imm32(1),
3311 &returnTrue);
3312 masm.move32(Imm32(0), output);
3313 masm.jump(ool->rejoin());
3314
3315 masm.bind(&returnTrue);
3316 masm.move32(Imm32(1), output);
3317
3318 masm.bind(ool->rejoin());
3319}
3320
3321class OutOfLineRegExpPrototypeOptimizable
3322 : public OutOfLineCodeBase<CodeGenerator> {
3323 LRegExpPrototypeOptimizable* ins_;
3324
3325 public:
3326 explicit OutOfLineRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
3327 : ins_(ins) {}
3328
3329 void accept(CodeGenerator* codegen) override {
3330 codegen->visitOutOfLineRegExpPrototypeOptimizable(this);
3331 }
3332 LRegExpPrototypeOptimizable* ins() const { return ins_; }
3333};
3334
3335void CodeGenerator::visitRegExpPrototypeOptimizable(
3336 LRegExpPrototypeOptimizable* ins) {
3337 Register object = ToRegister(ins->object());
3338 Register output = ToRegister(ins->output());
3339 Register temp = ToRegister(ins->temp0());
3340
3341 OutOfLineRegExpPrototypeOptimizable* ool =
3342 new (alloc()) OutOfLineRegExpPrototypeOptimizable(ins);
3343 addOutOfLineCode(ool, ins->mir());
3344
3345 const GlobalObject* global = gen->realm->maybeGlobal();
3346 MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(global)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("global", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3346); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")"
); do { *((volatile int*)__null) = 3346; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3347 masm.branchIfNotRegExpPrototypeOptimizable(object, temp, global,
3348 ool->entry());
3349 masm.move32(Imm32(0x1), output);
3350
3351 masm.bind(ool->rejoin());
3352}
3353
3354void CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable(
3355 OutOfLineRegExpPrototypeOptimizable* ool) {
3356 LRegExpPrototypeOptimizable* ins = ool->ins();
3357 Register object = ToRegister(ins->object());
3358 Register output = ToRegister(ins->output());
3359
3360 saveVolatile(output);
3361
3362 using Fn = bool (*)(JSContext* cx, JSObject* proto);
3363 masm.setupAlignedABICall();
3364 masm.loadJSContext(output);
3365 masm.passABIArg(output);
3366 masm.passABIArg(object);
3367 masm.callWithABI<Fn, RegExpPrototypeOptimizableRaw>();
3368 masm.storeCallBoolResult(output);
3369
3370 restoreVolatile(output);
3371
3372 masm.jump(ool->rejoin());
3373}
3374
3375class OutOfLineRegExpInstanceOptimizable
3376 : public OutOfLineCodeBase<CodeGenerator> {
3377 LRegExpInstanceOptimizable* ins_;
3378
3379 public:
3380 explicit OutOfLineRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
3381 : ins_(ins) {}
3382
3383 void accept(CodeGenerator* codegen) override {
3384 codegen->visitOutOfLineRegExpInstanceOptimizable(this);
3385 }
3386 LRegExpInstanceOptimizable* ins() const { return ins_; }
3387};
3388
3389void CodeGenerator::visitRegExpInstanceOptimizable(
3390 LRegExpInstanceOptimizable* ins) {
3391 Register object = ToRegister(ins->object());
3392 Register output = ToRegister(ins->output());
3393 Register temp = ToRegister(ins->temp0());
3394
3395 OutOfLineRegExpInstanceOptimizable* ool =
3396 new (alloc()) OutOfLineRegExpInstanceOptimizable(ins);
3397 addOutOfLineCode(ool, ins->mir());
3398
3399 const GlobalObject* global = gen->realm->maybeGlobal();
3400 MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(global)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("global", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3400); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")"
); do { *((volatile int*)__null) = 3400; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3401 masm.branchIfNotRegExpInstanceOptimizable(object, temp, global, ool->entry());
3402 masm.move32(Imm32(0x1), output);
3403
3404 masm.bind(ool->rejoin());
3405}
3406
3407void CodeGenerator::visitOutOfLineRegExpInstanceOptimizable(
3408 OutOfLineRegExpInstanceOptimizable* ool) {
3409 LRegExpInstanceOptimizable* ins = ool->ins();
3410 Register object = ToRegister(ins->object());
3411 Register proto = ToRegister(ins->proto());
3412 Register output = ToRegister(ins->output());
3413
3414 saveVolatile(output);
3415
3416 using Fn = bool (*)(JSContext* cx, JSObject* obj, JSObject* proto);
3417 masm.setupAlignedABICall();
3418 masm.loadJSContext(output);
3419 masm.passABIArg(output);
3420 masm.passABIArg(object);
3421 masm.passABIArg(proto);
3422 masm.callWithABI<Fn, RegExpInstanceOptimizableRaw>();
3423 masm.storeCallBoolResult(output);
3424
3425 restoreVolatile(output);
3426
3427 masm.jump(ool->rejoin());
3428}
3429
3430static void FindFirstDollarIndex(MacroAssembler& masm, Register str,
3431 Register len, Register temp0, Register temp1,
3432 Register output, CharEncoding encoding) {
3433#ifdef DEBUG1
3434 Label ok;
3435 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
3436 masm.assumeUnreachable("Length should be greater than 0.");
3437 masm.bind(&ok);
3438#endif
3439
3440 Register chars = temp0;
3441 masm.loadStringChars(str, chars, encoding);
3442
3443 masm.move32(Imm32(0), output);
3444
3445 Label start, done;
3446 masm.bind(&start);
3447
3448 Register currentChar = temp1;
3449 masm.loadChar(chars, output, currentChar, encoding);
3450 masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done);
3451
3452 masm.add32(Imm32(1), output);
3453 masm.branch32(Assembler::NotEqual, output, len, &start);
3454
3455 masm.move32(Imm32(-1), output);
3456
3457 masm.bind(&done);
3458}
3459
3460void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) {
3461 Register str = ToRegister(ins->str());
3462 Register output = ToRegister(ins->output());
3463 Register temp0 = ToRegister(ins->temp0());
3464 Register temp1 = ToRegister(ins->temp1());
3465 Register len = ToRegister(ins->temp2());
3466
3467 using Fn = bool (*)(JSContext*, JSString*, int32_t*);
3468 OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>(
3469 ins, ArgList(str), StoreRegisterTo(output));
3470
3471 masm.branchIfRope(str, ool->entry());
3472 masm.loadStringLength(str, len);
3473
3474 Label isLatin1, done;
3475 masm.branchLatin1String(str, &isLatin1);
3476 {
3477 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3478 CharEncoding::TwoByte);
3479 masm.jump(&done);
3480 }
3481 masm.bind(&isLatin1);
3482 {
3483 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3484 CharEncoding::Latin1);
3485 }
3486 masm.bind(&done);
3487 masm.bind(ool->rejoin());
3488}
3489
3490void CodeGenerator::visitStringReplace(LStringReplace* lir) {
3491 if (lir->replacement()->isConstant()) {
3492 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
3493 } else {
3494 pushArg(ToRegister(lir->replacement()));
3495 }
3496
3497 if (lir->pattern()->isConstant()) {
3498 pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString()));
3499 } else {
3500 pushArg(ToRegister(lir->pattern()));
3501 }
3502
3503 if (lir->string()->isConstant()) {
3504 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
3505 } else {
3506 pushArg(ToRegister(lir->string()));
3507 }
3508
3509 using Fn =
3510 JSString* (*)(JSContext*, HandleString, HandleString, HandleString);
3511 if (lir->mir()->isFlatReplacement()) {
3512 callVM<Fn, StringFlatReplaceString>(lir);
3513 } else {
3514 callVM<Fn, StringReplace>(lir);
3515 }
3516}
3517
3518void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) {
3519 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3520 TypedOrValueRegister lhs =
3521 TypedOrValueRegister(ToValue(lir, LBinaryValueCache::LhsIndex));
3522 TypedOrValueRegister rhs =
3523 TypedOrValueRegister(ToValue(lir, LBinaryValueCache::RhsIndex));
3524 ValueOperand output = ToOutValue(lir);
3525
3526 JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
3527
3528 switch (jsop) {
3529 case JSOp::Add:
3530 case JSOp::Sub:
3531 case JSOp::Mul:
3532 case JSOp::Div:
3533 case JSOp::Mod:
3534 case JSOp::Pow:
3535 case JSOp::BitAnd:
3536 case JSOp::BitOr:
3537 case JSOp::BitXor:
3538 case JSOp::Lsh:
3539 case JSOp::Rsh:
3540 case JSOp::Ursh: {
3541 IonBinaryArithIC ic(liveRegs, lhs, rhs, output);
3542 addIC(lir, allocateIC(ic));
3543 return;
3544 }
3545 default:
3546 MOZ_CRASH("Unsupported jsop in MBinaryValueCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryValueCache"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3546); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache"
")"); do { *((volatile int*)__null) = 3546; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3547 }
3548}
3549
3550void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) {
3551 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3552 TypedOrValueRegister lhs =
3553 TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::LhsIndex));
3554 TypedOrValueRegister rhs =
3555 TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::RhsIndex));
3556 Register output = ToRegister(lir->output());
3557
3558 JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
3559
3560 switch (jsop) {
3561 case JSOp::Lt:
3562 case JSOp::Le:
3563 case JSOp::Gt:
3564 case JSOp::Ge:
3565 case JSOp::Eq:
3566 case JSOp::Ne:
3567 case JSOp::StrictEq:
3568 case JSOp::StrictNe: {
3569 IonCompareIC ic(liveRegs, lhs, rhs, output);
3570 addIC(lir, allocateIC(ic));
3571 return;
3572 }
3573 default:
3574 MOZ_CRASH("Unsupported jsop in MBinaryBoolCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryBoolCache"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3574); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache"
")"); do { *((volatile int*)__null) = 3574; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3575 }
3576}
3577
3578void CodeGenerator::visitUnaryCache(LUnaryCache* lir) {
3579 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3580 TypedOrValueRegister input =
3581 TypedOrValueRegister(ToValue(lir, LUnaryCache::InputIndex));
3582 ValueOperand output = ToOutValue(lir);
3583
3584 IonUnaryArithIC ic(liveRegs, input, output);
3585 addIC(lir, allocateIC(ic));
3586}
3587
3588void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) {
3589 pushArg(ImmPtr(lir->mir()->module()));
3590
3591 using Fn = JSObject* (*)(JSContext*, HandleObject);
3592 callVM<Fn, js::GetOrCreateModuleMetaObject>(lir);
3593}
3594
3595void CodeGenerator::visitDynamicImport(LDynamicImport* lir) {
3596 pushArg(ToValue(lir, LDynamicImport::OptionsIndex));
3597 pushArg(ToValue(lir, LDynamicImport::SpecifierIndex));
3598 pushArg(ImmGCPtr(current->mir()->info().script()));
3599
3600 using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue);
3601 callVM<Fn, js::StartDynamicModuleImport>(lir);
3602}
3603
3604void CodeGenerator::visitLambda(LLambda* lir) {
3605 Register envChain = ToRegister(lir->environmentChain());
3606 Register output = ToRegister(lir->output());
3607 Register tempReg = ToRegister(lir->temp0());
3608
3609 JSFunction* fun = lir->mir()->templateFunction();
3610
3611 using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject);
3612 OutOfLineCode* ool = oolCallVM<Fn, js::Lambda>(
3613 lir, ArgList(ImmGCPtr(fun), envChain), StoreRegisterTo(output));
3614
3615 TemplateObject templateObject(fun);
3616 masm.createGCObject(output, tempReg, templateObject, gc::Heap::Default,
3617 ool->entry());
3618
3619 masm.storeValue(JSVAL_TYPE_OBJECT, envChain,
3620 Address(output, JSFunction::offsetOfEnvironment()));
3621 // No post barrier needed because output is guaranteed to be allocated in
3622 // the nursery.
3623
3624 masm.bind(ool->rejoin());
3625}
3626
3627void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) {
3628 Register envChain = ToRegister(lir->envChain());
3629 Register prototype = ToRegister(lir->prototype());
3630
3631 pushArg(prototype);
3632 pushArg(envChain);
3633 pushArg(ImmGCPtr(lir->mir()->function()));
3634
3635 using Fn =
3636 JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject);
3637 callVM<Fn, js::FunWithProtoOperation>(lir);
3638}
3639
3640void CodeGenerator::visitSetFunName(LSetFunName* lir) {
3641 pushArg(Imm32(lir->mir()->prefixKind()));
3642 pushArg(ToValue(lir, LSetFunName::NameIndex));
3643 pushArg(ToRegister(lir->fun()));
3644
3645 using Fn =
3646 bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
3647 callVM<Fn, js::SetFunctionName>(lir);
3648}
3649
3650void CodeGenerator::visitOsiPoint(LOsiPoint* lir) {
3651 // Note: markOsiPoint ensures enough space exists between the last
3652 // LOsiPoint and this one to patch adjacent call instructions.
3653
3654 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3654); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 3654; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3655
3656 uint32_t osiCallPointOffset = markOsiPoint(lir);
3657
3658 LSafepoint* safepoint = lir->associatedSafepoint();
3659 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3659); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()"
")"); do { *((volatile int*)__null) = 3659; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3660 safepoint->setOsiCallPointOffset(osiCallPointOffset);
3661
3662#ifdef DEBUG1
3663 // There should be no movegroups or other instructions between
3664 // an instruction and its OsiPoint. This is necessary because
3665 // we use the OsiPoint's snapshot from within VM calls.
3666 for (LInstructionReverseIterator iter(current->rbegin(lir));
3667 iter != current->rend(); iter++) {
3668 if (*iter == lir) {
3669 continue;
3670 }
3671 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3671); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!iter->isMoveGroup()"
")"); do { *((volatile int*)__null) = 3671; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3672 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3672); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint"
")"); do { *((volatile int*)__null) = 3672; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3673 break;
3674 }
3675#endif
3676
3677#ifdef CHECK_OSIPOINT_REGISTERS1
3678 if (shouldVerifyOsiPointRegs(safepoint)) {
3679 verifyOsiPointRegs(safepoint);
3680 }
3681#endif
3682}
3683
3684void CodeGenerator::visitPhi(LPhi* lir) {
3685 MOZ_CRASH("Unexpected LPhi in CodeGenerator")do { do { } while (false); MOZ_ReportCrash("" "Unexpected LPhi in CodeGenerator"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3685); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator"
")"); do { *((volatile int*)__null) = 3685; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3686}
3687
3688void CodeGenerator::visitGoto(LGoto* lir) { jumpToBlock(lir->target()); }
3689
3690void CodeGenerator::visitTableSwitch(LTableSwitch* ins) {
3691 MTableSwitch* mir = ins->mir();
3692 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
3693 const LAllocation* temp;
3694
3695 if (mir->getOperand(0)->type() != MIRType::Int32) {
3696 temp = ins->tempInt()->output();
3697
3698 // The input is a double, so try and convert it to an integer.
3699 // If it does not fit in an integer, take the default case.
3700 masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp),
3701 defaultcase, false);
3702 } else {
3703 temp = ins->index();
3704 }
3705
3706 emitTableSwitchDispatch(mir, ToRegister(temp),
3707 ToRegisterOrInvalid(ins->tempPointer()));
3708}
3709
3710void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) {
3711 MTableSwitch* mir = ins->mir();
3712 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
3713
3714 Register index = ToRegister(ins->tempInt());
3715 ValueOperand value = ToValue(ins, LTableSwitchV::InputValue);
3716 Register tag = masm.extractTag(value, index);
3717 masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase);
3718
3719 Label unboxInt, isInt;
3720 masm.branchTestInt32(Assembler::Equal, tag, &unboxInt);
3721 {
3722 FloatRegister floatIndex = ToFloatRegister(ins->tempFloat());
3723 masm.unboxDouble(value, floatIndex);
3724 masm.convertDoubleToInt32(floatIndex, index, defaultcase, false);
3725 masm.jump(&isInt);
3726 }
3727
3728 masm.bind(&unboxInt);
3729 masm.unboxInt32(value, index);
3730
3731 masm.bind(&isInt);
3732
3733 emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
3734}
3735
3736void CodeGenerator::visitParameter(LParameter* lir) {}
3737
3738void CodeGenerator::visitCallee(LCallee* lir) {
3739 Register callee = ToRegister(lir->output());
3740 Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken());
3741
3742 masm.loadFunctionFromCalleeToken(ptr, callee);
3743}
3744
3745void CodeGenerator::visitIsConstructing(LIsConstructing* lir) {
3746 Register output = ToRegister(lir->output());
3747 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
3748 masm.loadPtr(calleeToken, output);
3749
3750 // We must be inside a function.
3751 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3751); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()"
")"); do { *((volatile int*)__null) = 3751; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3752
3753 // The low bit indicates whether this call is constructing, just clear the
3754 // other bits.
3755 static_assert(CalleeToken_Function == 0x0,
3756 "CalleeTokenTag value should match");
3757 static_assert(CalleeToken_FunctionConstructing == 0x1,
3758 "CalleeTokenTag value should match");
3759 masm.andPtr(Imm32(0x1), output);
3760}
3761
3762void CodeGenerator::visitReturn(LReturn* lir) {
3763#if defined(JS_NUNBOX32)
3764 DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX);
3765 DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX);
3766 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3766); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type"
")"); do { *((volatile int*)__null) = 3766; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3767 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3767); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data"
")"); do { *((volatile int*)__null) = 3767; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3768#elif defined(JS_PUNBOX641)
3769 DebugOnly<LAllocation*> result = lir->getOperand(0);
3770 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3770); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg"
")"); do { *((volatile int*)__null) = 3770; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3771#endif
3772 // Don't emit a jump to the return label if this is the last block, as
3773 // it'll fall through to the epilogue.
3774 //
3775 // This is -not- true however for a Generator-return, which may appear in the
3776 // middle of the last block, so we should always emit the jump there.
3777 if (current->mir() != *gen->graph().poBegin() || lir->isGenerator()) {
3778 masm.jump(&returnLabel_);
3779 }
3780}
3781
3782void CodeGenerator::visitOsrEntry(LOsrEntry* lir) {
3783 Register temp = ToRegister(lir->temp());
3784
3785 // Remember the OSR entry offset into the code buffer.
3786 masm.flushBuffer();
3787 setOsrEntryOffset(masm.size());
3788
3789 // Allocate the full frame for this function
3790 // Note we have a new entry here. So we reset MacroAssembler::framePushed()
3791 // to 0, before reserving the stack.
3792 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3792); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 3792; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3793 masm.setFramePushed(0);
3794
3795 // The Baseline code ensured both the frame pointer and stack pointer point to
3796 // the JitFrameLayout on the stack.
3797
3798 // If profiling, save the current frame pointer to a per-thread global field.
3799 if (isProfilerInstrumentationEnabled()) {
3800 masm.profilerEnterFrame(FramePointer, temp);
3801 }
3802
3803 masm.reserveStack(frameSize());
3804 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3804); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 3804; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3805
3806 // Ensure that the Ion frames is properly aligned.
3807 masm.assertStackAlignment(JitStackAlignment, 0);
3808}
3809
3810void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) {
3811 const LAllocation* frame = lir->getOperand(0);
3812 const LDefinition* object = lir->getDef(0);
3813
3814 const ptrdiff_t frameOffset =
3815 BaselineFrame::reverseOffsetOfEnvironmentChain();
3816
3817 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
3818}
3819
3820void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) {
3821 const LAllocation* frame = lir->getOperand(0);
3822 const LDefinition* object = lir->getDef(0);
3823
3824 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj();
3825
3826 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
3827}
3828
3829void CodeGenerator::visitOsrValue(LOsrValue* value) {
3830 const LAllocation* frame = value->getOperand(0);
3831 const ValueOperand out = ToOutValue(value);
3832
3833 const ptrdiff_t frameOffset = value->mir()->frameOffset();
3834
3835 masm.loadValue(Address(ToRegister(frame), frameOffset), out);
3836}
3837
3838void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) {
3839 const LAllocation* frame = lir->getOperand(0);
3840 const ValueOperand out = ToOutValue(lir);
3841
3842 Address flags =
3843 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags());
3844 Address retval =
3845 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue());
3846
3847 masm.moveValue(UndefinedValue(), out);
3848
3849 Label done;
3850 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL),
3851 &done);
3852 masm.loadValue(retval, out);
3853 masm.bind(&done);
3854}
3855
3856void CodeGenerator::visitStackArgT(LStackArgT* lir) {
3857 const LAllocation* arg = lir->arg();
3858 MIRType argType = lir->type();
3859 uint32_t argslot = lir->argslot();
3860 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3860); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { *((volatile int*)__null) = 3860; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3861
3862 Address dest = AddressOfPassedArg(argslot);
3863
3864 if (arg->isFloatReg()) {
3865 masm.boxDouble(ToFloatRegister(arg), dest);
3866 } else if (arg->isRegister()) {
3867 masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest);
3868 } else {
3869 masm.storeValue(arg->toConstant()->toJSValue(), dest);
3870 }
3871}
3872
3873void CodeGenerator::visitStackArgV(LStackArgV* lir) {
3874 ValueOperand val = ToValue(lir, 0);
3875 uint32_t argslot = lir->argslot();
3876 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3876); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { *((volatile int*)__null) = 3876; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3877
3878 masm.storeValue(val, AddressOfPassedArg(argslot));
3879}
3880
3881void CodeGenerator::visitMoveGroup(LMoveGroup* group) {
3882 if (!group->numMoves()) {
3883 return;
3884 }
3885
3886 MoveResolver& resolver = masm.moveResolver();
3887
3888 for (size_t i = 0; i < group->numMoves(); i++) {
3889 const LMove& move = group->getMove(i);
3890
3891 LAllocation from = move.from();
3892 LAllocation to = move.to();
3893 LDefinition::Type type = move.type();
3894
3895 // No bogus moves.
3896 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3896); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to"
")"); do { *((volatile int*)__null) = 3896; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3897 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3897); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!from.isConstant()"
")"); do { *((volatile int*)__null) = 3897; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3898 MoveOp::Type moveType;
3899 switch (type) {
3900 case LDefinition::OBJECT:
3901 case LDefinition::SLOTS:
3902 case LDefinition::WASM_ANYREF:
3903#ifdef JS_NUNBOX32
3904 case LDefinition::TYPE:
3905 case LDefinition::PAYLOAD:
3906#else
3907 case LDefinition::BOX:
3908#endif
3909 case LDefinition::GENERAL:
3910 case LDefinition::STACKRESULTS:
3911 moveType = MoveOp::GENERAL;
3912 break;
3913 case LDefinition::INT32:
3914 moveType = MoveOp::INT32;
3915 break;
3916 case LDefinition::FLOAT32:
3917 moveType = MoveOp::FLOAT32;
3918 break;
3919 case LDefinition::DOUBLE:
3920 moveType = MoveOp::DOUBLE;
3921 break;
3922 case LDefinition::SIMD128:
3923 moveType = MoveOp::SIMD128;
3924 break;
3925 default:
3926 MOZ_CRASH("Unexpected move type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected move type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 3926); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected move type"
")"); do { *((volatile int*)__null) = 3926; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3927 }
3928
3929 masm.propagateOOM(
3930 resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType));
3931 }
3932
3933 masm.propagateOOM(resolver.resolve());
3934 if (masm.oom()) {
3935 return;
3936 }
3937
3938 MoveEmitter emitter(masm);
3939
3940#ifdef JS_CODEGEN_X86
3941 if (group->maybeScratchRegister().isGeneralReg()) {
3942 emitter.setScratchRegister(
3943 group->maybeScratchRegister().toGeneralReg()->reg());
3944 } else {
3945 resolver.sortMemoryToMemoryMoves();
3946 }
3947#endif
3948
3949 emitter.emit(resolver);
3950 emitter.finish();
3951}
3952
3953void CodeGenerator::visitInteger(LInteger* lir) {
3954 masm.move32(Imm32(lir->i32()), ToRegister(lir->output()));
3955}
3956
3957void CodeGenerator::visitInteger64(LInteger64* lir) {
3958 masm.move64(Imm64(lir->i64()), ToOutRegister64(lir));
3959}
3960
3961void CodeGenerator::visitPointer(LPointer* lir) {
3962 masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output()));
3963}
3964
3965void CodeGenerator::visitNurseryObject(LNurseryObject* lir) {
3966 Register output = ToRegister(lir->output());
3967 uint32_t nurseryIndex = lir->mir()->nurseryIndex();
3968
3969 // Load a pointer to the entry in IonScript's nursery objects list.
3970 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output);
3971 masm.propagateOOM(ionNurseryObjectLabels_.emplaceBack(label, nurseryIndex));
3972
3973 // Load the JSObject*.
3974 masm.loadPtr(Address(output, 0), output);
3975}
3976
3977void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) {
3978 // No-op.
3979}
3980
3981void CodeGenerator::visitDebugEnterGCUnsafeRegion(
3982 LDebugEnterGCUnsafeRegion* lir) {
3983 Register temp = ToRegister(lir->temp0());
3984
3985 masm.loadJSContext(temp);
3986
3987 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
3988 masm.add32(Imm32(1), inUnsafeRegion);
3989
3990 Label ok;
3991 masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok);
3992 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
3993 masm.bind(&ok);
3994}
3995
3996void CodeGenerator::visitDebugLeaveGCUnsafeRegion(
3997 LDebugLeaveGCUnsafeRegion* lir) {
3998 Register temp = ToRegister(lir->temp0());
3999
4000 masm.loadJSContext(temp);
4001
4002 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4003 masm.add32(Imm32(-1), inUnsafeRegion);
4004
4005 Label ok;
4006 masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok);
4007 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4008 masm.bind(&ok);
4009}
4010
4011void CodeGenerator::visitSlots(LSlots* lir) {
4012 Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots());
4013 masm.loadPtr(slots, ToRegister(lir->output()));
4014}
4015
4016void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) {
4017 ValueOperand dest = ToOutValue(lir);
4018 Register base = ToRegister(lir->input());
4019 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4020
4021 masm.loadValue(Address(base, offset), dest);
4022}
4023
4024static ConstantOrRegister ToConstantOrRegister(const LAllocation* value,
4025 MIRType valueType) {
4026 if (value->isConstant()) {
4027 return ConstantOrRegister(value->toConstant()->toJSValue());
4028 }
4029 return TypedOrValueRegister(valueType, ToAnyRegister(value));
4030}
4031
4032void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) {
4033 Register base = ToRegister(lir->slots());
4034 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4035 Address dest(base, offset);
4036
4037 if (lir->mir()->needsBarrier()) {
4038 emitPreBarrier(dest);
4039 }
4040
4041 MIRType valueType = lir->mir()->value()->type();
4042 ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType);
4043 masm.storeUnboxedValue(value, valueType, dest);
4044}
4045
4046void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) {
4047 Register base = ToRegister(lir->slots());
4048 int32_t offset = lir->mir()->slot() * sizeof(Value);
4049
4050 const ValueOperand value = ToValue(lir, LStoreDynamicSlotV::ValueIndex);
4051
4052 if (lir->mir()->needsBarrier()) {
4053 emitPreBarrier(Address(base, offset));
4054 }
4055
4056 masm.storeValue(value, Address(base, offset));
4057}
4058
4059void CodeGenerator::visitElements(LElements* lir) {
4060 Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
4061 masm.loadPtr(elements, ToRegister(lir->output()));
4062}
4063
4064void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) {
4065 Address environment(ToRegister(lir->function()),
4066 JSFunction::offsetOfEnvironment());
4067 masm.unboxObject(environment, ToRegister(lir->output()));
4068}
4069
4070void CodeGenerator::visitHomeObject(LHomeObject* lir) {
4071 Register func = ToRegister(lir->function());
4072 Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
4073
4074 masm.assertFunctionIsExtended(func);
4075#ifdef DEBUG1
4076 Label isObject;
4077 masm.branchTestObject(Assembler::Equal, homeObject, &isObject);
4078 masm.assumeUnreachable("[[HomeObject]] must be Object");
4079 masm.bind(&isObject);
4080#endif
4081
4082 masm.unboxObject(homeObject, ToRegister(lir->output()));
4083}
4084
4085void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) {
4086 Register homeObject = ToRegister(lir->homeObject());
4087 ValueOperand output = ToOutValue(lir);
4088 Register temp = output.scratchReg();
4089
4090 masm.loadObjProto(homeObject, temp);
4091
4092#ifdef DEBUG1
4093 // We won't encounter a lazy proto, because the prototype is guaranteed to
4094 // either be a JSFunction or a PlainObject, and only proxy objects can have a
4095 // lazy proto.
4096 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 4096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 4096; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4097
4098 Label proxyCheckDone;
4099 masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone);
4100 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase");
4101 masm.bind(&proxyCheckDone);
4102#endif
4103
4104 Label nullProto, done;
4105 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
4106
4107 // Box prototype and return
4108 masm.tagValue(JSVAL_TYPE_OBJECT, temp, output);
4109 masm.jump(&done);
4110
4111 masm.bind(&nullProto);
4112 masm.moveValue(NullValue(), output);
4113
4114 masm.bind(&done);
4115}
4116
4117template <class T>
4118static T* ToConstantObject(MDefinition* def) {
4119 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 4119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->isConstant()"
")"); do { *((volatile int*)__null) = 4119; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4120 return &def->toConstant()->toObject().as<T>();
4121}
4122
4123void CodeGenerator::visitNewLexicalEnvironmentObject(
4124 LNewLexicalEnvironmentObject* lir) {
4125 Register output = ToRegister(lir->output());
4126 Register temp = ToRegister(lir->temp0());
4127
4128 auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>(
4129 lir->mir()->templateObj());
4130 auto* scope = &templateObj->scope();
4131 gc::Heap initialHeap = gc::Heap::Default;
4132
4133 using Fn =
4134 BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>);
4135 auto* ool =
4136 oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>(
4137 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4138
4139 TemplateObject templateObject(templateObj);
4140 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4141
4142 masm.bind(ool->rejoin());
4143}
4144
4145void CodeGenerator::visitNewClassBodyEnvironmentObject(
4146 LNewClassBodyEnvironmentObject* lir) {
4147 Register output = ToRegister(lir->output());
4148 Register temp = ToRegister(lir->temp0());
4149
4150 auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>(
4151 lir->mir()->templateObj());
4152 auto* scope = &templateObj->scope();
4153 gc::Heap initialHeap = gc::Heap::Default;
4154
4155 using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*,
4156 Handle<ClassBodyScope*>);
4157 auto* ool =
4158 oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>(
4159 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4160
4161 TemplateObject templateObject(templateObj);
4162 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4163
4164 masm.bind(ool->rejoin());
4165}
4166
4167void CodeGenerator::visitNewVarEnvironmentObject(
4168 LNewVarEnvironmentObject* lir) {
4169 Register output = ToRegister(lir->output());
4170 Register temp = ToRegister(lir->temp0());
4171
4172 auto* templateObj =
4173 ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj());
4174 auto* scope = &templateObj->scope().as<VarScope>();
4175 gc::Heap initialHeap = gc::Heap::Default;
4176
4177 using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>);
4178 auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>(
4179 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4180
4181 TemplateObject templateObject(templateObj);
4182 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4183
4184 masm.bind(ool->rejoin());
4185}
4186
4187void CodeGenerator::visitGuardShape(LGuardShape* guard) {
4188 Register obj = ToRegister(guard->input());
4189 Register temp = ToTempRegisterOrInvalid(guard->temp0());
4190 Label bail;
4191 masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp,
4192 obj, &bail);
4193 bailoutFrom(&bail, guard->snapshot());
4194}
4195
4196void CodeGenerator::visitGuardFuse(LGuardFuse* guard) {
4197 auto fuseIndex = guard->mir()->fuseIndex();
4198 switch (fuseIndex) {
4199 case RealmFuses::FuseIndex::OptimizeGetIteratorFuse:
4200 addOptimizeGetIteratorFuseDependency();
4201 return;
4202 default:
4203 // validateAndRegisterFuseDependencies doesn't have
4204 // handling for this yet, actively check fuse instead.
4205 break;
4206 }
4207
4208 Register temp = ToRegister(guard->temp0());
4209 Label bail;
4210
4211 // Bake specific fuse address for Ion code, because we won't share this code
4212 // across realms.
4213 GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex);
4214 masm.loadPtr(AbsoluteAddress(fuse->fuseRef()), temp);
4215 masm.branchPtr(Assembler::NotEqual, temp, ImmPtr(nullptr), &bail);
4216
4217 bailoutFrom(&bail, guard->snapshot());
4218}
4219
4220void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) {
4221 Register obj = ToRegister(guard->object());
4222 Register shapeList = ToRegister(guard->shapeList());
4223 Register temp = ToRegister(guard->temp0());
4224 Register temp2 = ToRegister(guard->temp1());
4225 Register temp3 = ToRegister(guard->temp2());
4226 Register spectre = ToTempRegisterOrInvalid(guard->temp3());
4227
4228 Label bail;
4229 masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp);
4230 masm.branchTestObjShapeList(Assembler::NotEqual, obj, temp, temp2, temp3,
4231 spectre, &bail);
4232 bailoutFrom(&bail, guard->snapshot());
4233}
4234
4235void CodeGenerator::visitGuardProto(LGuardProto* guard) {
4236 Register obj = ToRegister(guard->object());
4237 Register expected = ToRegister(guard->expected());
4238 Register temp = ToRegister(guard->temp0());
4239
4240 masm.loadObjProto(obj, temp);
4241
4242 Label bail;
4243 masm.branchPtr(Assembler::NotEqual, temp, expected, &bail);
4244 bailoutFrom(&bail, guard->snapshot());
4245}
4246
4247void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) {
4248 Register obj = ToRegister(guard->input());
4249 Register temp = ToRegister(guard->temp0());
4250
4251 masm.loadObjProto(obj, temp);
4252
4253 Label bail;
4254 masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail);
4255 bailoutFrom(&bail, guard->snapshot());
4256}
4257
4258void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) {
4259 Register obj = ToRegister(guard->input());
4260 Register temp = ToRegister(guard->temp0());
4261
4262 Label bail;
4263 masm.branchIfNonNativeObj(obj, temp, &bail);
4264 bailoutFrom(&bail, guard->snapshot());
4265}
4266
4267void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) {
4268 Register temp = ToRegister(guard->temp0());
4269 Label bail;
4270
4271 masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp);
4272 masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()),
4273 &bail);
4274 bailoutFrom(&bail, guard->snapshot());
4275}
4276
4277void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) {
4278 Register obj = ToRegister(guard->input());
4279 Register temp = ToRegister(guard->temp0());
4280
4281 Label bail;
4282 masm.branchTestObjectIsProxy(false, obj, temp, &bail);
4283 bailoutFrom(&bail, guard->snapshot());
4284}
4285
4286void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) {
4287 Register obj = ToRegister(guard->input());
4288 Register temp = ToRegister(guard->temp0());
4289
4290 Label bail;
4291 masm.branchTestObjectIsProxy(true, obj, temp, &bail);
4292 bailoutFrom(&bail, guard->snapshot());
4293}
4294
4295void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) {
4296 Register proxy = ToRegister(guard->proxy());
4297 Register temp = ToRegister(guard->temp0());
4298
4299 Label bail;
4300 masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp,
4301 GetDOMProxyHandlerFamily(), &bail);
4302 bailoutFrom(&bail, guard->snapshot());
4303}
4304
4305void CodeGenerator::visitProxyGet(LProxyGet* lir) {
4306 Register proxy = ToRegister(lir->proxy());
4307 Register temp = ToRegister(lir->temp0());
4308
4309 pushArg(lir->mir()->id(), temp);
4310 pushArg(proxy);
4311
4312 using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue);
4313 callVM<Fn, ProxyGetProperty>(lir);
4314}
4315
4316void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) {
4317 Register proxy = ToRegister(lir->proxy());
4318 ValueOperand idVal = ToValue(lir, LProxyGetByValue::IdIndex);
4319
4320 pushArg(idVal);
4321 pushArg(proxy);
4322
4323 using Fn =
4324 bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
4325 callVM<Fn, ProxyGetPropertyByValue>(lir);
4326}
4327
4328void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) {
4329 Register proxy = ToRegister(lir->proxy());
4330 ValueOperand idVal = ToValue(lir, LProxyHasProp::IdIndex);
4331
4332 pushArg(idVal);
4333 pushArg(proxy);
4334
4335 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*);
4336 if (lir->mir()->hasOwn()) {
4337 callVM<Fn, ProxyHasOwn>(lir);
4338 } else {
4339 callVM<Fn, ProxyHas>(lir);
4340 }
4341}
4342
4343void CodeGenerator::visitProxySet(LProxySet* lir) {
4344 Register proxy = ToRegister(lir->proxy());
4345 ValueOperand rhs = ToValue(lir, LProxySet::RhsIndex);
4346 Register temp = ToRegister(lir->temp0());
4347
4348 pushArg(Imm32(lir->mir()->strict()));
4349 pushArg(rhs);
4350 pushArg(lir->mir()->id(), temp);
4351 pushArg(proxy);
4352
4353 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
4354 callVM<Fn, ProxySetProperty>(lir);
4355}
4356
4357void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) {
4358 Register proxy = ToRegister(lir->proxy());
4359 ValueOperand idVal = ToValue(lir, LProxySetByValue::IdIndex);
4360 ValueOperand rhs = ToValue(lir, LProxySetByValue::RhsIndex);
4361
4362 pushArg(Imm32(lir->mir()->strict()));
4363 pushArg(rhs);
4364 pushArg(idVal);
4365 pushArg(proxy);
4366
4367 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
4368 callVM<Fn, ProxySetPropertyByValue>(lir);
4369}
4370
4371void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) {
4372 Register obj = ToRegister(lir->obj());
4373 ValueOperand rhs = ToValue(lir, LCallSetArrayLength::RhsIndex);
4374
4375 pushArg(Imm32(lir->mir()->strict()));
4376 pushArg(rhs);
4377 pushArg(obj);
4378
4379 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool);
4380 callVM<Fn, jit::SetArrayLength>(lir);
4381}
4382
4383void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) {
4384 Register obj = ToRegister(lir->object());
4385 Register temp0 = ToRegister(lir->temp0());
4386 Register temp1 = ToRegister(lir->temp1());
4387 Register temp2 = ToRegister(lir->temp2());
4388 Register temp3 = ToRegister(lir->temp3());
4389 ValueOperand output = ToOutValue(lir);
4390
4391 Label cacheHit;
4392 masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2,
4393 output, &cacheHit);
4394
4395 Label bail;
4396 masm.branchIfNonNativeObj(obj, temp0, &bail);
4397
4398 masm.Push(UndefinedValue());
4399 masm.moveStackPtrTo(temp3);
4400
4401 using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id,
4402 MegamorphicCache::Entry* cacheEntry, Value* vp);
4403 masm.setupAlignedABICall();
4404 masm.loadJSContext(temp0);
4405 masm.passABIArg(temp0);
4406 masm.passABIArg(obj);
4407 masm.movePropertyKey(lir->mir()->name(), temp1);
4408 masm.passABIArg(temp1);
4409 masm.passABIArg(temp2);
4410 masm.passABIArg(temp3);
4411
4412 masm.callWithABI<Fn, GetNativeDataPropertyPure>();
4413
4414 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 4414); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)"
")"); do { *((volatile int*)__null) = 4414; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4415 masm.Pop(output);
4416
4417 masm.branchIfFalseBool(ReturnReg, &bail);
4418 masm.bind(&cacheHit);
4419
4420 bailoutFrom(&bail, lir->snapshot());
4421}
4422
4423void CodeGenerator::visitMegamorphicLoadSlotPermissive(
4424 LMegamorphicLoadSlotPermissive* lir) {
4425 Register obj = ToRegister(lir->object());
4426 Register temp0 = ToRegister(lir->temp0());
4427 Register temp1 = ToRegister(lir->temp1());
4428 Register temp2 = ToRegister(lir->temp2());
4429 ValueOperand output = ToOutValue(lir);
4430
4431 Label cacheHit;
4432 masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2,
4433 output, &cacheHit);
4434
4435 masm.movePropertyKey(lir->mir()->name(), temp1);
4436 pushArg(temp2);
4437 pushArg(temp1);
4438 pushArg(obj);
4439
4440 using Fn = bool (*)(JSContext*, HandleObject, HandleId,
4441 MegamorphicCacheEntry*, MutableHandleValue);
4442 callVM<Fn, GetPropMaybeCached>(lir);
4443
4444 masm.bind(&cacheHit);
4445}
4446
4447void CodeGenerator::visitMegamorphicLoadSlotByValue(
4448 LMegamorphicLoadSlotByValue* lir) {
4449 Register obj = ToRegister(lir->object());
4450 ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex);
4451 Register temp0 = ToRegister(lir->temp0());
4452 Register temp1 = ToRegister(lir->temp1());
4453 Register temp2 = ToRegister(lir->temp2());
4454 ValueOperand output = ToOutValue(lir);
4455
4456 Label cacheHit, bail;
4457 masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2,
4458 output, &cacheHit);
4459
4460 masm.branchIfNonNativeObj(obj, temp0, &bail);
4461
4462 // idVal will be in vp[0], result will be stored in vp[1].
4463 masm.reserveStack(sizeof(Value));
4464 masm.Push(idVal);
4465 masm.moveStackPtrTo(temp0);
4466
4467 using Fn = bool (*)(JSContext* cx, JSObject* obj,
4468 MegamorphicCache::Entry* cacheEntry, Value* vp);
4469 masm.setupAlignedABICall();
4470 masm.loadJSContext(temp1);
4471 masm.passABIArg(temp1);
4472 masm.passABIArg(obj);
4473 masm.passABIArg(temp2);
4474 masm.passABIArg(temp0);
4475 masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>();
4476
4477 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 4477); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)"
")"); do { *((volatile int*)__null) = 4477; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4478 masm.storeCallPointerResult(temp0);
4479 masm.Pop(idVal);
4480
4481 uint32_t framePushed = masm.framePushed();
4482 Label ok;
4483 masm.branchIfTrueBool(temp0, &ok);
4484 masm.freeStack(sizeof(Value)); // Discard result Value.
4485 masm.jump(&bail);
4486
4487 masm.bind(&ok);
4488 masm.setFramePushed(framePushed);
4489 masm.Pop(output);
4490
4491 masm.bind(&cacheHit);
4492
4493 bailoutFrom(&bail, lir->snapshot());
4494}
4495
4496void CodeGenerator::visitMegamorphicLoadSlotByValuePermissive(
4497 LMegamorphicLoadSlotByValuePermissive* lir) {
4498 Register obj = ToRegister(lir->object());
4499 ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex);
4500 Register temp0 = ToRegister(lir->temp0());
4501 Register temp1 = ToRegister(lir->temp1());
4502 Register temp2 = ToRegister(lir->temp2());
4503 ValueOperand output = ToOutValue(lir);
4504
4505 Label cacheHit;
4506 masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2,
4507 output, &cacheHit);
4508
4509 pushArg(temp2);
4510 pushArg(idVal);
4511 pushArg(obj);
4512
4513 using Fn = bool (*)(JSContext*, HandleObject, HandleValue,
4514 MegamorphicCacheEntry*, MutableHandleValue);
4515 callVM<Fn, GetElemMaybeCached>(lir);
4516
4517 masm.bind(&cacheHit);
4518}
4519
4520void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) {
4521 Register obj = ToRegister(lir->object());
4522 ValueOperand value = ToValue(lir, LMegamorphicStoreSlot::RhsIndex);
4523
4524 Register temp0 = ToRegister(lir->temp0());
4525#ifndef JS_CODEGEN_X86
4526 Register temp1 = ToRegister(lir->temp1());
4527 Register temp2 = ToRegister(lir->temp2());
4528#endif
4529
4530 Label cacheHit, done;
4531#ifdef JS_CODEGEN_X86
4532 masm.emitMegamorphicCachedSetSlot(
4533 lir->mir()->name(), obj, temp0, value, &cacheHit,
4534 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
4535 EmitPreBarrier(masm, addr, mirType);
4536 });
4537#else
4538 masm.emitMegamorphicCachedSetSlot(
4539 lir->mir()->name(), obj, temp0, temp1, temp2, value, &cacheHit,
4540 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
4541 EmitPreBarrier(masm, addr, mirType);
4542 });
4543#endif
4544
4545 pushArg(Imm32(lir->mir()->strict()));
4546 pushArg(value);
4547 pushArg(lir->mir()->name(), temp0);
4548 pushArg(obj);
4549
4550 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
4551 callVM<Fn, SetPropertyMegamorphic<true>>(lir);
4552
4553 masm.jump(&done);
4554 masm.bind(&cacheHit);
4555
4556 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
4557 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
4558
4559 saveVolatile(temp0);
4560 emitPostWriteBarrier(obj);
4561 restoreVolatile(temp0);
4562
4563 masm.bind(&done);
4564}
4565
4566void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) {
4567 Register obj = ToRegister(lir->object());
4568 ValueOperand idVal = ToValue(lir, LMegamorphicHasProp::IdIndex);
4569 Register temp0 = ToRegister(lir->temp0());
4570 Register temp1 = ToRegister(lir->temp1());
4571 Register temp2 = ToRegister(lir->temp2());
4572 Register output = ToRegister(lir->output());
4573
4574 Label bail, cacheHit;
4575 masm.emitMegamorphicCacheLookupExists(idVal, obj, temp0, temp1, temp2, output,
4576 &cacheHit, lir->mir()->hasOwn());
4577
4578 masm.branchIfNonNativeObj(obj, temp0, &bail);
4579
4580 // idVal will be in vp[0], result will be stored in vp[1].
4581 masm.reserveStack(sizeof(Value));
4582 masm.Push(idVal);
4583 masm.moveStackPtrTo(temp0);
4584
4585 using Fn = bool (*)(JSContext* cx, JSObject* obj,
4586 MegamorphicCache::Entry* cacheEntry, Value* vp);
4587 masm.setupAlignedABICall();
4588 masm.loadJSContext(temp1);
4589 masm.passABIArg(temp1);
4590 masm.passABIArg(obj);
4591 masm.passABIArg(temp2);
4592 masm.passABIArg(temp0);
4593 if (lir->mir()->hasOwn()) {
4594 masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>();
4595 } else {
4596 masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>();
4597 }
4598
4599 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 4599); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)"
")"); do { *((volatile int*)__null) = 4599; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4600 masm.storeCallPointerResult(temp0);
4601 masm.Pop(idVal);
4602
4603 uint32_t framePushed = masm.framePushed();
4604 Label ok;
4605 masm.branchIfTrueBool(temp0, &ok);
4606 masm.freeStack(sizeof(Value)); // Discard result Value.
4607 masm.jump(&bail);
4608
4609 masm.bind(&ok);
4610 masm.setFramePushed(framePushed);
4611 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
4612 masm.freeStack(sizeof(Value));
4613 masm.bind(&cacheHit);
4614
4615 bailoutFrom(&bail, lir->snapshot());
4616}
4617
4618void CodeGenerator::visitSmallObjectVariableKeyHasProp(
4619 LSmallObjectVariableKeyHasProp* lir) {
4620 Register id = ToRegister(lir->id());
4621 Register output = ToRegister(lir->output());
4622
4623#ifdef DEBUG1
4624 Label isAtom;
4625 masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()),
4626 Imm32(JSString::ATOM_BIT), &isAtom);
4627 masm.assumeUnreachable("Expected atom input");
4628 masm.bind(&isAtom);
4629#endif
4630
4631 SharedShape* shape = &lir->mir()->shape()->asShared();
4632
4633 Label done, success;
4634 for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) {
4635 masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()),
4636 &success);
4637 }
4638 masm.move32(Imm32(0), output);
4639 masm.jump(&done);
4640 masm.bind(&success);
4641 masm.move32(Imm32(1), output);
4642 masm.bind(&done);
4643}
4644
4645void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared(
4646 LGuardIsNotArrayBufferMaybeShared* guard) {
4647 Register obj = ToRegister(guard->input());
4648 Register temp = ToRegister(guard->temp0());
4649
4650 Label bail;
4651 masm.loadObjClassUnsafe(obj, temp);
4652 masm.branchPtr(Assembler::Equal, temp,
4653 ImmPtr(&FixedLengthArrayBufferObject::class_), &bail);
4654 masm.branchPtr(Assembler::Equal, temp,
4655 ImmPtr(&FixedLengthSharedArrayBufferObject::class_), &bail);
4656 masm.branchPtr(Assembler::Equal, temp,
4657 ImmPtr(&ResizableArrayBufferObject::class_), &bail);
4658 masm.branchPtr(Assembler::Equal, temp,
4659 ImmPtr(&GrowableSharedArrayBufferObject::class_), &bail);
4660 bailoutFrom(&bail, guard->snapshot());
4661}
4662
4663void CodeGenerator::visitGuardIsTypedArray(LGuardIsTypedArray* guard) {
4664 Register obj = ToRegister(guard->input());
4665 Register temp = ToRegister(guard->temp0());
4666
4667 Label bail;
4668 masm.loadObjClassUnsafe(obj, temp);
4669 masm.branchIfClassIsNotTypedArray(temp, &bail);
4670 bailoutFrom(&bail, guard->snapshot());
4671}
4672
4673void CodeGenerator::visitGuardIsFixedLengthTypedArray(
4674 LGuardIsFixedLengthTypedArray* guard) {
4675 Register obj = ToRegister(guard->input());
4676 Register temp = ToRegister(guard->temp0());
4677
4678 Label bail;
4679 masm.loadObjClassUnsafe(obj, temp);
4680 masm.branchIfClassIsNotFixedLengthTypedArray(temp, &bail);
4681 bailoutFrom(&bail, guard->snapshot());
4682}
4683
4684void CodeGenerator::visitGuardIsResizableTypedArray(
4685 LGuardIsResizableTypedArray* guard) {
4686 Register obj = ToRegister(guard->input());
4687 Register temp = ToRegister(guard->temp0());
4688
4689 Label bail;
4690 masm.loadObjClassUnsafe(obj, temp);
4691 masm.branchIfClassIsNotResizableTypedArray(temp, &bail);
4692 bailoutFrom(&bail, guard->snapshot());
4693}
4694
4695void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) {
4696 Register obj = ToRegister(guard->input());
4697
4698 Label bail;
4699
4700 Address handlerAddr(obj, ProxyObject::offsetOfHandler());
4701 masm.branchPtr(Assembler::NotEqual, handlerAddr,
4702 ImmPtr(guard->mir()->handler()), &bail);
4703
4704 bailoutFrom(&bail, guard->snapshot());
4705}
4706
4707void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) {
4708 Register input = ToRegister(guard->input());
4709 Register expected = ToRegister(guard->expected());
4710
4711 Assembler::Condition cond =
4712 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
4713 bailoutCmpPtr(cond, input, expected, guard->snapshot());
4714}
4715
4716void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) {
4717 Register input = ToRegister(guard->input());
4718 Register expected = ToRegister(guard->expected());
4719
4720 bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot());
4721}
4722
4723void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) {
4724 Register str = ToRegister(guard->str());
4725 Register scratch = ToRegister(guard->temp0());
4726
4727 LiveRegisterSet volatileRegs = liveVolatileRegs(guard);
4728 volatileRegs.takeUnchecked(scratch);
4729
4730 Label bail;
4731 masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs,
4732 &bail);
4733 bailoutFrom(&bail, guard->snapshot());
4734}
4735
4736void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) {
4737 Register symbol = ToRegister(guard->symbol());
4738
4739 bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()),
4740 guard->snapshot());
4741}
4742
4743void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) {
4744 Register num = ToRegister(guard->num());
4745
4746 bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()),
4747 guard->snapshot());
4748}
4749
4750void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) {
4751 Register str = ToRegister(lir->string());
4752 Register output = ToRegister(lir->output());
4753
4754 Label vmCall, done;
4755 masm.loadStringIndexValue(str, output, &vmCall);
4756 masm.jump(&done);
4757
4758 {
4759 masm.bind(&vmCall);
4760
4761 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
4762 volatileRegs.takeUnchecked(output);
4763 masm.PushRegsInMask(volatileRegs);
4764
4765 using Fn = int32_t (*)(JSString* str);
4766 masm.setupAlignedABICall();
4767 masm.passABIArg(str);
4768 masm.callWithABI<Fn, GetIndexFromString>();
4769 masm.storeCallInt32Result(output);
4770
4771 masm.PopRegsInMask(volatileRegs);
4772
4773 // GetIndexFromString returns a negative value on failure.
4774 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
4775 }
4776
4777 masm.bind(&done);
4778}
4779
4780void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) {
4781 Register str = ToRegister(lir->string());
4782 Register output = ToRegister(lir->output());
4783 Register temp = ToRegister(lir->temp0());
4784
4785 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
4786
4787 Label bail;
4788 masm.guardStringToInt32(str, output, temp, volatileRegs, &bail);
4789 bailoutFrom(&bail, lir->snapshot());
4790}
4791
4792void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) {
4793 Register str = ToRegister(lir->string());
4794 FloatRegister output = ToFloatRegister(lir->output());
4795 Register temp0 = ToRegister(lir->temp0());
4796 Register temp1 = ToRegister(lir->temp1());
4797
4798 Label vmCall, done;
4799 // Use indexed value as fast path if possible.
4800 masm.loadStringIndexValue(str, temp0, &vmCall);
4801 masm.convertInt32ToDouble(temp0, output);
4802 masm.jump(&done);
4803 {
4804 masm.bind(&vmCall);
4805
4806 // Reserve stack for holding the result value of the call.
4807 masm.reserveStack(sizeof(double));
4808 masm.moveStackPtrTo(temp0);
4809
4810 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
4811 volatileRegs.takeUnchecked(temp0);
4812 volatileRegs.takeUnchecked(temp1);
4813 masm.PushRegsInMask(volatileRegs);
4814
4815 using Fn = bool (*)(JSContext* cx, JSString* str, double* result);
4816 masm.setupAlignedABICall();
4817 masm.loadJSContext(temp1);
4818 masm.passABIArg(temp1);
4819 masm.passABIArg(str);
4820 masm.passABIArg(temp0);
4821 masm.callWithABI<Fn, StringToNumberPure>();
4822 masm.storeCallPointerResult(temp0);
4823
4824 masm.PopRegsInMask(volatileRegs);
4825
4826 Label ok;
4827 masm.branchIfTrueBool(temp0, &ok);
4828 {
4829 // OOM path, recovered by StringToNumberPure.
4830 //
4831 // Use addToStackPtr instead of freeStack as freeStack tracks stack height
4832 // flow-insensitively, and using it here would confuse the stack height
4833 // tracking.
4834 masm.addToStackPtr(Imm32(sizeof(double)));
4835 bailout(lir->snapshot());
4836 }
4837 masm.bind(&ok);
4838 masm.Pop(output);
4839 }
4840 masm.bind(&done);
4841}
4842
4843void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) {
4844 Register obj = ToRegister(guard->input());
4845 Register temp = ToRegister(guard->temp0());
4846
4847 // Load obj->elements.
4848 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp);
4849
4850 // Make sure there are no dense elements.
4851 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
4852 bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot());
4853}
4854
4855void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) {
4856 Register input = ToRegister(lir->input());
4857 Register64 output = ToOutRegister64(lir);
4858
4859 masm.move32To64ZeroExtend(input, output);
4860}
4861
4862void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input,
4863 Register64 output) {
4864 Register temp = output.scratchReg();
4865
4866 saveLive(lir);
4867
4868 masm.reserveStack(sizeof(uint64_t));
4869 masm.moveStackPtrTo(temp);
4870 pushArg(temp);
4871 pushArg(input);
4872
4873 using Fn = bool (*)(JSContext*, HandleString, uint64_t*);
4874 callVM<Fn, DoStringToInt64>(lir);
4875
4876 masm.load64(Address(masm.getStackPointer(), 0), output);
4877 masm.freeStack(sizeof(uint64_t));
4878
4879 restoreLiveIgnore(lir, StoreValueTo(output).clobbered());
4880}
4881
4882void CodeGenerator::visitStringToInt64(LStringToInt64* lir) {
4883 Register input = ToRegister(lir->input());
4884 Register64 output = ToOutRegister64(lir);
4885
4886 emitStringToInt64(lir, input, output);
4887}
4888
4889void CodeGenerator::visitValueToInt64(LValueToInt64* lir) {
4890 ValueOperand input = ToValue(lir, LValueToInt64::InputIndex);
4891 Register temp = ToRegister(lir->temp0());
4892 Register64 output = ToOutRegister64(lir);
4893
4894 int checks = 3;
4895
4896 Label fail, done;
4897 // Jump to fail if this is the last check and we fail it,
4898 // otherwise to the next test.
4899 auto emitTestAndUnbox = [&](auto testAndUnbox) {
4900 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 4900); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks > 0"
")"); do { *((volatile int*)__null) = 4900; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4901
4902 checks--;
4903 Label notType;
4904 Label* target = checks ? &notType : &fail;
4905
4906 testAndUnbox(target);
4907
4908 if (checks) {
4909 masm.jump(&done);
4910 masm.bind(&notType);
4911 }
4912 };
4913
4914 Register tag = masm.extractTag(input, temp);
4915
4916 // BigInt.
4917 emitTestAndUnbox([&](Label* target) {
4918 masm.branchTestBigInt(Assembler::NotEqual, tag, target);
4919 masm.unboxBigInt(input, temp);
4920 masm.loadBigInt64(temp, output);
4921 });
4922
4923 // Boolean
4924 emitTestAndUnbox([&](Label* target) {
4925 masm.branchTestBoolean(Assembler::NotEqual, tag, target);
4926 masm.unboxBoolean(input, temp);
4927 masm.move32To64ZeroExtend(temp, output);
4928 });
4929
4930 // String
4931 emitTestAndUnbox([&](Label* target) {
4932 masm.branchTestString(Assembler::NotEqual, tag, target);
4933 masm.unboxString(input, temp);
4934 emitStringToInt64(lir, temp, output);
4935 });
4936
4937 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 4937); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0"
")"); do { *((volatile int*)__null) = 4937; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4938
4939 bailoutFrom(&fail, lir->snapshot());
4940 masm.bind(&done);
4941}
4942
4943void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) {
4944 Register operand = ToRegister(lir->input());
4945 Register64 output = ToOutRegister64(lir);
4946
4947 masm.loadBigInt64(operand, output);
4948}
4949
4950OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir,
4951 Scalar::Type type,
4952 Register64 input,
4953 Register output) {
4954#if JS_BITS_PER_WORD64 == 32
4955 using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t);
4956 auto args = ArgList(input.low, input.high);
4957#else
4958 using Fn = BigInt* (*)(JSContext*, uint64_t);
4959 auto args = ArgList(input);
4960#endif
4961
4962 if (type == Scalar::BigInt64) {
4963 return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args,
4964 StoreRegisterTo(output));
4965 }
4966 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 4966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == Scalar::BigUint64"
")"); do { *((volatile int*)__null) = 4966; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4967 return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args,
4968 StoreRegisterTo(output));
4969}
4970
4971void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type,
4972 Register64 input, Register output,
4973 Register maybeTemp) {
4974 OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output);
4975
4976 if (maybeTemp != InvalidReg) {
4977 masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry());
4978 } else {
4979 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
4980 regs.take(input);
4981 regs.take(output);
4982
4983 Register temp = regs.takeAny();
4984
4985 masm.push(temp);
4986
4987 Label fail, ok;
4988 masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail);
4989 masm.pop(temp);
4990 masm.jump(&ok);
4991 masm.bind(&fail);
4992 masm.pop(temp);
4993 masm.jump(ool->entry());
4994 masm.bind(&ok);
4995 }
4996 masm.initializeBigInt64(type, output, input);
4997 masm.bind(ool->rejoin());
4998}
4999
5000void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) {
5001 Register64 input = ToRegister64(lir->input());
5002 Register temp = ToRegister(lir->temp0());
5003 Register output = ToRegister(lir->output());
5004
5005 emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp);
5006}
5007
5008void CodeGenerator::visitGuardValue(LGuardValue* lir) {
5009 ValueOperand input = ToValue(lir, LGuardValue::InputIndex);
5010 Value expected = lir->mir()->expected();
5011 Label bail;
5012 masm.branchTestValue(Assembler::NotEqual, input, expected, &bail);
5013 bailoutFrom(&bail, lir->snapshot());
5014}
5015
5016void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) {
5017 ValueOperand input = ToValue(lir, LGuardNullOrUndefined::InputIndex);
5018
5019 ScratchTagScope tag(masm, input);
5020 masm.splitTagForTest(input, tag);
5021
5022 Label done;
5023 masm.branchTestNull(Assembler::Equal, tag, &done);
5024
5025 Label bail;
5026 masm.branchTestUndefined(Assembler::NotEqual, tag, &bail);
5027 bailoutFrom(&bail, lir->snapshot());
5028
5029 masm.bind(&done);
5030}
5031
5032void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) {
5033 ValueOperand input = ToValue(lir, LGuardIsNotObject::InputIndex);
5034
5035 Label bail;
5036 masm.branchTestObject(Assembler::Equal, input, &bail);
5037 bailoutFrom(&bail, lir->snapshot());
5038}
5039
5040void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) {
5041 Register function = ToRegister(lir->function());
5042
5043 Label bail;
5044 if (uint16_t flags = lir->mir()->expectedFlags()) {
5045 masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail);
5046 }
5047 if (uint16_t flags = lir->mir()->unexpectedFlags()) {
5048 masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail);
5049 }
5050 bailoutFrom(&bail, lir->snapshot());
5051}
5052
5053void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor(
5054 LGuardFunctionIsNonBuiltinCtor* lir) {
5055 Register function = ToRegister(lir->function());
5056 Register temp = ToRegister(lir->temp0());
5057
5058 Label bail;
5059 masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail);
5060 bailoutFrom(&bail, lir->snapshot());
5061}
5062
5063void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) {
5064 Register function = ToRegister(lir->function());
5065 Register temp = ToRegister(lir->temp0());
5066
5067 Assembler::Condition cond =
5068 lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
5069
5070 Label bail;
5071 masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail);
5072 bailoutFrom(&bail, lir->snapshot());
5073}
5074
5075void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) {
5076 Register function = ToRegister(lir->function());
5077
5078 Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript());
5079 bailoutCmpPtr(Assembler::NotEqual, scriptAddr,
5080 ImmGCPtr(lir->mir()->expected()), lir->snapshot());
5081}
5082
5083// Out-of-line path to update the store buffer.
5084class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> {
5085 LInstruction* lir_;
5086 const LAllocation* object_;
5087
5088 public:
5089 OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object)
5090 : lir_(lir), object_(object) {}
5091
5092 void accept(CodeGenerator* codegen) override {
5093 codegen->visitOutOfLineCallPostWriteBarrier(this);
5094 }
5095
5096 LInstruction* lir() const { return lir_; }
5097 const LAllocation* object() const { return object_; }
5098};
5099
5100static void EmitStoreBufferCheckForConstant(MacroAssembler& masm,
5101 const gc::TenuredCell* cell,
5102 AllocatableGeneralRegisterSet& regs,
5103 Label* exit, Label* callVM) {
5104 Register temp = regs.takeAny();
5105
5106 gc::Arena* arena = cell->arena();
5107
5108 Register cells = temp;
5109 masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells);
5110
5111 size_t index = gc::ArenaCellSet::getCellIndex(cell);
5112 size_t word;
5113 uint32_t mask;
5114 gc::ArenaCellSet::getWordIndexAndMask(index, &word, &mask);
5115 size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t);
5116
5117 masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask),
5118 exit);
5119
5120 // Check whether this is the sentinel set and if so call the VM to allocate
5121 // one for this arena.
5122 masm.branchPtr(Assembler::Equal,
5123 Address(cells, gc::ArenaCellSet::offsetOfArena()),
5124 ImmPtr(nullptr), callVM);
5125
5126 // Add the cell to the set.
5127 masm.or32(Imm32(mask), Address(cells, offset));
5128 masm.jump(exit);
5129
5130 regs.add(temp);
5131}
5132
5133static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime,
5134 Register objreg, JSObject* maybeConstant,
5135 bool isGlobal,
5136 AllocatableGeneralRegisterSet& regs) {
5137 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeConstant"
")"); do { *((volatile int*)__null) = 5137; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5138
5139 Label callVM;
5140 Label exit;
5141
5142 Register temp = regs.takeAny();
5143
5144 // We already have a fast path to check whether a global is in the store
5145 // buffer.
5146 if (!isGlobal) {
5147 if (maybeConstant) {
5148 // Check store buffer bitmap directly for known object.
5149 EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs,
5150 &exit, &callVM);
5151 } else {
5152 // Check one element cache to avoid VM call.
5153 masm.branchPtr(Assembler::Equal,
5154 AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()),
5155 objreg, &exit);
5156 }
5157 }
5158
5159 // Call into the VM to barrier the write.
5160 masm.bind(&callVM);
5161
5162 Register runtimereg = temp;
5163 masm.mov(ImmPtr(runtime), runtimereg);
5164
5165 masm.setupAlignedABICall();
5166 masm.passABIArg(runtimereg);
5167 masm.passABIArg(objreg);
5168 if (isGlobal) {
5169 using Fn = void (*)(JSRuntime* rt, GlobalObject* obj);
5170 masm.callWithABI<Fn, PostGlobalWriteBarrier>();
5171 } else {
5172 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj);
5173 masm.callWithABI<Fn, PostWriteBarrier>();
5174 }
5175
5176 masm.bind(&exit);
5177}
5178
5179void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) {
5180 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5181
5182 Register objreg;
5183 JSObject* object = nullptr;
5184 bool isGlobal = false;
5185 if (obj->isConstant()) {
5186 object = &obj->toConstant()->toObject();
5187 isGlobal = isGlobalObject(object);
5188 objreg = regs.takeAny();
5189 masm.movePtr(ImmGCPtr(object), objreg);
5190 } else {
5191 objreg = ToRegister(obj);
5192 regs.takeUnchecked(objreg);
5193 }
5194
5195 EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs);
5196}
5197
5198// Returns true if `def` might be allocated in the nursery.
5199static bool ValueNeedsPostBarrier(MDefinition* def) {
5200 if (def->isBox()) {
5201 def = def->toBox()->input();
5202 }
5203 if (def->type() == MIRType::Value) {
5204 return true;
5205 }
5206 return NeedsPostBarrier(def->type());
5207}
5208
5209class OutOfLineElementPostWriteBarrier
5210 : public OutOfLineCodeBase<CodeGenerator> {
5211 LiveRegisterSet liveVolatileRegs_;
5212 const LAllocation* index_;
5213 int32_t indexDiff_;
5214 Register obj_;
5215 Register scratch_;
5216
5217 public:
5218 OutOfLineElementPostWriteBarrier(const LiveRegisterSet& liveVolatileRegs,
5219 Register obj, const LAllocation* index,
5220 Register scratch, int32_t indexDiff)
5221 : liveVolatileRegs_(liveVolatileRegs),
5222 index_(index),
5223 indexDiff_(indexDiff),
5224 obj_(obj),
5225 scratch_(scratch) {}
5226
5227 void accept(CodeGenerator* codegen) override {
5228 codegen->visitOutOfLineElementPostWriteBarrier(this);
5229 }
5230
5231 const LiveRegisterSet& liveVolatileRegs() const { return liveVolatileRegs_; }
5232 const LAllocation* index() const { return index_; }
5233 int32_t indexDiff() const { return indexDiff_; }
5234
5235 Register object() const { return obj_; }
5236 Register scratch() const { return scratch_; }
5237};
5238
5239void CodeGenerator::emitElementPostWriteBarrier(
5240 MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj,
5241 const LAllocation* index, Register scratch, const ConstantOrRegister& val,
5242 int32_t indexDiff) {
5243 if (val.constant()) {
5244 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())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { *((volatile int*)__null) = 5245; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5245 !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())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { *((volatile int*)__null) = 5245; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5246 return;
5247 }
5248
5249 TypedOrValueRegister reg = val.reg();
5250 if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) {
5251 return;
5252 }
5253
5254 auto* ool = new (alloc()) OutOfLineElementPostWriteBarrier(
5255 liveVolatileRegs, obj, index, scratch, indexDiff);
5256 addOutOfLineCode(ool, mir);
5257
5258 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, ool->rejoin());
5259
5260 if (reg.hasValue()) {
5261 masm.branchValueIsNurseryCell(Assembler::Equal, reg.valueReg(), scratch,
5262 ool->entry());
5263 } else {
5264 masm.branchPtrInNurseryChunk(Assembler::Equal, reg.typedReg().gpr(),
5265 scratch, ool->entry());
5266 }
5267
5268 masm.bind(ool->rejoin());
5269}
5270
5271void CodeGenerator::visitOutOfLineElementPostWriteBarrier(
5272 OutOfLineElementPostWriteBarrier* ool) {
5273 Register obj = ool->object();
5274 Register scratch = ool->scratch();
5275 const LAllocation* index = ool->index();
5276 int32_t indexDiff = ool->indexDiff();
5277
5278 masm.PushRegsInMask(ool->liveVolatileRegs());
5279
5280 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5281 regs.takeUnchecked(obj);
5282 regs.takeUnchecked(scratch);
5283
5284 Register indexReg;
5285 if (index->isConstant()) {
5286 indexReg = regs.takeAny();
5287 masm.move32(Imm32(ToInt32(index) + indexDiff), indexReg);
5288 } else {
5289 indexReg = ToRegister(index);
5290 regs.takeUnchecked(indexReg);
5291 if (indexDiff != 0) {
5292 masm.add32(Imm32(indexDiff), indexReg);
5293 }
5294 }
5295
5296 masm.setupUnalignedABICall(scratch);
5297 masm.movePtr(ImmPtr(gen->runtime), scratch);
5298 masm.passABIArg(scratch);
5299 masm.passABIArg(obj);
5300 masm.passABIArg(indexReg);
5301 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
5302 masm.callWithABI<Fn, PostWriteElementBarrier>();
5303
5304 // We don't need a sub32 here because indexReg must be in liveVolatileRegs
5305 // if indexDiff is not zero, so it will be restored below.
5306 MOZ_ASSERT_IF(indexDiff != 0, ool->liveVolatileRegs().has(indexReg))do { if (indexDiff != 0) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(ool->liveVolatileRegs
().has(indexReg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ool->liveVolatileRegs().has
(indexReg)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ool->liveVolatileRegs().has(indexReg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5306); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ool->liveVolatileRegs().has(indexReg)"
")"); do { *((volatile int*)__null) = 5306; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5307
5308 masm.PopRegsInMask(ool->liveVolatileRegs());
5309
5310 masm.jump(ool->rejoin());
5311}
5312
5313void CodeGenerator::emitPostWriteBarrier(Register objreg) {
5314 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5315 regs.takeUnchecked(objreg);
5316 EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs);
5317}
5318
5319void CodeGenerator::visitOutOfLineCallPostWriteBarrier(
5320 OutOfLineCallPostWriteBarrier* ool) {
5321 saveLiveVolatile(ool->lir());
5322 const LAllocation* obj = ool->object();
5323 emitPostWriteBarrier(obj);
5324 restoreLiveVolatile(ool->lir());
5325
5326 masm.jump(ool->rejoin());
5327}
5328
5329void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal,
5330 OutOfLineCode* ool) {
5331 // Check whether an object is a global that we have already barriered before
5332 // calling into the VM.
5333 //
5334 // We only check for the script's global, not other globals within the same
5335 // compartment, because we bake in a pointer to realm->globalWriteBarriered
5336 // and doing that would be invalid for other realms because they could be
5337 // collected before the Ion code is discarded.
5338
5339 if (!maybeGlobal->isConstant()) {
5340 return;
5341 }
5342
5343 JSObject* obj = &maybeGlobal->toConstant()->toObject();
5344 if (gen->realm->maybeGlobal() != obj) {
5345 return;
5346 }
5347
5348 const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered();
5349 masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0),
5350 ool->rejoin());
5351}
5352
5353template <class LPostBarrierType, MIRType nurseryType>
5354void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir,
5355 OutOfLineCode* ool) {
5356 static_assert(NeedsPostBarrier(nurseryType));
5357
5358 addOutOfLineCode(ool, lir->mir());
5359
5360 Register temp = ToTempRegisterOrInvalid(lir->temp0());
5361
5362 if (lir->object()->isConstant()) {
5363 // Constant nursery objects cannot appear here, see
5364 // LIRGenerator::visitPostWriteElementBarrier.
5365 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())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5365); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { *((volatile int*)__null) = 5365; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5366 } else {
5367 masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()),
5368 temp, ool->rejoin());
5369 }
5370
5371 maybeEmitGlobalBarrierCheck(lir->object(), ool);
5372
5373 Register value = ToRegister(lir->value());
5374 if constexpr (nurseryType == MIRType::Object) {
5375 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5375); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object"
")"); do { *((volatile int*)__null) = 5375; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5376 } else if constexpr (nurseryType == MIRType::String) {
5377 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5377); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String"
")"); do { *((volatile int*)__null) = 5377; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5378 } else {
5379 static_assert(nurseryType == MIRType::BigInt);
5380 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5380); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt"
")"); do { *((volatile int*)__null) = 5380; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5381 }
5382 masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry());
5383
5384 masm.bind(ool->rejoin());
5385}
5386
5387template <class LPostBarrierType>
5388void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir,
5389 OutOfLineCode* ool) {
5390 addOutOfLineCode(ool, lir->mir());
5391
5392 Register temp = ToTempRegisterOrInvalid(lir->temp0());
5393
5394 if (lir->object()->isConstant()) {
5395 // Constant nursery objects cannot appear here, see
5396 // LIRGenerator::visitPostWriteElementBarrier.
5397 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())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5397); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { *((volatile int*)__null) = 5397; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5398 } else {
5399 masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()),
5400 temp, ool->rejoin());
5401 }
5402
5403 maybeEmitGlobalBarrierCheck(lir->object(), ool);
5404
5405 ValueOperand value = ToValue(lir, LPostBarrierType::ValueIndex);
5406 masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry());
5407
5408 masm.bind(ool->rejoin());
5409}
5410
5411void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) {
5412 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5413 visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool);
5414}
5415
5416void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) {
5417 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5418 visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool);
5419}
5420
5421void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) {
5422 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5423 visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool);
5424}
5425
5426void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) {
5427 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5428 visitPostWriteBarrierCommonV(lir, ool);
5429}
5430
5431// Out-of-line path to update the store buffer.
5432class OutOfLineCallPostWriteElementBarrier
5433 : public OutOfLineCodeBase<CodeGenerator> {
5434 LInstruction* lir_;
5435 const LAllocation* object_;
5436 const LAllocation* index_;
5437
5438 public:
5439 OutOfLineCallPostWriteElementBarrier(LInstruction* lir,
5440 const LAllocation* object,
5441 const LAllocation* index)
5442 : lir_(lir), object_(object), index_(index) {}
5443
5444 void accept(CodeGenerator* codegen) override {
5445 codegen->visitOutOfLineCallPostWriteElementBarrier(this);
5446 }
5447
5448 LInstruction* lir() const { return lir_; }
5449
5450 const LAllocation* object() const { return object_; }
5451
5452 const LAllocation* index() const { return index_; }
5453};
5454
5455void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier(
5456 OutOfLineCallPostWriteElementBarrier* ool) {
5457 saveLiveVolatile(ool->lir());
5458
5459 const LAllocation* obj = ool->object();
5460 const LAllocation* index = ool->index();
5461
5462 Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj);
5463 Register indexreg = ToRegister(index);
5464
5465 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5466 regs.takeUnchecked(indexreg);
5467
5468 if (obj->isConstant()) {
5469 objreg = regs.takeAny();
5470 masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg);
5471 } else {
5472 regs.takeUnchecked(objreg);
5473 }
5474
5475 Register runtimereg = regs.takeAny();
5476 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
5477 masm.setupAlignedABICall();
5478 masm.mov(ImmPtr(gen->runtime), runtimereg);
5479 masm.passABIArg(runtimereg);
5480 masm.passABIArg(objreg);
5481 masm.passABIArg(indexreg);
5482 masm.callWithABI<Fn, PostWriteElementBarrier>();
5483
5484 restoreLiveVolatile(ool->lir());
5485
5486 masm.jump(ool->rejoin());
5487}
5488
5489void CodeGenerator::visitPostWriteElementBarrierO(
5490 LPostWriteElementBarrierO* lir) {
5491 auto ool = new (alloc())
5492 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5493 visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir,
5494 ool);
5495}
5496
5497void CodeGenerator::visitPostWriteElementBarrierS(
5498 LPostWriteElementBarrierS* lir) {
5499 auto ool = new (alloc())
5500 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5501 visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir,
5502 ool);
5503}
5504
5505void CodeGenerator::visitPostWriteElementBarrierBI(
5506 LPostWriteElementBarrierBI* lir) {
5507 auto ool = new (alloc())
5508 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5509 visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir,
5510 ool);
5511}
5512
5513void CodeGenerator::visitPostWriteElementBarrierV(
5514 LPostWriteElementBarrierV* lir) {
5515 auto ool = new (alloc())
5516 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5517 visitPostWriteBarrierCommonV(lir, ool);
5518}
5519
5520void CodeGenerator::visitAssertCanElidePostWriteBarrier(
5521 LAssertCanElidePostWriteBarrier* lir) {
5522 Register object = ToRegister(lir->object());
5523 ValueOperand value =
5524 ToValue(lir, LAssertCanElidePostWriteBarrier::ValueIndex);
5525 Register temp = ToRegister(lir->temp0());
5526
5527 Label ok;
5528 masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok);
5529 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok);
5530
5531 masm.assumeUnreachable("Unexpected missing post write barrier");
5532
5533 masm.bind(&ok);
5534}
5535
5536template <typename LCallIns>
5537void CodeGenerator::emitCallNative(LCallIns* call, JSNative native,
5538 Register argContextReg, Register argUintNReg,
5539 Register argVpReg, Register tempReg,
5540 uint32_t unusedStack) {
5541 masm.checkStackAlignment();
5542
5543 // Native functions have the signature:
5544 // bool (*)(JSContext*, unsigned, Value* vp)
5545 // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
5546 // are the function arguments.
5547
5548 // Allocate space for the outparam, moving the StackPointer to what will be
5549 // &vp[1].
5550 masm.adjustStack(unusedStack);
5551
5552 // Push a Value containing the callee object: natives are allowed to access
5553 // their callee before setting the return value. The StackPointer is moved
5554 // to &vp[0].
5555 //
5556 // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|.
5557 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
5558 Register calleeReg = ToRegister(call->getCallee());
5559 masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg)));
5560
5561 // Enter the callee realm.
5562 if (call->mir()->maybeCrossRealm()) {
5563 masm.switchToObjectRealm(calleeReg, tempReg);
5564 }
5565 } else {
5566 WrappedFunction* target = call->mir()->getSingleTarget();
5567 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
5568
5569 // Enter the callee realm.
5570 if (call->mir()->maybeCrossRealm()) {
5571 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg);
5572 masm.switchToObjectRealm(tempReg, tempReg);
5573 }
5574 }
5575
5576 // Preload arguments into registers.
5577 masm.loadJSContext(argContextReg);
5578 masm.moveStackPtrTo(argVpReg);
5579
5580 // Initialize |NativeExitFrameLayout::argc_|.
5581 masm.Push(argUintNReg);
5582
5583 // Construct native exit frame.
5584 //
5585 // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and
5586 // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|.
5587 //
5588 // The NativeExitFrameLayout is now fully initialized.
5589 uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg);
5590 masm.enterFakeExitFrameForNative(argContextReg, tempReg,
5591 call->mir()->isConstructing());
5592
5593 markSafepointAt(safepointOffset, call);
5594
5595 // Construct and execute call.
5596 masm.setupAlignedABICall();
5597 masm.passABIArg(argContextReg);
5598 masm.passABIArg(argUintNReg);
5599 masm.passABIArg(argVpReg);
5600
5601 ensureOsiSpace();
5602 // If we're using a simulator build, `native` will already point to the
5603 // simulator's call-redirection code for LCallClassHook. Load the address in
5604 // a register first so that we don't try to redirect it a second time.
5605 bool emittedCall = false;
5606#ifdef JS_SIMULATOR
5607 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
5608 masm.movePtr(ImmPtr(native), tempReg);
5609 masm.callWithABI(tempReg);
5610 emittedCall = true;
5611 }
5612#endif
5613 if (!emittedCall) {
5614 masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General,
5615 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
5616 }
5617
5618 // Test for failure.
5619 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
5620
5621 // Exit the callee realm.
5622 if (call->mir()->maybeCrossRealm()) {
5623 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5624 }
5625
5626 // Load the outparam vp[0] into output register(s).
5627 masm.loadValue(
5628 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
5629 JSReturnOperand);
5630
5631 // Until C++ code is instrumented against Spectre, prevent speculative
5632 // execution from returning any private data.
5633 if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() &&
5634 call->mir()->hasLiveDefUses()) {
5635 masm.speculationBarrier();
5636 }
5637
5638#ifdef DEBUG1
5639 // Native constructors are guaranteed to return an Object value.
5640 if (call->mir()->isConstructing()) {
5641 Label notPrimitive;
5642 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
5643 &notPrimitive);
5644 masm.assumeUnreachable("native constructors don't return primitives");
5645 masm.bind(&notPrimitive);
5646 }
5647#endif
5648}
5649
5650template <typename LCallIns>
5651void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) {
5652 uint32_t unusedStack =
5653 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
5654
5655 // Registers used for callWithABI() argument-passing.
5656 const Register argContextReg = ToRegister(call->getArgContextReg());
5657 const Register argUintNReg = ToRegister(call->getArgUintNReg());
5658 const Register argVpReg = ToRegister(call->getArgVpReg());
5659
5660 // Misc. temporary registers.
5661 const Register tempReg = ToRegister(call->getTempReg());
5662
5663 DebugOnly<uint32_t> initialStack = masm.framePushed();
5664
5665 // Initialize the argc register.
5666 masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg);
5667
5668 // Create the exit frame and call the native.
5669 emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg,
5670 unusedStack);
5671
5672 // The next instruction is removing the footer of the exit frame, so there
5673 // is no need for leaveFakeExitFrame.
5674
5675 // Move the StackPointer back to its original location, unwinding the native
5676 // exit frame.
5677 masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack);
5678 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5678); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 5678; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5679}
5680
5681void CodeGenerator::visitCallNative(LCallNative* call) {
5682 WrappedFunction* target = call->getSingleTarget();
5683 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5683); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { *((volatile int*)__null) = 5683; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5684 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5684); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 5684; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5685
5686 JSNative native = target->native();
5687 if (call->ignoresReturnValue() && target->hasJitInfo()) {
5688 const JSJitInfo* jitInfo = target->jitInfo();
5689 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
5690 native = jitInfo->ignoresReturnValueMethod;
5691 }
5692 }
5693 emitCallNative(call, native);
5694}
5695
5696void CodeGenerator::visitCallClassHook(LCallClassHook* call) {
5697 emitCallNative(call, call->mir()->target());
5698}
5699
5700static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv,
5701 DOMObjectKind kind) {
5702 // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This
5703 // will be in the first slot but may be fixed or non-fixed.
5704 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5704); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv"
")"); do { *((volatile int*)__null) = 5704; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5705
5706 switch (kind) {
5707 case DOMObjectKind::Native:
5708 // If it's a native object, the value must be in a fixed slot.
5709 // See CanAttachDOMCall in CacheIR.cpp.
5710 masm.debugAssertObjHasFixedSlots(obj, priv);
5711 masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv);
5712 break;
5713 case DOMObjectKind::Proxy: {
5714#ifdef DEBUG1
5715 // Sanity check: it must be a DOM proxy.
5716 Label isDOMProxy;
5717 masm.branchTestProxyHandlerFamily(
5718 Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy);
5719 masm.assumeUnreachable("Expected a DOM proxy");
5720 masm.bind(&isDOMProxy);
5721#endif
5722 masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), priv);
5723 masm.loadPrivate(
5724 Address(priv, js::detail::ProxyReservedSlots::offsetOfSlot(0)), priv);
5725 break;
5726 }
5727 }
5728}
5729
5730void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) {
5731 WrappedFunction* target = call->getSingleTarget();
5732 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5732); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { *((volatile int*)__null) = 5732; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5733 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5733); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 5733; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5734 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5734); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitInfo()"
")"); do { *((volatile int*)__null) = 5734; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5735 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5735); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()"
")"); do { *((volatile int*)__null) = 5735; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5736
5737 int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
5738
5739 // Registers used for callWithABI() argument-passing.
5740 const Register argJSContext = ToRegister(call->getArgJSContext());
5741 const Register argObj = ToRegister(call->getArgObj());
5742 const Register argPrivate = ToRegister(call->getArgPrivate());
5743 const Register argArgs = ToRegister(call->getArgArgs());
5744
5745 DebugOnly<uint32_t> initialStack = masm.framePushed();
5746
5747 masm.checkStackAlignment();
5748
5749 // DOM methods have the signature:
5750 // bool (*)(JSContext*, HandleObject, void* private, const
5751 // JSJitMethodCallArgs& args)
5752 // Where args is initialized from an argc and a vp, vp[0] is space for an
5753 // outparam and the callee, vp[1] is |this|, and vp[2] onward are the
5754 // function arguments. Note that args stores the argv, not the vp, and
5755 // argv == vp + 2.
5756
5757 // Nestle the stack up against the pushed arguments, leaving StackPointer at
5758 // &vp[1]
5759 masm.adjustStack(unusedStack);
5760 // argObj is filled with the extracted object, then returned.
5761 Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj);
5762 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5762); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj == argObj"
")"); do { *((volatile int*)__null) = 5762; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5763
5764 // Push a Value containing the callee object: natives are allowed to access
5765 // their callee before setting the return value. After this the StackPointer
5766 // points to &vp[0].
5767 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
5768
5769 // Now compute the argv value. Since StackPointer is pointing to &vp[0] and
5770 // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
5771 // StackPointer.
5772 static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
5773 static_assert(JSJitMethodCallArgsTraits::offsetOfArgc ==
5774 IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
5775 masm.computeEffectiveAddress(
5776 Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs);
5777
5778 LoadDOMPrivate(masm, obj, argPrivate,
5779 static_cast<MCallDOMNative*>(call->mir())->objectKind());
5780
5781 // Push argc from the call instruction into what will become the IonExitFrame
5782 masm.Push(Imm32(call->numActualArgs()));
5783
5784 // Push our argv onto the stack
5785 masm.Push(argArgs);
5786 // And store our JSJitMethodCallArgs* in argArgs.
5787 masm.moveStackPtrTo(argArgs);
5788
5789 // Push |this| object for passing HandleObject. We push after argc to
5790 // maintain the same sp-relative location of the object pointer with other
5791 // DOMExitFrames.
5792 masm.Push(argObj);
5793 masm.moveStackPtrTo(argObj);
5794
5795 if (call->mir()->maybeCrossRealm()) {
5796 // We use argJSContext as scratch register here.
5797 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext);
5798 masm.switchToObjectRealm(argJSContext, argJSContext);
5799 }
5800
5801 // Construct native exit frame.
5802 uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext);
5803 masm.loadJSContext(argJSContext);
5804 masm.enterFakeExitFrame(argJSContext, argJSContext,
5805 ExitFrameType::IonDOMMethod);
5806
5807 markSafepointAt(safepointOffset, call);
5808
5809 // Construct and execute call.
5810 masm.setupAlignedABICall();
5811 masm.loadJSContext(argJSContext);
5812 masm.passABIArg(argJSContext);
5813 masm.passABIArg(argObj);
5814 masm.passABIArg(argPrivate);
5815 masm.passABIArg(argArgs);
5816 ensureOsiSpace();
5817 masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method),
5818 ABIType::General,
5819 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
5820
5821 if (target->jitInfo()->isInfallible) {
5822 masm.loadValue(Address(masm.getStackPointer(),
5823 IonDOMMethodExitFrameLayout::offsetOfResult()),
5824 JSReturnOperand);
5825 } else {
5826 // Test for failure.
5827 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
5828
5829 // Load the outparam vp[0] into output register(s).
5830 masm.loadValue(Address(masm.getStackPointer(),
5831 IonDOMMethodExitFrameLayout::offsetOfResult()),
5832 JSReturnOperand);
5833 }
5834
5835 // Switch back to the current realm if needed. Note: if the DOM method threw
5836 // an exception, the exception handler will do this.
5837 if (call->mir()->maybeCrossRealm()) {
5838 static_assert(!JSReturnOperand.aliases(ReturnReg),
5839 "Clobbering ReturnReg should not affect the return value");
5840 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5841 }
5842
5843 // Until C++ code is instrumented against Spectre, prevent speculative
5844 // execution from returning any private data.
5845 if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) {
5846 masm.speculationBarrier();
5847 }
5848
5849 // The next instruction is removing the footer of the exit frame, so there
5850 // is no need for leaveFakeExitFrame.
5851
5852 // Move the StackPointer back to its original location, unwinding the native
5853 // exit frame.
5854 masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
5855 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 5855; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5856}
5857
5858void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) {
5859 pushArg(ImmGCPtr(lir->mir()->name()));
5860
5861 using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue);
5862 callVM<Fn, GetIntrinsicValue>(lir);
5863}
5864
5865void CodeGenerator::emitCallInvokeFunction(
5866 LInstruction* call, Register calleereg, bool constructing,
5867 bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) {
5868 // Nestle %esp up to the argument vector.
5869 // Each path must account for framePushed_ separately, for callVM to be valid.
5870 masm.freeStack(unusedStack);
5871
5872 pushArg(masm.getStackPointer()); // argv.
5873 pushArg(Imm32(argc)); // argc.
5874 pushArg(Imm32(ignoresReturnValue));
5875 pushArg(Imm32(constructing)); // constructing.
5876 pushArg(calleereg); // JSFunction*.
5877
5878 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
5879 MutableHandleValue);
5880 callVM<Fn, jit::InvokeFunction>(call);
5881
5882 // Un-nestle %esp from the argument vector. No prefix was pushed.
5883 masm.reserveStack(unusedStack);
5884}
5885
5886void CodeGenerator::visitCallGeneric(LCallGeneric* call) {
5887 // The callee is passed straight through to the trampoline.
5888 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5888); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg"
")"); do { *((volatile int*)__null) = 5888; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5889
5890 Register argcReg = ToRegister(call->getArgc());
5891 uint32_t unusedStack =
5892 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
5893
5894 // Known-target case is handled by LCallKnown.
5895 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5895); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->hasSingleTarget()"
")"); do { *((volatile int*)__null) = 5895; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5896
5897 masm.checkStackAlignment();
5898
5899 masm.move32(Imm32(call->numActualArgs()), argcReg);
5900
5901 // Nestle the StackPointer up to the argument vector.
5902 masm.freeStack(unusedStack);
5903 ensureOsiSpace();
5904
5905 auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct
5906 : IonGenericCallKind::Call;
5907
5908 TrampolinePtr genericCallStub =
5909 gen->jitRuntime()->getIonGenericCallStub(kind);
5910 uint32_t callOffset = masm.callJit(genericCallStub);
5911 markSafepointAt(callOffset, call);
5912
5913 if (call->mir()->maybeCrossRealm()) {
5914 static_assert(!JSReturnOperand.aliases(ReturnReg),
5915 "ReturnReg available as scratch after scripted calls");
5916 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5917 }
5918
5919 // Restore stack pointer.
5920 masm.setFramePushed(frameSize());
5921 emitRestoreStackPointerFromFP();
5922
5923 // If the return value of the constructing function is Primitive,
5924 // replace the return value with the Object from CreateThis.
5925 if (call->mir()->isConstructing()) {
5926 Label notPrimitive;
5927 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
5928 &notPrimitive);
5929 masm.loadValue(Address(masm.getStackPointer(), unusedStack),
5930 JSReturnOperand);
5931#ifdef DEBUG1
5932 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
5933 &notPrimitive);
5934 masm.assumeUnreachable("CreateThis creates an object");
5935#endif
5936 masm.bind(&notPrimitive);
5937 }
5938}
5939
5940void JitRuntime::generateIonGenericCallArgumentsShift(
5941 MacroAssembler& masm, Register argc, Register curr, Register end,
5942 Register scratch, Label* done) {
5943 static_assert(sizeof(Value) == 8);
5944 // There are |argc| Values on the stack. Shift them all down by 8 bytes,
5945 // overwriting the first value.
5946
5947 // Initialize `curr` to the destination of the first copy, and `end` to the
5948 // final value of curr.
5949 masm.moveStackPtrTo(curr);
5950 masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end);
5951
5952 Label loop;
5953 masm.bind(&loop);
5954 masm.branchPtr(Assembler::Equal, curr, end, done);
5955 masm.loadPtr(Address(curr, 8), scratch);
5956 masm.storePtr(scratch, Address(curr, 0));
5957 masm.addPtr(Imm32(sizeof(uintptr_t)), curr);
5958 masm.jump(&loop);
5959}
5960
5961void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm,
5962 IonGenericCallKind kind) {
5963 AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub");
5964 ionGenericCallStubOffset_[kind] = startTrampolineCode(masm);
5965
5966 // This code is tightly coupled with visitCallGeneric.
5967 //
5968 // Upon entry:
5969 // IonGenericCallCalleeReg contains a pointer to the callee object.
5970 // IonGenericCallArgcReg contains the number of actual args.
5971 // The arguments have been pushed onto the stack:
5972 // [newTarget] (iff isConstructing)
5973 // [argN]
5974 // ...
5975 // [arg1]
5976 // [arg0]
5977 // [this]
5978 // <return address> (if not JS_USE_LINK_REGISTER)
5979 //
5980 // This trampoline is responsible for entering the callee's realm,
5981 // massaging the stack into the right shape, and then performing a
5982 // tail call. We will return directly to the Ion code from the
5983 // callee.
5984 //
5985 // To do a tail call, we keep the return address in a register, even
5986 // on platforms that don't normally use a link register, and push it
5987 // just before jumping to the callee, after we are done setting up
5988 // the stack.
5989 //
5990 // The caller is responsible for switching back to the caller's
5991 // realm and cleaning up the stack.
5992
5993 Register calleeReg = IonGenericCallCalleeReg;
5994 Register argcReg = IonGenericCallArgcReg;
5995 Register scratch = IonGenericCallScratch;
5996 Register scratch2 = IonGenericCallScratch2;
5997
5998#ifndef JS_USE_LINK_REGISTER
5999 Register returnAddrReg = IonGenericCallReturnAddrReg;
6000 masm.pop(returnAddrReg);
6001#endif
6002
6003#ifdef JS_CODEGEN_ARM
6004 // The default second scratch register on arm is lr, which we need
6005 // preserved for tail calls.
6006 AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg);
6007#endif
6008
6009 bool isConstructing = kind == IonGenericCallKind::Construct;
6010
6011 Label entry, notFunction, noJitEntry, vmCall;
6012 masm.bind(&entry);
6013
6014 // Guard that the callee is actually a function.
6015 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
6016 calleeReg, &notFunction);
6017
6018 // Guard that the callee supports the [[Call]] or [[Construct]] operation.
6019 // If these tests fail, we will call into the VM to throw an exception.
6020 if (isConstructing) {
6021 masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR,
6022 Assembler::Zero, &vmCall);
6023 } else {
6024 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
6025 calleeReg, scratch, &vmCall);
6026 }
6027
6028 if (isConstructing) {
6029 // Use the slow path if CreateThis was unable to create the |this| object.
6030 Address thisAddr(masm.getStackPointer(), 0);
6031 masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall);
6032 }
6033
6034 masm.switchToObjectRealm(calleeReg, scratch);
6035
6036 // Load jitCodeRaw for callee if it exists.
6037 masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry);
6038
6039 // ****************************
6040 // * Functions with jit entry *
6041 // ****************************
6042 masm.loadJitCodeRaw(calleeReg, scratch2);
6043
6044 // Construct the JitFrameLayout.
6045 masm.PushCalleeToken(calleeReg, isConstructing);
6046 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch);
6047#ifndef JS_USE_LINK_REGISTER
6048 masm.push(returnAddrReg);
6049#endif
6050
6051 // Check whether we need a rectifier frame.
6052 Label noRectifier;
6053 masm.loadFunctionArgCount(calleeReg, scratch);
6054 masm.branch32(Assembler::BelowOrEqual, scratch, argcReg, &noRectifier);
6055 {
6056 // Tail-call the arguments rectifier.
6057 // Because all trampolines are created at the same time,
6058 // we can't create a TrampolinePtr for the arguments rectifier,
6059 // because it hasn't been linked yet. We can, however, directly
6060 // encode its offset.
6061 Label rectifier;
6062 bindLabelToOffset(&rectifier, argumentsRectifierOffset_);
6063
6064 masm.jump(&rectifier);
6065 }
6066
6067 // Tail call the jit entry.
6068 masm.bind(&noRectifier);
6069 masm.jump(scratch2);
6070
6071 // ********************
6072 // * Native functions *
6073 // ********************
6074 masm.bind(&noJitEntry);
6075 if (!isConstructing) {
6076 generateIonGenericCallFunCall(masm, &entry, &vmCall);
6077 }
6078 generateIonGenericCallNativeFunction(masm, isConstructing);
6079
6080 // *******************
6081 // * Bound functions *
6082 // *******************
6083 // TODO: support class hooks?
6084 masm.bind(&notFunction);
6085 if (!isConstructing) {
6086 // TODO: support generic bound constructors?
6087 generateIonGenericCallBoundFunction(masm, &entry, &vmCall);
6088 }
6089
6090 // ********************
6091 // * Fallback VM call *
6092 // ********************
6093 masm.bind(&vmCall);
6094
6095 masm.push(masm.getStackPointer()); // argv
6096 masm.push(argcReg); // argc
6097 masm.push(Imm32(false)); // ignores return value
6098 masm.push(Imm32(isConstructing)); // constructing
6099 masm.push(calleeReg); // callee
6100
6101 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6102 MutableHandleValue);
6103 VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id;
6104 uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)];
6105 Label invokeFunctionVMEntry;
6106 bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset);
6107
6108 masm.pushFrameDescriptor(FrameType::IonJS);
6109#ifndef JS_USE_LINK_REGISTER
6110 masm.push(returnAddrReg);
6111#endif
6112 masm.jump(&invokeFunctionVMEntry);
6113}
6114
6115void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm,
6116 bool isConstructing) {
6117 Register calleeReg = IonGenericCallCalleeReg;
6118 Register argcReg = IonGenericCallArgcReg;
6119 Register scratch = IonGenericCallScratch;
6120 Register scratch2 = IonGenericCallScratch2;
6121 Register contextReg = IonGenericCallScratch3;
6122#ifndef JS_USE_LINK_REGISTER
6123 Register returnAddrReg = IonGenericCallReturnAddrReg;
6124#endif
6125
6126 // Push a value containing the callee, which will become argv[0].
6127 masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg);
6128
6129 // Load the callee address into calleeReg.
6130#ifdef JS_SIMULATOR
6131 masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg);
6132#else
6133 masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
6134 calleeReg);
6135#endif
6136
6137 // Load argv into scratch2.
6138 masm.moveStackPtrTo(scratch2);
6139
6140 // Push argc.
6141 masm.push(argcReg);
6142
6143 masm.loadJSContext(contextReg);
6144
6145 // Construct native exit frame. Note that unlike other cases in this
6146 // trampoline, this code does not use a tail call.
6147 masm.pushFrameDescriptor(FrameType::IonJS);
6148#ifdef JS_USE_LINK_REGISTER
6149 masm.pushReturnAddress();
6150#else
6151 masm.push(returnAddrReg);
6152#endif
6153
6154 masm.push(FramePointer);
6155 masm.moveStackPtrTo(FramePointer);
6156 masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing);
6157
6158 masm.setupUnalignedABICall(scratch);
6159 masm.passABIArg(contextReg); // cx
6160 masm.passABIArg(argcReg); // argc
6161 masm.passABIArg(scratch2); // argv
6162
6163 masm.callWithABI(calleeReg);
6164
6165 // Test for failure.
6166 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
6167
6168 masm.loadValue(
6169 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
6170 JSReturnOperand);
6171
6172 // Leave the exit frame.
6173 masm.moveToStackPtr(FramePointer);
6174 masm.pop(FramePointer);
6175
6176 // Return.
6177 masm.ret();
6178}
6179
6180void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm,
6181 Label* entry, Label* vmCall) {
6182 Register calleeReg = IonGenericCallCalleeReg;
6183 Register argcReg = IonGenericCallArgcReg;
6184 Register scratch = IonGenericCallScratch;
6185 Register scratch2 = IonGenericCallScratch2;
6186 Register scratch3 = IonGenericCallScratch3;
6187
6188 Label notFunCall;
6189 masm.branchPtr(Assembler::NotEqual,
6190 Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
6191 ImmPtr(js::fun_call), &notFunCall);
6192
6193 // In general, we can implement fun_call by replacing calleeReg with
6194 // |this|, sliding all the other arguments down, and decrementing argc.
6195 //
6196 // *BEFORE* *AFTER*
6197 // [argN] argc = N+1 <padding>
6198 // ... [argN] argc = N
6199 // [arg1] ...
6200 // [arg0] [arg1] <- now arg0
6201 // [this] <- top of stack (aligned) [arg0] <- now this
6202 //
6203 // The only exception is when argc is already 0, in which case instead
6204 // of shifting arguments down we replace [this] with UndefinedValue():
6205 //
6206 // *BEFORE* *AFTER*
6207 // [this] argc = 0 [undef] argc = 0
6208 //
6209 // After making this transformation, we can jump back to the beginning
6210 // of this trampoline to handle the inner call.
6211
6212 // Guard that |this| is an object. If it is, replace calleeReg.
6213 masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall);
6214 masm.movePtr(scratch, calleeReg);
6215
6216 Label hasArgs;
6217 masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs);
6218
6219 // No arguments. Replace |this| with |undefined| and start from the top.
6220 masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0));
6221 masm.jump(entry);
6222
6223 masm.bind(&hasArgs);
6224
6225 Label doneSliding;
6226 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
6227 scratch3, &doneSliding);
6228 masm.bind(&doneSliding);
6229 masm.sub32(Imm32(1), argcReg);
6230
6231 masm.jump(entry);
6232
6233 masm.bind(&notFunCall);
6234}
6235
6236void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm,
6237 Label* entry,
6238 Label* vmCall) {
6239 Register calleeReg = IonGenericCallCalleeReg;
6240 Register argcReg = IonGenericCallArgcReg;
6241 Register scratch = IonGenericCallScratch;
6242 Register scratch2 = IonGenericCallScratch2;
6243 Register scratch3 = IonGenericCallScratch3;
6244
6245 masm.branchTestObjClass(Assembler::NotEqual, calleeReg,
6246 &BoundFunctionObject::class_, scratch, calleeReg,
6247 vmCall);
6248
6249 Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
6250 Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot());
6251 Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot());
6252 Address firstInlineArgSlot(
6253 calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg());
6254
6255 // Check that we won't be pushing too many arguments.
6256 masm.load32(flagsSlot, scratch);
6257 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
6258 masm.add32(argcReg, scratch);
6259 masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall);
6260
6261 // The stack is currently correctly aligned for a jit call. We will
6262 // be updating the `this` value and potentially adding additional
6263 // arguments. On platforms with 16-byte alignment, if the number of
6264 // bound arguments is odd, we have to move the arguments that are
6265 // currently on the stack. For example, with one bound argument:
6266 //
6267 // *BEFORE* *AFTER*
6268 // [argN] <padding>
6269 // ... [argN] |
6270 // [arg1] ... | These arguments have been
6271 // [arg0] [arg1] | shifted down 8 bytes.
6272 // [this] <- top of stack (aligned) [arg0] v
6273 // [bound0] <- one bound argument (odd)
6274 // [boundThis] <- top of stack (aligned)
6275 //
6276 Label poppedThis;
6277 if (JitStackValueAlignment > 1) {
6278 Label alreadyAligned;
6279 masm.branchTest32(Assembler::Zero, flagsSlot,
6280 Imm32(1 << BoundFunctionObject::NumBoundArgsShift),
6281 &alreadyAligned);
6282
6283 // We have an odd number of bound arguments. Shift the existing arguments
6284 // down by 8 bytes.
6285 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
6286 scratch3, &poppedThis);
6287 masm.bind(&alreadyAligned);
6288 }
6289
6290 // Pop the current `this`. It will be replaced with the bound `this`.
6291 masm.freeStack(sizeof(Value));
6292 masm.bind(&poppedThis);
6293
6294 // Load the number of bound arguments in scratch
6295 masm.load32(flagsSlot, scratch);
6296 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
6297
6298 Label donePushingBoundArguments;
6299 masm.branch32(Assembler::Equal, scratch, Imm32(0),
6300 &donePushingBoundArguments);
6301
6302 // Update argc to include bound arguments.
6303 masm.add32(scratch, argcReg);
6304
6305 // Load &boundArgs[0] in scratch2.
6306 Label outOfLineBoundArguments, haveBoundArguments;
6307 masm.branch32(Assembler::Above, scratch,
6308 Imm32(BoundFunctionObject::MaxInlineBoundArgs),
6309 &outOfLineBoundArguments);
6310 masm.computeEffectiveAddress(firstInlineArgSlot, scratch2);
6311 masm.jump(&haveBoundArguments);
6312
6313 masm.bind(&outOfLineBoundArguments);
6314 masm.unboxObject(firstInlineArgSlot, scratch2);
6315 masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
6316
6317 masm.bind(&haveBoundArguments);
6318
6319 // Load &boundArgs[numBoundArgs] in scratch.
6320 BaseObjectElementIndex lastBoundArg(scratch2, scratch);
6321 masm.computeEffectiveAddress(lastBoundArg, scratch);
6322
6323 // Push the bound arguments, starting with the last one.
6324 // Copying pre-decrements scratch until scratch2 is reached.
6325 Label boundArgumentsLoop;
6326 masm.bind(&boundArgumentsLoop);
6327 masm.subPtr(Imm32(sizeof(Value)), scratch);
6328 masm.pushValue(Address(scratch, 0));
6329 masm.branchPtr(Assembler::Above, scratch, scratch2, &boundArgumentsLoop);
6330 masm.bind(&donePushingBoundArguments);
6331
6332 // Push the bound `this`.
6333 masm.pushValue(thisSlot);
6334
6335 // Load the target in calleeReg.
6336 masm.unboxObject(targetSlot, calleeReg);
6337
6338 // At this point, all preconditions for entering the trampoline are met:
6339 // - calleeReg contains a pointer to the callee object
6340 // - argcReg contains the number of actual args (now including bound args)
6341 // - the arguments are on the stack with the correct alignment.
6342 // Instead of generating more code, we can jump back to the entry point
6343 // of the trampoline to call the bound target.
6344 masm.jump(entry);
6345}
6346
6347void CodeGenerator::visitCallKnown(LCallKnown* call) {
6348 Register calleereg = ToRegister(call->getFunction());
6349 Register objreg = ToRegister(call->getTempObject());
6350 uint32_t unusedStack =
6351 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6352 WrappedFunction* target = call->getSingleTarget();
6353
6354 // Native single targets (except Wasm and TrampolineNative functions) are
6355 // handled by LCallNative.
6356 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6356); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitEntry()"
")"); do { *((volatile int*)__null) = 6356; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6357
6358 // Missing arguments must have been explicitly appended by WarpBuilder.
6359 DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
6360 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
")"); do { *((volatile int*)__null) = 6361; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6361 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
")"); do { *((volatile int*)__null) = 6361; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6362
6363 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6363); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isConstructor()"
")"); do { *((volatile int*)__null) = 6363; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6364
6365 masm.checkStackAlignment();
6366
6367 if (target->isClassConstructor() && !call->isConstructing()) {
6368 emitCallInvokeFunction(call, calleereg, call->isConstructing(),
6369 call->ignoresReturnValue(), call->numActualArgs(),
6370 unusedStack);
6371 return;
6372 }
6373
6374 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6374); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->isConstructing()"
")"); do { *((volatile int*)__null) = 6374; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6375
6376 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->mir()->needsThisCheck()"
")"); do { *((volatile int*)__null) = 6376; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6377
6378 if (call->mir()->maybeCrossRealm()) {
6379 masm.switchToObjectRealm(calleereg, objreg);
6380 }
6381
6382 masm.loadJitCodeRaw(calleereg, objreg);
6383
6384 // Nestle the StackPointer up to the argument vector.
6385 masm.freeStack(unusedStack);
6386
6387 // Construct the JitFrameLayout.
6388 masm.PushCalleeToken(calleereg, call->mir()->isConstructing());
6389 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, call->numActualArgs());
6390
6391 // Finally call the function in objreg.
6392 ensureOsiSpace();
6393 uint32_t callOffset = masm.callJit(objreg);
6394 markSafepointAt(callOffset, call);
6395
6396 if (call->mir()->maybeCrossRealm()) {
6397 static_assert(!JSReturnOperand.aliases(ReturnReg),
6398 "ReturnReg available as scratch after scripted calls");
6399 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6400 }
6401
6402 // Restore stack pointer: pop JitFrameLayout fields still left on the stack
6403 // and undo the earlier |freeStack(unusedStack)|.
6404 int prefixGarbage =
6405 sizeof(JitFrameLayout) - JitFrameLayout::bytesPoppedAfterCall();
6406 masm.adjustStack(prefixGarbage - unusedStack);
6407
6408 // If the return value of the constructing function is Primitive,
6409 // replace the return value with the Object from CreateThis.
6410 if (call->mir()->isConstructing()) {
6411 Label notPrimitive;
6412 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6413 &notPrimitive);
6414 masm.loadValue(Address(masm.getStackPointer(), unusedStack),
6415 JSReturnOperand);
6416#ifdef DEBUG1
6417 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6418 &notPrimitive);
6419 masm.assumeUnreachable("CreateThis creates an object");
6420#endif
6421 masm.bind(&notPrimitive);
6422 }
6423}
6424
6425template <typename T>
6426void CodeGenerator::emitCallInvokeFunction(T* apply) {
6427 pushArg(masm.getStackPointer()); // argv.
6428 pushArg(ToRegister(apply->getArgc())); // argc.
6429 pushArg(Imm32(apply->mir()->ignoresReturnValue())); // ignoresReturnValue.
6430 pushArg(Imm32(apply->mir()->isConstructing())); // isConstructing.
6431 pushArg(ToRegister(apply->getFunction())); // JSFunction*.
6432
6433 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6434 MutableHandleValue);
6435 callVM<Fn, jit::InvokeFunction>(apply);
6436}
6437
6438// Do not bailout after the execution of this function since the stack no longer
6439// correspond to what is expected by the snapshots.
6440void CodeGenerator::emitAllocateSpaceForApply(Register argcreg,
6441 Register scratch) {
6442 // Use scratch register to calculate stack space (including padding).
6443 masm.movePtr(argcreg, scratch);
6444
6445 // Align the JitFrameLayout on the JitStackAlignment.
6446 if (JitStackValueAlignment > 1) {
6447 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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6448; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6448 "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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6448; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6449 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6449); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
")"); do { *((volatile int*)__null) = 6449; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6450 Label noPaddingNeeded;
6451 // If the number of arguments is odd, then we do not need any padding.
6452 //
6453 // Note: The |JitStackValueAlignment == 2| condition requires that the
6454 // overall number of values on the stack is even. When we have an odd number
6455 // of arguments, we don't need any padding, because the |thisValue| is
6456 // pushed after the arguments, so the overall number of values on the stack
6457 // is even.
6458 masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded);
6459 masm.addPtr(Imm32(1), scratch);
6460 masm.bind(&noPaddingNeeded);
6461 }
6462
6463 // Reserve space for copying the arguments.
6464 NativeObject::elementsSizeMustNotOverflow();
6465 masm.lshiftPtr(Imm32(ValueShift), scratch);
6466 masm.subFromStackPtr(scratch);
6467
6468#ifdef DEBUG1
6469 // Put a magic value in the space reserved for padding. Note, this code cannot
6470 // be merged with the previous test, as not all architectures can write below
6471 // their stack pointers.
6472 if (JitStackValueAlignment > 1) {
6473 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
")"); do { *((volatile int*)__null) = 6473; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6474 Label noPaddingNeeded;
6475 // If the number of arguments is odd, then we do not need any padding.
6476 masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded);
6477 BaseValueIndex dstPtr(masm.getStackPointer(), argcreg);
6478 masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr);
6479 masm.bind(&noPaddingNeeded);
6480 }
6481#endif
6482}
6483
6484// Do not bailout after the execution of this function since the stack no longer
6485// correspond to what is expected by the snapshots.
6486void CodeGenerator::emitAllocateSpaceForConstructAndPushNewTarget(
6487 Register argcreg, Register newTargetAndScratch) {
6488 // Align the JitFrameLayout on the JitStackAlignment. Contrary to
6489 // |emitAllocateSpaceForApply()|, we're always pushing a magic value, because
6490 // we can't write to |newTargetAndScratch| before |new.target| has been pushed
6491 // onto the stack.
6492 if (JitStackValueAlignment > 1) {
6493 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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6494); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6494; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6494 "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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6494); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6494; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6495 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6495); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
")"); do { *((volatile int*)__null) = 6495; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6496
6497 Label noPaddingNeeded;
6498 // If the number of arguments is even, then we do not need any padding.
6499 //
6500 // Note: The |JitStackValueAlignment == 2| condition requires that the
6501 // overall number of values on the stack is even. When we have an even
6502 // number of arguments, we don't need any padding, because |new.target| is
6503 // is pushed before the arguments and |thisValue| is pushed after all
6504 // arguments, so the overall number of values on the stack is even.
6505 masm.branchTestPtr(Assembler::Zero, argcreg, Imm32(1), &noPaddingNeeded);
6506 masm.pushValue(MagicValue(JS_ARG_POISON));
6507 masm.bind(&noPaddingNeeded);
6508 }
6509
6510 // Push |new.target| after the padding value, but before any arguments.
6511 masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch);
6512
6513 // Use newTargetAndScratch to calculate stack space (including padding).
6514 masm.movePtr(argcreg, newTargetAndScratch);
6515
6516 // Reserve space for copying the arguments.
6517 NativeObject::elementsSizeMustNotOverflow();
6518 masm.lshiftPtr(Imm32(ValueShift), newTargetAndScratch);
6519 masm.subFromStackPtr(newTargetAndScratch);
6520}
6521
6522// Destroys argvIndex and copyreg.
6523void CodeGenerator::emitCopyValuesForApply(Register argvSrcBase,
6524 Register argvIndex, Register copyreg,
6525 size_t argvSrcOffset,
6526 size_t argvDstOffset) {
6527 Label loop;
6528 masm.bind(&loop);
6529
6530 // As argvIndex is off by 1, and we use the decBranchPtr instruction to loop
6531 // back, we have to substract the size of the word which are copied.
6532 BaseValueIndex srcPtr(argvSrcBase, argvIndex,
6533 int32_t(argvSrcOffset) - sizeof(void*));
6534 BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex,
6535 int32_t(argvDstOffset) - sizeof(void*));
6536 masm.loadPtr(srcPtr, copyreg);
6537 masm.storePtr(copyreg, dstPtr);
6538
6539 // Handle 32 bits architectures.
6540 if (sizeof(Value) == 2 * sizeof(void*)) {
6541 BaseValueIndex srcPtrLow(argvSrcBase, argvIndex,
6542 int32_t(argvSrcOffset) - 2 * sizeof(void*));
6543 BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex,
6544 int32_t(argvDstOffset) - 2 * sizeof(void*));
6545 masm.loadPtr(srcPtrLow, copyreg);
6546 masm.storePtr(copyreg, dstPtrLow);
6547 }
6548
6549 masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop);
6550}
6551
6552void CodeGenerator::emitRestoreStackPointerFromFP() {
6553 // This is used to restore the stack pointer after a call with a dynamic
6554 // number of arguments.
6555
6556 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6556); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 6556; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6557
6558 int32_t offset = -int32_t(frameSize());
6559 masm.computeEffectiveAddress(Address(FramePointer, offset),
6560 masm.getStackPointer());
6561}
6562
6563void CodeGenerator::emitPushArguments(Register argcreg, Register scratch,
6564 Register copyreg, uint32_t extraFormals) {
6565 Label end;
6566
6567 // Skip the copy of arguments if there are none.
6568 masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end);
6569
6570 // clang-format off
6571 //
6572 // We are making a copy of the arguments which are above the JitFrameLayout
6573 // of the current Ion frame.
6574 //
6575 // [arg1] [arg0] <- src [this] [JitFrameLayout] [.. frameSize ..] [pad] [arg1] [arg0] <- dst
6576 //
6577 // clang-format on
6578
6579 // Compute the source and destination offsets into the stack.
6580 //
6581 // The |extraFormals| parameter is used when copying rest-parameters and
6582 // allows to skip the initial parameters before the actual rest-parameters.
6583 Register argvSrcBase = FramePointer;
6584 size_t argvSrcOffset =
6585 JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value);
6586 size_t argvDstOffset = 0;
6587
6588 Register argvIndex = scratch;
6589 masm.move32(argcreg, argvIndex);
6590
6591 // Copy arguments.
6592 emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset,
6593 argvDstOffset);
6594
6595 // Join with all arguments copied.
6596 masm.bind(&end);
6597}
6598
6599void CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply) {
6600 // Holds the function nargs.
6601 Register argcreg = ToRegister(apply->getArgc());
6602 Register copyreg = ToRegister(apply->getTempObject());
6603 Register scratch = ToRegister(apply->getTempForArgCopy());
6604 uint32_t extraFormals = apply->numExtraFormals();
6605
6606 // Allocate space on the stack for arguments.
6607 emitAllocateSpaceForApply(argcreg, scratch);
6608
6609 emitPushArguments(argcreg, scratch, copyreg, extraFormals);
6610
6611 // Push |this|.
6612 masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex));
6613}
6614
6615void CodeGenerator::emitPushArguments(LApplyArgsObj* apply) {
6616 Register argsObj = ToRegister(apply->getArgsObj());
6617 Register tmpArgc = ToRegister(apply->getTempObject());
6618 Register scratch = ToRegister(apply->getTempForArgCopy());
6619
6620 // argc and argsObj are mapped to the same calltemp register.
6621 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())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6621); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argsObj == ToRegister(apply->getArgc())"
")"); do { *((volatile int*)__null) = 6621; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6622
6623 // Load argc into tmpArgc.
6624 masm.loadArgumentsObjectLength(argsObj, tmpArgc);
6625
6626 // Allocate space on the stack for arguments.
6627 emitAllocateSpaceForApply(tmpArgc, scratch);
6628
6629 // Load arguments data.
6630 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
6631 argsObj);
6632 size_t argsSrcOffset = ArgumentsData::offsetOfArgs();
6633
6634 // This is the end of the lifetime of argsObj.
6635 // After this call, the argsObj register holds the argument count instead.
6636 emitPushArrayAsArguments(tmpArgc, argsObj, scratch, argsSrcOffset);
6637
6638 // Push |this|.
6639 masm.pushValue(ToValue(apply, LApplyArgsObj::ThisIndex));
6640}
6641
6642void CodeGenerator::emitPushArrayAsArguments(Register tmpArgc,
6643 Register srcBaseAndArgc,
6644 Register scratch,
6645 size_t argvSrcOffset) {
6646 // Preconditions:
6647 // 1. |tmpArgc| * sizeof(Value) bytes have been allocated at the top of
6648 // the stack to hold arguments.
6649 // 2. |srcBaseAndArgc| + |srcOffset| points to an array of |tmpArgc| values.
6650 //
6651 // Postconditions:
6652 // 1. The arguments at |srcBaseAndArgc| + |srcOffset| have been copied into
6653 // the allocated space.
6654 // 2. |srcBaseAndArgc| now contains the original value of |tmpArgc|.
6655 //
6656 // |scratch| is used as a temp register within this function and clobbered.
6657
6658 Label noCopy, epilogue;
6659
6660 // Skip the copy of arguments if there are none.
6661 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
6662 {
6663 // Copy the values. This code is skipped entirely if there are no values.
6664 size_t argvDstOffset = 0;
6665
6666 Register argvSrcBase = srcBaseAndArgc;
6667
6668 // Stash away |tmpArgc| and adjust argvDstOffset accordingly.
6669 masm.push(tmpArgc);
6670 Register argvIndex = tmpArgc;
6671 argvDstOffset += sizeof(void*);
6672
6673 // Copy
6674 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
6675 argvDstOffset);
6676
6677 // Restore.
6678 masm.pop(srcBaseAndArgc); // srcBaseAndArgc now contains argc.
6679 masm.jump(&epilogue);
6680 }
6681 masm.bind(&noCopy);
6682 {
6683 // Clear argc if we skipped the copy step.
6684 masm.movePtr(ImmWord(0), srcBaseAndArgc);
6685 }
6686
6687 // Join with all arguments copied.
6688 // Note, "srcBase" has become "argc".
6689 masm.bind(&epilogue);
6690}
6691
6692void CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply) {
6693 Register elements = ToRegister(apply->getElements());
6694 Register tmpArgc = ToRegister(apply->getTempObject());
6695 Register scratch = ToRegister(apply->getTempForArgCopy());
6696
6697 // argc and elements are mapped to the same calltemp register.
6698 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())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6698); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(apply->getArgc())"
")"); do { *((volatile int*)__null) = 6698; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6699
6700 // Invariants guarded in the caller:
6701 // - the array is not too long
6702 // - the array length equals its initialized length
6703
6704 // The array length is our argc for the purposes of allocating space.
6705 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
6706
6707 // Allocate space for the values.
6708 emitAllocateSpaceForApply(tmpArgc, scratch);
6709
6710 // After this call "elements" has become "argc".
6711 size_t elementsOffset = 0;
6712 emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset);
6713
6714 // Push |this|.
6715 masm.pushValue(ToValue(apply, LApplyArrayGeneric::ThisIndex));
6716}
6717
6718void CodeGenerator::emitPushArguments(LConstructArgsGeneric* construct) {
6719 // Holds the function nargs.
6720 Register argcreg = ToRegister(construct->getArgc());
6721 Register copyreg = ToRegister(construct->getTempObject());
6722 Register scratch = ToRegister(construct->getTempForArgCopy());
6723 uint32_t extraFormals = construct->numExtraFormals();
6724
6725 // newTarget and scratch are mapped to the same calltemp register.
6726 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())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6726); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())"
")"); do { *((volatile int*)__null) = 6726; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6727
6728 // Allocate space for the values.
6729 // After this call "newTarget" has become "scratch".
6730 emitAllocateSpaceForConstructAndPushNewTarget(argcreg, scratch);
6731
6732 emitPushArguments(argcreg, scratch, copyreg, extraFormals);
6733
6734 // Push |this|.
6735 masm.pushValue(ToValue(construct, LConstructArgsGeneric::ThisIndex));
6736}
6737
6738void CodeGenerator::emitPushArguments(LConstructArrayGeneric* construct) {
6739 Register elements = ToRegister(construct->getElements());
6740 Register tmpArgc = ToRegister(construct->getTempObject());
6741 Register scratch = ToRegister(construct->getTempForArgCopy());
6742
6743 // argc and elements are mapped to the same calltemp register.
6744 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())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6744); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(construct->getArgc())"
")"); do { *((volatile int*)__null) = 6744; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6745
6746 // newTarget and scratch are mapped to the same calltemp register.
6747 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())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6747); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())"
")"); do { *((volatile int*)__null) = 6747; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6748
6749 // Invariants guarded in the caller:
6750 // - the array is not too long
6751 // - the array length equals its initialized length
6752
6753 // The array length is our argc for the purposes of allocating space.
6754 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
6755
6756 // Allocate space for the values.
6757 // After this call "newTarget" has become "scratch".
6758 emitAllocateSpaceForConstructAndPushNewTarget(tmpArgc, scratch);
6759
6760 // After this call "elements" has become "argc".
6761 size_t elementsOffset = 0;
6762 emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset);
6763
6764 // Push |this|.
6765 masm.pushValue(ToValue(construct, LConstructArrayGeneric::ThisIndex));
6766}
6767
6768template <typename T>
6769void CodeGenerator::emitApplyGeneric(T* apply) {
6770 // Holds the function object.
6771 Register calleereg = ToRegister(apply->getFunction());
6772
6773 // Temporary register for modifying the function object.
6774 Register objreg = ToRegister(apply->getTempObject());
6775 Register scratch = ToRegister(apply->getTempForArgCopy());
6776
6777 // Holds the function nargs, computed in the invoker or (for ApplyArray,
6778 // ConstructArray, or ApplyArgsObj) in the argument pusher.
6779 Register argcreg = ToRegister(apply->getArgc());
6780
6781 // Copy the arguments of the current function.
6782 //
6783 // In the case of ApplyArray, ConstructArray, or ApplyArgsObj, also compute
6784 // argc. The argc register and the elements/argsObj register are the same;
6785 // argc must not be referenced before the call to emitPushArguments() and
6786 // elements/argsObj must not be referenced after it returns.
6787 //
6788 // In the case of ConstructArray or ConstructArgs, also overwrite newTarget;
6789 // newTarget must not be referenced after this point.
6790 //
6791 // objreg is dead across this call.
6792 emitPushArguments(apply);
6793
6794 masm.checkStackAlignment();
6795
6796 bool constructing = apply->mir()->isConstructing();
6797
6798 // If the function is native, the call is compiled through emitApplyNative.
6799 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 6800; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
6800 !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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 6800; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6801
6802 Label end, invoke;
6803
6804 // Unless already known, guard that calleereg is actually a function object.
6805 if (!apply->hasSingleTarget()) {
6806 masm.branchTestObjIsFunction(Assembler::NotEqual, calleereg, objreg,
6807 calleereg, &invoke);
6808 }
6809
6810 // Guard that calleereg is an interpreted function with a JSScript.
6811 masm.branchIfFunctionHasNoJitEntry(calleereg, &invoke);
6812
6813 // Guard that callee allows the [[Call]] or [[Construct]] operation required.
6814 if (constructing) {
6815 masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR,
6816 Assembler::Zero, &invoke);
6817 } else {
6818 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
6819 calleereg, objreg, &invoke);
6820 }
6821
6822 // Use the slow path if CreateThis was unable to create the |this| object.
6823 if (constructing) {
6824 Address thisAddr(masm.getStackPointer(), 0);
6825 masm.branchTestNull(Assembler::Equal, thisAddr, &invoke);
6826 }
6827
6828 // Call with an Ion frame or a rectifier frame.
6829 {
6830 if (apply->mir()->maybeCrossRealm()) {
6831 masm.switchToObjectRealm(calleereg, objreg);
6832 }
6833
6834 // Knowing that calleereg is a non-native function, load jitcode.
6835 masm.loadJitCodeRaw(calleereg, objreg);
6836
6837 masm.PushCalleeToken(calleereg, constructing);
6838 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcreg, scratch);
6839
6840 Label underflow, rejoin;
6841
6842 // Check whether the provided arguments satisfy target argc.
6843 if (!apply->hasSingleTarget()) {
6844 Register nformals = scratch;
6845 masm.loadFunctionArgCount(calleereg, nformals);
6846 masm.branch32(Assembler::Below, argcreg, nformals, &underflow);
6847 } else {
6848 masm.branch32(Assembler::Below, argcreg,
6849 Imm32(apply->getSingleTarget()->nargs()), &underflow);
6850 }
6851
6852 // Skip the construction of the rectifier frame because we have no
6853 // underflow.
6854 masm.jump(&rejoin);
6855
6856 // Argument fixup needed. Get ready to call the argumentsRectifier.
6857 {
6858 masm.bind(&underflow);
6859
6860 // Hardcode the address of the argumentsRectifier code.
6861 TrampolinePtr argumentsRectifier =
6862 gen->jitRuntime()->getArgumentsRectifier();
6863 masm.movePtr(argumentsRectifier, objreg);
6864 }
6865
6866 masm.bind(&rejoin);
6867
6868 // Finally call the function in objreg, as assigned by one of the paths
6869 // above.
6870 ensureOsiSpace();
6871 uint32_t callOffset = masm.callJit(objreg);
6872 markSafepointAt(callOffset, apply);
6873
6874 if (apply->mir()->maybeCrossRealm()) {
6875 static_assert(!JSReturnOperand.aliases(ReturnReg),
6876 "ReturnReg available as scratch after scripted calls");
6877 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6878 }
6879
6880 // Discard JitFrameLayout fields still left on the stack.
6881 masm.freeStack(sizeof(JitFrameLayout) -
6882 JitFrameLayout::bytesPoppedAfterCall());
6883 masm.jump(&end);
6884 }
6885
6886 // Handle uncompiled or native functions.
6887 {
6888 masm.bind(&invoke);
6889 emitCallInvokeFunction(apply);
6890 }
6891
6892 masm.bind(&end);
6893
6894 // If the return value of the constructing function is Primitive, replace the
6895 // return value with the Object from CreateThis.
6896 if (constructing) {
6897 Label notPrimitive;
6898 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6899 &notPrimitive);
6900 masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand);
6901
6902#ifdef DEBUG1
6903 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6904 &notPrimitive);
6905 masm.assumeUnreachable("CreateThis creates an object");
6906#endif
6907
6908 masm.bind(&notPrimitive);
6909 }
6910
6911 // Pop arguments and continue.
6912 emitRestoreStackPointerFromFP();
6913}
6914
6915template <typename T>
6916void CodeGenerator::emitAlignStackForApplyNative(T* apply, Register argc) {
6917 static_assert(JitStackAlignment % ABIStackAlignment == 0,
6918 "aligning on JIT stack subsumes ABI alignment");
6919
6920 // Align the arguments on the JitStackAlignment.
6921 if (JitStackValueAlignment > 1) {
6922 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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
") (" "Stack padding adds exactly one Value" ")"); do { *((volatile
int*)__null) = 6923; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
6923 "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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
") (" "Stack padding adds exactly one Value" ")"); do { *((volatile
int*)__null) = 6923; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
6924 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" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6925); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6925; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6925 "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" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 6925); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6925; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6926
6927 Assembler::Condition cond;
6928 if constexpr (T::isConstructing()) {
6929 // If the number of arguments is even, then we do not need any padding.
6930 //
6931 // Also see emitAllocateSpaceForApply().
6932 cond = Assembler::Zero;
6933 } else {
6934 // If the number of arguments is odd, then we do not need any padding.
6935 //
6936 // Also see emitAllocateSpaceForConstructAndPushNewTarget().
6937 cond = Assembler::NonZero;
6938 }
6939
6940 Label noPaddingNeeded;
6941 masm.branchTestPtr(cond, argc, Imm32(1), &noPaddingNeeded);
6942 masm.pushValue(MagicValue(JS_ARG_POISON));
6943 masm.bind(&noPaddingNeeded);
6944 }
6945}
6946
6947template <typename T>
6948void CodeGenerator::emitPushNativeArguments(T* apply) {
6949 Register argc = ToRegister(apply->getArgc());
6950 Register tmpArgc = ToRegister(apply->getTempObject());
6951 Register scratch = ToRegister(apply->getTempForArgCopy());
6952 uint32_t extraFormals = apply->numExtraFormals();
6953
6954 // Align stack.
6955 emitAlignStackForApplyNative(apply, argc);
6956
6957 // Push newTarget.
6958 if constexpr (T::isConstructing()) {
6959 masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget()));
6960 }
6961
6962 // Push arguments.
6963 Label noCopy;
6964 masm.branchTestPtr(Assembler::Zero, argc, argc, &noCopy);
6965 {
6966 // Use scratch register to calculate stack space.
6967 masm.movePtr(argc, scratch);
6968
6969 // Reserve space for copying the arguments.
6970 NativeObject::elementsSizeMustNotOverflow();
6971 masm.lshiftPtr(Imm32(ValueShift), scratch);
6972 masm.subFromStackPtr(scratch);
6973
6974 // Compute the source and destination offsets into the stack.
6975 Register argvSrcBase = FramePointer;
6976 size_t argvSrcOffset =
6977 JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value);
6978 size_t argvDstOffset = 0;
6979
6980 Register argvIndex = tmpArgc;
6981 masm.move32(argc, argvIndex);
6982
6983 // Copy arguments.
6984 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
6985 argvDstOffset);
6986 }
6987 masm.bind(&noCopy);
6988
6989 // Push |this|.
6990 if constexpr (T::isConstructing()) {
6991 masm.pushValue(MagicValue(JS_IS_CONSTRUCTING));
6992 } else {
6993 masm.pushValue(ToValue(apply, T::ThisIndex));
6994 }
6995}
6996
6997template <typename T>
6998void CodeGenerator::emitPushArrayAsNativeArguments(T* apply) {
6999 Register argc = ToRegister(apply->getArgc());
7000 Register elements = ToRegister(apply->getElements());
7001 Register tmpArgc = ToRegister(apply->getTempObject());
7002 Register scratch = ToRegister(apply->getTempForArgCopy());
7003
7004 // NB: argc and elements are mapped to the same register.
7005 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7005); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == elements"
")"); do { *((volatile int*)__null) = 7005; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7006
7007 // Invariants guarded in the caller:
7008 // - the array is not too long
7009 // - the array length equals its initialized length
7010
7011 // The array length is our argc.
7012 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
7013
7014 // Align stack.
7015 emitAlignStackForApplyNative(apply, tmpArgc);
7016
7017 // Push newTarget.
7018 if constexpr (T::isConstructing()) {
7019 masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget()));
7020 }
7021
7022 // Skip the copy of arguments if there are none.
7023 Label noCopy;
7024 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
7025 {
7026 // |tmpArgc| is off-by-one, so adjust the offset accordingly.
7027 BaseObjectElementIndex srcPtr(elements, tmpArgc,
7028 -int32_t(sizeof(JS::Value)));
7029
7030 Label loop;
7031 masm.bind(&loop);
7032 masm.pushValue(srcPtr, scratch);
7033 masm.decBranchPtr(Assembler::NonZero, tmpArgc, Imm32(1), &loop);
7034 }
7035 masm.bind(&noCopy);
7036
7037 // Set argc in preparation for calling the native function.
7038 masm.load32(Address(elements, ObjectElements::offsetOfLength()), argc);
7039
7040 // Push |this|.
7041 if constexpr (T::isConstructing()) {
7042 masm.pushValue(MagicValue(JS_IS_CONSTRUCTING));
7043 } else {
7044 masm.pushValue(ToValue(apply, T::ThisIndex));
7045 }
7046}
7047
7048void CodeGenerator::emitPushArguments(LApplyArgsNative* apply) {
7049 emitPushNativeArguments(apply);
7050}
7051
7052void CodeGenerator::emitPushArguments(LApplyArrayNative* apply) {
7053 emitPushArrayAsNativeArguments(apply);
7054}
7055
7056void CodeGenerator::emitPushArguments(LConstructArgsNative* construct) {
7057 emitPushNativeArguments(construct);
7058}
7059
7060void CodeGenerator::emitPushArguments(LConstructArrayNative* construct) {
7061 emitPushArrayAsNativeArguments(construct);
7062}
7063
7064void CodeGenerator::emitPushArguments(LApplyArgsObjNative* apply) {
7065 Register argc = ToRegister(apply->getArgc());
7066 Register argsObj = ToRegister(apply->getArgsObj());
7067 Register tmpArgc = ToRegister(apply->getTempObject());
7068 Register scratch = ToRegister(apply->getTempForArgCopy());
7069 Register scratch2 = ToRegister(apply->getTempExtra());
7070
7071 // NB: argc and argsObj are mapped to the same register.
7072 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7072); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == argsObj"
")"); do { *((volatile int*)__null) = 7072; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7073
7074 // Load argc into tmpArgc.
7075 masm.loadArgumentsObjectLength(argsObj, tmpArgc);
7076
7077 // Align stack.
7078 emitAlignStackForApplyNative(apply, tmpArgc);
7079
7080 // Push arguments.
7081 Label noCopy, epilogue;
7082 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
7083 {
7084 // Use scratch register to calculate stack space.
7085 masm.movePtr(tmpArgc, scratch);
7086
7087 // Reserve space for copying the arguments.
7088 NativeObject::elementsSizeMustNotOverflow();
7089 masm.lshiftPtr(Imm32(ValueShift), scratch);
7090 masm.subFromStackPtr(scratch);
7091
7092 // Load arguments data.
7093 Register argvSrcBase = argsObj;
7094 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
7095 argvSrcBase);
7096 size_t argvSrcOffset = ArgumentsData::offsetOfArgs();
7097 size_t argvDstOffset = 0;
7098
7099 Register argvIndex = scratch2;
7100 masm.move32(tmpArgc, argvIndex);
7101
7102 // Copy the values.
7103 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
7104 argvDstOffset);
7105 }
7106 masm.bind(&noCopy);
7107
7108 // Set argc in preparation for calling the native function.
7109 masm.movePtr(tmpArgc, argc);
7110
7111 // Push |this|.
7112 masm.pushValue(ToValue(apply, LApplyArgsObjNative::ThisIndex));
7113}
7114
7115template <typename T>
7116void CodeGenerator::emitApplyNative(T* apply) {
7117 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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7118); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()"
") (" "isConstructing condition must be consistent" ")"); do
{ *((volatile int*)__null) = 7118; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
7118 "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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7118); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()"
") (" "isConstructing condition must be consistent" ")"); do
{ *((volatile int*)__null) = 7118; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7119
7120 WrappedFunction* target = apply->mir()->getSingleTarget();
7121 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 7121; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7122
7123 JSNative native = target->native();
7124 if (apply->mir()->ignoresReturnValue() && target->hasJitInfo()) {
7125 const JSJitInfo* jitInfo = target->jitInfo();
7126 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
7127 native = jitInfo->ignoresReturnValueMethod;
7128 }
7129 }
7130
7131 // Push arguments, including newTarget and |this|.
7132 emitPushArguments(apply);
7133
7134 // Registers used for callWithABI() argument-passing.
7135 Register argContextReg = ToRegister(apply->getTempObject());
7136 Register argUintNReg = ToRegister(apply->getArgc());
7137 Register argVpReg = ToRegister(apply->getTempForArgCopy());
7138 Register tempReg = ToRegister(apply->getTempExtra());
7139
7140 // No unused stack for variadic calls.
7141 uint32_t unusedStack = 0;
7142
7143 // Pushed arguments don't change the pushed frames amount.
7144 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7144); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 7144; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7145
7146 // Create the exit frame and call the native.
7147 emitCallNative(apply, native, argContextReg, argUintNReg, argVpReg, tempReg,
7148 unusedStack);
7149
7150 // The exit frame is still on the stack.
7151 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7151); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()"
")"); do { *((volatile int*)__null) = 7151; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7152
7153 // The next instruction is removing the exit frame, so there is no need for
7154 // leaveFakeExitFrame.
7155
7156 // Pop arguments and continue.
7157 masm.setFramePushed(frameSize());
7158 emitRestoreStackPointerFromFP();
7159}
7160
7161template <typename T>
7162void CodeGenerator::emitApplyArgsGuard(T* apply) {
7163 LSnapshot* snapshot = apply->snapshot();
7164 Register argcreg = ToRegister(apply->getArgc());
7165
7166 // Ensure that we have a reasonable number of arguments.
7167 bailoutCmp32(Assembler::Above, argcreg, Imm32(JIT_ARGS_LENGTH_MAX), snapshot);
7168}
7169
7170template <typename T>
7171void CodeGenerator::emitApplyArgsObjGuard(T* apply) {
7172 Register argsObj = ToRegister(apply->getArgsObj());
7173 Register temp = ToRegister(apply->getTempObject());
7174
7175 Label bail;
7176 masm.loadArgumentsObjectLength(argsObj, temp, &bail);
7177 masm.branch32(Assembler::Above, temp, Imm32(JIT_ARGS_LENGTH_MAX), &bail);
7178 bailoutFrom(&bail, apply->snapshot());
7179}
7180
7181template <typename T>
7182void CodeGenerator::emitApplyArrayGuard(T* apply) {
7183 LSnapshot* snapshot = apply->snapshot();
7184 Register elements = ToRegister(apply->getElements());
7185 Register tmp = ToRegister(apply->getTempObject());
7186
7187 Address length(elements, ObjectElements::offsetOfLength());
7188 masm.load32(length, tmp);
7189
7190 // Ensure that we have a reasonable number of arguments.
7191 bailoutCmp32(Assembler::Above, tmp, Imm32(JIT_ARGS_LENGTH_MAX), snapshot);
7192
7193 // Ensure that the array does not contain an uninitialized tail.
7194
7195 Address initializedLength(elements,
7196 ObjectElements::offsetOfInitializedLength());
7197 masm.sub32(initializedLength, tmp);
7198 bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot);
7199}
7200
7201void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) {
7202 emitApplyArgsGuard(apply);
7203 emitApplyGeneric(apply);
7204}
7205
7206void CodeGenerator::visitApplyArgsObj(LApplyArgsObj* apply) {
7207 emitApplyArgsObjGuard(apply);
7208 emitApplyGeneric(apply);
7209}
7210
7211void CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) {
7212 emitApplyArrayGuard(apply);
7213 emitApplyGeneric(apply);
7214}
7215
7216void CodeGenerator::visitConstructArgsGeneric(LConstructArgsGeneric* lir) {
7217 emitApplyArgsGuard(lir);
7218 emitApplyGeneric(lir);
7219}
7220
7221void CodeGenerator::visitConstructArrayGeneric(LConstructArrayGeneric* lir) {
7222 emitApplyArrayGuard(lir);
7223 emitApplyGeneric(lir);
7224}
7225
7226void CodeGenerator::visitApplyArgsNative(LApplyArgsNative* lir) {
7227 emitApplyArgsGuard(lir);
7228 emitApplyNative(lir);
7229}
7230
7231void CodeGenerator::visitApplyArgsObjNative(LApplyArgsObjNative* lir) {
7232 emitApplyArgsObjGuard(lir);
7233 emitApplyNative(lir);
7234}
7235
7236void CodeGenerator::visitApplyArrayNative(LApplyArrayNative* lir) {
7237 emitApplyArrayGuard(lir);
7238 emitApplyNative(lir);
7239}
7240
7241void CodeGenerator::visitConstructArgsNative(LConstructArgsNative* lir) {
7242 emitApplyArgsGuard(lir);
7243 emitApplyNative(lir);
7244}
7245
7246void CodeGenerator::visitConstructArrayNative(LConstructArrayNative* lir) {
7247 emitApplyArrayGuard(lir);
7248 emitApplyNative(lir);
7249}
7250
7251void CodeGenerator::visitBail(LBail* lir) { bailout(lir->snapshot()); }
7252
7253void CodeGenerator::visitUnreachable(LUnreachable* lir) {
7254 masm.assumeUnreachable("end-of-block assumed unreachable");
7255}
7256
7257void CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot* lir) {
7258 encode(lir->snapshot());
7259}
7260
7261void CodeGenerator::visitUnreachableResultV(LUnreachableResultV* lir) {
7262 masm.assumeUnreachable("must be unreachable");
7263}
7264
7265void CodeGenerator::visitUnreachableResultT(LUnreachableResultT* lir) {
7266 masm.assumeUnreachable("must be unreachable");
7267}
7268
7269// Out-of-line path to report over-recursed error and fail.
7270class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator> {
7271 LInstruction* lir_;
7272
7273 public:
7274 explicit CheckOverRecursedFailure(LInstruction* lir) : lir_(lir) {}
7275
7276 void accept(CodeGenerator* codegen) override {
7277 codegen->visitCheckOverRecursedFailure(this);
7278 }
7279
7280 LInstruction* lir() const { return lir_; }
7281};
7282
7283void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) {
7284 // If we don't push anything on the stack, skip the check.
7285 if (omitOverRecursedCheck()) {
7286 return;
7287 }
7288
7289 // Ensure that this frame will not cross the stack limit.
7290 // This is a weak check, justified by Ion using the C stack: we must always
7291 // be some distance away from the actual limit, since if the limit is
7292 // crossed, an error must be thrown, which requires more frames.
7293 //
7294 // It must always be possible to trespass past the stack limit.
7295 // Ion may legally place frames very close to the limit. Calling additional
7296 // C functions may then violate the limit without any checking.
7297 //
7298 // Since Ion frames exist on the C stack, the stack limit may be
7299 // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
7300
7301 CheckOverRecursedFailure* ool = new (alloc()) CheckOverRecursedFailure(lir);
7302 addOutOfLineCode(ool, lir->mir());
7303
7304 // Conditional forward (unlikely) branch to failure.
7305 const void* limitAddr = gen->runtime->addressOfJitStackLimit();
7306 masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr),
7307 ool->entry());
7308 masm.bind(ool->rejoin());
7309}
7310
7311void CodeGenerator::visitCheckOverRecursedFailure(
7312 CheckOverRecursedFailure* ool) {
7313 // The OOL path is hit if the recursion depth has been exceeded.
7314 // Throw an InternalError for over-recursion.
7315
7316 // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
7317 // to save all live registers to avoid crashes if CheckOverRecursed triggers
7318 // a GC.
7319 saveLive(ool->lir());
7320
7321 using Fn = bool (*)(JSContext*);
7322 callVM<Fn, CheckOverRecursed>(ool->lir());
7323
7324 restoreLive(ool->lir());
7325 masm.jump(ool->rejoin());
7326}
7327
7328IonScriptCounts* CodeGenerator::maybeCreateScriptCounts() {
7329 // If scripts are being profiled, create a new IonScriptCounts for the
7330 // profiling data, which will be attached to the associated JSScript or
7331 // wasm module after code generation finishes.
7332 if (!gen->hasProfilingScripts()) {
7333 return nullptr;
7334 }
7335
7336 // This test inhibits IonScriptCount creation for wasm code which is
7337 // currently incompatible with wasm codegen for two reasons: (1) wasm code
7338 // must be serializable and script count codegen bakes in absolute
7339 // addresses, (2) wasm code does not have a JSScript with which to associate
7340 // code coverage data.
7341 JSScript* script = gen->outerInfo().script();
7342 if (!script) {
7343 return nullptr;
7344 }
7345
7346 auto counts = MakeUnique<IonScriptCounts>();
7347 if (!counts || !counts->init(graph.numBlocks())) {
7348 return nullptr;
7349 }
7350
7351 for (size_t i = 0; i < graph.numBlocks(); i++) {
7352 MBasicBlock* block = graph.getBlock(i)->mir();
7353
7354 uint32_t offset = 0;
7355 char* description = nullptr;
7356 if (MResumePoint* resume = block->entryResumePoint()) {
7357 // Find a PC offset in the outermost script to use. If this
7358 // block is from an inlined script, find a location in the
7359 // outer script to associate information about the inlining
7360 // with.
7361 while (resume->caller()) {
7362 resume = resume->caller();
7363 }
7364 offset = script->pcToOffset(resume->pc());
7365
7366 if (block->entryResumePoint()->caller()) {
7367 // Get the filename and line number of the inner script.
7368 JSScript* innerScript = block->info().script();
7369 description = js_pod_calloc<char>(200);
7370 if (description) {
7371 snprintf(description, 200, "%s:%u", innerScript->filename(),
7372 innerScript->lineno());
7373 }
7374 }
7375 }
7376
7377 if (!counts->block(i).init(block->id(), offset, description,
7378 block->numSuccessors())) {
7379 return nullptr;
7380 }
7381
7382 for (size_t j = 0; j < block->numSuccessors(); j++) {
7383 counts->block(i).setSuccessor(
7384 j, skipTrivialBlocks(block->getSuccessor(j))->id());
7385 }
7386 }
7387
7388 scriptCounts_ = counts.release();
7389 return scriptCounts_;
7390}
7391
7392// Structure for managing the state tracked for a block by script counters.
7393struct ScriptCountBlockState {
7394 IonBlockCounts& block;
7395 MacroAssembler& masm;
7396
7397 Sprinter printer;
7398
7399 public:
7400 ScriptCountBlockState(IonBlockCounts* block, MacroAssembler* masm)
7401 : block(*block), masm(*masm), printer(GetJitContext()->cx, false) {}
7402
7403 bool init() {
7404 if (!printer.init()) {
7405 return false;
7406 }
7407
7408 // Bump the hit count for the block at the start. This code is not
7409 // included in either the text for the block or the instruction byte
7410 // counts.
7411 masm.inc64(AbsoluteAddress(block.addressOfHitCount()));
7412
7413 // Collect human readable assembly for the code generated in the block.
7414 masm.setPrinter(&printer);
7415
7416 return true;
7417 }
7418
7419 void visitInstruction(LInstruction* ins) {
7420#ifdef JS_JITSPEW1
7421 // Prefix stream of assembly instructions with their LIR instruction
7422 // name and any associated high level info.
7423 if (const char* extra = ins->getExtraName()) {
7424 printer.printf("[%s:%s]\n", ins->opName(), extra);
7425 } else {
7426 printer.printf("[%s]\n", ins->opName());
7427 }
7428#endif
7429 }
7430
7431 ~ScriptCountBlockState() {
7432 masm.setPrinter(nullptr);
7433
7434 if (JS::UniqueChars str = printer.release()) {
7435 block.setCode(str.get());
7436 }
7437 }
7438};
7439
7440void CodeGenerator::branchIfInvalidated(Register temp, Label* invalidated) {
7441 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp);
7442 masm.propagateOOM(ionScriptLabels_.append(label));
7443
7444 // If IonScript::invalidationCount_ != 0, the script has been invalidated.
7445 masm.branch32(Assembler::NotEqual,
7446 Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0),
7447 invalidated);
7448}
7449
7450#ifdef DEBUG1
7451void CodeGenerator::emitAssertGCThingResult(Register input,
7452 const MDefinition* mir) {
7453 MIRType type = mir->type();
7454 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
")"); do { *((volatile int*)__null) = 7455; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7455 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
")"); do { *((volatile int*)__null) = 7455; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7456
7457 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
7458 regs.take(input);
7459
7460 Register temp = regs.takeAny();
7461 masm.push(temp);
7462
7463 // Don't check if the script has been invalidated. In that case invalid
7464 // types are expected (until we reach the OsiPoint and bailout).
7465 Label done;
7466 branchIfInvalidated(temp, &done);
7467
7468# ifndef JS_SIMULATOR
7469 // Check that we have a valid GC pointer.
7470 // Disable for wasm because we don't have a context on wasm compilation
7471 // threads and this needs a context.
7472 // Also disable for simulator builds because the C++ call is a lot slower
7473 // there than on actual hardware.
7474 if (JitOptions.fullDebugChecks && !IsCompilingWasm()) {
7475 saveVolatile();
7476 masm.setupUnalignedABICall(temp);
7477 masm.loadJSContext(temp);
7478 masm.passABIArg(temp);
7479 masm.passABIArg(input);
7480
7481 switch (type) {
7482 case MIRType::Object: {
7483 using Fn = void (*)(JSContext* cx, JSObject* obj);
7484 masm.callWithABI<Fn, AssertValidObjectPtr>();
7485 break;
7486 }
7487 case MIRType::String: {
7488 using Fn = void (*)(JSContext* cx, JSString* str);
7489 masm.callWithABI<Fn, AssertValidStringPtr>();
7490 break;
7491 }
7492 case MIRType::Symbol: {
7493 using Fn = void (*)(JSContext* cx, JS::Symbol* sym);
7494 masm.callWithABI<Fn, AssertValidSymbolPtr>();
7495 break;
7496 }
7497 case MIRType::BigInt: {
7498 using Fn = void (*)(JSContext* cx, JS::BigInt* bi);
7499 masm.callWithABI<Fn, AssertValidBigIntPtr>();
7500 break;
7501 }
7502 default:
7503 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7503); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 7503; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
7504 }
7505
7506 restoreVolatile();
7507 }
7508# endif
7509
7510 masm.bind(&done);
7511 masm.pop(temp);
7512}
7513
7514void CodeGenerator::emitAssertResultV(const ValueOperand input,
7515 const MDefinition* mir) {
7516 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
7517 regs.take(input);
7518
7519 Register temp1 = regs.takeAny();
7520 Register temp2 = regs.takeAny();
7521 masm.push(temp1);
7522 masm.push(temp2);
7523
7524 // Don't check if the script has been invalidated. In that case invalid
7525 // types are expected (until we reach the OsiPoint and bailout).
7526 Label done;
7527 branchIfInvalidated(temp1, &done);
7528
7529 // Check that we have a valid GC pointer.
7530 if (JitOptions.fullDebugChecks) {
7531 saveVolatile();
7532
7533 masm.pushValue(input);
7534 masm.moveStackPtrTo(temp1);
7535
7536 using Fn = void (*)(JSContext* cx, Value* v);
7537 masm.setupUnalignedABICall(temp2);
7538 masm.loadJSContext(temp2);
7539 masm.passABIArg(temp2);
7540 masm.passABIArg(temp1);
7541 masm.callWithABI<Fn, AssertValidValue>();
7542 masm.popValue(input);
7543 restoreVolatile();
7544 }
7545
7546 masm.bind(&done);
7547 masm.pop(temp2);
7548 masm.pop(temp1);
7549}
7550
7551void CodeGenerator::emitGCThingResultChecks(LInstruction* lir,
7552 MDefinition* mir) {
7553 if (lir->numDefs() == 0) {
7554 return;
7555 }
7556
7557 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7557); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1"
")"); do { *((volatile int*)__null) = 7557; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7558 if (lir->getDef(0)->isBogusTemp()) {
7559 return;
7560 }
7561
7562 Register output = ToRegister(lir->getDef(0));
7563 emitAssertGCThingResult(output, mir);
7564}
7565
7566void CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir) {
7567 if (lir->numDefs() == 0) {
7568 return;
7569 }
7570
7571 MOZ_ASSERT(lir->numDefs() == BOX_PIECES)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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1"
")"); do { *((volatile int*)__null) = 7571; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7572 if (!lir->getDef(0)->output()->isRegister()) {
7573 return;
7574 }
7575
7576 ValueOperand output = ToOutValue(lir);
7577
7578 emitAssertResultV(output, mir);
7579}
7580
7581void CodeGenerator::emitDebugResultChecks(LInstruction* ins) {
7582 // In debug builds, check that LIR instructions return valid values.
7583
7584 MDefinition* mir = ins->mirRaw();
7585 if (!mir) {
7586 return;
7587 }
7588
7589 switch (mir->type()) {
7590 case MIRType::Object:
7591 case MIRType::String:
7592 case MIRType::Symbol:
7593 case MIRType::BigInt:
7594 emitGCThingResultChecks(ins, mir);
7595 break;
7596 case MIRType::Value:
7597 emitValueResultChecks(ins, mir);
7598 break;
7599 default:
7600 break;
7601 }
7602}
7603
7604void CodeGenerator::emitDebugForceBailing(LInstruction* lir) {
7605 if (MOZ_LIKELY(!gen->options.ionBailAfterEnabled())(__builtin_expect(!!(!gen->options.ionBailAfterEnabled()),
1))
) {
7606 return;
7607 }
7608 if (!lir->snapshot()) {
7609 return;
7610 }
7611 if (lir->isOsiPoint()) {
7612 return;
7613 }
7614
7615 masm.comment("emitDebugForceBailing");
7616 const void* bailAfterCounterAddr =
7617 gen->runtime->addressOfIonBailAfterCounter();
7618
7619 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
7620
7621 Label done, notBail;
7622 masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterCounterAddr),
7623 Imm32(0), &done);
7624 {
7625 Register temp = regs.takeAny();
7626
7627 masm.push(temp);
7628 masm.load32(AbsoluteAddress(bailAfterCounterAddr), temp);
7629 masm.sub32(Imm32(1), temp);
7630 masm.store32(temp, AbsoluteAddress(bailAfterCounterAddr));
7631
7632 masm.branch32(Assembler::NotEqual, temp, Imm32(0), &notBail);
7633 {
7634 masm.pop(temp);
7635 bailout(lir->snapshot());
7636 }
7637 masm.bind(&notBail);
7638 masm.pop(temp);
7639 }
7640 masm.bind(&done);
7641}
7642#endif
7643
7644bool CodeGenerator::generateBody() {
7645 JitSpewCont(JitSpew_Codegen, "\n");
7646 AutoCreatedBy acb(masm, "CodeGenerator::generateBody");
7647
7648 JitSpew(JitSpew_Codegen, "==== BEGIN CodeGenerator::generateBody ====");
7649 IonScriptCounts* counts = maybeCreateScriptCounts();
7650
7651 const bool compilingWasm = gen->compilingWasm();
7652
7653 for (size_t i = 0; i < graph.numBlocks(); i++) {
7654 current = graph.getBlock(i);
7655
7656 // Don't emit any code for trivial blocks, containing just a goto. Such
7657 // blocks are created to split critical edges, and if we didn't end up
7658 // putting any instructions in them, we can skip them.
7659 if (current->isTrivial()) {
7660 continue;
7661 }
7662
7663#ifdef JS_JITSPEW1
7664 const char* filename = nullptr;
7665 size_t lineNumber = 0;
7666 JS::LimitedColumnNumberOneOrigin columnNumber;
7667 if (current->mir()->info().script()) {
7668 filename = current->mir()->info().script()->filename();
7669 if (current->mir()->pc()) {
7670 lineNumber = PCToLineNumber(current->mir()->info().script(),
7671 current->mir()->pc(), &columnNumber);
7672 }
7673 }
7674 JitSpew(JitSpew_Codegen, "--------------------------------");
7675 JitSpew(JitSpew_Codegen, "# block%zu %s:%zu:%u%s:", i,
7676 filename ? filename : "?", lineNumber,
7677 columnNumber.oneOriginValue(),
7678 current->mir()->isLoopHeader() ? " (loop header)" : "");
7679#endif
7680
7681 if (current->mir()->isLoopHeader() && compilingWasm) {
7682 masm.nopAlign(CodeAlignment);
7683 }
7684
7685 masm.bind(current->label());
7686
7687 mozilla::Maybe<ScriptCountBlockState> blockCounts;
7688 if (counts) {
7689 blockCounts.emplace(&counts->block(i), &masm);
7690 if (!blockCounts->init()) {
7691 return false;
7692 }
7693 }
7694
7695 for (LInstructionIterator iter = current->begin(); iter != current->end();
7696 iter++) {
7697 if (!alloc().ensureBallast()) {
7698 return false;
7699 }
7700
7701 perfSpewer_.recordInstruction(masm, *iter);
7702#ifdef JS_JITSPEW1
7703 JitSpewStart(JitSpew_Codegen, " # LIR=%s",
7704 iter->opName());
7705 if (const char* extra = iter->getExtraName()) {
7706 JitSpewCont(JitSpew_Codegen, ":%s", extra);
7707 }
7708 JitSpewFin(JitSpew_Codegen);
7709#endif
7710
7711 if (counts) {
7712 blockCounts->visitInstruction(*iter);
7713 }
7714
7715#ifdef CHECK_OSIPOINT_REGISTERS1
7716 if (iter->safepoint() && !compilingWasm) {
7717 resetOsiPointRegs(iter->safepoint());
7718 }
7719#endif
7720
7721 if (!compilingWasm) {
7722 if (MDefinition* mir = iter->mirRaw()) {
7723 if (!addNativeToBytecodeEntry(mir->trackedSite())) {
7724 return false;
7725 }
7726 }
7727 }
7728
7729 setElement(*iter); // needed to encode correct snapshot location.
7730
7731#ifdef DEBUG1
7732 emitDebugForceBailing(*iter);
7733#endif
7734
7735 switch (iter->op()) {
7736#ifndef JS_CODEGEN_NONE
7737# define LIROP(op) \
7738 case LNode::Opcode::op: \
7739 visit##op(iter->to##op()); \
7740 break;
7741 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(NurseryObject)LIROP(Parameter)LIROP(Callee)LIROP
(IsConstructing)LIROP(Goto)LIROP(NewArray)LIROP(NewArrayDynamicLength
)LIROP(NewIterator)LIROP(NewTypedArray)LIROP(NewTypedArrayDynamicLength
)LIROP(NewTypedArrayFromArray)LIROP(NewTypedArrayFromArrayBuffer
)LIROP(BindFunction)LIROP(NewBoundFunction)LIROP(NewObject)LIROP
(NewPlainObject)LIROP(NewArrayObject)LIROP(NewNamedLambdaObject
)LIROP(NewCallObject)LIROP(NewStringObject)LIROP(InitElemGetterSetter
)LIROP(MutateProto)LIROP(InitPropGetterSetter)LIROP(CheckOverRecursed
)LIROP(WasmTrap)LIROP(WasmTrapIfNull)LIROP(WasmRefIsSubtypeOfConcrete
)LIROP(WasmRefIsSubtypeOfAbstract)LIROP(WasmRefIsSubtypeOfConcreteAndBranch
)LIROP(WasmRefIsSubtypeOfAbstractAndBranch)LIROP(WasmNewStructObject
)LIROP(WasmNewArrayObject)LIROP(WasmReinterpret)LIROP(WasmReinterpretFromI64
)LIROP(WasmReinterpretToI64)LIROP(Rotate)LIROP(RotateI64)LIROP
(InterruptCheck)LIROP(WasmStackSwitchToMain)LIROP(WasmStackSwitchToSuspendable
)LIROP(WasmStackContinueOnSuspendable)LIROP(WasmInterruptCheck
)LIROP(TypeOfV)LIROP(TypeOfO)LIROP(TypeOfName)LIROP(TypeOfIsNonPrimitiveV
)LIROP(TypeOfIsNonPrimitiveO)LIROP(TypeOfIsPrimitive)LIROP(ToAsyncIter
)LIROP(ToPropertyKeyCache)LIROP(CreateThis)LIROP(CreateArgumentsObject
)LIROP(CreateInlinedArgumentsObject)LIROP(GetInlinedArgument)
LIROP(GetInlinedArgumentHole)LIROP(GetArgumentsObjectArg)LIROP
(SetArgumentsObjectArg)LIROP(LoadArgumentsObjectArg)LIROP(LoadArgumentsObjectArgHole
)LIROP(InArgumentsObjectArg)LIROP(ArgumentsObjectLength)LIROP
(ArrayFromArgumentsObject)LIROP(GuardArgumentsObjectFlags)LIROP
(BoundFunctionNumArgs)LIROP(GuardBoundFunctionIsConstructor)LIROP
(ReturnFromCtor)LIROP(BoxNonStrictThis)LIROP(ImplicitThis)LIROP
(StackArgT)LIROP(StackArgV)LIROP(CallGeneric)LIROP(CallKnown)
LIROP(CallNative)LIROP(CallDOMNative)LIROP(CallClassHook)LIROP
(Bail)LIROP(Unreachable)LIROP(EncodeSnapshot)LIROP(UnreachableResultV
)LIROP(UnreachableResultT)LIROP(GetDOMProperty)LIROP(GetDOMMemberV
)LIROP(GetDOMMemberT)LIROP(SetDOMProperty)LIROP(LoadDOMExpandoValue
)LIROP(LoadDOMExpandoValueGuardGeneration)LIROP(LoadDOMExpandoValueIgnoreGeneration
)LIROP(GuardDOMExpandoMissingOrGuardShape)LIROP(ApplyArgsGeneric
)LIROP(ApplyArgsObj)LIROP(ApplyArrayGeneric)LIROP(ConstructArgsGeneric
)LIROP(ConstructArrayGeneric)LIROP(ApplyArgsNative)LIROP(ApplyArgsObjNative
)LIROP(ApplyArrayNative)LIROP(ConstructArgsNative)LIROP(ConstructArrayNative
)LIROP(TestIAndBranch)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(BitAndAndBranch
)LIROP(IsNullOrLikeUndefinedV)LIROP(IsNullOrLikeUndefinedT)LIROP
(IsNull)LIROP(IsUndefined)LIROP(IsNullOrLikeUndefinedAndBranchV
)LIROP(IsNullOrLikeUndefinedAndBranchT)LIROP(IsNullAndBranch)
LIROP(IsUndefinedAndBranch)LIROP(SameValueDouble)LIROP(SameValue
)LIROP(NotI)LIROP(NotI64)LIROP(NotD)LIROP(NotF)LIROP(NotBI)LIROP
(NotO)LIROP(NotV)LIROP(BitNotI)LIROP(BitNotI64)LIROP(BitOpI)LIROP
(BitOpI64)LIROP(ShiftI)LIROP(ShiftI64)LIROP(SignExtendInt32)LIROP
(SignExtendInt64)LIROP(UrshD)LIROP(Return)LIROP(Throw)LIROP(ThrowWithStack
)LIROP(MinMaxI)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(MathFunctionD)LIROP(MathFunctionF
)LIROP(AddI)LIROP(AddI64)LIROP(SubI)LIROP(SubI64)LIROP(MulI64
)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(Int32ToStringWithBase)LIROP(NumberParseInt)LIROP(DoubleParseInt
)LIROP(Concat)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(StringIncludesSIMD
)LIROP(StringIndexOf)LIROP(StringIndexOfSIMD)LIROP(StringLastIndexOf
)LIROP(StringStartsWith)LIROP(StringStartsWithInline)LIROP(StringEndsWith
)LIROP(StringEndsWithInline)LIROP(StringToLowerCase)LIROP(CharCodeToLowerCase
)LIROP(StringToUpperCase)LIROP(CharCodeToUpperCase)LIROP(StringTrimStartIndex
)LIROP(StringTrimEndIndex)LIROP(StringSplit)LIROP(Substr)LIROP
(Int32ToDouble)LIROP(Float32ToDouble)LIROP(DoubleToFloat32)LIROP
(Int32ToFloat32)LIROP(DoubleToFloat16)LIROP(Float32ToFloat16)
LIROP(Int32ToFloat16)LIROP(ValueToDouble)LIROP(ValueToFloat32
)LIROP(ValueToFloat16)LIROP(ValueToInt32)LIROP(ValueToBigInt)
LIROP(DoubleToInt32)LIROP(Float32ToInt32)LIROP(TruncateDToInt32
)LIROP(WasmBuiltinTruncateDToInt32)LIROP(TruncateFToInt32)LIROP
(WasmBuiltinTruncateFToInt32)LIROP(WasmTruncateToInt32)LIROP(
WrapInt64ToInt32)LIROP(ExtendInt32ToInt64)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(RegExpMatcher)LIROP(RegExpSearcher)LIROP(
RegExpSearcherLastLimit)LIROP(RegExpExecMatch)LIROP(RegExpExecTest
)LIROP(RegExpHasCaptureGroups)LIROP(RegExpPrototypeOptimizable
)LIROP(RegExpInstanceOptimizable)LIROP(GetFirstDollarIndex)LIROP
(StringReplace)LIROP(BinaryValueCache)LIROP(BinaryBoolCache)LIROP
(UnaryCache)LIROP(ModuleMetadata)LIROP(DynamicImport)LIROP(Lambda
)LIROP(FunctionWithProto)LIROP(SetFunName)LIROP(KeepAliveObject
)LIROP(DebugEnterGCUnsafeRegion)LIROP(DebugLeaveGCUnsafeRegion
)LIROP(Slots)LIROP(Elements)LIROP(InitializedLength)LIROP(SetInitializedLength
)LIROP(ArrayLength)LIROP(SetArrayLength)LIROP(FunctionLength)
LIROP(FunctionName)LIROP(GetNextEntryForIterator)LIROP(ArrayBufferByteLength
)LIROP(ArrayBufferViewLength)LIROP(ArrayBufferViewByteOffset)
LIROP(ArrayBufferViewElements)LIROP(TypedArrayElementSize)LIROP
(ResizableTypedArrayLength)LIROP(ResizableTypedArrayByteOffsetMaybeOutOfBounds
)LIROP(ResizableDataViewByteLength)LIROP(GrowableSharedArrayBufferByteLength
)LIROP(GuardResizableArrayBufferViewInBounds)LIROP(GuardResizableArrayBufferViewInBoundsOrDetached
)LIROP(GuardHasAttachedArrayBuffer)LIROP(GuardNumberToIntPtrIndex
)LIROP(BoundsCheck)LIROP(BoundsCheckRange)LIROP(BoundsCheckLower
)LIROP(SpectreMaskIndex)LIROP(LoadElementV)LIROP(InArray)LIROP
(GuardElementNotHole)LIROP(LoadElementHole)LIROP(StoreElementV
)LIROP(StoreElementT)LIROP(StoreHoleValueElement)LIROP(StoreElementHoleV
)LIROP(StoreElementHoleT)LIROP(ArrayPopShift)LIROP(ArrayPush)
LIROP(ArraySlice)LIROP(ArgumentsSlice)LIROP(FrameArgumentsSlice
)LIROP(InlineArgumentsSlice)LIROP(NormalizeSliceTerm)LIROP(ArrayJoin
)LIROP(ObjectKeys)LIROP(ObjectKeysLength)LIROP(LoadUnboxedScalar
)LIROP(LoadUnboxedBigInt)LIROP(LoadDataViewElement)LIROP(LoadTypedArrayElementHole
)LIROP(LoadTypedArrayElementHoleBigInt)LIROP(StoreUnboxedScalar
)LIROP(StoreUnboxedBigInt)LIROP(StoreDataViewElement)LIROP(StoreTypedArrayElementHole
)LIROP(StoreTypedArrayElementHoleBigInt)LIROP(AtomicIsLockFree
)LIROP(CompareExchangeTypedArrayElement)LIROP(AtomicExchangeTypedArrayElement
)LIROP(AtomicTypedArrayElementBinop)LIROP(AtomicTypedArrayElementBinopForEffect
)LIROP(AtomicLoad64)LIROP(AtomicStore64)LIROP(CompareExchangeTypedArrayElement64
)LIROP(AtomicExchangeTypedArrayElement64)LIROP(AtomicTypedArrayElementBinop64
)LIROP(AtomicTypedArrayElementBinopForEffect64)LIROP(EffectiveAddress
)LIROP(ClampIToUint8)LIROP(ClampDToUint8)LIROP(ClampVToUint8)
LIROP(LoadScriptedProxyHandler)LIROP(CheckScriptedProxyGetResult
)LIROP(IdToStringOrSymbol)LIROP(LoadFixedSlotV)LIROP(LoadFixedSlotAndAtomize
)LIROP(LoadFixedSlotT)LIROP(LoadFixedSlotAndUnbox)LIROP(LoadDynamicSlotAndUnbox
)LIROP(LoadElementAndUnbox)LIROP(LoadFixedSlotUnboxAndAtomize
)LIROP(LoadDynamicSlotUnboxAndAtomize)LIROP(AddAndStoreSlot)LIROP
(AllocateAndStoreSlot)LIROP(AddSlotAndCallAddPropHook)LIROP(StoreFixedSlotV
)LIROP(StoreFixedSlotT)LIROP(GetNameCache)LIROP(CallGetIntrinsicValue
)LIROP(GetPropSuperCache)LIROP(GetPropertyCache)LIROP(BindNameCache
)LIROP(CallBindVar)LIROP(LoadDynamicSlotV)LIROP(LoadDynamicSlotAndAtomize
)LIROP(StoreDynamicSlotV)LIROP(StoreDynamicSlotT)LIROP(StringLength
)LIROP(Floor)LIROP(FloorF)LIROP(Ceil)LIROP(CeilF)LIROP(Round)
LIROP(RoundF)LIROP(Trunc)LIROP(TruncF)LIROP(NearbyInt)LIROP(NearbyIntF
)LIROP(FunctionEnvironment)LIROP(HomeObject)LIROP(HomeObjectSuperBase
)LIROP(NewLexicalEnvironmentObject)LIROP(NewClassBodyEnvironmentObject
)LIROP(NewVarEnvironmentObject)LIROP(MegamorphicSetElement)LIROP
(CallDeleteProperty)LIROP(CallDeleteElement)LIROP(ObjectToIterator
)LIROP(ValueToIterator)LIROP(IteratorHasIndicesAndBranch)LIROP
(LoadSlotByIteratorIndex)LIROP(StoreSlotByIteratorIndex)LIROP
(SetPropertyCache)LIROP(GetIteratorCache)LIROP(OptimizeSpreadCallCache
)LIROP(IteratorMore)LIROP(IsNoIterAndBranch)LIROP(IteratorEnd
)LIROP(CloseIterCache)LIROP(OptimizeGetIteratorCache)LIROP(ArgumentsLength
)LIROP(GetFrameArgument)LIROP(GetFrameArgumentHole)LIROP(Rest
)LIROP(Int32ToIntPtr)LIROP(NonNegativeIntPtrToInt32)LIROP(IntPtrToDouble
)LIROP(AdjustDataViewLength)LIROP(BooleanToInt64)LIROP(StringToInt64
)LIROP(ValueToInt64)LIROP(TruncateBigIntToInt64)LIROP(Int64ToBigInt
)LIROP(PostWriteBarrierO)LIROP(PostWriteBarrierS)LIROP(PostWriteBarrierBI
)LIROP(PostWriteBarrierV)LIROP(PostWriteElementBarrierO)LIROP
(PostWriteElementBarrierS)LIROP(PostWriteElementBarrierBI)LIROP
(PostWriteElementBarrierV)LIROP(AssertCanElidePostWriteBarrier
)LIROP(GuardObjectIdentity)LIROP(GuardSpecificFunction)LIROP(
GuardSpecificAtom)LIROP(GuardSpecificSymbol)LIROP(GuardSpecificInt32
)LIROP(GuardStringToIndex)LIROP(GuardStringToInt32)LIROP(GuardStringToDouble
)LIROP(GuardShape)LIROP(GuardMultipleShapes)LIROP(GuardProto)
LIROP(GuardNullProto)LIROP(GuardIsNativeObject)LIROP(GuardGlobalGeneration
)LIROP(GuardFuse)LIROP(GuardIsProxy)LIROP(GuardIsNotProxy)LIROP
(GuardIsNotDOMProxy)LIROP(ProxyGet)LIROP(ProxyGetByValue)LIROP
(ProxyHasProp)LIROP(ProxySet)LIROP(ProxySetByValue)LIROP(CallSetArrayLength
)LIROP(MegamorphicLoadSlot)LIROP(MegamorphicLoadSlotByValue)LIROP
(MegamorphicLoadSlotPermissive)LIROP(MegamorphicLoadSlotByValuePermissive
)LIROP(MegamorphicStoreSlot)LIROP(MegamorphicHasProp)LIROP(SmallObjectVariableKeyHasProp
)LIROP(GuardIsNotArrayBufferMaybeShared)LIROP(GuardIsTypedArray
)LIROP(GuardIsFixedLengthTypedArray)LIROP(GuardIsResizableTypedArray
)LIROP(GuardHasProxyHandler)LIROP(GuardNoDenseElements)LIROP(
InCache)LIROP(HasOwnCache)LIROP(CheckPrivateFieldCache)LIROP(
NewPrivateName)LIROP(InstanceOfO)LIROP(InstanceOfV)LIROP(InstanceOfCache
)LIROP(IsCallableO)LIROP(IsCallableV)LIROP(IsConstructor)LIROP
(IsCrossRealmArrayConstructor)LIROP(IsArrayO)LIROP(IsArrayV)LIROP
(IsTypedArray)LIROP(IsObject)LIROP(IsObjectAndBranch)LIROP(IsNullOrUndefined
)LIROP(IsNullOrUndefinedAndBranch)LIROP(HasClass)LIROP(GuardToClass
)LIROP(GuardToEitherClass)LIROP(GuardToFunction)LIROP(ObjectClassToString
)LIROP(WasmSelect)LIROP(WasmSelectI64)LIROP(WasmCompareAndSelect
)LIROP(WasmAddOffset)LIROP(WasmAddOffset64)LIROP(WasmBoundsCheck
)LIROP(WasmBoundsCheck64)LIROP(WasmBoundsCheckRange32)LIROP(WasmExtendU32Index
)LIROP(WasmWrapU32Index)LIROP(WasmClampTable64Index)LIROP(WasmAlignmentCheck
)LIROP(WasmAlignmentCheck64)LIROP(WasmLoadInstance)LIROP(WasmLoadInstance64
)LIROP(WasmHeapReg)LIROP(WasmLoad)LIROP(WasmLoadI64)LIROP(WasmStore
)LIROP(WasmStoreI64)LIROP(AsmJSLoadHeap)LIROP(AsmJSStoreHeap)
LIROP(WasmCompareExchangeHeap)LIROP(WasmFence)LIROP(WasmAtomicExchangeHeap
)LIROP(WasmAtomicBinopHeap)LIROP(WasmAtomicBinopHeapForEffect
)LIROP(WasmLoadSlot)LIROP(WasmLoadElement)LIROP(WasmLoadSlotI64
)LIROP(WasmLoadElementI64)LIROP(WasmStoreSlot)LIROP(WasmStoreSlotI64
)LIROP(WasmStoreElement)LIROP(WasmStoreElementI64)LIROP(WasmStoreElementRef
)LIROP(WasmLoadTableElement)LIROP(WasmDerivedPointer)LIROP(WasmDerivedIndexPointer
)LIROP(WasmStoreRef)LIROP(WasmPostWriteBarrierImmediate)LIROP
(WasmPostWriteBarrierIndex)LIROP(WasmParameter)LIROP(WasmParameterI64
)LIROP(WasmReturn)LIROP(WasmReturnI64)LIROP(WasmReturnVoid)LIROP
(WasmStackArg)LIROP(WasmStackArgI64)LIROP(WasmNullConstant)LIROP
(WasmCallIndirectAdjunctSafepoint)LIROP(WasmCall)LIROP(WasmCallLandingPrePad
)LIROP(WasmRegisterResult)LIROP(WasmRegisterPairResult)LIROP(
WasmStackResultArea)LIROP(WasmStackResult)LIROP(WasmStackResult64
)LIROP(AssertRangeI)LIROP(AssertRangeD)LIROP(AssertRangeF)LIROP
(AssertRangeV)LIROP(AssertClass)LIROP(AssertShape)LIROP(GuardValue
)LIROP(GuardNullOrUndefined)LIROP(GuardIsNotObject)LIROP(GuardFunctionFlags
)LIROP(GuardFunctionIsNonBuiltinCtor)LIROP(GuardFunctionKind)
LIROP(GuardFunctionScript)LIROP(IncrementWarmUpCounter)LIROP(
LexicalCheck)LIROP(ThrowRuntimeLexicalError)LIROP(ThrowMsg)LIROP
(GlobalDeclInstantiation)LIROP(MemoryBarrier)LIROP(Debugger)LIROP
(NewTarget)LIROP(Random)LIROP(CheckReturn)LIROP(CheckIsObj)LIROP
(CheckObjCoercible)LIROP(CheckClassHeritage)LIROP(CheckThis)LIROP
(CheckThisReinit)LIROP(Generator)LIROP(AsyncResolve)LIROP(AsyncReject
)LIROP(AsyncAwait)LIROP(CanSkipAwait)LIROP(MaybeExtractAwaitValue
)LIROP(DebugCheckSelfHosted)LIROP(IsPackedArray)LIROP(GuardArrayIsPacked
)LIROP(GetPrototypeOf)LIROP(ObjectWithProto)LIROP(ObjectStaticProto
)LIROP(BuiltinObject)LIROP(SuperFunction)LIROP(InitHomeObject
)LIROP(IsTypedArrayConstructor)LIROP(LoadValueTag)LIROP(GuardTagNotEqual
)LIROP(LoadWrapperTarget)LIROP(GuardHasGetterSetter)LIROP(GuardIsExtensible
)LIROP(GuardInt32IsNonNegative)LIROP(GuardInt32Range)LIROP(GuardIndexIsNotDenseElement
)LIROP(GuardIndexIsValidUpdateOrAdd)LIROP(CallAddOrUpdateSparseElement
)LIROP(CallGetSparseElement)LIROP(CallNativeGetElement)LIROP(
CallNativeGetElementSuper)LIROP(CallObjectHasSparseElement)LIROP
(BigIntAsIntN)LIROP(BigIntAsIntN64)LIROP(BigIntAsIntN32)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(SetObjectSize)LIROP(MapObjectHasNonBigInt
)LIROP(MapObjectHasBigInt)LIROP(MapObjectHasValue)LIROP(MapObjectHasValueVMCall
)LIROP(MapObjectGetNonBigInt)LIROP(MapObjectGetBigInt)LIROP(MapObjectGetValue
)LIROP(MapObjectGetValueVMCall)LIROP(MapObjectSize)LIROP(BigIntAsUintN
)LIROP(BigIntAsUintN64)LIROP(BigIntAsUintN32)LIROP(IonToWasmCall
)LIROP(IonToWasmCallV)LIROP(IonToWasmCallI64)LIROP(WasmAnyRefFromJSValue
)LIROP(WasmAnyRefFromJSObject)LIROP(WasmAnyRefFromJSString)LIROP
(WasmNewI31Ref)LIROP(WasmI31RefGet)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(WasmUnarySimd128
)LIROP(WasmReduceSimd128)LIROP(WasmReduceAndBranchSimd128)LIROP
(WasmReduceSimd128ToInt64)LIROP(WasmLoadLaneSimd128)LIROP(WasmStoreLaneSimd128
)LIROP(Unbox)LIROP(UnboxFloatingPoint)LIROP(WasmUint32ToDouble
)LIROP(WasmUint32ToFloat32)LIROP(DivI)LIROP(ModI)LIROP(DivPowTwoI
)LIROP(ModPowTwoI)LIROP(TableSwitch)LIROP(TableSwitchV)LIROP(
MulI)LIROP(DivOrModI64)LIROP(UDivOrModI64)LIROP(DivOrModConstantI
)LIROP(UDivOrMod)LIROP(UDivOrModConstant)LIROP(WasmTruncateToInt64
)LIROP(Int64ToFloatingPoint)
7742# undef LIROP
7743#endif
7744 case LNode::Opcode::Invalid:
7745 default:
7746 MOZ_CRASH("Invalid LIR op")do { do { } while (false); MOZ_ReportCrash("" "Invalid LIR op"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7746); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid LIR op"
")"); do { *((volatile int*)__null) = 7746; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
7747 }
7748
7749#ifdef DEBUG1
7750 if (!counts) {
7751 emitDebugResultChecks(*iter);
7752 }
7753#endif
7754 }
7755 if (masm.oom()) {
7756 return false;
7757 }
7758 }
7759
7760 JitSpew(JitSpew_Codegen, "==== END CodeGenerator::generateBody ====\n");
7761 return true;
7762}
7763
7764// Out-of-line object allocation for LNewArray.
7765class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator> {
7766 LNewArray* lir_;
7767
7768 public:
7769 explicit OutOfLineNewArray(LNewArray* lir) : lir_(lir) {}
7770
7771 void accept(CodeGenerator* codegen) override {
7772 codegen->visitOutOfLineNewArray(this);
7773 }
7774
7775 LNewArray* lir() const { return lir_; }
7776};
7777
7778void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) {
7779 Register objReg = ToRegister(lir->output());
7780
7781 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7781); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()"
")"); do { *((volatile int*)__null) = 7781; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7782 saveLive(lir);
7783
7784 JSObject* templateObject = lir->mir()->templateObject();
7785
7786 if (templateObject) {
7787 pushArg(ImmGCPtr(templateObject->shape()));
7788 pushArg(Imm32(lir->mir()->length()));
7789
7790 using Fn = ArrayObject* (*)(JSContext*, uint32_t, Handle<Shape*>);
7791 callVM<Fn, NewArrayWithShape>(lir);
7792 } else {
7793 pushArg(Imm32(GenericObject));
7794 pushArg(Imm32(lir->mir()->length()));
7795
7796 using Fn = ArrayObject* (*)(JSContext*, uint32_t, NewObjectKind);
7797 callVM<Fn, NewArrayOperation>(lir);
7798 }
7799
7800 masm.storeCallPointerResult(objReg);
7801
7802 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7802); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)"
")"); do { *((volatile int*)__null) = 7802; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7803 restoreLive(lir);
7804}
7805
7806void CodeGenerator::visitAtan2D(LAtan2D* lir) {
7807 FloatRegister y = ToFloatRegister(lir->y());
7808 FloatRegister x = ToFloatRegister(lir->x());
7809
7810 using Fn = double (*)(double x, double y);
7811 masm.setupAlignedABICall();
7812 masm.passABIArg(y, ABIType::Float64);
7813 masm.passABIArg(x, ABIType::Float64);
7814 masm.callWithABI<Fn, ecmaAtan2>(ABIType::Float64);
7815
7816 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7816); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 7816; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7817}
7818
7819void CodeGenerator::visitHypot(LHypot* lir) {
7820 uint32_t numArgs = lir->numArgs();
7821 masm.setupAlignedABICall();
7822
7823 for (uint32_t i = 0; i < numArgs; ++i) {
7824 masm.passABIArg(ToFloatRegister(lir->getOperand(i)), ABIType::Float64);
7825 }
7826
7827 switch (numArgs) {
7828 case 2: {
7829 using Fn = double (*)(double x, double y);
7830 masm.callWithABI<Fn, ecmaHypot>(ABIType::Float64);
7831 break;
7832 }
7833 case 3: {
7834 using Fn = double (*)(double x, double y, double z);
7835 masm.callWithABI<Fn, hypot3>(ABIType::Float64);
7836 break;
7837 }
7838 case 4: {
7839 using Fn = double (*)(double x, double y, double z, double w);
7840 masm.callWithABI<Fn, hypot4>(ABIType::Float64);
7841 break;
7842 }
7843 default:
7844 MOZ_CRASH("Unexpected number of arguments to hypot function.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected number of arguments to hypot function."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7844); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected number of arguments to hypot function."
")"); do { *((volatile int*)__null) = 7844; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
7845 }
7846 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7846); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 7846; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7847}
7848
7849void CodeGenerator::visitNewArray(LNewArray* lir) {
7850 Register objReg = ToRegister(lir->output());
7851 Register tempReg = ToRegister(lir->temp());
7852 DebugOnly<uint32_t> length = lir->mir()->length();
7853
7854 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7854); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT"
")"); do { *((volatile int*)__null) = 7854; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7855
7856 if (lir->mir()->isVMCall()) {
7857 visitNewArrayCallVM(lir);
7858 return;
7859 }
7860
7861 OutOfLineNewArray* ool = new (alloc()) OutOfLineNewArray(lir);
7862 addOutOfLineCode(ool, lir->mir());
7863
7864 TemplateObject templateObject(lir->mir()->templateObject());
7865#ifdef DEBUG1
7866 size_t numInlineElements = gc::GetGCKindSlots(templateObject.getAllocKind()) -
7867 ObjectElements::VALUES_PER_HEADER;
7868 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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7869); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements"
") (" "Inline allocation only supports inline elements" ")")
; do { *((volatile int*)__null) = 7869; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7869 "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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7869); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements"
") (" "Inline allocation only supports inline elements" ")")
; do { *((volatile int*)__null) = 7869; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7870#endif
7871 masm.createGCObject(objReg, tempReg, templateObject,
7872 lir->mir()->initialHeap(), ool->entry());
7873
7874 masm.bind(ool->rejoin());
7875}
7876
7877void CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray* ool) {
7878 visitNewArrayCallVM(ool->lir());
7879 masm.jump(ool->rejoin());
7880}
7881
7882void CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) {
7883 Register lengthReg = ToRegister(lir->length());
7884 Register objReg = ToRegister(lir->output());
7885 Register tempReg = ToRegister(lir->temp0());
7886
7887 JSObject* templateObject = lir->mir()->templateObject();
7888 gc::Heap initialHeap = lir->mir()->initialHeap();
7889
7890 using Fn = ArrayObject* (*)(JSContext*, Handle<ArrayObject*>, int32_t length);
7891 OutOfLineCode* ool = oolCallVM<Fn, ArrayConstructorOneArg>(
7892 lir, ArgList(ImmGCPtr(templateObject), lengthReg),
7893 StoreRegisterTo(objReg));
7894
7895 bool canInline = true;
7896 size_t inlineLength = 0;
7897 if (templateObject->as<ArrayObject>().hasFixedElements()) {
7898 size_t numSlots =
7899 gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
7900 inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
7901 } else {
7902 canInline = false;
7903 }
7904
7905 if (canInline) {
7906 // Try to do the allocation inline if the template object is big enough
7907 // for the length in lengthReg. If the length is bigger we could still
7908 // use the template object and not allocate the elements, but it's more
7909 // efficient to do a single big allocation than (repeatedly) reallocating
7910 // the array later on when filling it.
7911 masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength),
7912 ool->entry());
7913
7914 TemplateObject templateObj(templateObject);
7915 masm.createGCObject(objReg, tempReg, templateObj, initialHeap,
7916 ool->entry());
7917
7918 size_t lengthOffset = NativeObject::offsetOfFixedElements() +
7919 ObjectElements::offsetOfLength();
7920 masm.store32(lengthReg, Address(objReg, lengthOffset));
7921 } else {
7922 masm.jump(ool->entry());
7923 }
7924
7925 masm.bind(ool->rejoin());
7926}
7927
7928void CodeGenerator::visitNewIterator(LNewIterator* lir) {
7929 Register objReg = ToRegister(lir->output());
7930 Register tempReg = ToRegister(lir->temp0());
7931
7932 OutOfLineCode* ool;
7933 switch (lir->mir()->type()) {
7934 case MNewIterator::ArrayIterator: {
7935 using Fn = ArrayIteratorObject* (*)(JSContext*);
7936 ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(),
7937 StoreRegisterTo(objReg));
7938 break;
7939 }
7940 case MNewIterator::StringIterator: {
7941 using Fn = StringIteratorObject* (*)(JSContext*);
7942 ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(),
7943 StoreRegisterTo(objReg));
7944 break;
7945 }
7946 case MNewIterator::RegExpStringIterator: {
7947 using Fn = RegExpStringIteratorObject* (*)(JSContext*);
7948 ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(),
7949 StoreRegisterTo(objReg));
7950 break;
7951 }
7952 default:
7953 MOZ_CRASH("unexpected iterator type")do { do { } while (false); MOZ_ReportCrash("" "unexpected iterator type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7953); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected iterator type"
")"); do { *((volatile int*)__null) = 7953; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
7954 }
7955
7956 TemplateObject templateObject(lir->mir()->templateObject());
7957 masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default,
7958 ool->entry());
7959
7960 masm.bind(ool->rejoin());
7961}
7962
7963void CodeGenerator::visitNewTypedArray(LNewTypedArray* lir) {
7964 Register objReg = ToRegister(lir->output());
7965 Register tempReg = ToRegister(lir->temp0());
7966 Register lengthReg = ToRegister(lir->temp1());
7967 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
7968
7969 JSObject* templateObject = lir->mir()->templateObject();
7970 gc::Heap initialHeap = lir->mir()->initialHeap();
7971
7972 auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>();
7973
7974 size_t n = ttemplate->length();
7975 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" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)"
") (" "Template objects are only created for int32 lengths" ")"
); do { *((volatile int*)__null) = 7976; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7976 "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" ")"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)"
") (" "Template objects are only created for int32 lengths" ")"
); do { *((volatile int*)__null) = 7976; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7977
7978 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length);
7979 OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
7980 lir, ArgList(ImmGCPtr(templateObject), Imm32(n)),
7981 StoreRegisterTo(objReg));
7982
7983 TemplateObject templateObj(templateObject);
7984 masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry());
7985
7986 masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(),
7987 ttemplate, MacroAssembler::TypedArrayLength::Fixed);
7988
7989 masm.bind(ool->rejoin());
7990}
7991
7992void CodeGenerator::visitNewTypedArrayDynamicLength(
7993 LNewTypedArrayDynamicLength* lir) {
7994 Register lengthReg = ToRegister(lir->length());
7995 Register objReg = ToRegister(lir->output());
7996 Register tempReg = ToRegister(lir->temp0());
7997 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
7998
7999 JSObject* templateObject = lir->mir()->templateObject();
8000 gc::Heap initialHeap = lir->mir()->initialHeap();
8001
8002 auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>();
8003
8004 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length);
8005 OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
8006 lir, ArgList(ImmGCPtr(templateObject), lengthReg),
8007 StoreRegisterTo(objReg));
8008
8009 // Volatile |lengthReg| is saved across the ABI call in |initTypedArraySlots|.
8010 MOZ_ASSERT_IF(lengthReg.volatile_(), liveRegs.has(lengthReg))do { if (lengthReg.volatile_()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(liveRegs.has(lengthReg
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(liveRegs.has(lengthReg)))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("liveRegs.has(lengthReg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8010); AnnotateMozCrashReason("MOZ_ASSERT" "(" "liveRegs.has(lengthReg)"
")"); do { *((volatile int*)__null) = 8010; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
8011
8012 TemplateObject templateObj(templateObject);
8013 masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry());
8014
8015 masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(),
8016 ttemplate,
8017 MacroAssembler::TypedArrayLength::Dynamic);
8018
8019 masm.bind(ool->rejoin());
8020}
8021
8022void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) {
8023 pushArg(ToRegister(lir->array()));
8024 pushArg(ImmGCPtr(lir->mir()->templateObject()));
8025
8026 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject);
8027 callVM<Fn, js::NewTypedArrayWithTemplateAndArray>(lir);
8028}
8029
8030void CodeGenerator::visitNewTypedArrayFromArrayBuffer(
8031 LNewTypedArrayFromArrayBuffer* lir) {
8032 pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::LengthIndex));
8033 pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::ByteOffsetIndex));
8034 pushArg(ToRegister(lir->arrayBuffer()));
8035 pushArg(ImmGCPtr(lir->mir()->templateObject()));
8036
8037 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject,
8038 HandleValue, HandleValue);
8039 callVM<Fn, js::NewTypedArrayWithTemplateAndBuffer>(lir);
8040}
8041
8042void CodeGenerator::visitBindFunction(LBindFunction* lir) {
8043 Register target = ToRegister(lir->target());
8044 Register temp1 = ToRegister(lir->temp0());
8045 Register temp2 = ToRegister(lir->temp1());
8046
8047 // Try to allocate a new BoundFunctionObject we can pass to the VM function.
8048 // If this fails, we set temp1 to nullptr so we do the allocation in C++.
8049 TemplateObject templateObject(lir->mir()->templateObject());
8050 Label allocOk, allocFailed;
8051 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
8052 &allocFailed);
8053 masm.jump(&allocOk);
8054
8055 masm.bind(&allocFailed);
8056 masm.movePtr(ImmWord(0), temp1);
8057
8058 masm.bind(&allocOk);
8059
8060 // Set temp2 to the address of the first argument on the stack.
8061 // Note that the Value slots used for arguments are currently aligned for a
8062 // JIT call, even though that's not strictly necessary for calling into C++.
8063 uint32_t argc = lir->mir()->numStackArgs();
8064 if (JitStackValueAlignment > 1) {
8065 argc = AlignBytes(argc, JitStackValueAlignment);
8066 }
8067 uint32_t unusedStack = UnusedStackBytesForCall(argc);
8068 masm.computeEffectiveAddress(Address(masm.getStackPointer(), unusedStack),
8069 temp2);
8070
8071 pushArg(temp1);
8072 pushArg(Imm32(lir->mir()->numStackArgs()));
8073 pushArg(temp2);
8074 pushArg(target);
8075
8076 using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*,
8077 uint32_t, Handle<BoundFunctionObject*>);
8078 callVM<Fn, js::BoundFunctionObject::functionBindImpl>(lir);
8079}
8080
8081void CodeGenerator::visitNewBoundFunction(LNewBoundFunction* lir) {
8082 Register output = ToRegister(lir->output());
8083 Register temp = ToRegister(lir->temp0());
8084
8085 JSObject* templateObj = lir->mir()->templateObj();
8086
8087 using Fn = BoundFunctionObject* (*)(JSContext*, Handle<BoundFunctionObject*>);
8088 OutOfLineCode* ool = oolCallVM<Fn, BoundFunctionObject::createWithTemplate>(
8089 lir, ArgList(ImmGCPtr(templateObj)), StoreRegisterTo(output));
8090
8091 TemplateObject templateObject(templateObj);
8092 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
8093 ool->entry());
8094
8095 masm.bind(ool->rejoin());
8096}
8097
8098// Out-of-line object allocation for JSOp::NewObject.
8099class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator> {
8100 LNewObject* lir_;
8101
8102 public:
8103 explicit OutOfLineNewObject(LNewObject* lir) : lir_(lir) {}
8104
8105 void accept(CodeGenerator* codegen) override {
8106 codegen->visitOutOfLineNewObject(this);
8107 }
8108
8109 LNewObject* lir() const { return lir_; }
8110};
8111
8112void CodeGenerator::visitNewObjectVMCall(LNewObject* lir) {
8113 Register objReg = ToRegister(lir->output());
8114
8115 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8115); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()"
")"); do { *((volatile int*)__null) = 8115; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8116 saveLive(lir);
8117
8118 JSObject* templateObject = lir->mir()->templateObject();
8119
8120 // If we're making a new object with a class prototype (that is, an object
8121 // that derives its class from its prototype instead of being
8122 // PlainObject::class_'d) from self-hosted code, we need a different init
8123 // function.
8124 switch (lir->mir()->mode()) {
8125 case MNewObject::ObjectLiteral: {
8126 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8126); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateObject"
")"); do { *((volatile int*)__null) = 8126; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8127 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
8128 pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
8129
8130 using Fn = JSObject* (*)(JSContext*, HandleScript, const jsbytecode* pc);
8131 callVM<Fn, NewObjectOperation>(lir);
8132 break;
8133 }
8134 case MNewObject::ObjectCreate: {
8135 pushArg(ImmGCPtr(templateObject));
8136
8137 using Fn = PlainObject* (*)(JSContext*, Handle<PlainObject*>);
8138 callVM<Fn, ObjectCreateWithTemplate>(lir);
8139 break;
8140 }
8141 }
8142
8143 masm.storeCallPointerResult(objReg);
8144
8145 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8145); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)"
")"); do { *((volatile int*)__null) = 8145; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8146 restoreLive(lir);
8147}
8148
8149static bool ShouldInitFixedSlots(LNewPlainObject* lir, const Shape* shape,
8150 uint32_t nfixed) {
8151 // Look for StoreFixedSlot instructions following an object allocation
8152 // that write to this object before a GC is triggered or this object is
8153 // passed to a VM call. If all fixed slots will be initialized, the
8154 // allocation code doesn't need to set the slots to |undefined|.
8155
8156 if (nfixed == 0) {
8157 return false;
8158 }
8159
8160 // Keep track of the fixed slots that are initialized. initializedSlots is
8161 // a bit mask with a bit for each slot.
8162 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8162); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nfixed <= NativeObject::MAX_FIXED_SLOTS"
")"); do { *((volatile int*)__null) = 8162; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8163 static_assert(NativeObject::MAX_FIXED_SLOTS <= 32,
8164 "Slot bits must fit in 32 bits");
8165 uint32_t initializedSlots = 0;
8166 uint32_t numInitialized = 0;
8167
8168 MInstruction* allocMir = lir->mir();
8169 MBasicBlock* block = allocMir->block();
8170
8171 // Skip the allocation instruction.
8172 MInstructionIterator iter = block->begin(allocMir);
8173 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "*iter == allocMir"
")"); do { *((volatile int*)__null) = 8173; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8174 iter++;
8175
8176 // Handle the leading shape guard, if present.
8177 for (; iter != block->end(); iter++) {
8178 if (iter->isConstant()) {
8179 // This instruction won't trigger a GC or read object slots.
8180 continue;
8181 }
8182 if (iter->isGuardShape()) {
8183 auto* guard = iter->toGuardShape();
8184 if (guard->object() != allocMir || guard->shape() != shape) {
8185 return true;
8186 }
8187 allocMir = guard;
8188 iter++;
8189 }
8190 break;
8191 }
8192
8193 for (; iter != block->end(); iter++) {
8194 if (iter->isConstant() || iter->isPostWriteBarrier()) {
8195 // These instructions won't trigger a GC or read object slots.
8196 continue;
8197 }
8198
8199 if (iter->isStoreFixedSlot()) {
8200 MStoreFixedSlot* store = iter->toStoreFixedSlot();
8201 if (store->object() != allocMir) {
8202 return true;
8203 }
8204
8205 // We may not initialize this object slot on allocation, so the
8206 // pre-barrier could read uninitialized memory. Simply disable
8207 // the barrier for this store: the object was just initialized
8208 // so the barrier is not necessary.
8209 store->setNeedsBarrier(false);
8210
8211 uint32_t slot = store->slot();
8212 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8212); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot < nfixed"
")"); do { *((volatile int*)__null) = 8212; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8213 if ((initializedSlots & (1 << slot)) == 0) {
8214 numInitialized++;
8215 initializedSlots |= (1 << slot);
8216
8217 if (numInitialized == nfixed) {
8218 // All fixed slots will be initialized.
8219 MOZ_ASSERT(mozilla::CountPopulation32(initializedSlots) == nfixed)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mozilla::CountPopulation32(initializedSlots) == nfixed
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mozilla::CountPopulation32(initializedSlots) == nfixed
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"mozilla::CountPopulation32(initializedSlots) == nfixed", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8219); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::CountPopulation32(initializedSlots) == nfixed"
")"); do { *((volatile int*)__null) = 8219; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8220 return false;
8221 }
8222 }
8223 continue;
8224 }
8225
8226 // Unhandled instruction, assume it bails or reads object slots.
8227 return true;
8228 }
8229
8230 MOZ_CRASH("Shouldn't get here")do { do { } while (false); MOZ_ReportCrash("" "Shouldn't get here"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8230); AnnotateMozCrashReason("MOZ_CRASH(" "Shouldn't get here"
")"); do { *((volatile int*)__null) = 8230; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
8231}
8232
8233void CodeGenerator::visitNewObject(LNewObject* lir) {
8234 Register objReg = ToRegister(lir->output());
8235 Register tempReg = ToRegister(lir->temp());
8236
8237 if (lir->mir()->isVMCall()) {
8238 visitNewObjectVMCall(lir);
8239 return;
8240 }
8241
8242 OutOfLineNewObject* ool = new (alloc()) OutOfLineNewObject(lir);
8243 addOutOfLineCode(ool, lir->mir());
8244
8245 TemplateObject templateObject(lir->mir()->templateObject());
8246
8247 masm.createGCObject(objReg, tempReg, templateObject,
8248 lir->mir()->initialHeap(), ool->entry());
8249
8250 masm.bind(ool->rejoin());
8251}
8252
8253void CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject* ool) {
8254 visitNewObjectVMCall(ool->lir());
8255 masm.jump(ool->rejoin());
8256}
8257
8258void CodeGenerator::visitNewPlainObject(LNewPlainObject* lir) {
8259 Register objReg = ToRegister(lir->output());
8260 Register temp0Reg = ToRegister(lir->temp0());
8261 Register temp1Reg = ToRegister(lir->temp1());
8262 Register shapeReg = ToRegister(lir->temp2());
8263
8264 auto* mir = lir->mir();
8265 const Shape* shape = mir->shape();
8266 gc::Heap initialHeap = mir->initialHeap();
8267 gc::AllocKind allocKind = mir->allocKind();
8268
8269 using Fn =
8270 JSObject* (*)(JSContext*, Handle<SharedShape*>, gc::AllocKind, gc::Heap);
8271 OutOfLineCode* ool = oolCallVM<Fn, NewPlainObjectOptimizedFallback>(
8272 lir,
8273 ArgList(ImmGCPtr(shape), Imm32(int32_t(allocKind)),
8274 Imm32(int32_t(initialHeap))),
8275 StoreRegisterTo(objReg));
8276
8277 bool initContents = ShouldInitFixedSlots(lir, shape, mir->numFixedSlots());
8278
8279 masm.movePtr(ImmGCPtr(shape), shapeReg);
8280 masm.createPlainGCObject(
8281 objReg, shapeReg, temp0Reg, temp1Reg, mir->numFixedSlots(),
8282 mir->numDynamicSlots(), allocKind, initialHeap, ool->entry(),
8283 AllocSiteInput(gc::CatchAllAllocSite::Optimized), initContents);
8284
8285#ifdef DEBUG1
8286 // ShouldInitFixedSlots expects that the leading GuardShape will never fail,
8287 // so ensure the newly created object has the correct shape. Should the guard
8288 // ever fail, we may end up with uninitialized fixed slots, which can confuse
8289 // the GC.
8290 Label ok;
8291 masm.branchTestObjShape(Assembler::Equal, objReg, shape, temp0Reg, objReg,
8292 &ok);
8293 masm.assumeUnreachable("Newly created object has the correct shape");
8294 masm.bind(&ok);
8295#endif
8296
8297 masm.bind(ool->rejoin());
8298}
8299
8300void CodeGenerator::visitNewArrayObject(LNewArrayObject* lir) {
8301 Register objReg = ToRegister(lir->output());
8302 Register temp0Reg = ToRegister(lir->temp0());
8303 Register shapeReg = ToRegister(lir->temp1());
8304
8305 auto* mir = lir->mir();
8306 uint32_t arrayLength = mir->length();
8307
8308 gc::AllocKind allocKind = GuessArrayGCKind(arrayLength);
8309 MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject
::class_))>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(allocKind
, &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)"
")"); do { *((volatile int*)__null) = 8309; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8310 allocKind = ForegroundToBackgroundAllocKind(allocKind);
8311
8312 uint32_t slotCount = GetGCKindSlots(allocKind);
8313 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotCount >= ObjectElements::VALUES_PER_HEADER"
")"); do { *((volatile int*)__null) = 8313; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8314 uint32_t arrayCapacity = slotCount - ObjectElements::VALUES_PER_HEADER;
8315
8316 const Shape* shape = mir->shape();
8317
8318 NewObjectKind objectKind =
8319 mir->initialHeap() == gc::Heap::Tenured ? TenuredObject : GenericObject;
8320
8321 using Fn =
8322 ArrayObject* (*)(JSContext*, uint32_t, gc::AllocKind, NewObjectKind);
8323 OutOfLineCode* ool = oolCallVM<Fn, NewArrayObjectOptimizedFallback>(
8324 lir,
8325 ArgList(Imm32(arrayLength), Imm32(int32_t(allocKind)), Imm32(objectKind)),
8326 StoreRegisterTo(objReg));
8327
8328 masm.movePtr(ImmPtr(shape), shapeReg);
8329 masm.createArrayWithFixedElements(
8330 objReg, shapeReg, temp0Reg, InvalidReg, arrayLength, arrayCapacity, 0, 0,
8331 allocKind, mir->initialHeap(), ool->entry(),
8332 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
8333 masm.bind(ool->rejoin());
8334}
8335
8336void CodeGenerator::visitNewNamedLambdaObject(LNewNamedLambdaObject* lir) {
8337 Register objReg = ToRegister(lir->output());
8338 Register tempReg = ToRegister(lir->temp0());
8339 const CompileInfo& info = lir->mir()->block()->info();
8340
8341 using Fn = js::NamedLambdaObject* (*)(JSContext*, HandleFunction);
8342 OutOfLineCode* ool = oolCallVM<Fn, NamedLambdaObject::createWithoutEnclosing>(
8343 lir, ArgList(info.funMaybeLazy()), StoreRegisterTo(objReg));
8344
8345 TemplateObject templateObject(lir->mir()->templateObj());
8346
8347 masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default,
8348 ool->entry());
8349
8350 masm.bind(ool->rejoin());
8351}
8352
8353void CodeGenerator::visitNewCallObject(LNewCallObject* lir) {
8354 Register objReg = ToRegister(lir->output());
8355 Register tempReg = ToRegister(lir->temp0());
8356
8357 CallObject* templateObj = lir->mir()->templateObject();
8358
8359 using Fn = CallObject* (*)(JSContext*, Handle<SharedShape*>);
8360 OutOfLineCode* ool = oolCallVM<Fn, CallObject::createWithShape>(
8361 lir, ArgList(ImmGCPtr(templateObj->sharedShape())),
8362 StoreRegisterTo(objReg));
8363
8364 // Inline call object creation, using the OOL path only for tricky cases.
8365 TemplateObject templateObject(templateObj);
8366 masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default,
8367 ool->entry());
8368
8369 masm.bind(ool->rejoin());
8370}
8371
8372void CodeGenerator::visitNewStringObject(LNewStringObject* lir) {
8373 Register input = ToRegister(lir->input());
8374 Register output = ToRegister(lir->output());
8375 Register temp = ToRegister(lir->temp0());
8376
8377 StringObject* templateObj = lir->mir()->templateObj();
8378
8379 using Fn = JSObject* (*)(JSContext*, HandleString);
8380 OutOfLineCode* ool = oolCallVM<Fn, NewStringObject>(lir, ArgList(input),
8381 StoreRegisterTo(output));
8382
8383 TemplateObject templateObject(templateObj);
8384 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
8385 ool->entry());
8386
8387 masm.loadStringLength(input, temp);
8388
8389 masm.storeValue(JSVAL_TYPE_STRING, input,
8390 Address(output, StringObject::offsetOfPrimitiveValue()));
8391 masm.storeValue(JSVAL_TYPE_INT32, temp,
8392 Address(output, StringObject::offsetOfLength()));
8393
8394 masm.bind(ool->rejoin());
8395}
8396
8397void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) {
8398 Register obj = ToRegister(lir->object());
8399 Register value = ToRegister(lir->value());
8400
8401 pushArg(value);
8402 pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex));
8403 pushArg(obj);
8404 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
8405
8406 using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue,
8407 HandleObject);
8408 callVM<Fn, InitElemGetterSetterOperation>(lir);
8409}
8410
8411void CodeGenerator::visitMutateProto(LMutateProto* lir) {
8412 Register objReg = ToRegister(lir->object());
8413
8414 pushArg(ToValue(lir, LMutateProto::ValueIndex));
8415 pushArg(objReg);
8416
8417 using Fn =
8418 bool (*)(JSContext* cx, Handle<PlainObject*> obj, HandleValue value);
8419 callVM<Fn, MutatePrototype>(lir);
8420}
8421
8422void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) {
8423 Register obj = ToRegister(lir->object());
8424 Register value = ToRegister(lir->value());
8425
8426 pushArg(value);
8427 pushArg(ImmGCPtr(lir->mir()->name()));
8428 pushArg(obj);
8429 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
8430
8431 using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject,
8432 Handle<PropertyName*>, HandleObject);
8433 callVM<Fn, InitPropGetterSetterOperation>(lir);
8434}
8435
8436void CodeGenerator::visitCreateThis(LCreateThis* lir) {
8437 const LAllocation* callee = lir->callee();
8438 const LAllocation* newTarget = lir->newTarget();
8439
8440 if (newTarget->isConstant()) {
8441 pushArg(ImmGCPtr(&newTarget->toConstant()->toObject()));
8442 } else {
8443 pushArg(ToRegister(newTarget));
8444 }
8445
8446 if (callee->isConstant()) {
8447 pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
8448 } else {
8449 pushArg(ToRegister(callee));
8450 }
8451
8452 using Fn = bool (*)(JSContext* cx, HandleObject callee,
8453 HandleObject newTarget, MutableHandleValue rval);
8454 callVM<Fn, jit::CreateThisFromIon>(lir);
8455}
8456
8457void CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) {
8458 // This should be getting constructed in the first block only, and not any OSR
8459 // entry blocks.
8460 MOZ_ASSERT(lir->mir()->block()->id() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->block()->id() == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mir()->block()->id() == 0))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("lir->mir()->block()->id() == 0"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->block()->id() == 0"
")"); do { *((volatile int*)__null) = 8460; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8461
8462 Register callObj = ToRegister(lir->callObject());
8463 Register temp0 = ToRegister(lir->temp0());
8464 Label done;
8465
8466 if (ArgumentsObject* templateObj = lir->mir()->templateObject()) {
8467 Register objTemp = ToRegister(lir->temp1());
8468 Register cxTemp = ToRegister(lir->temp2());
8469
8470 masm.Push(callObj);
8471
8472 // Try to allocate an arguments object. This will leave the reserved
8473 // slots uninitialized, so it's important we don't GC until we
8474 // initialize these slots in ArgumentsObject::finishForIonPure.
8475 Label failure;
8476 TemplateObject templateObject(templateObj);
8477 masm.createGCObject(objTemp, temp0, templateObject, gc::Heap::Default,
8478 &failure,
8479 /* initContents = */ false);
8480
8481 masm.moveStackPtrTo(temp0);
8482 masm.addPtr(Imm32(masm.framePushed()), temp0);
8483
8484 using Fn = ArgumentsObject* (*)(JSContext* cx, jit::JitFrameLayout* frame,
8485 JSObject* scopeChain, ArgumentsObject* obj);
8486 masm.setupAlignedABICall();
8487 masm.loadJSContext(cxTemp);
8488 masm.passABIArg(cxTemp);
8489 masm.passABIArg(temp0);
8490 masm.passABIArg(callObj);
8491 masm.passABIArg(objTemp);
8492
8493 masm.callWithABI<Fn, ArgumentsObject::finishForIonPure>();
8494 masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
8495
8496 // Discard saved callObj on the stack.
8497 masm.addToStackPtr(Imm32(sizeof(uintptr_t)));
8498 masm.jump(&done);
8499
8500 masm.bind(&failure);
8501 masm.Pop(callObj);
8502 }
8503
8504 masm.moveStackPtrTo(temp0);
8505 masm.addPtr(Imm32(frameSize()), temp0);
8506
8507 pushArg(callObj);
8508 pushArg(temp0);
8509
8510 using Fn = ArgumentsObject* (*)(JSContext*, JitFrameLayout*, HandleObject);
8511 callVM<Fn, ArgumentsObject::createForIon>(lir);
8512
8513 masm.bind(&done);
8514}
8515
8516void CodeGenerator::visitCreateInlinedArgumentsObject(
8517 LCreateInlinedArgumentsObject* lir) {
8518 Register callObj = ToRegister(lir->getCallObject());
8519 Register callee = ToRegister(lir->getCallee());
8520 Register argsAddress = ToRegister(lir->temp1());
8521 Register argsObj = ToRegister(lir->temp2());
8522
8523 // TODO: Do we have to worry about alignment here?
8524
8525 // Create a contiguous array of values for ArgumentsObject::create
8526 // by pushing the arguments onto the stack in reverse order.
8527 uint32_t argc = lir->mir()->numActuals();
8528 for (uint32_t i = 0; i < argc; i++) {
8529 uint32_t argNum = argc - i - 1;
8530 uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(argNum);
8531 ConstantOrRegister arg =
8532 toConstantOrRegister(lir, index, lir->mir()->getArg(argNum)->type());
8533 masm.Push(arg);
8534 }
8535 masm.moveStackPtrTo(argsAddress);
8536
8537 Label done;
8538 if (ArgumentsObject* templateObj = lir->mir()->templateObject()) {
8539 LiveRegisterSet liveRegs;
8540 liveRegs.add(callObj);
8541 liveRegs.add(callee);
8542
8543 masm.PushRegsInMask(liveRegs);
8544
8545 // We are free to clobber all registers, as LCreateInlinedArgumentsObject is
8546 // a call instruction.
8547 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
8548 allRegs.take(callObj);
8549 allRegs.take(callee);
8550 allRegs.take(argsObj);
8551 allRegs.take(argsAddress);
8552
8553 Register temp3 = allRegs.takeAny();
8554 Register temp4 = allRegs.takeAny();
8555
8556 // Try to allocate an arguments object. This will leave the reserved slots
8557 // uninitialized, so it's important we don't GC until we initialize these
8558 // slots in ArgumentsObject::finishForIonPure.
8559 Label failure;
8560 TemplateObject templateObject(templateObj);
8561 masm.createGCObject(argsObj, temp3, templateObject, gc::Heap::Default,
8562 &failure,
8563 /* initContents = */ false);
8564
8565 Register numActuals = temp3;
8566 masm.move32(Imm32(argc), numActuals);
8567
8568 using Fn = ArgumentsObject* (*)(JSContext*, JSObject*, JSFunction*, Value*,
8569 uint32_t, ArgumentsObject*);
8570 masm.setupAlignedABICall();
8571 masm.loadJSContext(temp4);
8572 masm.passABIArg(temp4);
8573 masm.passABIArg(callObj);
8574 masm.passABIArg(callee);
8575 masm.passABIArg(argsAddress);
8576 masm.passABIArg(numActuals);
8577 masm.passABIArg(argsObj);
8578
8579 masm.callWithABI<Fn, ArgumentsObject::finishInlineForIonPure>();
8580 masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
8581
8582 // Discard saved callObj, callee, and values array on the stack.
8583 masm.addToStackPtr(
8584 Imm32(MacroAssembler::PushRegsInMaskSizeInBytes(liveRegs) +
8585 argc * sizeof(Value)));
8586 masm.jump(&done);
8587
8588 masm.bind(&failure);
8589 masm.PopRegsInMask(liveRegs);
8590
8591 // Reload argsAddress because it may have been overridden.
8592 masm.moveStackPtrTo(argsAddress);
8593 }
8594
8595 pushArg(Imm32(argc));
8596 pushArg(callObj);
8597 pushArg(callee);
8598 pushArg(argsAddress);
8599
8600 using Fn = ArgumentsObject* (*)(JSContext*, Value*, HandleFunction,
8601 HandleObject, uint32_t);
8602 callVM<Fn, ArgumentsObject::createForInlinedIon>(lir);
8603
8604 // Discard the array of values.
8605 masm.freeStack(argc * sizeof(Value));
8606
8607 masm.bind(&done);
8608}
8609
8610template <class GetInlinedArgument>
8611void CodeGenerator::emitGetInlinedArgument(GetInlinedArgument* lir,
8612 Register index,
8613 ValueOperand output) {
8614 uint32_t numActuals = lir->mir()->numActuals();
8615 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8615); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numActuals <= ArgumentsObject::MaxInlinedArgs"
")"); do { *((volatile int*)__null) = 8615; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8616
8617 // The index has already been bounds-checked, so the code we
8618 // generate here should be unreachable. We can end up in this
8619 // situation in self-hosted code using GetArgument(), or in a
8620 // monomorphically inlined function if we've inlined some CacheIR
8621 // that was created for a different caller.
8622 if (numActuals == 0) {
8623 masm.assumeUnreachable("LGetInlinedArgument: invalid index");
8624 return;
8625 }
8626
8627 // Check the first n-1 possible indices.
8628 Label done;
8629 for (uint32_t i = 0; i < numActuals - 1; i++) {
8630 Label skip;
8631 ConstantOrRegister arg = toConstantOrRegister(
8632 lir, GetInlinedArgument::ArgIndex(i), lir->mir()->getArg(i)->type());
8633 masm.branch32(Assembler::NotEqual, index, Imm32(i), &skip);
8634 masm.moveValue(arg, output);
8635
8636 masm.jump(&done);
8637 masm.bind(&skip);
8638 }
8639
8640#ifdef DEBUG1
8641 Label skip;
8642 masm.branch32(Assembler::Equal, index, Imm32(numActuals - 1), &skip);
8643 masm.assumeUnreachable("LGetInlinedArgument: invalid index");
8644 masm.bind(&skip);
8645#endif
8646
8647 // The index has already been bounds-checked, so load the last argument.
8648 uint32_t lastIdx = numActuals - 1;
8649 ConstantOrRegister arg =
8650 toConstantOrRegister(lir, GetInlinedArgument::ArgIndex(lastIdx),
8651 lir->mir()->getArg(lastIdx)->type());
8652 masm.moveValue(arg, output);
8653 masm.bind(&done);
8654}
8655
8656void CodeGenerator::visitGetInlinedArgument(LGetInlinedArgument* lir) {
8657 Register index = ToRegister(lir->getIndex());
8658 ValueOperand output = ToOutValue(lir);
8659
8660 emitGetInlinedArgument(lir, index, output);
8661}
8662
8663void CodeGenerator::visitGetInlinedArgumentHole(LGetInlinedArgumentHole* lir) {
8664 Register index = ToRegister(lir->getIndex());
8665 ValueOperand output = ToOutValue(lir);
8666
8667 uint32_t numActuals = lir->mir()->numActuals();
8668
8669 if (numActuals == 0) {
8670 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
8671 masm.moveValue(UndefinedValue(), output);
8672 return;
8673 }
8674
8675 Label outOfBounds, done;
8676 masm.branch32(Assembler::AboveOrEqual, index, Imm32(numActuals),
8677 &outOfBounds);
8678
8679 emitGetInlinedArgument(lir, index, output);
8680 masm.jump(&done);
8681
8682 masm.bind(&outOfBounds);
8683 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
8684 masm.moveValue(UndefinedValue(), output);
8685
8686 masm.bind(&done);
8687}
8688
8689void CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir) {
8690 Register temp = ToRegister(lir->temp0());
8691 Register argsObj = ToRegister(lir->argsObject());
8692 ValueOperand out = ToOutValue(lir);
8693
8694 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
8695 temp);
8696 Address argAddr(temp, ArgumentsData::offsetOfArgs() +
8697 lir->mir()->argno() * sizeof(Value));
8698 masm.loadValue(argAddr, out);
8699#ifdef DEBUG1
8700 Label success;
8701 masm.branchTestMagic(Assembler::NotEqual, out, &success);
8702 masm.assumeUnreachable(
8703 "Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
8704 masm.bind(&success);
8705#endif
8706}
8707
8708void CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir) {
8709 Register temp = ToRegister(lir->getTemp(0));
8710 Register argsObj = ToRegister(lir->argsObject());
8711 ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex);
8712
8713 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
8714 temp);
8715 Address argAddr(temp, ArgumentsData::offsetOfArgs() +
8716 lir->mir()->argno() * sizeof(Value));
8717 emitPreBarrier(argAddr);
8718#ifdef DEBUG1
8719 Label success;
8720 masm.branchTestMagic(Assembler::NotEqual, argAddr, &success);
8721 masm.assumeUnreachable(
8722 "Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
8723 masm.bind(&success);
8724#endif
8725 masm.storeValue(value, argAddr);
8726}
8727
8728void CodeGenerator::visitLoadArgumentsObjectArg(LLoadArgumentsObjectArg* lir) {
8729 Register temp = ToRegister(lir->temp0());
8730 Register argsObj = ToRegister(lir->argsObject());
8731 Register index = ToRegister(lir->index());
8732 ValueOperand out = ToOutValue(lir);
8733
8734 Label bail;
8735 masm.loadArgumentsObjectElement(argsObj, index, out, temp, &bail);
8736 bailoutFrom(&bail, lir->snapshot());
8737}
8738
8739void CodeGenerator::visitLoadArgumentsObjectArgHole(
8740 LLoadArgumentsObjectArgHole* lir) {
8741 Register temp = ToRegister(lir->temp0());
8742 Register argsObj = ToRegister(lir->argsObject());
8743 Register index = ToRegister(lir->index());
8744 ValueOperand out = ToOutValue(lir);
8745
8746 Label bail;
8747 masm.loadArgumentsObjectElementHole(argsObj, index, out, temp, &bail);
8748 bailoutFrom(&bail, lir->snapshot());
8749}
8750
8751void CodeGenerator::visitInArgumentsObjectArg(LInArgumentsObjectArg* lir) {
8752 Register temp = ToRegister(lir->temp0());
8753 Register argsObj = ToRegister(lir->argsObject());
8754 Register index = ToRegister(lir->index());
8755 Register out = ToRegister(lir->output());
8756
8757 Label bail;
8758 masm.loadArgumentsObjectElementExists(argsObj, index, out, temp, &bail);
8759 bailoutFrom(&bail, lir->snapshot());
8760}
8761
8762void CodeGenerator::visitArgumentsObjectLength(LArgumentsObjectLength* lir) {
8763 Register argsObj = ToRegister(lir->argsObject());
8764 Register out = ToRegister(lir->output());
8765
8766 Label bail;
8767 masm.loadArgumentsObjectLength(argsObj, out, &bail);
8768 bailoutFrom(&bail, lir->snapshot());
8769}
8770
8771void CodeGenerator::visitArrayFromArgumentsObject(
8772 LArrayFromArgumentsObject* lir) {
8773 pushArg(ToRegister(lir->argsObject()));
8774
8775 using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>);
8776 callVM<Fn, js::ArrayFromArgumentsObject>(lir);
8777}
8778
8779void CodeGenerator::visitGuardArgumentsObjectFlags(
8780 LGuardArgumentsObjectFlags* lir) {
8781 Register argsObj = ToRegister(lir->argsObject());
8782 Register temp = ToRegister(lir->temp0());
8783
8784 Label bail;
8785 masm.branchTestArgumentsObjectFlags(argsObj, temp, lir->mir()->flags(),
8786 Assembler::NonZero, &bail);
8787 bailoutFrom(&bail, lir->snapshot());
8788}
8789
8790void CodeGenerator::visitBoundFunctionNumArgs(LBoundFunctionNumArgs* lir) {
8791 Register obj = ToRegister(lir->object());
8792 Register output = ToRegister(lir->output());
8793
8794 masm.unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()),
8795 output);
8796 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), output);
8797}
8798
8799void CodeGenerator::visitGuardBoundFunctionIsConstructor(
8800 LGuardBoundFunctionIsConstructor* lir) {
8801 Register obj = ToRegister(lir->object());
8802
8803 Label bail;
8804 Address flagsSlot(obj, BoundFunctionObject::offsetOfFlagsSlot());
8805 masm.branchTest32(Assembler::Zero, flagsSlot,
8806 Imm32(BoundFunctionObject::IsConstructorFlag), &bail);
8807 bailoutFrom(&bail, lir->snapshot());
8808}
8809
8810void CodeGenerator::visitReturnFromCtor(LReturnFromCtor* lir) {
8811 ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex);
8812 Register obj = ToRegister(lir->object());
8813 Register output = ToRegister(lir->output());
8814
8815 Label valueIsObject, end;
8816
8817 masm.branchTestObject(Assembler::Equal, value, &valueIsObject);
8818
8819 // Value is not an object. Return that other object.
8820 masm.movePtr(obj, output);
8821 masm.jump(&end);
8822
8823 // Value is an object. Return unbox(Value).
8824 masm.bind(&valueIsObject);
8825 Register payload = masm.extractObject(value, output);
8826 if (payload != output) {
8827 masm.movePtr(payload, output);
8828 }
8829
8830 masm.bind(&end);
8831}
8832
8833class OutOfLineBoxNonStrictThis : public OutOfLineCodeBase<CodeGenerator> {
8834 LBoxNonStrictThis* ins_;
8835
8836 public:
8837 explicit OutOfLineBoxNonStrictThis(LBoxNonStrictThis* ins) : ins_(ins) {}
8838 void accept(CodeGenerator* codegen) override {
8839 codegen->visitOutOfLineBoxNonStrictThis(this);
8840 }
8841 LBoxNonStrictThis* ins() const { return ins_; }
8842};
8843
8844void CodeGenerator::visitBoxNonStrictThis(LBoxNonStrictThis* lir) {
8845 ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex);
8846 Register output = ToRegister(lir->output());
8847
8848 auto* ool = new (alloc()) OutOfLineBoxNonStrictThis(lir);
8849 addOutOfLineCode(ool, lir->mir());
8850
8851 masm.fallibleUnboxObject(value, output, ool->entry());
8852 masm.bind(ool->rejoin());
8853}
8854
8855void CodeGenerator::visitOutOfLineBoxNonStrictThis(
8856 OutOfLineBoxNonStrictThis* ool) {
8857 LBoxNonStrictThis* lir = ool->ins();
8858
8859 ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex);
8860 Register output = ToRegister(lir->output());
8861
8862 Label notNullOrUndefined;
8863 {
8864 Label isNullOrUndefined;
8865 ScratchTagScope tag(masm, value);
8866 masm.splitTagForTest(value, tag);
8867 masm.branchTestUndefined(Assembler::Equal, tag, &isNullOrUndefined);
8868 masm.branchTestNull(Assembler::NotEqual, tag, &notNullOrUndefined);
8869 masm.bind(&isNullOrUndefined);
8870 masm.movePtr(ImmGCPtr(lir->mir()->globalThis()), output);
8871 masm.jump(ool->rejoin());
8872 }
8873
8874 masm.bind(&notNullOrUndefined);
8875
8876 saveLive(lir);
8877
8878 pushArg(value);
8879 using Fn = JSObject* (*)(JSContext*, HandleValue);
8880 callVM<Fn, BoxNonStrictThis>(lir);
8881
8882 StoreRegisterTo(output).generate(this);
8883 restoreLiveIgnore(lir, StoreRegisterTo(output).clobbered());
8884
8885 masm.jump(ool->rejoin());
8886}
8887
8888void CodeGenerator::visitImplicitThis(LImplicitThis* lir) {
8889 pushArg(ImmGCPtr(lir->mir()->name()));
8890 pushArg(ToRegister(lir->env()));
8891
8892 using Fn = bool (*)(JSContext*, HandleObject, Handle<PropertyName*>,
8893 MutableHandleValue);
8894 callVM<Fn, ImplicitThisOperation>(lir);
8895}
8896
8897void CodeGenerator::visitArrayLength(LArrayLength* lir) {
8898 Register elements = ToRegister(lir->elements());
8899 Register output = ToRegister(lir->output());
8900
8901 Address length(elements, ObjectElements::offsetOfLength());
8902 masm.load32(length, output);
8903
8904 // Bail out if the length doesn't fit in int32.
8905 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
8906}
8907
8908static void SetLengthFromIndex(MacroAssembler& masm, const LAllocation* index,
8909 const Address& length) {
8910 if (index->isConstant()) {
8911 masm.store32(Imm32(ToInt32(index) + 1), length);
8912 } else {
8913 Register newLength = ToRegister(index);
8914 masm.add32(Imm32(1), newLength);
8915 masm.store32(newLength, length);
8916 masm.sub32(Imm32(1), newLength);
8917 }
8918}
8919
8920void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) {
8921 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
8922 SetLengthFromIndex(masm, lir->index(), length);
8923}
8924
8925void CodeGenerator::visitFunctionLength(LFunctionLength* lir) {
8926 Register function = ToRegister(lir->function());
8927 Register output = ToRegister(lir->output());
8928
8929 Label bail;
8930
8931 // Get the JSFunction flags.
8932 masm.load32(Address(function, JSFunction::offsetOfFlagsAndArgCount()),
8933 output);
8934
8935 // Functions with a SelfHostedLazyScript must be compiled with the slow-path
8936 // before the function length is known. If the length was previously resolved,
8937 // the length property may be shadowed.
8938 masm.branchTest32(
8939 Assembler::NonZero, output,
8940 Imm32(FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH),
8941 &bail);
8942
8943 masm.loadFunctionLength(function, output, output, &bail);
8944
8945 bailoutFrom(&bail, lir->snapshot());
8946}
8947
8948void CodeGenerator::visitFunctionName(LFunctionName* lir) {
8949 Register function = ToRegister(lir->function());
8950 Register output = ToRegister(lir->output());
8951
8952 Label bail;
8953
8954 const JSAtomState& names = gen->runtime->names();
8955 masm.loadFunctionName(function, output, ImmGCPtr(names.empty_), &bail);
8956
8957 bailoutFrom(&bail, lir->snapshot());
8958}
8959
8960template <class OrderedHashTable>
8961static void RangeFront(MacroAssembler&, Register, Register, Register);
8962
8963template <>
8964void RangeFront<ValueMap>(MacroAssembler& masm, Register range, Register i,
8965 Register front) {
8966 masm.loadPtr(Address(range, ValueMap::Range::offsetOfHashTable()), front);
8967 masm.loadPtr(Address(front, ValueMap::offsetOfImplData()), front);
8968
8969 MOZ_ASSERT(ValueMap::offsetOfImplDataElement() == 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ValueMap::offsetOfImplDataElement() == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ValueMap::offsetOfImplDataElement() == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("ValueMap::offsetOfImplDataElement() == 0"
" (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8970); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ValueMap::offsetOfImplDataElement() == 0"
") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int
*)__null) = 8970; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
8970 "offsetof(Data, element) is 0")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ValueMap::offsetOfImplDataElement() == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ValueMap::offsetOfImplDataElement() == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("ValueMap::offsetOfImplDataElement() == 0"
" (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8970); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ValueMap::offsetOfImplDataElement() == 0"
") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int
*)__null) = 8970; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
8971 static_assert(ValueMap::sizeofImplData() == 24, "sizeof(Data) is 24");
8972 masm.mulBy3(i, i);
8973 masm.lshiftPtr(Imm32(3), i);
8974 masm.addPtr(i, front);
8975}
8976
8977template <>
8978void RangeFront<ValueSet>(MacroAssembler& masm, Register range, Register i,
8979 Register front) {
8980 masm.loadPtr(Address(range, ValueSet::Range::offsetOfHashTable()), front);
8981 masm.loadPtr(Address(front, ValueSet::offsetOfImplData()), front);
8982
8983 MOZ_ASSERT(ValueSet::offsetOfImplDataElement() == 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ValueSet::offsetOfImplDataElement() == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ValueSet::offsetOfImplDataElement() == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("ValueSet::offsetOfImplDataElement() == 0"
" (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ValueSet::offsetOfImplDataElement() == 0"
") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int
*)__null) = 8984; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
8984 "offsetof(Data, element) is 0")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ValueSet::offsetOfImplDataElement() == 0)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(ValueSet::offsetOfImplDataElement() == 0))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("ValueSet::offsetOfImplDataElement() == 0"
" (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 8984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ValueSet::offsetOfImplDataElement() == 0"
") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int
*)__null) = 8984; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
8985 static_assert(ValueSet::sizeofImplData() == 16, "sizeof(Data) is 16");
8986 masm.lshiftPtr(Imm32(4), i);
8987 masm.addPtr(i, front);
8988}
8989
8990template <class OrderedHashTable>
8991static void RangePopFront(MacroAssembler& masm, Register range, Register front,
8992 Register dataLength, Register temp) {
8993 Register i = temp;
8994
8995 masm.add32(Imm32(1),
8996 Address(range, OrderedHashTable::Range::offsetOfCount()));
8997
8998 masm.load32(Address(range, OrderedHashTable::Range::offsetOfI()), i);
8999
9000 Label done, seek;
9001 masm.bind(&seek);
9002 masm.add32(Imm32(1), i);
9003 masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done);
9004
9005 // We can add sizeof(Data) to |front| to select the next element, because
9006 // |front| and |range.ht.data[i]| point to the same location.
9007 MOZ_ASSERT(OrderedHashTable::offsetOfImplDataElement() == 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(OrderedHashTable::offsetOfImplDataElement() == 0)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(OrderedHashTable::offsetOfImplDataElement() == 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("OrderedHashTable::offsetOfImplDataElement() == 0"
" (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "OrderedHashTable::offsetOfImplDataElement() == 0"
") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int
*)__null) = 9008; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
9008 "offsetof(Data, element) is 0")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(OrderedHashTable::offsetOfImplDataElement() == 0)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(OrderedHashTable::offsetOfImplDataElement() == 0))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("OrderedHashTable::offsetOfImplDataElement() == 0"
" (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "OrderedHashTable::offsetOfImplDataElement() == 0"
") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int
*)__null) = 9008; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
9009 masm.addPtr(Imm32(OrderedHashTable::sizeofImplData()), front);
9010
9011 masm.branchTestMagic(Assembler::Equal,
9012 Address(front, OrderedHashTable::offsetOfEntryKey()),
9013 JS_HASH_KEY_EMPTY, &seek);
9014
9015 masm.bind(&done);
9016 masm.store32(i, Address(range, OrderedHashTable::Range::offsetOfI()));
9017}
9018
9019template <class OrderedHashTable>
9020static inline void RangeDestruct(MacroAssembler& masm, Register iter,
9021 Register range, Register temp0,
9022 Register temp1) {
9023 Register next = temp0;
9024 Register prevp = temp1;
9025
9026 masm.loadPtr(Address(range, OrderedHashTable::Range::offsetOfNext()), next);
9027 masm.loadPtr(Address(range, OrderedHashTable::Range::offsetOfPrevP()), prevp);
9028 masm.storePtr(next, Address(prevp, 0));
9029
9030 Label hasNoNext;
9031 masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext);
9032
9033 masm.storePtr(prevp, Address(next, OrderedHashTable::Range::offsetOfPrevP()));
9034
9035 masm.bind(&hasNoNext);
9036
9037 Label nurseryAllocated;
9038 masm.branchPtrInNurseryChunk(Assembler::Equal, iter, temp0,
9039 &nurseryAllocated);
9040
9041 masm.callFreeStub(range);
9042
9043 masm.bind(&nurseryAllocated);
9044}
9045
9046template <>
9047void CodeGenerator::emitLoadIteratorValues<ValueMap>(Register result,
9048 Register temp,
9049 Register front) {
9050 size_t elementsOffset = NativeObject::offsetOfFixedElements();
9051
9052 Address keyAddress(front, ValueMap::Entry::offsetOfKey());
9053 Address valueAddress(front, ValueMap::Entry::offsetOfValue());
9054 Address keyElemAddress(result, elementsOffset);
9055 Address valueElemAddress(result, elementsOffset + sizeof(Value));
9056 masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value);
9057 masm.guardedCallPreBarrier(valueElemAddress, MIRType::Value);
9058 masm.storeValue(keyAddress, keyElemAddress, temp);
9059 masm.storeValue(valueAddress, valueElemAddress, temp);
9060
9061 Label emitBarrier, skipBarrier;
9062 masm.branchValueIsNurseryCell(Assembler::Equal, keyAddress, temp,
9063 &emitBarrier);
9064 masm.branchValueIsNurseryCell(Assembler::NotEqual, valueAddress, temp,
9065 &skipBarrier);
9066 {
9067 masm.bind(&emitBarrier);
9068 saveVolatile(temp);
9069 emitPostWriteBarrier(result);
9070 restoreVolatile(temp);
9071 }
9072 masm.bind(&skipBarrier);
9073}
9074
9075template <>
9076void CodeGenerator::emitLoadIteratorValues<ValueSet>(Register result,
9077 Register temp,
9078 Register front) {
9079 size_t elementsOffset = NativeObject::offsetOfFixedElements();
9080
9081 Address keyAddress(front, ValueSet::offsetOfEntryKey());
9082 Address keyElemAddress(result, elementsOffset);
9083 masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value);
9084 masm.storeValue(keyAddress, keyElemAddress, temp);
9085
9086 Label skipBarrier;
9087 masm.branchValueIsNurseryCell(Assembler::NotEqual, keyAddress, temp,
9088 &skipBarrier);
9089 {
9090 saveVolatile(temp);
9091 emitPostWriteBarrier(result);
9092 restoreVolatile(temp);
9093 }
9094 masm.bind(&skipBarrier);
9095}
9096
9097template <class IteratorObject, class OrderedHashTable>
9098void CodeGenerator::emitGetNextEntryForIterator(LGetNextEntryForIterator* lir) {
9099 Register iter = ToRegister(lir->iter());
9100 Register result = ToRegister(lir->result());
9101 Register temp = ToRegister(lir->temp0());
9102 Register dataLength = ToRegister(lir->temp1());
9103 Register range = ToRegister(lir->temp2());
9104 Register output = ToRegister(lir->output());
9105
9106#ifdef DEBUG1
9107 // Self-hosted code is responsible for ensuring GetNextEntryForIterator is
9108 // only called with the correct iterator class. Assert here all self-
9109 // hosted callers of GetNextEntryForIterator perform this class check.
9110 // No Spectre mitigations are needed because this is DEBUG-only code.
9111 Label success;
9112 masm.branchTestObjClassNoSpectreMitigations(
9113 Assembler::Equal, iter, &IteratorObject::class_, temp, &success);
9114 masm.assumeUnreachable("Iterator object should have the correct class.");
9115 masm.bind(&success);
9116#endif
9117
9118 masm.loadPrivate(Address(iter, NativeObject::getFixedSlotOffset(
9119 IteratorObject::RangeSlot)),
9120 range);
9121
9122 Label iterAlreadyDone, iterDone, done;
9123 masm.branchTestPtr(Assembler::Zero, range, range, &iterAlreadyDone);
9124
9125 masm.load32(Address(range, OrderedHashTable::Range::offsetOfI()), temp);
9126 masm.loadPtr(Address(range, OrderedHashTable::Range::offsetOfHashTable()),
9127 dataLength);
9128 masm.load32(Address(dataLength, OrderedHashTable::offsetOfImplDataLength()),
9129 dataLength);
9130 masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone);
9131 {
9132 masm.Push(iter);
9133
9134 Register front = iter;
9135 RangeFront<OrderedHashTable>(masm, range, temp, front);
9136
9137 emitLoadIteratorValues<OrderedHashTable>(result, temp, front);
9138
9139 RangePopFront<OrderedHashTable>(masm, range, front, dataLength, temp);
9140
9141 masm.Pop(iter);
9142 masm.move32(Imm32(0), output);
9143 }
9144 masm.jump(&done);
9145 {
9146 masm.bind(&iterDone);
9147
9148 RangeDestruct<OrderedHashTable>(masm, iter, range, temp, dataLength);
9149
9150 masm.storeValue(PrivateValue(nullptr),
9151 Address(iter, NativeObject::getFixedSlotOffset(
9152 IteratorObject::RangeSlot)));
9153
9154 masm.bind(&iterAlreadyDone);
9155
9156 masm.move32(Imm32(1), output);
9157 }
9158 masm.bind(&done);
9159}
9160
9161void CodeGenerator::visitGetNextEntryForIterator(
9162 LGetNextEntryForIterator* lir) {
9163 if (lir->mir()->mode() == MGetNextEntryForIterator::Map) {
9164 emitGetNextEntryForIterator<MapIteratorObject, ValueMap>(lir);
9165 } else {
9166 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9166); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MGetNextEntryForIterator::Set"
")"); do { *((volatile int*)__null) = 9166; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9167 emitGetNextEntryForIterator<SetIteratorObject, ValueSet>(lir);
9168 }
9169}
9170
9171// The point of these is to inform Ion of where these values already are; they
9172// don't normally generate (much) code.
9173void CodeGenerator::visitWasmRegisterPairResult(LWasmRegisterPairResult* lir) {}
9174void CodeGenerator::visitWasmStackResult(LWasmStackResult* lir) {}
9175void CodeGenerator::visitWasmStackResult64(LWasmStackResult64* lir) {}
9176
9177void CodeGenerator::visitWasmStackResultArea(LWasmStackResultArea* lir) {
9178 LAllocation* output = lir->getDef(0)->output();
9179 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9179); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output->isStackArea()"
")"); do { *((volatile int*)__null) = 9179; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9180 bool tempInit = false;
9181 for (auto iter = output->toStackArea()->results(); iter; iter.next()) {
9182 // Zero out ref stack results.
9183 if (iter.isWasmAnyRef()) {
9184 Register temp = ToRegister(lir->temp0());
9185 if (!tempInit) {
9186 masm.xorPtr(temp, temp);
9187 tempInit = true;
9188 }
9189 masm.storePtr(temp, ToAddress(iter.alloc()));
9190 }
9191 }
9192}
9193
9194void CodeGenerator::visitWasmRegisterResult(LWasmRegisterResult* lir) {
9195#ifdef JS_64BIT1
9196 if (MWasmRegisterResult* mir = lir->mir()) {
9197 if (mir->type() == MIRType::Int32) {
9198 masm.widenInt32(ToRegister(lir->output()));
9199 }
9200 }
9201#endif
9202}
9203
9204void CodeGenerator::visitWasmCall(LWasmCall* lir) {
9205 const MWasmCallBase* callBase = lir->callBase();
9206 bool isReturnCall = lir->isReturnCall();
9207
9208 // If this call is in Wasm try code block, initialise a wasm::TryNote for this
9209 // call.
9210 bool inTry = callBase->inTry();
9211 if (inTry) {
9212 size_t tryNoteIndex = callBase->tryNoteIndex();
9213 wasm::TryNoteVector& tryNotes = masm.tryNotes();
9214 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
9215 tryNote.setTryBodyBegin(masm.currentOffset());
9216 }
9217
9218 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9219); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
")"); do { *((volatile int*)__null) = 9219; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9219 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9219); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
")"); do { *((volatile int*)__null) = 9219; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9220 static_assert(
9221 WasmStackAlignment >= ABIStackAlignment &&
9222 WasmStackAlignment % ABIStackAlignment == 0,
9223 "The wasm stack alignment should subsume the ABI-required alignment");
9224
9225#ifdef DEBUG1
9226 Label ok;
9227 masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok);
9228 masm.breakpoint();
9229 masm.bind(&ok);
9230#endif
9231
9232 // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the
9233 // instance and pinned regs. The only case where where we don't have to
9234 // reload the instance and pinned regs is when the callee preserves them.
9235 bool reloadRegs = true;
9236 bool switchRealm = true;
9237
9238 const wasm::CallSiteDesc& desc = callBase->desc();
9239 const wasm::CalleeDesc& callee = callBase->callee();
9240 CodeOffset retOffset;
9241 CodeOffset secondRetOffset;
9242 switch (callee.which()) {
9243 case wasm::CalleeDesc::Func:
9244#ifdef ENABLE_WASM_TAIL_CALLS1
9245 if (isReturnCall) {
9246 ReturnCallAdjustmentInfo retCallInfo(
9247 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
9248 masm.wasmReturnCall(desc, callee.funcIndex(), retCallInfo);
9249 // The rest of the method is unnecessary for a return call.
9250 return;
9251 }
9252#endif
9253 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9253); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9253; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9254 retOffset = masm.call(desc, callee.funcIndex());
9255 reloadRegs = false;
9256 switchRealm = false;
9257 break;
9258 case wasm::CalleeDesc::Import:
9259#ifdef ENABLE_WASM_TAIL_CALLS1
9260 if (isReturnCall) {
9261 ReturnCallAdjustmentInfo retCallInfo(
9262 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
9263 masm.wasmReturnCallImport(desc, callee, retCallInfo);
9264 // The rest of the method is unnecessary for a return call.
9265 return;
9266 }
9267#endif
9268 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9268); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9268; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9269 retOffset = masm.wasmCallImport(desc, callee);
9270 break;
9271 case wasm::CalleeDesc::AsmJSTable:
9272 retOffset = masm.asmCallIndirect(desc, callee);
9273 break;
9274 case wasm::CalleeDesc::WasmTable: {
9275 Label* boundsCheckFailed = nullptr;
9276 if (lir->needsBoundsCheck()) {
9277 OutOfLineAbortingWasmTrap* ool =
9278 new (alloc()) OutOfLineAbortingWasmTrap(
9279 wasm::BytecodeOffset(desc.lineOrBytecode()),
9280 wasm::Trap::OutOfBounds);
9281 if (lir->isCatchable()) {
9282 addOutOfLineCode(ool, lir->mirCatchable());
9283 } else if (isReturnCall) {
9284#ifdef ENABLE_WASM_TAIL_CALLS1
9285 addOutOfLineCode(ool, lir->mirReturnCall());
9286#else
9287 MOZ_CRASH("Return calls are disabled.")do { do { } while (false); MOZ_ReportCrash("" "Return calls are disabled."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9287); AnnotateMozCrashReason("MOZ_CRASH(" "Return calls are disabled."
")"); do { *((volatile int*)__null) = 9287; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
9288#endif
9289 } else {
9290 addOutOfLineCode(ool, lir->mirUncatchable());
9291 }
9292 boundsCheckFailed = ool->entry();
9293 }
9294 Label* nullCheckFailed = nullptr;
9295#ifndef WASM_HAS_HEAPREG1
9296 {
9297 OutOfLineAbortingWasmTrap* ool =
9298 new (alloc()) OutOfLineAbortingWasmTrap(
9299 wasm::BytecodeOffset(desc.lineOrBytecode()),
9300 wasm::Trap::IndirectCallToNull);
9301 if (lir->isCatchable()) {
9302 addOutOfLineCode(ool, lir->mirCatchable());
9303 } else if (isReturnCall) {
9304# ifdef ENABLE_WASM_TAIL_CALLS1
9305 addOutOfLineCode(ool, lir->mirReturnCall());
9306# else
9307 MOZ_CRASH("Return calls are disabled.")do { do { } while (false); MOZ_ReportCrash("" "Return calls are disabled."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9307); AnnotateMozCrashReason("MOZ_CRASH(" "Return calls are disabled."
")"); do { *((volatile int*)__null) = 9307; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
9308# endif
9309 } else {
9310 addOutOfLineCode(ool, lir->mirUncatchable());
9311 }
9312 nullCheckFailed = ool->entry();
9313 }
9314#endif
9315#ifdef ENABLE_WASM_TAIL_CALLS1
9316 if (isReturnCall) {
9317 ReturnCallAdjustmentInfo retCallInfo(
9318 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
9319 masm.wasmReturnCallIndirect(desc, callee, boundsCheckFailed,
9320 nullCheckFailed, mozilla::Nothing(),
9321 retCallInfo);
9322 // The rest of the method is unnecessary for a return call.
9323 return;
9324 }
9325#endif
9326 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9326); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9326; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9327 masm.wasmCallIndirect(desc, callee, boundsCheckFailed, nullCheckFailed,
9328 lir->tableSize(), &retOffset, &secondRetOffset);
9329 // Register reloading and realm switching are handled dynamically inside
9330 // wasmCallIndirect. There are two return offsets, one for each call
9331 // instruction (fast path and slow path).
9332 reloadRegs = false;
9333 switchRealm = false;
9334 break;
9335 }
9336 case wasm::CalleeDesc::Builtin:
9337 retOffset = masm.call(desc, callee.builtin());
9338 reloadRegs = false;
9339 switchRealm = false;
9340 break;
9341 case wasm::CalleeDesc::BuiltinInstanceMethod:
9342 retOffset = masm.wasmCallBuiltinInstanceMethod(
9343 desc, callBase->instanceArg(), callee.builtin(),
9344 callBase->builtinMethodFailureMode());
9345 switchRealm = false;
9346 break;
9347 case wasm::CalleeDesc::FuncRef:
9348#ifdef ENABLE_WASM_TAIL_CALLS1
9349 if (isReturnCall) {
9350 ReturnCallAdjustmentInfo retCallInfo(
9351 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
9352 masm.wasmReturnCallRef(desc, callee, retCallInfo);
9353 // The rest of the method is unnecessary for a return call.
9354 return;
9355 }
9356#endif
9357 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9357); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9357; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9358 // Register reloading and realm switching are handled dynamically inside
9359 // wasmCallRef. There are two return offsets, one for each call
9360 // instruction (fast path and slow path).
9361 masm.wasmCallRef(desc, callee, &retOffset, &secondRetOffset);
9362 reloadRegs = false;
9363 switchRealm = false;
9364 break;
9365 }
9366
9367 // Note the assembler offset for the associated LSafePoint.
9368 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9368); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9368; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9369 markSafepointAt(retOffset.offset(), lir);
9370
9371 // Now that all the outbound in-memory args are on the stack, note the
9372 // required lower boundary point of the associated StackMap.
9373 uint32_t framePushedAtStackMapBase =
9374 masm.framePushed() -
9375 wasm::AlignStackArgAreaSize(callBase->stackArgAreaSizeUnaligned());
9376 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAtStackMapBase);
9377 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9378); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
")"); do { *((volatile int*)__null) = 9378; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9378 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9378); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
")"); do { *((volatile int*)__null) = 9378; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9379
9380 // Note the assembler offset and framePushed for use by the adjunct
9381 // LSafePoint, see visitor for LWasmCallIndirectAdjunctSafepoint below.
9382 if (callee.which() == wasm::CalleeDesc::WasmTable) {
9383 lir->adjunctSafepoint()->recordSafepointInfo(secondRetOffset,
9384 framePushedAtStackMapBase);
9385 }
9386
9387 if (reloadRegs) {
9388 masm.loadPtr(
9389 Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall),
9390 InstanceReg);
9391 masm.loadWasmPinnedRegsFromInstance();
9392 if (switchRealm) {
9393 masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
9394 }
9395 } else {
9396 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9396); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!switchRealm"
")"); do { *((volatile int*)__null) = 9396; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9397 }
9398
9399#ifdef ENABLE_WASM_TAIL_CALLS1
9400 switch (callee.which()) {
9401 case wasm::CalleeDesc::Func:
9402 case wasm::CalleeDesc::Import:
9403 case wasm::CalleeDesc::WasmTable:
9404 case wasm::CalleeDesc::FuncRef:
9405 // Stack allocation could change during Wasm (return) calls,
9406 // recover pre-call state.
9407 masm.freeStackTo(masm.framePushed());
9408 break;
9409 default:
9410 break;
9411 }
9412#endif // ENABLE_WASM_TAIL_CALLS
9413
9414 if (inTry) {
9415 // Set the end of the try note range
9416 size_t tryNoteIndex = callBase->tryNoteIndex();
9417 wasm::TryNoteVector& tryNotes = masm.tryNotes();
9418 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
9419
9420 // Don't set the end of the try note if we've OOM'ed, as the above
9421 // instructions may not have been emitted, which will trigger an assert
9422 // about zero-length try-notes. This is okay as this compilation will be
9423 // thrown away.
9424 if (!masm.oom()) {
9425 tryNote.setTryBodyEnd(masm.currentOffset());
9426 }
9427
9428 // This instruction or the adjunct safepoint must be the last instruction
9429 // in the block. No other instructions may be inserted.
9430 LBlock* block = lir->block();
9431 MOZ_RELEASE_ASSERT(*block->rbegin() == lir ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*block->rbegin() == lir || (block->rbegin()->
isWasmCallIndirectAdjunctSafepoint() && *(++block->
rbegin()) == lir))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir ||
(block->rbegin()->isWasmCallIndirectAdjunctSafepoint()
&& *(++block->rbegin()) == lir)))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9433); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
")"); do { *((volatile int*)__null) = 9433; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9432 (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*block->rbegin() == lir || (block->rbegin()->
isWasmCallIndirectAdjunctSafepoint() && *(++block->
rbegin()) == lir))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir ||
(block->rbegin()->isWasmCallIndirectAdjunctSafepoint()
&& *(++block->rbegin()) == lir)))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9433); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
")"); do { *((volatile int*)__null) = 9433; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9433 *(++block->rbegin()) == lir))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(*block->rbegin() == lir || (block->rbegin()->
isWasmCallIndirectAdjunctSafepoint() && *(++block->
rbegin()) == lir))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir ||
(block->rbegin()->isWasmCallIndirectAdjunctSafepoint()
&& *(++block->rbegin()) == lir)))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9433); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
")"); do { *((volatile int*)__null) = 9433; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9434
9435 // Jump to the fallthrough block
9436 jumpToBlock(lir->mirCatchable()->getSuccessor(
9437 MWasmCallCatchable::FallthroughBranchIndex));
9438 }
9439}
9440
9441#ifdef ENABLE_WASM_JSPI1
9442void CodeGenerator::callWasmUpdateSuspenderState(
9443 wasm::UpdateSuspenderStateAction kind, Register suspender, Register temp) {
9444 masm.Push(InstanceReg);
9445 int32_t framePushedAfterInstance = masm.framePushed();
9446
9447 masm.move32(Imm32(uint32_t(kind)), temp);
9448
9449 masm.setupWasmABICall();
9450 masm.passABIArg(InstanceReg);
9451 masm.passABIArg(suspender);
9452 masm.passABIArg(temp);
9453 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
9454 masm.callWithABI(wasm::BytecodeOffset(0),
9455 wasm::SymbolicAddress::UpdateSuspenderState,
9456 mozilla::Some(instanceOffset));
9457
9458 masm.Pop(InstanceReg);
9459}
9460
9461void CodeGenerator::prepareWasmStackSwitchTrampolineCall(Register suspender,
9462 Register data) {
9463 // Reserve stack space for the wasm call.
9464 unsigned argDecrement;
9465 {
9466 WasmABIArgGenerator abi;
9467 ABIArg arg;
9468 arg = abi.next(MIRType::Pointer);
9469 arg = abi.next(MIRType::Pointer);
9470 argDecrement = StackDecrementForCall(WasmStackAlignment, 0,
9471 abi.stackBytesConsumedSoFar());
9472 }
9473 masm.reserveStack(argDecrement);
9474
9475 // Pass the suspender and data params through the wasm function ABI registers.
9476 WasmABIArgGenerator abi;
9477 ABIArg arg;
9478 arg = abi.next(MIRType::Pointer);
9479 if (arg.kind() == ABIArg::GPR) {
9480 masm.movePtr(suspender, arg.gpr());
9481 } else {
9482 MOZ_ASSERT(arg.kind() == ABIArg::Stack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arg.kind() == ABIArg::Stack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arg.kind() == ABIArg::Stack)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arg.kind() == ABIArg::Stack"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9482); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack"
")"); do { *((volatile int*)__null) = 9482; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9483 masm.storePtr(suspender,
9484 Address(masm.getStackPointer(), arg.offsetFromArgBase()));
9485 }
9486 arg = abi.next(MIRType::Pointer);
9487 if (arg.kind() == ABIArg::GPR) {
9488 masm.movePtr(data, arg.gpr());
9489 } else {
9490 MOZ_ASSERT(arg.kind() == ABIArg::Stack)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(arg.kind() == ABIArg::Stack)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(arg.kind() == ABIArg::Stack)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arg.kind() == ABIArg::Stack"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9490); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack"
")"); do { *((volatile int*)__null) = 9490; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9491 masm.storePtr(data,
9492 Address(masm.getStackPointer(), arg.offsetFromArgBase()));
9493 }
9494
9495 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
9496 WasmCallerInstanceOffsetBeforeCall));
9497}
9498#endif // ENABLE_WASM_JSPI
9499
9500void CodeGenerator::visitWasmStackSwitchToSuspendable(
9501 LWasmStackSwitchToSuspendable* lir) {
9502#ifdef ENABLE_WASM_JSPI1
9503 const Register SuspenderReg = lir->suspender()->toRegister().gpr();
9504 const Register FnReg = lir->fn()->toRegister().gpr();
9505 const Register DataReg = lir->data()->toRegister().gpr();
9506 const Register SuspenderDataReg = ABINonArgReg3;
9507
9508# ifdef JS_CODEGEN_ARM64
9509 vixl::UseScratchRegisterScope temps(&masm);
9510 const Register ScratchReg1 = temps.AcquireX().asUnsized();
9511# elif defined(JS_CODEGEN_X86)
9512 const Register ScratchReg1 = ABINonArgReg3;
9513# elif defined(JS_CODEGEN_X641)
9514 const Register ScratchReg1 = ScratchReg;
9515# elif defined(JS_CODEGEN_ARM)
9516 const Register ScratchReg1 = ABINonArgReturnVolatileReg;
9517# else
9518# error "NYI: scratch register"
9519# endif
9520
9521 masm.Push(SuspenderReg);
9522 masm.Push(FnReg);
9523 masm.Push(DataReg);
9524
9525 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Enter,
9526 SuspenderReg, ScratchReg1);
9527 masm.Pop(DataReg);
9528 masm.Pop(FnReg);
9529 masm.Pop(SuspenderReg);
9530
9531 masm.Push(SuspenderReg);
9532 int32_t framePushedAtSuspender = masm.framePushed();
9533 masm.Push(InstanceReg);
9534
9535 wasm::CallSiteDesc desc(wasm::CallSiteDesc::Kind::StackSwitch);
9536 CodeLabel returnCallsite;
9537
9538 // Aligning stack before trampoline call.
9539 uint32_t reserve = ComputeByteAlignment(
9540 masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment);
9541 masm.reserveStack(reserve);
9542
9543 masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset(
9544 wasm::SuspenderObjectDataSlot)),
9545 SuspenderDataReg);
9546
9547 // Switch stacks to suspendable, keep original FP to maintain
9548 // frames chain between main and suspendable stack segments.
9549 masm.storeStackPtr(
9550 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP()));
9551 masm.storePtr(
9552 FramePointer,
9553 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()));
9554
9555 masm.loadStackPtr(Address(
9556 SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP()));
9557
9558 masm.assertStackAlignment(WasmStackAlignment);
9559
9560 // The FramePointer is not changed for SwitchToSuspendable.
9561 uint32_t framePushed = masm.framePushed();
9562
9563 // On different stack, reset framePushed. FramePointer is not valid here.
9564 masm.setFramePushed(0);
9565
9566 prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg);
9567
9568 // Get wasm instance pointer for callee.
9569 size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot(
9570 FunctionExtended::WASM_INSTANCE_SLOT);
9571 masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg);
9572
9573 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
9574 WasmCalleeInstanceOffsetBeforeCall));
9575 masm.loadWasmPinnedRegsFromInstance();
9576
9577 masm.assertStackAlignment(WasmStackAlignment);
9578
9579 const Register ReturnAddressReg = ScratchReg1;
9580
9581 // DataReg is not needed anymore, using it as a scratch register.
9582 const Register ScratchReg2 = DataReg;
9583
9584 // Save future of suspendable stack exit frame pointer.
9585 masm.computeEffectiveAddress(
9586 Address(masm.getStackPointer(), -int32_t(sizeof(wasm::Frame))),
9587 ScratchReg2);
9588 masm.storePtr(
9589 ScratchReg2,
9590 Address(SuspenderDataReg,
9591 wasm::SuspenderObjectData::offsetOfSuspendableExitFP()));
9592
9593 masm.mov(&returnCallsite, ReturnAddressReg);
9594
9595 // Call wasm function fast.
9596# ifdef JS_USE_LINK_REGISTER
9597 masm.mov(ReturnAddressReg, lr);
9598# else
9599 masm.Push(ReturnAddressReg);
9600# endif
9601 // Get funcUncheckedCallEntry() from the function's
9602 // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot.
9603 size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot(
9604 FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT);
9605 masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2);
9606 masm.jump(ScratchReg2);
9607
9608 // About to use valid FramePointer -- restore framePushed.
9609 masm.setFramePushed(framePushed);
9610
9611 // For IsPlausibleStackMapKey check for the following callsite.
9612 masm.wasmTrapInstruction();
9613
9614 // Callsite for return from main stack.
9615 masm.bind(&returnCallsite);
9616 masm.append(desc, *returnCallsite.target());
9617 masm.addCodeLabel(returnCallsite);
9618
9619 masm.assertStackAlignment(WasmStackAlignment);
9620
9621 markSafepointAt(returnCallsite.target()->offset(), lir);
9622 lir->safepoint()->setFramePushedAtStackMapBase(framePushed);
9623 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
9624 // Rooting SuspenderReg.
9625 masm.propagateOOM(
9626 lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender));
9627
9628 masm.freeStackTo(framePushed);
9629
9630 masm.freeStack(reserve);
9631 masm.Pop(InstanceReg);
9632 masm.Pop(SuspenderReg);
9633
9634 masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2);
9635
9636 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave,
9637 SuspenderReg, ScratchReg1);
9638#else
9639 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9639); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do {
*((volatile int*)__null) = 9639; __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
9640#endif // ENABLE_WASM_JSPI
9641}
9642
9643void CodeGenerator::visitWasmStackSwitchToMain(LWasmStackSwitchToMain* lir) {
9644#ifdef ENABLE_WASM_JSPI1
9645 const Register SuspenderReg = lir->suspender()->toRegister().gpr();
9646 const Register FnReg = lir->fn()->toRegister().gpr();
9647 const Register DataReg = lir->data()->toRegister().gpr();
9648 const Register SuspenderDataReg = ABINonArgReg3;
9649
9650# ifdef JS_CODEGEN_ARM64
9651 vixl::UseScratchRegisterScope temps(&masm);
9652 const Register ScratchReg1 = temps.AcquireX().asUnsized();
9653# elif defined(JS_CODEGEN_X86)
9654 const Register ScratchReg1 = ABINonArgReg3;
9655# elif defined(JS_CODEGEN_X641)
9656 const Register ScratchReg1 = ScratchReg;
9657# elif defined(JS_CODEGEN_ARM)
9658 const Register ScratchReg1 = ABINonArgReturnVolatileReg;
9659# else
9660# error "NYI: scratch register"
9661# endif
9662
9663 masm.Push(SuspenderReg);
9664 masm.Push(FnReg);
9665 masm.Push(DataReg);
9666
9667 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Suspend,
9668 SuspenderReg, ScratchReg1);
9669
9670 masm.Pop(DataReg);
9671 masm.Pop(FnReg);
9672 masm.Pop(SuspenderReg);
9673
9674 masm.Push(SuspenderReg);
9675 int32_t framePushedAtSuspender = masm.framePushed();
9676 masm.Push(InstanceReg);
9677
9678 wasm::CallSiteDesc desc(wasm::CallSiteDesc::Kind::StackSwitch);
9679 CodeLabel returnCallsite;
9680
9681 // Aligning stack before trampoline call.
9682 uint32_t reserve = ComputeByteAlignment(
9683 masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment);
9684 masm.reserveStack(reserve);
9685
9686 masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset(
9687 wasm::SuspenderObjectDataSlot)),
9688 SuspenderDataReg);
9689
9690 // Switch stacks to main.
9691 masm.storeStackPtr(Address(
9692 SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP()));
9693 masm.storePtr(FramePointer,
9694 Address(SuspenderDataReg,
9695 wasm::SuspenderObjectData::offsetOfSuspendableFP()));
9696
9697 masm.loadStackPtr(
9698 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP()));
9699 masm.loadPtr(
9700 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()),
9701 FramePointer);
9702
9703 // Set main_ra field to returnCallsite.
9704# ifdef JS_CODEGEN_X86
9705 // SuspenderDataReg is also ScratchReg1, use DataReg as a scratch register.
9706 MOZ_ASSERT(ScratchReg1 == SuspenderDataReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ScratchReg1 == SuspenderDataReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ScratchReg1 == SuspenderDataReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ScratchReg1 == SuspenderDataReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9706); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 == SuspenderDataReg"
")"); do { *((volatile int*)__null) = 9706; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9707 masm.push(DataReg);
9708 masm.mov(&returnCallsite, DataReg);
9709 masm.storePtr(
9710 DataReg,
9711 Address(SuspenderDataReg,
9712 wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()));
9713 masm.pop(DataReg);
9714# else
9715 MOZ_ASSERT(ScratchReg1 != SuspenderDataReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ScratchReg1 != SuspenderDataReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ScratchReg1 != SuspenderDataReg
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ScratchReg1 != SuspenderDataReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9715); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 != SuspenderDataReg"
")"); do { *((volatile int*)__null) = 9715; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9716 masm.mov(&returnCallsite, ScratchReg1);
9717 masm.storePtr(
9718 ScratchReg1,
9719 Address(SuspenderDataReg,
9720 wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()));
9721# endif
9722
9723 masm.assertStackAlignment(WasmStackAlignment);
9724
9725 // The FramePointer is pointing to the same
9726 // place as before switch happened.
9727 uint32_t framePushed = masm.framePushed();
9728
9729 // On different stack, reset framePushed. FramePointer is not valid here.
9730 masm.setFramePushed(0);
9731
9732 prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg);
9733
9734 // Get wasm instance pointer for callee.
9735 size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot(
9736 FunctionExtended::WASM_INSTANCE_SLOT);
9737 masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg);
9738
9739 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
9740 WasmCalleeInstanceOffsetBeforeCall));
9741 masm.loadWasmPinnedRegsFromInstance();
9742
9743 masm.assertStackAlignment(WasmStackAlignment);
9744
9745 const Register ReturnAddressReg = ScratchReg1;
9746 // DataReg is not needed anymore, using it as a scratch register.
9747 const Register ScratchReg2 = DataReg;
9748
9749 // Load InstanceReg from suspendable stack exit frame.
9750 masm.loadPtr(Address(SuspenderDataReg,
9751 wasm::SuspenderObjectData::offsetOfSuspendableExitFP()),
9752 ScratchReg2);
9753 masm.loadPtr(
9754 Address(ScratchReg2, wasm::FrameWithInstances::callerInstanceOffset()),
9755 ScratchReg2);
9756 masm.storePtr(ScratchReg2, Address(masm.getStackPointer(),
9757 WasmCallerInstanceOffsetBeforeCall));
9758
9759 // Load RA from suspendable stack exit frame.
9760 masm.loadPtr(Address(SuspenderDataReg,
9761 wasm::SuspenderObjectData::offsetOfSuspendableExitFP()),
9762 ScratchReg1);
9763 masm.loadPtr(Address(ScratchReg1, wasm::Frame::returnAddressOffset()),
9764 ReturnAddressReg);
9765
9766 // Call wasm function fast.
9767# ifdef JS_USE_LINK_REGISTER
9768 masm.mov(ReturnAddressReg, lr);
9769# else
9770 masm.Push(ReturnAddressReg);
9771# endif
9772 // Get funcUncheckedCallEntry() from the function's
9773 // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot.
9774 size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot(
9775 FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT);
9776 masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2);
9777 masm.jump(ScratchReg2);
9778
9779 // About to use valid FramePointer -- restore framePushed.
9780 masm.setFramePushed(framePushed);
9781
9782 // For IsPlausibleStackMapKey check for the following callsite.
9783 masm.wasmTrapInstruction();
9784
9785 // Callsite for return from suspendable stack.
9786 masm.bind(&returnCallsite);
9787 masm.append(desc, *returnCallsite.target());
9788 masm.addCodeLabel(returnCallsite);
9789
9790 masm.assertStackAlignment(WasmStackAlignment);
9791
9792 markSafepointAt(returnCallsite.target()->offset(), lir);
9793 lir->safepoint()->setFramePushedAtStackMapBase(framePushed);
9794 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
9795 // Rooting SuspenderReg.
9796 masm.propagateOOM(
9797 lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender));
9798
9799 masm.freeStackTo(framePushed);
9800
9801 masm.freeStack(reserve);
9802 masm.Pop(InstanceReg);
9803 masm.Pop(SuspenderReg);
9804
9805 masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2);
9806
9807 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Resume,
9808 SuspenderReg, ScratchReg1);
9809#else
9810 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9810); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do {
*((volatile int*)__null) = 9810; __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
9811#endif // ENABLE_WASM_JSPI
9812}
9813
9814void CodeGenerator::visitWasmStackContinueOnSuspendable(
9815 LWasmStackContinueOnSuspendable* lir) {
9816#ifdef ENABLE_WASM_JSPI1
9817 const Register SuspenderReg = lir->suspender()->toRegister().gpr();
9818 const Register SuspenderDataReg = ABINonArgReg3;
9819
9820# ifdef JS_CODEGEN_ARM64
9821 vixl::UseScratchRegisterScope temps(&masm);
9822 const Register ScratchReg1 = temps.AcquireX().asUnsized();
9823# elif defined(JS_CODEGEN_X86)
9824 const Register ScratchReg1 = ABINonArgReg2;
9825# elif defined(JS_CODEGEN_X641)
9826 const Register ScratchReg1 = ScratchReg;
9827# elif defined(JS_CODEGEN_ARM)
9828 const Register ScratchReg1 = ABINonArgReturnVolatileReg;
9829# else
9830# error "NYI: scratch register"
9831# endif
9832 const Register ScratchReg2 = ABINonArgReg1;
9833
9834 masm.Push(SuspenderReg);
9835 int32_t framePushedAtSuspender = masm.framePushed();
9836 masm.Push(InstanceReg);
9837
9838 wasm::CallSiteDesc desc(wasm::CallSiteDesc::Kind::StackSwitch);
9839 CodeLabel returnCallsite;
9840
9841 // Aligning stack before trampoline call.
9842 uint32_t reserve = ComputeByteAlignment(
9843 masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment);
9844 masm.reserveStack(reserve);
9845
9846 masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset(
9847 wasm::SuspenderObjectDataSlot)),
9848 SuspenderDataReg);
9849 masm.storeStackPtr(
9850 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP()));
9851 masm.storePtr(
9852 FramePointer,
9853 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()));
9854
9855 // Adjust exit frame FP.
9856 masm.loadPtr(Address(SuspenderDataReg,
9857 wasm::SuspenderObjectData::offsetOfSuspendableExitFP()),
9858 ScratchReg1);
9859 masm.storePtr(FramePointer,
9860 Address(ScratchReg1, wasm::Frame::callerFPOffset()));
9861
9862 // Adjust exit frame RA.
9863 masm.mov(&returnCallsite, ScratchReg2);
9864
9865 masm.storePtr(ScratchReg2,
9866 Address(ScratchReg1, wasm::Frame::returnAddressOffset()));
9867 // Adjust exit frame caller instance slot.
9868 masm.storePtr(
9869 InstanceReg,
9870 Address(ScratchReg1, wasm::FrameWithInstances::callerInstanceOffset()));
9871
9872 // Switch stacks to suspendable.
9873 masm.loadStackPtr(Address(
9874 SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP()));
9875 masm.loadPtr(Address(SuspenderDataReg,
9876 wasm::SuspenderObjectData::offsetOfSuspendableFP()),
9877 FramePointer);
9878
9879 masm.assertStackAlignment(WasmStackAlignment);
9880
9881 // The FramePointer is pointing to the same
9882 // place as before switch happened.
9883 uint32_t framePushed = masm.framePushed();
9884
9885 // On different stack, reset framePushed. FramePointer is not valid here.
9886 masm.setFramePushed(0);
9887
9888 // Restore shadow stack area and instance slots.
9889 WasmABIArgGenerator abi;
9890 unsigned reserveBeforeCall = abi.stackBytesConsumedSoFar();
9891 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0"
")"); do { *((volatile int*)__null) = 9891; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9892 unsigned argDecrement =
9893 StackDecrementForCall(WasmStackAlignment, 0, reserveBeforeCall);
9894 masm.reserveStack(argDecrement);
9895
9896 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
9897 WasmCallerInstanceOffsetBeforeCall));
9898 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
9899 WasmCalleeInstanceOffsetBeforeCall));
9900
9901 masm.assertStackAlignment(WasmStackAlignment);
9902
9903 const Register ReturnAddressReg = ScratchReg1;
9904
9905 // Pretend we just returned from the function.
9906 masm.loadPtr(
9907 Address(SuspenderDataReg,
9908 wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()),
9909 ReturnAddressReg);
9910 masm.jump(ReturnAddressReg);
9911
9912 // About to use valid FramePointer -- restore framePushed.
9913 masm.setFramePushed(framePushed);
9914
9915 // For IsPlausibleStackMapKey check for the following callsite.
9916 masm.wasmTrapInstruction();
9917
9918 // Callsite for return from suspendable stack.
9919 masm.bind(&returnCallsite);
9920 masm.append(desc, *returnCallsite.target());
9921 masm.addCodeLabel(returnCallsite);
9922
9923 masm.assertStackAlignment(WasmStackAlignment);
9924
9925 markSafepointAt(returnCallsite.target()->offset(), lir);
9926 lir->safepoint()->setFramePushedAtStackMapBase(framePushed);
9927 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
9928 // Rooting SuspenderReg.
9929 masm.propagateOOM(
9930 lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender));
9931
9932 masm.freeStackTo(framePushed);
9933
9934 masm.freeStack(reserve);
9935 masm.Pop(InstanceReg);
9936 masm.Pop(SuspenderReg);
9937
9938 // Using SuspenderDataReg and ABINonArgReg2 as temps.
9939 masm.switchToWasmInstanceRealm(SuspenderDataReg, ABINonArgReg2);
9940
9941 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave,
9942 SuspenderReg, ScratchReg1);
9943#else
9944 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9944); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do {
*((volatile int*)__null) = 9944; __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
9945#endif // ENABLE_WASM_JSPI
9946}
9947
9948void CodeGenerator::visitWasmCallLandingPrePad(LWasmCallLandingPrePad* lir) {
9949 LBlock* block = lir->block();
9950 MWasmCallLandingPrePad* mir = lir->mir();
9951 MBasicBlock* mirBlock = mir->block();
9952 MBasicBlock* callMirBlock = mir->callBlock();
9953
9954 // This block must be the pre-pad successor of the call block. No blocks may
9955 // be inserted between us, such as for critical edge splitting.
9956 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9957); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
")"); do { *((volatile int*)__null) = 9957; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9957 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9957); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
")"); do { *((volatile int*)__null) = 9957; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9958
9959 // This instruction or a move group must be the first instruction in the
9960 // block. No other instructions may be inserted.
9961 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9962); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
")"); do { *((volatile int*)__null) = 9962; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9962 *(++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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 9962); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
")"); do { *((volatile int*)__null) = 9962; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9963
9964 wasm::TryNoteVector& tryNotes = masm.tryNotes();
9965 wasm::TryNote& tryNote = tryNotes[mir->tryNoteIndex()];
9966 // Set the entry point for the call try note to be the beginning of this
9967 // block. The above assertions (and assertions in visitWasmCall) guarantee
9968 // that we are not skipping over instructions that should be executed.
9969 tryNote.setLandingPad(block->label()->offset(), masm.framePushed());
9970}
9971
9972void CodeGenerator::visitWasmCallIndirectAdjunctSafepoint(
9973 LWasmCallIndirectAdjunctSafepoint* lir) {
9974 markSafepointAt(lir->safepointLocation().offset(), lir);
9975 lir->safepoint()->setFramePushedAtStackMapBase(
9976 lir->framePushedAtStackMapBase());
9977}
9978
9979template <typename InstructionWithMaybeTrapSite>
9980void EmitSignalNullCheckTrapSite(MacroAssembler& masm,
9981 InstructionWithMaybeTrapSite* ins,
9982 FaultingCodeOffset fco,
9983 wasm::TrapMachineInsn tmi) {
9984 if (!ins->maybeTrap()) {
9985 return;
9986 }
9987 wasm::BytecodeOffset trapOffset(ins->maybeTrap()->offset);
9988 masm.append(wasm::Trap::NullPointerDereference,
9989 wasm::TrapSite(tmi, fco, trapOffset));
9990}
9991
9992template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex>
9993void CodeGenerator::emitWasmValueLoad(InstructionWithMaybeTrapSite* ins,
9994 MIRType type, MWideningOp wideningOp,
9995 AddressOrBaseIndex addr,
9996 AnyRegister dst) {
9997 FaultingCodeOffset fco;
9998 switch (type) {
9999 case MIRType::Int32:
10000 switch (wideningOp) {
10001 case MWideningOp::None:
10002 fco = masm.load32(addr, dst.gpr());
10003 EmitSignalNullCheckTrapSite(masm, ins, fco,
10004 wasm::TrapMachineInsn::Load32);
10005 break;
10006 case MWideningOp::FromU16:
10007 fco = masm.load16ZeroExtend(addr, dst.gpr());
10008 EmitSignalNullCheckTrapSite(masm, ins, fco,
10009 wasm::TrapMachineInsn::Load16);
10010 break;
10011 case MWideningOp::FromS16:
10012 fco = masm.load16SignExtend(addr, dst.gpr());
10013 EmitSignalNullCheckTrapSite(masm, ins, fco,
10014 wasm::TrapMachineInsn::Load16);
10015 break;
10016 case MWideningOp::FromU8:
10017 fco = masm.load8ZeroExtend(addr, dst.gpr());
10018 EmitSignalNullCheckTrapSite(masm, ins, fco,
10019 wasm::TrapMachineInsn::Load8);
10020 break;
10021 case MWideningOp::FromS8:
10022 fco = masm.load8SignExtend(addr, dst.gpr());
10023 EmitSignalNullCheckTrapSite(masm, ins, fco,
10024 wasm::TrapMachineInsn::Load8);
10025 break;
10026 default:
10027 MOZ_CRASH("unexpected widening op in ::visitWasmLoadElement")do { do { } while (false); MOZ_ReportCrash("" "unexpected widening op in ::visitWasmLoadElement"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10027); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected widening op in ::visitWasmLoadElement"
")"); do { *((volatile int*)__null) = 10027; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10028 }
10029 break;
10030 case MIRType::Float32:
10031 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10031); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10031; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10032 fco = masm.loadFloat32(addr, dst.fpu());
10033 EmitSignalNullCheckTrapSite(masm, ins, fco,
10034 wasm::TrapMachineInsn::Load32);
10035 break;
10036 case MIRType::Double:
10037 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10037); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10037; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10038 fco = masm.loadDouble(addr, dst.fpu());
10039 EmitSignalNullCheckTrapSite(masm, ins, fco,
10040 wasm::TrapMachineInsn::Load64);
10041 break;
10042 case MIRType::Pointer:
10043 case MIRType::WasmAnyRef:
10044 case MIRType::WasmArrayData:
10045 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10045); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10045; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10046 fco = masm.loadPtr(addr, dst.gpr());
10047 EmitSignalNullCheckTrapSite(masm, ins, fco,
10048 wasm::TrapMachineInsnForLoadWord());
10049 break;
10050 default:
10051 MOZ_CRASH("unexpected type in ::emitWasmValueLoad")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueLoad"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10051); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueLoad"
")"); do { *((volatile int*)__null) = 10051; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10052 }
10053}
10054
10055template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex>
10056void CodeGenerator::emitWasmValueStore(InstructionWithMaybeTrapSite* ins,
10057 MIRType type, MNarrowingOp narrowingOp,
10058 AnyRegister src,
10059 AddressOrBaseIndex addr) {
10060 FaultingCodeOffset fco;
10061 switch (type) {
10062 case MIRType::Int32:
10063 switch (narrowingOp) {
10064 case MNarrowingOp::None:
10065 fco = masm.store32(src.gpr(), addr);
10066 EmitSignalNullCheckTrapSite(masm, ins, fco,
10067 wasm::TrapMachineInsn::Store32);
10068 break;
10069 case MNarrowingOp::To16:
10070 fco = masm.store16(src.gpr(), addr);
10071 EmitSignalNullCheckTrapSite(masm, ins, fco,
10072 wasm::TrapMachineInsn::Store16);
10073 break;
10074 case MNarrowingOp::To8:
10075 fco = masm.store8(src.gpr(), addr);
10076 EmitSignalNullCheckTrapSite(masm, ins, fco,
10077 wasm::TrapMachineInsn::Store8);
10078 break;
10079 default:
10080 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10080); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 10080; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
10081 }
10082 break;
10083 case MIRType::Float32:
10084 fco = masm.storeFloat32(src.fpu(), addr);
10085 EmitSignalNullCheckTrapSite(masm, ins, fco,
10086 wasm::TrapMachineInsn::Store32);
10087 break;
10088 case MIRType::Double:
10089 fco = masm.storeDouble(src.fpu(), addr);
10090 EmitSignalNullCheckTrapSite(masm, ins, fco,
10091 wasm::TrapMachineInsn::Store64);
10092 break;
10093 case MIRType::Pointer:
10094 // This could be correct, but it would be a new usage, so check carefully.
10095 MOZ_CRASH("Unexpected type in ::emitWasmValueStore.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type in ::emitWasmValueStore."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10095); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected type in ::emitWasmValueStore."
")"); do { *((volatile int*)__null) = 10095; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10096 case MIRType::WasmAnyRef:
10097 MOZ_CRASH("Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef.")do { do { } while (false); MOZ_ReportCrash("" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10097); AnnotateMozCrashReason("MOZ_CRASH(" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef."
")"); do { *((volatile int*)__null) = 10097; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10098 default:
10099 MOZ_CRASH("unexpected type in ::emitWasmValueStore")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueStore"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10099); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueStore"
")"); do { *((volatile int*)__null) = 10099; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10100 }
10101}
10102
10103void CodeGenerator::visitWasmLoadSlot(LWasmLoadSlot* ins) {
10104 MIRType type = ins->type();
10105 MWideningOp wideningOp = ins->wideningOp();
10106 Register container = ToRegister(ins->containerRef());
10107 Address addr(container, ins->offset());
10108 AnyRegister dst = ToAnyRegister(ins->output());
10109
10110#ifdef ENABLE_WASM_SIMD1
10111 if (type == MIRType::Simd128) {
10112 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10112); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10112; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10113 FaultingCodeOffset fco = masm.loadUnalignedSimd128(addr, dst.fpu());
10114 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128);
10115 return;
10116 }
10117#endif
10118 emitWasmValueLoad(ins, type, wideningOp, addr, dst);
10119}
10120
10121void CodeGenerator::visitWasmLoadElement(LWasmLoadElement* ins) {
10122 MIRType type = ins->type();
10123 MWideningOp wideningOp = ins->wideningOp();
10124 Scale scale = ins->scale();
10125 Register base = ToRegister(ins->base());
10126 Register index = ToRegister(ins->index());
10127 AnyRegister dst = ToAnyRegister(ins->output());
10128
10129#ifdef ENABLE_WASM_SIMD1
10130 if (type == MIRType::Simd128) {
10131 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10131); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10131; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10132 FaultingCodeOffset fco;
10133 Register temp = ToRegister(ins->temp0());
10134 masm.movePtr(index, temp);
10135 masm.lshiftPtr(Imm32(4), temp);
10136 fco = masm.loadUnalignedSimd128(BaseIndex(base, temp, Scale::TimesOne),
10137 dst.fpu());
10138 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128);
10139 return;
10140 }
10141#endif
10142 emitWasmValueLoad(ins, type, wideningOp, BaseIndex(base, index, scale), dst);
10143}
10144
10145void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) {
10146 MIRType type = ins->type();
10147 MNarrowingOp narrowingOp = ins->narrowingOp();
10148 Register container = ToRegister(ins->containerRef());
10149 Address addr(container, ins->offset());
10150 AnyRegister src = ToAnyRegister(ins->value());
10151 if (type != MIRType::Int32) {
10152 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10152); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 10152; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10153 }
10154
10155#ifdef ENABLE_WASM_SIMD1
10156 if (type == MIRType::Simd128) {
10157 FaultingCodeOffset fco = masm.storeUnalignedSimd128(src.fpu(), addr);
10158 EmitSignalNullCheckTrapSite(masm, ins, fco,
10159 wasm::TrapMachineInsn::Store128);
10160 return;
10161 }
10162#endif
10163 emitWasmValueStore(ins, type, narrowingOp, src, addr);
10164}
10165
10166void CodeGenerator::visitWasmStoreElement(LWasmStoreElement* ins) {
10167 MIRType type = ins->type();
10168 MNarrowingOp narrowingOp = ins->narrowingOp();
10169 Scale scale = ins->scale();
10170 Register base = ToRegister(ins->base());
10171 Register index = ToRegister(ins->index());
10172 AnyRegister src = ToAnyRegister(ins->value());
10173 if (type != MIRType::Int32) {
10174 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10174); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 10174; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10175 }
10176
10177#ifdef ENABLE_WASM_SIMD1
10178 if (type == MIRType::Simd128) {
10179 Register temp = ToRegister(ins->temp0());
10180 masm.movePtr(index, temp);
10181 masm.lshiftPtr(Imm32(4), temp);
10182 FaultingCodeOffset fco = masm.storeUnalignedSimd128(
10183 src.fpu(), BaseIndex(base, temp, Scale::TimesOne));
10184 EmitSignalNullCheckTrapSite(masm, ins, fco,
10185 wasm::TrapMachineInsn::Store128);
10186 return;
10187 }
10188#endif
10189 emitWasmValueStore(ins, type, narrowingOp, src,
10190 BaseIndex(base, index, scale));
10191}
10192
10193void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) {
10194 Register elements = ToRegister(ins->elements());
10195 Register index = ToRegister(ins->index());
10196 Register output = ToRegister(ins->output());
10197 masm.loadPtr(BaseIndex(elements, index, ScalePointer), output);
10198}
10199
10200void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) {
10201 masm.movePtr(ToRegister(ins->base()), ToRegister(ins->output()));
10202 masm.addPtr(Imm32(int32_t(ins->offset())), ToRegister(ins->output()));
10203}
10204
10205void CodeGenerator::visitWasmDerivedIndexPointer(
10206 LWasmDerivedIndexPointer* ins) {
10207 Register base = ToRegister(ins->base());
10208 Register index = ToRegister(ins->index());
10209 Register output = ToRegister(ins->output());
10210 masm.computeEffectiveAddress(BaseIndex(base, index, ins->scale()), output);
10211}
10212
10213void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) {
10214 Register instance = ToRegister(ins->instance());
10215 Register valueBase = ToRegister(ins->valueBase());
10216 size_t offset = ins->offset();
10217 Register value = ToRegister(ins->value());
10218 Register temp = ToRegister(ins->temp0());
10219
10220 if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) {
10221 Label skipPreBarrier;
10222 wasm::EmitWasmPreBarrierGuard(
10223 masm, instance, temp, Address(valueBase, offset), &skipPreBarrier,
10224 ins->maybeTrap() ? &ins->maybeTrap()->offset : nullptr);
10225 wasm::EmitWasmPreBarrierCallImmediate(masm, instance, temp, valueBase,
10226 offset);
10227 masm.bind(&skipPreBarrier);
10228 }
10229
10230 FaultingCodeOffset fco = masm.storePtr(value, Address(valueBase, offset));
10231 EmitSignalNullCheckTrapSite(masm, ins, fco,
10232 wasm::TrapMachineInsnForStoreWord());
10233 // The postbarrier is handled separately.
10234}
10235
10236void CodeGenerator::visitWasmStoreElementRef(LWasmStoreElementRef* ins) {
10237 Register instance = ToRegister(ins->instance());
10238 Register base = ToRegister(ins->base());
10239 Register index = ToRegister(ins->index());
10240 Register value = ToRegister(ins->value());
10241 Register temp0 = ToTempRegisterOrInvalid(ins->temp0());
10242 Register temp1 = ToTempRegisterOrInvalid(ins->temp1());
10243
10244 BaseIndex addr(base, index, ScalePointer);
10245
10246 if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) {
10247 Label skipPreBarrier;
10248 wasm::EmitWasmPreBarrierGuard(
10249 masm, instance, temp0, addr, &skipPreBarrier,
10250 ins->maybeTrap() ? &ins->maybeTrap()->offset : nullptr);
10251 wasm::EmitWasmPreBarrierCallIndex(masm, instance, temp0, temp1, addr);
10252 masm.bind(&skipPreBarrier);
10253 }
10254
10255 FaultingCodeOffset fco = masm.storePtr(value, addr);
10256 EmitSignalNullCheckTrapSite(masm, ins, fco,
10257 wasm::TrapMachineInsnForStoreWord());
10258 // The postbarrier is handled separately.
10259}
10260
10261// Out-of-line path to update the store buffer for wasm references.
10262class OutOfLineWasmCallPostWriteBarrierImmediate
10263 : public OutOfLineCodeBase<CodeGenerator> {
10264 LInstruction* lir_;
10265 Register valueBase_;
10266 Register temp_;
10267 uint32_t valueOffset_;
10268
10269 public:
10270 OutOfLineWasmCallPostWriteBarrierImmediate(LInstruction* lir,
10271 Register valueBase, Register temp,
10272 uint32_t valueOffset)
10273 : lir_(lir),
10274 valueBase_(valueBase),
10275 temp_(temp),
10276 valueOffset_(valueOffset) {}
10277
10278 void accept(CodeGenerator* codegen) override {
10279 codegen->visitOutOfLineWasmCallPostWriteBarrierImmediate(this);
10280 }
10281
10282 LInstruction* lir() const { return lir_; }
10283 Register valueBase() const { return valueBase_; }
10284 Register temp() const { return temp_; }
10285 uint32_t valueOffset() const { return valueOffset_; }
10286};
10287
10288void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierImmediate(
10289 OutOfLineWasmCallPostWriteBarrierImmediate* ool) {
10290 saveLiveVolatile(ool->lir());
10291 masm.Push(InstanceReg);
10292 int32_t framePushedAfterInstance = masm.framePushed();
10293
10294 // Fold the value offset into the value base
10295 Register valueAddr = ool->valueBase();
10296 Register temp = ool->temp();
10297 masm.computeEffectiveAddress(Address(valueAddr, ool->valueOffset()), temp);
10298
10299 // Call Instance::postBarrier
10300 masm.setupWasmABICall();
10301 masm.passABIArg(InstanceReg);
10302 masm.passABIArg(temp);
10303 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
10304 masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier,
10305 mozilla::Some(instanceOffset), ABIType::General);
10306
10307 masm.Pop(InstanceReg);
10308 restoreLiveVolatile(ool->lir());
10309
10310 masm.jump(ool->rejoin());
10311}
10312
10313void CodeGenerator::visitWasmPostWriteBarrierImmediate(
10314 LWasmPostWriteBarrierImmediate* lir) {
10315 Register object = ToRegister(lir->object());
10316 Register value = ToRegister(lir->value());
10317 Register valueBase = ToRegister(lir->valueBase());
10318 Register temp = ToRegister(lir->temp0());
10319 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10319); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg"
")"); do { *((volatile int*)__null) = 10319; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10320 auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierImmediate(
10321 lir, valueBase, temp, lir->valueOffset());
10322 addOutOfLineCode(ool, lir->mir());
10323
10324 wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value,
10325 ool->rejoin());
10326 masm.jump(ool->entry());
10327 masm.bind(ool->rejoin());
10328}
10329
10330// Out-of-line path to update the store buffer for wasm references.
10331class OutOfLineWasmCallPostWriteBarrierIndex
10332 : public OutOfLineCodeBase<CodeGenerator> {
10333 LInstruction* lir_;
10334 Register valueBase_;
10335 Register index_;
10336 Register temp_;
10337 uint32_t elemSize_;
10338
10339 public:
10340 OutOfLineWasmCallPostWriteBarrierIndex(LInstruction* lir, Register valueBase,
10341 Register index, Register temp,
10342 uint32_t elemSize)
10343 : lir_(lir),
10344 valueBase_(valueBase),
10345 index_(index),
10346 temp_(temp),
10347 elemSize_(elemSize) {
10348 MOZ_ASSERT(elemSize == 1 || elemSize == 2 || elemSize == 4 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize
== 8 || elemSize == 16)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(elemSize == 1 || elemSize ==
2 || elemSize == 4 || elemSize == 8 || elemSize == 16))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 10349; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10349 elemSize == 8 || elemSize == 16)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize
== 8 || elemSize == 16)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(elemSize == 1 || elemSize ==
2 || elemSize == 4 || elemSize == 8 || elemSize == 16))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 10349; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10350 }
10351
10352 void accept(CodeGenerator* codegen) override {
10353 codegen->visitOutOfLineWasmCallPostWriteBarrierIndex(this);
10354 }
10355
10356 LInstruction* lir() const { return lir_; }
10357 Register valueBase() const { return valueBase_; }
10358 Register index() const { return index_; }
10359 Register temp() const { return temp_; }
10360 uint32_t elemSize() const { return elemSize_; }
10361};
10362
10363void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierIndex(
10364 OutOfLineWasmCallPostWriteBarrierIndex* ool) {
10365 saveLiveVolatile(ool->lir());
10366 masm.Push(InstanceReg);
10367 int32_t framePushedAfterInstance = masm.framePushed();
10368
10369 // Fold the value offset into the value base
10370 Register temp = ool->temp();
10371 if (ool->elemSize() == 16) {
10372 masm.movePtr(ool->index(), temp);
10373 masm.lshiftPtr(Imm32(4), temp);
10374 masm.addPtr(ool->valueBase(), temp);
10375 } else {
10376 masm.computeEffectiveAddress(BaseIndex(ool->valueBase(), ool->index(),
10377 ScaleFromElemWidth(ool->elemSize())),
10378 temp);
10379 }
10380
10381 // Call Instance::postBarrier
10382 masm.setupWasmABICall();
10383 masm.passABIArg(InstanceReg);
10384 masm.passABIArg(temp);
10385 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
10386 masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier,
10387 mozilla::Some(instanceOffset), ABIType::General);
10388
10389 masm.Pop(InstanceReg);
10390 restoreLiveVolatile(ool->lir());
10391
10392 masm.jump(ool->rejoin());
10393}
10394
10395void CodeGenerator::visitWasmPostWriteBarrierIndex(
10396 LWasmPostWriteBarrierIndex* lir) {
10397 Register object = ToRegister(lir->object());
10398 Register value = ToRegister(lir->value());
10399 Register valueBase = ToRegister(lir->valueBase());
10400 Register index = ToRegister(lir->index());
10401 Register temp = ToRegister(lir->temp0());
10402 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10402); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg"
")"); do { *((volatile int*)__null) = 10402; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10403 auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierIndex(
10404 lir, valueBase, index, temp, lir->elemSize());
10405 addOutOfLineCode(ool, lir->mir());
10406
10407 wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value,
10408 ool->rejoin());
10409 masm.jump(ool->entry());
10410 masm.bind(ool->rejoin());
10411}
10412
10413void CodeGenerator::visitWasmLoadSlotI64(LWasmLoadSlotI64* ins) {
10414 Register container = ToRegister(ins->containerRef());
10415 Address addr(container, ins->offset());
10416 Register64 output = ToOutRegister64(ins);
10417 // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one
10418 // transaction will always trap before the other, so it seems safest to
10419 // register both of them as potentially trapping.
10420#ifdef JS_64BIT1
10421 FaultingCodeOffset fco = masm.load64(addr, output);
10422 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64);
10423#else
10424 FaultingCodeOffsetPair fcop = masm.load64(addr, output);
10425 EmitSignalNullCheckTrapSite(masm, ins, fcop.first,
10426 wasm::TrapMachineInsn::Load32);
10427 EmitSignalNullCheckTrapSite(masm, ins, fcop.second,
10428 wasm::TrapMachineInsn::Load32);
10429#endif
10430}
10431
10432void CodeGenerator::visitWasmLoadElementI64(LWasmLoadElementI64* ins) {
10433 Register base = ToRegister(ins->base());
10434 Register index = ToRegister(ins->index());
10435 BaseIndex addr(base, index, Scale::TimesEight);
10436 Register64 output = ToOutRegister64(ins);
10437 // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one
10438 // transaction will always trap before the other, so it seems safest to
10439 // register both of them as potentially trapping.
10440#ifdef JS_64BIT1
10441 FaultingCodeOffset fco = masm.load64(addr, output);
10442 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64);
10443#else
10444 FaultingCodeOffsetPair fcop = masm.load64(addr, output);
10445 EmitSignalNullCheckTrapSite(masm, ins, fcop.first,
10446 wasm::TrapMachineInsn::Load32);
10447 EmitSignalNullCheckTrapSite(masm, ins, fcop.second,
10448 wasm::TrapMachineInsn::Load32);
10449#endif
10450}
10451
10452void CodeGenerator::visitWasmStoreSlotI64(LWasmStoreSlotI64* ins) {
10453 Register container = ToRegister(ins->containerRef());
10454 Address addr(container, ins->offset());
10455 Register64 value = ToRegister64(ins->value());
10456 // Either 1 or 2 words. As above we register both transactions in the
10457 // 2-word case.
10458#ifdef JS_64BIT1
10459 FaultingCodeOffset fco = masm.store64(value, addr);
10460 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64);
10461#else
10462 FaultingCodeOffsetPair fcop = masm.store64(value, addr);
10463 EmitSignalNullCheckTrapSite(masm, ins, fcop.first,
10464 wasm::TrapMachineInsn::Store32);
10465 EmitSignalNullCheckTrapSite(masm, ins, fcop.second,
10466 wasm::TrapMachineInsn::Store32);
10467#endif
10468}
10469
10470void CodeGenerator::visitWasmStoreElementI64(LWasmStoreElementI64* ins) {
10471 Register base = ToRegister(ins->base());
10472 Register index = ToRegister(ins->index());
10473 BaseIndex addr(base, index, Scale::TimesEight);
10474 Register64 value = ToRegister64(ins->value());
10475 // Either 1 or 2 words. As above we register both transactions in the
10476 // 2-word case.
10477#ifdef JS_64BIT1
10478 FaultingCodeOffset fco = masm.store64(value, addr);
10479 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64);
10480#else
10481 FaultingCodeOffsetPair fcop = masm.store64(value, addr);
10482 EmitSignalNullCheckTrapSite(masm, ins, fcop.first,
10483 wasm::TrapMachineInsn::Store32);
10484 EmitSignalNullCheckTrapSite(masm, ins, fcop.second,
10485 wasm::TrapMachineInsn::Store32);
10486#endif
10487}
10488
10489void CodeGenerator::visitWasmClampTable64Index(LWasmClampTable64Index* lir) {
10490#ifdef ENABLE_WASM_MEMORY641
10491 Register64 index = ToRegister64(lir->index());
10492 Register out = ToRegister(lir->output());
10493 masm.wasmClampTable64Index(index, out);
10494#else
10495 MOZ_CRASH("table64 indexes should not be valid without memory64")do { do { } while (false); MOZ_ReportCrash("" "table64 indexes should not be valid without memory64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10495); AnnotateMozCrashReason("MOZ_CRASH(" "table64 indexes should not be valid without memory64"
")"); do { *((volatile int*)__null) = 10495; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10496#endif
10497}
10498
10499void CodeGenerator::visitArrayBufferByteLength(LArrayBufferByteLength* lir) {
10500 Register obj = ToRegister(lir->object());
10501 Register out = ToRegister(lir->output());
10502 masm.loadArrayBufferByteLengthIntPtr(obj, out);
10503}
10504
10505void CodeGenerator::visitArrayBufferViewLength(LArrayBufferViewLength* lir) {
10506 Register obj = ToRegister(lir->object());
10507 Register out = ToRegister(lir->output());
10508 masm.loadArrayBufferViewLengthIntPtr(obj, out);
10509}
10510
10511void CodeGenerator::visitArrayBufferViewByteOffset(
10512 LArrayBufferViewByteOffset* lir) {
10513 Register obj = ToRegister(lir->object());
10514 Register out = ToRegister(lir->output());
10515 masm.loadArrayBufferViewByteOffsetIntPtr(obj, out);
10516}
10517
10518void CodeGenerator::visitArrayBufferViewElements(
10519 LArrayBufferViewElements* lir) {
10520 Register obj = ToRegister(lir->object());
10521 Register out = ToRegister(lir->output());
10522 masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), out);
10523}
10524
10525void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) {
10526 Register obj = ToRegister(lir->object());
10527 Register out = ToRegister(lir->output());
10528
10529 masm.typedArrayElementSize(obj, out);
10530}
10531
10532void CodeGenerator::visitResizableTypedArrayByteOffsetMaybeOutOfBounds(
10533 LResizableTypedArrayByteOffsetMaybeOutOfBounds* lir) {
10534 Register obj = ToRegister(lir->object());
10535 Register out = ToRegister(lir->output());
10536 Register temp = ToRegister(lir->temp0());
10537
10538 masm.loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr(obj, out, temp);
10539}
10540
10541void CodeGenerator::visitResizableTypedArrayLength(
10542 LResizableTypedArrayLength* lir) {
10543 Register obj = ToRegister(lir->object());
10544 Register out = ToRegister(lir->output());
10545 Register temp = ToRegister(lir->temp0());
10546
10547 masm.loadResizableTypedArrayLengthIntPtr(lir->synchronization(), obj, out,
10548 temp);
10549}
10550
10551void CodeGenerator::visitResizableDataViewByteLength(
10552 LResizableDataViewByteLength* lir) {
10553 Register obj = ToRegister(lir->object());
10554 Register out = ToRegister(lir->output());
10555 Register temp = ToRegister(lir->temp0());
10556
10557 masm.loadResizableDataViewByteLengthIntPtr(lir->synchronization(), obj, out,
10558 temp);
10559}
10560
10561void CodeGenerator::visitGrowableSharedArrayBufferByteLength(
10562 LGrowableSharedArrayBufferByteLength* lir) {
10563 Register obj = ToRegister(lir->object());
10564 Register out = ToRegister(lir->output());
10565
10566 // Explicit |byteLength| accesses are seq-consistent atomic loads.
10567 auto sync = Synchronization::Load();
10568
10569 masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out);
10570}
10571
10572void CodeGenerator::visitGuardResizableArrayBufferViewInBounds(
10573 LGuardResizableArrayBufferViewInBounds* lir) {
10574 Register obj = ToRegister(lir->object());
10575 Register temp = ToRegister(lir->temp0());
10576
10577 Label bail;
10578 masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail);
10579 bailoutFrom(&bail, lir->snapshot());
10580}
10581
10582void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached(
10583 LGuardResizableArrayBufferViewInBoundsOrDetached* lir) {
10584 Register obj = ToRegister(lir->object());
10585 Register temp = ToRegister(lir->temp0());
10586
10587 Label done, bail;
10588 masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done);
10589 masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail);
10590 masm.bind(&done);
10591 bailoutFrom(&bail, lir->snapshot());
10592}
10593
10594void CodeGenerator::visitGuardHasAttachedArrayBuffer(
10595 LGuardHasAttachedArrayBuffer* lir) {
10596 Register obj = ToRegister(lir->object());
10597 Register temp = ToRegister(lir->temp0());
10598
10599 Label bail;
10600 masm.branchIfHasDetachedArrayBuffer(obj, temp, &bail);
10601 bailoutFrom(&bail, lir->snapshot());
10602}
10603
10604class OutOfLineGuardNumberToIntPtrIndex
10605 : public OutOfLineCodeBase<CodeGenerator> {
10606 LGuardNumberToIntPtrIndex* lir_;
10607
10608 public:
10609 explicit OutOfLineGuardNumberToIntPtrIndex(LGuardNumberToIntPtrIndex* lir)
10610 : lir_(lir) {}
10611
10612 void accept(CodeGenerator* codegen) override {
10613 codegen->visitOutOfLineGuardNumberToIntPtrIndex(this);
10614 }
10615 LGuardNumberToIntPtrIndex* lir() const { return lir_; }
10616};
10617
10618void CodeGenerator::visitGuardNumberToIntPtrIndex(
10619 LGuardNumberToIntPtrIndex* lir) {
10620 FloatRegister input = ToFloatRegister(lir->input());
10621 Register output = ToRegister(lir->output());
10622
10623 if (!lir->mir()->supportOOB()) {
10624 Label bail;
10625 masm.convertDoubleToPtr(input, output, &bail, false);
10626 bailoutFrom(&bail, lir->snapshot());
10627 return;
10628 }
10629
10630 auto* ool = new (alloc()) OutOfLineGuardNumberToIntPtrIndex(lir);
10631 addOutOfLineCode(ool, lir->mir());
10632
10633 masm.convertDoubleToPtr(input, output, ool->entry(), false);
10634 masm.bind(ool->rejoin());
10635}
10636
10637void CodeGenerator::visitOutOfLineGuardNumberToIntPtrIndex(
10638 OutOfLineGuardNumberToIntPtrIndex* ool) {
10639 // Substitute the invalid index with an arbitrary out-of-bounds index.
10640 masm.movePtr(ImmWord(-1), ToRegister(ool->lir()->output()));
10641 masm.jump(ool->rejoin());
10642}
10643
10644void CodeGenerator::visitStringLength(LStringLength* lir) {
10645 Register input = ToRegister(lir->string());
10646 Register output = ToRegister(lir->output());
10647
10648 masm.loadStringLength(input, output);
10649}
10650
10651void CodeGenerator::visitMinMaxI(LMinMaxI* ins) {
10652 Register first = ToRegister(ins->first());
10653 Register output = ToRegister(ins->output());
10654
10655 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "first == output"
")"); do { *((volatile int*)__null) = 10655; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10656
10657 Assembler::Condition cond =
10658 ins->mir()->isMax() ? Assembler::GreaterThan : Assembler::LessThan;
10659
10660 if (ins->second()->isConstant()) {
10661 Label done;
10662 masm.branch32(cond, first, Imm32(ToInt32(ins->second())), &done);
10663 masm.move32(Imm32(ToInt32(ins->second())), output);
10664 masm.bind(&done);
10665 } else {
10666 Register second = ToRegister(ins->second());
10667 masm.cmp32Move32(cond, second, first, second, output);
10668 }
10669}
10670
10671void CodeGenerator::visitMinMaxArrayI(LMinMaxArrayI* ins) {
10672 Register array = ToRegister(ins->array());
10673 Register output = ToRegister(ins->output());
10674 Register temp1 = ToRegister(ins->temp1());
10675 Register temp2 = ToRegister(ins->temp2());
10676 Register temp3 = ToRegister(ins->temp3());
10677 bool isMax = ins->isMax();
10678
10679 Label bail;
10680 masm.minMaxArrayInt32(array, output, temp1, temp2, temp3, isMax, &bail);
10681 bailoutFrom(&bail, ins->snapshot());
10682}
10683
10684void CodeGenerator::visitMinMaxArrayD(LMinMaxArrayD* ins) {
10685 Register array = ToRegister(ins->array());
10686 FloatRegister output = ToFloatRegister(ins->output());
10687 Register temp1 = ToRegister(ins->temp1());
10688 Register temp2 = ToRegister(ins->temp2());
10689 FloatRegister floatTemp = ToFloatRegister(ins->floatTemp());
10690 bool isMax = ins->isMax();
10691
10692 Label bail;
10693 masm.minMaxArrayNumber(array, output, floatTemp, temp1, temp2, isMax, &bail);
10694 bailoutFrom(&bail, ins->snapshot());
10695}
10696
10697// For Abs*, lowering will have tied input to output on platforms where that is
10698// sensible, and otherwise left them untied.
10699
10700void CodeGenerator::visitAbsI(LAbsI* ins) {
10701 Register input = ToRegister(ins->input());
10702 Register output = ToRegister(ins->output());
10703
10704 if (ins->mir()->fallible()) {
10705 Label positive;
10706 if (input != output) {
10707 masm.move32(input, output);
10708 }
10709 masm.branchTest32(Assembler::NotSigned, output, output, &positive);
10710 Label bail;
10711 masm.branchNeg32(Assembler::Overflow, output, &bail);
10712 bailoutFrom(&bail, ins->snapshot());
10713 masm.bind(&positive);
10714 } else {
10715 masm.abs32(input, output);
10716 }
10717}
10718
10719void CodeGenerator::visitAbsD(LAbsD* ins) {
10720 masm.absDouble(ToFloatRegister(ins->input()), ToFloatRegister(ins->output()));
10721}
10722
10723void CodeGenerator::visitAbsF(LAbsF* ins) {
10724 masm.absFloat32(ToFloatRegister(ins->input()),
10725 ToFloatRegister(ins->output()));
10726}
10727
10728void CodeGenerator::visitPowII(LPowII* ins) {
10729 Register value = ToRegister(ins->value());
10730 Register power = ToRegister(ins->power());
10731 Register output = ToRegister(ins->output());
10732 Register temp0 = ToRegister(ins->temp0());
10733 Register temp1 = ToRegister(ins->temp1());
10734
10735 Label bailout;
10736 masm.pow32(value, power, output, temp0, temp1, &bailout);
10737 bailoutFrom(&bailout, ins->snapshot());
10738}
10739
10740void CodeGenerator::visitPowI(LPowI* ins) {
10741 FloatRegister value = ToFloatRegister(ins->value());
10742 Register power = ToRegister(ins->power());
10743
10744 using Fn = double (*)(double x, int32_t y);
10745 masm.setupAlignedABICall();
10746 masm.passABIArg(value, ABIType::Float64);
10747 masm.passABIArg(power);
10748
10749 masm.callWithABI<Fn, js::powi>(ABIType::Float64);
10750 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10750); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 10750; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10751}
10752
10753void CodeGenerator::visitPowD(LPowD* ins) {
10754 FloatRegister value = ToFloatRegister(ins->value());
10755 FloatRegister power = ToFloatRegister(ins->power());
10756
10757 using Fn = double (*)(double x, double y);
10758 masm.setupAlignedABICall();
10759 masm.passABIArg(value, ABIType::Float64);
10760 masm.passABIArg(power, ABIType::Float64);
10761 masm.callWithABI<Fn, ecmaPow>(ABIType::Float64);
10762
10763 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10763); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 10763; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10764}
10765
10766void CodeGenerator::visitPowOfTwoI(LPowOfTwoI* ins) {
10767 Register power = ToRegister(ins->power());
10768 Register output = ToRegister(ins->output());
10769
10770 uint32_t base = ins->base();
10771 MOZ_ASSERT(mozilla::IsPowerOfTwo(base))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mozilla::IsPowerOfTwo(base))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mozilla::IsPowerOfTwo(base))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mozilla::IsPowerOfTwo(base)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10771); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(base)"
")"); do { *((volatile int*)__null) = 10771; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10772
10773 uint32_t n = mozilla::FloorLog2(base);
10774 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10774); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n != 0" ")"
); do { *((volatile int*)__null) = 10774; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
10775
10776 // Hacker's Delight, 2nd edition, theorem D2.
10777 auto ceilingDiv = [](uint32_t x, uint32_t y) { return (x + y - 1) / y; };
10778
10779 // Take bailout if |power| is greater-or-equals |log_y(2^31)| or is negative.
10780 // |2^(n*y) < 2^31| must hold, hence |n*y < 31| resp. |y < 31/n|.
10781 //
10782 // Note: it's important for this condition to match the code in CacheIR.cpp
10783 // (CanAttachInt32Pow) to prevent failure loops.
10784 bailoutCmp32(Assembler::AboveOrEqual, power, Imm32(ceilingDiv(31, n)),
10785 ins->snapshot());
10786
10787 // Compute (2^n)^y as 2^(n*y) using repeated shifts. We could directly scale
10788 // |power| and perform a single shift, but due to the lack of necessary
10789 // MacroAssembler functionality, like multiplying a register with an
10790 // immediate, we restrict the number of generated shift instructions when
10791 // lowering this operation.
10792 masm.move32(Imm32(1), output);
10793 do {
10794 masm.lshift32(power, output);
10795 n--;
10796 } while (n > 0);
10797}
10798
10799void CodeGenerator::visitSqrtD(LSqrtD* ins) {
10800 FloatRegister input = ToFloatRegister(ins->input());
10801 FloatRegister output = ToFloatRegister(ins->output());
10802 masm.sqrtDouble(input, output);
10803}
10804
10805void CodeGenerator::visitSqrtF(LSqrtF* ins) {
10806 FloatRegister input = ToFloatRegister(ins->input());
10807 FloatRegister output = ToFloatRegister(ins->output());
10808 masm.sqrtFloat32(input, output);
10809}
10810
10811void CodeGenerator::visitSignI(LSignI* ins) {
10812 Register input = ToRegister(ins->input());
10813 Register output = ToRegister(ins->output());
10814 masm.signInt32(input, output);
10815}
10816
10817void CodeGenerator::visitSignD(LSignD* ins) {
10818 FloatRegister input = ToFloatRegister(ins->input());
10819 FloatRegister output = ToFloatRegister(ins->output());
10820 masm.signDouble(input, output);
10821}
10822
10823void CodeGenerator::visitSignDI(LSignDI* ins) {
10824 FloatRegister input = ToFloatRegister(ins->input());
10825 FloatRegister temp = ToFloatRegister(ins->temp0());
10826 Register output = ToRegister(ins->output());
10827
10828 Label bail;
10829 masm.signDoubleToInt32(input, output, temp, &bail);
10830 bailoutFrom(&bail, ins->snapshot());
10831}
10832
10833void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) {
10834 FloatRegister input = ToFloatRegister(ins->input());
10835 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10835); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 10835; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10836
10837 UnaryMathFunction fun = ins->mir()->function();
10838 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(fun);
10839
10840 masm.setupAlignedABICall();
10841
10842 masm.passABIArg(input, ABIType::Float64);
10843 masm.callWithABI(DynamicFunction<UnaryMathFunctionType>(funPtr),
10844 ABIType::Float64);
10845}
10846
10847void CodeGenerator::visitMathFunctionF(LMathFunctionF* ins) {
10848 FloatRegister input = ToFloatRegister(ins->input());
10849 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10849); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnFloat32Reg"
")"); do { *((volatile int*)__null) = 10849; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10850
10851 masm.setupAlignedABICall();
10852 masm.passABIArg(input, ABIType::Float32);
10853
10854 using Fn = float (*)(float x);
10855 Fn funptr = nullptr;
10856 CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check;
10857 switch (ins->mir()->function()) {
10858 case UnaryMathFunction::Floor:
10859 funptr = floorf;
10860 check = CheckUnsafeCallWithABI::DontCheckOther;
10861 break;
10862 case UnaryMathFunction::Round:
10863 funptr = math_roundf_impl;
10864 break;
10865 case UnaryMathFunction::Trunc:
10866 funptr = math_truncf_impl;
10867 break;
10868 case UnaryMathFunction::Ceil:
10869 funptr = ceilf;
10870 check = CheckUnsafeCallWithABI::DontCheckOther;
10871 break;
10872 default:
10873 MOZ_CRASH("Unknown or unsupported float32 math function")do { do { } while (false); MOZ_ReportCrash("" "Unknown or unsupported float32 math function"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10873); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown or unsupported float32 math function"
")"); do { *((volatile int*)__null) = 10873; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10874 }
10875
10876 masm.callWithABI(DynamicFunction<Fn>(funptr), ABIType::Float32, check);
10877}
10878
10879void CodeGenerator::visitModD(LModD* ins) {
10880 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10880); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 10880; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10881
10882 FloatRegister lhs = ToFloatRegister(ins->lhs());
10883 FloatRegister rhs = ToFloatRegister(ins->rhs());
10884
10885 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10885); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 10885; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10886
10887 using Fn = double (*)(double a, double b);
10888 masm.setupAlignedABICall();
10889 masm.passABIArg(lhs, ABIType::Float64);
10890 masm.passABIArg(rhs, ABIType::Float64);
10891 masm.callWithABI<Fn, NumberMod>(ABIType::Float64);
10892}
10893
10894void CodeGenerator::visitModPowTwoD(LModPowTwoD* ins) {
10895 FloatRegister lhs = ToFloatRegister(ins->lhs());
10896 uint32_t divisor = ins->divisor();
10897 MOZ_ASSERT(mozilla::IsPowerOfTwo(divisor))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mozilla::IsPowerOfTwo(divisor))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mozilla::IsPowerOfTwo(divisor
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("mozilla::IsPowerOfTwo(divisor)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10897); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(divisor)"
")"); do { *((volatile int*)__null) = 10897; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10898
10899 FloatRegister output = ToFloatRegister(ins->output());
10900
10901 // Compute |n % d| using |copysign(n - (d * trunc(n / d)), n)|.
10902 //
10903 // This doesn't work if |d| isn't a power of two, because we may lose too much
10904 // precision. For example |Number.MAX_VALUE % 3 == 2|, but
10905 // |3 * trunc(Number.MAX_VALUE / 3) == Infinity|.
10906
10907 Label done;
10908 {
10909 ScratchDoubleScope scratch(masm);
10910
10911 // Subnormals can lead to performance degradation, which can make calling
10912 // |fmod| faster than this inline implementation. Work around this issue by
10913 // directly returning the input for any value in the interval ]-1, +1[.
10914 Label notSubnormal;
10915 masm.loadConstantDouble(1.0, scratch);
10916 masm.loadConstantDouble(-1.0, output);
10917 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, lhs, scratch,
10918 &notSubnormal);
10919 masm.branchDouble(Assembler::DoubleLessThanOrEqual, lhs, output,
10920 &notSubnormal);
10921
10922 masm.moveDouble(lhs, output);
10923 masm.jump(&done);
10924
10925 masm.bind(&notSubnormal);
10926
10927 if (divisor == 1) {
10928 // The pattern |n % 1 == 0| is used to detect integer numbers. We can skip
10929 // the multiplication by one in this case.
10930 masm.moveDouble(lhs, output);
10931 masm.nearbyIntDouble(RoundingMode::TowardsZero, output, scratch);
10932 masm.subDouble(scratch, output);
10933 } else {
10934 masm.loadConstantDouble(1.0 / double(divisor), scratch);
10935 masm.loadConstantDouble(double(divisor), output);
10936
10937 masm.mulDouble(lhs, scratch);
10938 masm.nearbyIntDouble(RoundingMode::TowardsZero, scratch, scratch);
10939 masm.mulDouble(output, scratch);
10940
10941 masm.moveDouble(lhs, output);
10942 masm.subDouble(scratch, output);
10943 }
10944 }
10945
10946 masm.copySignDouble(output, lhs, output);
10947 masm.bind(&done);
10948}
10949
10950void CodeGenerator::visitWasmBuiltinModD(LWasmBuiltinModD* ins) {
10951 masm.Push(InstanceReg);
10952 int32_t framePushedAfterInstance = masm.framePushed();
10953
10954 FloatRegister lhs = ToFloatRegister(ins->lhs());
10955 FloatRegister rhs = ToFloatRegister(ins->rhs());
10956
10957 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10957); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 10957; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10958
10959 masm.setupWasmABICall();
10960 masm.passABIArg(lhs, ABIType::Float64);
10961 masm.passABIArg(rhs, ABIType::Float64);
10962
10963 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
10964 masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD,
10965 mozilla::Some(instanceOffset), ABIType::Float64);
10966
10967 masm.Pop(InstanceReg);
10968}
10969
10970void CodeGenerator::visitBigIntAdd(LBigIntAdd* ins) {
10971 Register lhs = ToRegister(ins->lhs());
10972 Register rhs = ToRegister(ins->rhs());
10973 Register temp1 = ToRegister(ins->temp1());
10974 Register temp2 = ToRegister(ins->temp2());
10975 Register output = ToRegister(ins->output());
10976
10977 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
10978 auto* ool = oolCallVM<Fn, BigInt::add>(ins, ArgList(lhs, rhs),
10979 StoreRegisterTo(output));
10980
10981 // 0n + x == x
10982 Label lhsNonZero;
10983 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
10984 masm.movePtr(rhs, output);
10985 masm.jump(ool->rejoin());
10986 masm.bind(&lhsNonZero);
10987
10988 // x + 0n == x
10989 Label rhsNonZero;
10990 masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero);
10991 masm.movePtr(lhs, output);
10992 masm.jump(ool->rejoin());
10993 masm.bind(&rhsNonZero);
10994
10995 // Call into the VM when either operand can't be loaded into a pointer-sized
10996 // register.
10997 masm.loadBigIntNonZero(lhs, temp1, ool->entry());
10998 masm.loadBigIntNonZero(rhs, temp2, ool->entry());
10999
11000 masm.branchAddPtr(Assembler::Overflow, temp2, temp1, ool->entry());
11001
11002 // Create and return the result.
11003 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11004 masm.initializeBigInt(output, temp1);
11005
11006 masm.bind(ool->rejoin());
11007}
11008
11009void CodeGenerator::visitBigIntSub(LBigIntSub* ins) {
11010 Register lhs = ToRegister(ins->lhs());
11011 Register rhs = ToRegister(ins->rhs());
11012 Register temp1 = ToRegister(ins->temp1());
11013 Register temp2 = ToRegister(ins->temp2());
11014 Register output = ToRegister(ins->output());
11015
11016 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11017 auto* ool = oolCallVM<Fn, BigInt::sub>(ins, ArgList(lhs, rhs),
11018 StoreRegisterTo(output));
11019
11020 // x - 0n == x
11021 Label rhsNonZero;
11022 masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero);
11023 masm.movePtr(lhs, output);
11024 masm.jump(ool->rejoin());
11025 masm.bind(&rhsNonZero);
11026
11027 // Call into the VM when either operand can't be loaded into a pointer-sized
11028 // register.
11029 masm.loadBigInt(lhs, temp1, ool->entry());
11030 masm.loadBigIntNonZero(rhs, temp2, ool->entry());
11031
11032 masm.branchSubPtr(Assembler::Overflow, temp2, temp1, ool->entry());
11033
11034 // Create and return the result.
11035 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11036 masm.initializeBigInt(output, temp1);
11037
11038 masm.bind(ool->rejoin());
11039}
11040
11041void CodeGenerator::visitBigIntMul(LBigIntMul* ins) {
11042 Register lhs = ToRegister(ins->lhs());
11043 Register rhs = ToRegister(ins->rhs());
11044 Register temp1 = ToRegister(ins->temp1());
11045 Register temp2 = ToRegister(ins->temp2());
11046 Register output = ToRegister(ins->output());
11047
11048 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11049 auto* ool = oolCallVM<Fn, BigInt::mul>(ins, ArgList(lhs, rhs),
11050 StoreRegisterTo(output));
11051
11052 // 0n * x == 0n
11053 Label lhsNonZero;
11054 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11055 masm.movePtr(lhs, output);
11056 masm.jump(ool->rejoin());
11057 masm.bind(&lhsNonZero);
11058
11059 // x * 0n == 0n
11060 Label rhsNonZero;
11061 masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero);
11062 masm.movePtr(rhs, output);
11063 masm.jump(ool->rejoin());
11064 masm.bind(&rhsNonZero);
11065
11066 // Call into the VM when either operand can't be loaded into a pointer-sized
11067 // register.
11068 masm.loadBigIntNonZero(lhs, temp1, ool->entry());
11069 masm.loadBigIntNonZero(rhs, temp2, ool->entry());
11070
11071 masm.branchMulPtr(Assembler::Overflow, temp2, temp1, ool->entry());
11072
11073 // Create and return the result.
11074 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11075 masm.initializeBigInt(output, temp1);
11076
11077 masm.bind(ool->rejoin());
11078}
11079
11080void CodeGenerator::visitBigIntDiv(LBigIntDiv* ins) {
11081 Register lhs = ToRegister(ins->lhs());
11082 Register rhs = ToRegister(ins->rhs());
11083 Register temp1 = ToRegister(ins->temp1());
11084 Register temp2 = ToRegister(ins->temp2());
11085 Register output = ToRegister(ins->output());
11086
11087 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11088 auto* ool = oolCallVM<Fn, BigInt::div>(ins, ArgList(lhs, rhs),
11089 StoreRegisterTo(output));
11090
11091 // x / 0 throws an error.
11092 if (ins->mir()->canBeDivideByZero()) {
11093 masm.branchIfBigIntIsZero(rhs, ool->entry());
11094 }
11095
11096 // 0n / x == 0n
11097 Label lhsNonZero;
11098 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11099 masm.movePtr(lhs, output);
11100 masm.jump(ool->rejoin());
11101 masm.bind(&lhsNonZero);
11102
11103 // Call into the VM when either operand can't be loaded into a pointer-sized
11104 // register.
11105 masm.loadBigIntNonZero(lhs, temp1, ool->entry());
11106 masm.loadBigIntNonZero(rhs, temp2, ool->entry());
11107
11108 // |BigInt::div()| returns |lhs| for |lhs / 1n|, which means there's no
11109 // allocation which might trigger a minor GC to free up nursery space. This
11110 // requires us to apply the same optimization here, otherwise we'd end up with
11111 // always entering the OOL call, because the nursery is never evicted.
11112 Label notOne;
11113 masm.branchPtr(Assembler::NotEqual, temp2, ImmWord(1), &notOne);
11114 masm.movePtr(lhs, output);
11115 masm.jump(ool->rejoin());
11116 masm.bind(&notOne);
11117
11118 static constexpr auto DigitMin = std::numeric_limits<
11119 mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min();
11120
11121 // Handle an integer overflow from INT{32,64}_MIN / -1.
11122 Label notOverflow;
11123 masm.branchPtr(Assembler::NotEqual, temp1, ImmWord(DigitMin), &notOverflow);
11124 masm.branchPtr(Assembler::Equal, temp2, ImmWord(-1), ool->entry());
11125 masm.bind(&notOverflow);
11126
11127 emitBigIntDiv(ins, temp1, temp2, output, ool->entry());
11128
11129 masm.bind(ool->rejoin());
11130}
11131
11132void CodeGenerator::visitBigIntMod(LBigIntMod* ins) {
11133 Register lhs = ToRegister(ins->lhs());
11134 Register rhs = ToRegister(ins->rhs());
11135 Register temp1 = ToRegister(ins->temp1());
11136 Register temp2 = ToRegister(ins->temp2());
11137 Register output = ToRegister(ins->output());
11138
11139 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11140 auto* ool = oolCallVM<Fn, BigInt::mod>(ins, ArgList(lhs, rhs),
11141 StoreRegisterTo(output));
11142
11143 // x % 0 throws an error.
11144 if (ins->mir()->canBeDivideByZero()) {
11145 masm.branchIfBigIntIsZero(rhs, ool->entry());
11146 }
11147
11148 // 0n % x == 0n
11149 Label lhsNonZero;
11150 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11151 masm.movePtr(lhs, output);
11152 masm.jump(ool->rejoin());
11153 masm.bind(&lhsNonZero);
11154
11155 // Call into the VM when either operand can't be loaded into a pointer-sized
11156 // register.
11157 masm.loadBigIntAbsolute(lhs, temp1, ool->entry());
11158 masm.loadBigIntAbsolute(rhs, temp2, ool->entry());
11159
11160 // Similar to the case for BigInt division, we must apply the same allocation
11161 // optimizations as performed in |BigInt::mod()|.
11162 Label notBelow;
11163 masm.branchPtr(Assembler::AboveOrEqual, temp1, temp2, &notBelow);
11164 masm.movePtr(lhs, output);
11165 masm.jump(ool->rejoin());
11166 masm.bind(&notBelow);
11167
11168 // Convert both digits to signed pointer-sized values.
11169 masm.bigIntDigitToSignedPtr(lhs, temp1, ool->entry());
11170 masm.bigIntDigitToSignedPtr(rhs, temp2, ool->entry());
11171
11172 static constexpr auto DigitMin = std::numeric_limits<
11173 mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min();
11174
11175 // Handle an integer overflow from INT{32,64}_MIN / -1.
11176 Label notOverflow;
11177 masm.branchPtr(Assembler::NotEqual, temp1, ImmWord(DigitMin), &notOverflow);
11178 masm.branchPtr(Assembler::NotEqual, temp2, ImmWord(-1), &notOverflow);
11179 masm.movePtr(ImmWord(0), temp1);
11180 masm.bind(&notOverflow);
11181
11182 emitBigIntMod(ins, temp1, temp2, output, ool->entry());
11183
11184 masm.bind(ool->rejoin());
11185}
11186
11187void CodeGenerator::visitBigIntPow(LBigIntPow* ins) {
11188 Register lhs = ToRegister(ins->lhs());
11189 Register rhs = ToRegister(ins->rhs());
11190 Register temp1 = ToRegister(ins->temp1());
11191 Register temp2 = ToRegister(ins->temp2());
11192 Register output = ToRegister(ins->output());
11193
11194 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11195 auto* ool = oolCallVM<Fn, BigInt::pow>(ins, ArgList(lhs, rhs),
11196 StoreRegisterTo(output));
11197
11198 // x ** -y throws an error.
11199 if (ins->mir()->canBeNegativeExponent()) {
11200 masm.branchIfBigIntIsNegative(rhs, ool->entry());
11201 }
11202
11203 Register dest = temp1;
11204 Register base = temp2;
11205 Register exponent = output;
11206
11207 Label done;
11208 masm.movePtr(ImmWord(1), dest); // p = 1
11209
11210 // 1n ** y == 1n
11211 // -1n ** y == 1n when y is even
11212 // -1n ** y == -1n when y is odd
11213 Label lhsNotOne;
11214 masm.branch32(Assembler::Above, Address(lhs, BigInt::offsetOfLength()),
11215 Imm32(1), &lhsNotOne);
11216 masm.loadFirstBigIntDigitOrZero(lhs, base);
11217 masm.branchPtr(Assembler::NotEqual, base, Imm32(1), &lhsNotOne);
11218 {
11219 masm.loadFirstBigIntDigitOrZero(rhs, exponent);
11220
11221 Label lhsNonNegative;
11222 masm.branchIfBigIntIsNonNegative(lhs, &lhsNonNegative);
11223 masm.branchTestPtr(Assembler::Zero, exponent, Imm32(1), &done);
11224 masm.bind(&lhsNonNegative);
11225 masm.movePtr(lhs, output);
11226 masm.jump(ool->rejoin());
11227 }
11228 masm.bind(&lhsNotOne);
11229
11230 // x ** 0n == 1n
11231 masm.branchIfBigIntIsZero(rhs, &done);
11232
11233 // 0n ** y == 0n with y != 0n
11234 Label lhsNonZero;
11235 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11236 {
11237 masm.movePtr(lhs, output);
11238 masm.jump(ool->rejoin());
11239 }
11240 masm.bind(&lhsNonZero);
11241
11242 // Call into the VM when the exponent can't be loaded into a pointer-sized
11243 // register.
11244 masm.loadBigIntAbsolute(rhs, exponent, ool->entry());
11245
11246 // x ** y with x > 1 and y >= DigitBits can't be pointer-sized.
11247 masm.branchPtr(Assembler::AboveOrEqual, exponent, Imm32(BigInt::DigitBits),
11248 ool->entry());
11249
11250 // x ** 1n == x
11251 Label rhsNotOne;
11252 masm.branch32(Assembler::NotEqual, exponent, Imm32(1), &rhsNotOne);
11253 {
11254 masm.movePtr(lhs, output);
11255 masm.jump(ool->rejoin());
11256 }
11257 masm.bind(&rhsNotOne);
11258
11259 // Call into the VM when the base operand can't be loaded into a pointer-sized
11260 // register.
11261 masm.loadBigIntNonZero(lhs, base, ool->entry());
11262
11263 // MacroAssembler::pow32() adjusted to work on pointer-sized registers.
11264 {
11265 // m = base
11266 // n = exponent
11267
11268 Label start, loop;
11269 masm.jump(&start);
11270 masm.bind(&loop);
11271
11272 // m *= m
11273 masm.branchMulPtr(Assembler::Overflow, base, base, ool->entry());
11274
11275 masm.bind(&start);
11276
11277 // if ((n & 1) != 0) p *= m
11278 Label even;
11279 masm.branchTest32(Assembler::Zero, exponent, Imm32(1), &even);
11280 masm.branchMulPtr(Assembler::Overflow, base, dest, ool->entry());
11281 masm.bind(&even);
11282
11283 // n >>= 1
11284 // if (n == 0) return p
11285 masm.branchRshift32(Assembler::NonZero, Imm32(1), exponent, &loop);
11286 }
11287
11288 MOZ_ASSERT(temp1 == dest)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 == dest)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 == dest))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("temp1 == dest",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11288); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp1 == dest"
")"); do { *((volatile int*)__null) = 11288; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11289
11290 // Create and return the result.
11291 masm.bind(&done);
11292 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11293 masm.initializeBigInt(output, temp1);
11294
11295 masm.bind(ool->rejoin());
11296}
11297
11298void CodeGenerator::visitBigIntBitAnd(LBigIntBitAnd* ins) {
11299 Register lhs = ToRegister(ins->lhs());
11300 Register rhs = ToRegister(ins->rhs());
11301 Register temp1 = ToRegister(ins->temp1());
11302 Register temp2 = ToRegister(ins->temp2());
11303 Register output = ToRegister(ins->output());
11304
11305 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11306 auto* ool = oolCallVM<Fn, BigInt::bitAnd>(ins, ArgList(lhs, rhs),
11307 StoreRegisterTo(output));
11308
11309 // 0n & x == 0n
11310 Label lhsNonZero;
11311 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11312 masm.movePtr(lhs, output);
11313 masm.jump(ool->rejoin());
11314 masm.bind(&lhsNonZero);
11315
11316 // x & 0n == 0n
11317 Label rhsNonZero;
11318 masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero);
11319 masm.movePtr(rhs, output);
11320 masm.jump(ool->rejoin());
11321 masm.bind(&rhsNonZero);
11322
11323 // Call into the VM when either operand can't be loaded into a pointer-sized
11324 // register.
11325 masm.loadBigIntNonZero(lhs, temp1, ool->entry());
11326 masm.loadBigIntNonZero(rhs, temp2, ool->entry());
11327
11328 masm.andPtr(temp2, temp1);
11329
11330 // Create and return the result.
11331 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11332 masm.initializeBigInt(output, temp1);
11333
11334 masm.bind(ool->rejoin());
11335}
11336
11337void CodeGenerator::visitBigIntBitOr(LBigIntBitOr* ins) {
11338 Register lhs = ToRegister(ins->lhs());
11339 Register rhs = ToRegister(ins->rhs());
11340 Register temp1 = ToRegister(ins->temp1());
11341 Register temp2 = ToRegister(ins->temp2());
11342 Register output = ToRegister(ins->output());
11343
11344 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11345 auto* ool = oolCallVM<Fn, BigInt::bitOr>(ins, ArgList(lhs, rhs),
11346 StoreRegisterTo(output));
11347
11348 // 0n | x == x
11349 Label lhsNonZero;
11350 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11351 masm.movePtr(rhs, output);
11352 masm.jump(ool->rejoin());
11353 masm.bind(&lhsNonZero);
11354
11355 // x | 0n == x
11356 Label rhsNonZero;
11357 masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero);
11358 masm.movePtr(lhs, output);
11359 masm.jump(ool->rejoin());
11360 masm.bind(&rhsNonZero);
11361
11362 // Call into the VM when either operand can't be loaded into a pointer-sized
11363 // register.
11364 masm.loadBigIntNonZero(lhs, temp1, ool->entry());
11365 masm.loadBigIntNonZero(rhs, temp2, ool->entry());
11366
11367 masm.orPtr(temp2, temp1);
11368
11369 // Create and return the result.
11370 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11371 masm.initializeBigInt(output, temp1);
11372
11373 masm.bind(ool->rejoin());
11374}
11375
11376void CodeGenerator::visitBigIntBitXor(LBigIntBitXor* ins) {
11377 Register lhs = ToRegister(ins->lhs());
11378 Register rhs = ToRegister(ins->rhs());
11379 Register temp1 = ToRegister(ins->temp1());
11380 Register temp2 = ToRegister(ins->temp2());
11381 Register output = ToRegister(ins->output());
11382
11383 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11384 auto* ool = oolCallVM<Fn, BigInt::bitXor>(ins, ArgList(lhs, rhs),
11385 StoreRegisterTo(output));
11386
11387 // 0n ^ x == x
11388 Label lhsNonZero;
11389 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11390 masm.movePtr(rhs, output);
11391 masm.jump(ool->rejoin());
11392 masm.bind(&lhsNonZero);
11393
11394 // x ^ 0n == x
11395 Label rhsNonZero;
11396 masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero);
11397 masm.movePtr(lhs, output);
11398 masm.jump(ool->rejoin());
11399 masm.bind(&rhsNonZero);
11400
11401 // Call into the VM when either operand can't be loaded into a pointer-sized
11402 // register.
11403 masm.loadBigIntNonZero(lhs, temp1, ool->entry());
11404 masm.loadBigIntNonZero(rhs, temp2, ool->entry());
11405
11406 masm.xorPtr(temp2, temp1);
11407
11408 // Create and return the result.
11409 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11410 masm.initializeBigInt(output, temp1);
11411
11412 masm.bind(ool->rejoin());
11413}
11414
11415void CodeGenerator::visitBigIntLsh(LBigIntLsh* ins) {
11416 Register lhs = ToRegister(ins->lhs());
11417 Register rhs = ToRegister(ins->rhs());
11418 Register temp1 = ToRegister(ins->temp1());
11419 Register temp2 = ToRegister(ins->temp2());
11420 Register temp3 = ToRegister(ins->temp3());
11421 Register output = ToRegister(ins->output());
11422
11423 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11424 auto* ool = oolCallVM<Fn, BigInt::lsh>(ins, ArgList(lhs, rhs),
11425 StoreRegisterTo(output));
11426
11427 // 0n << x == 0n
11428 Label lhsNonZero;
11429 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11430 masm.movePtr(lhs, output);
11431 masm.jump(ool->rejoin());
11432 masm.bind(&lhsNonZero);
11433
11434 // x << 0n == x
11435 Label rhsNonZero;
11436 masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero);
11437 masm.movePtr(lhs, output);
11438 masm.jump(ool->rejoin());
11439 masm.bind(&rhsNonZero);
11440
11441 // Inline |BigInt::lsh| for the case when |lhs| contains a single digit.
11442
11443 Label rhsTooLarge;
11444 masm.loadBigIntAbsolute(rhs, temp2, &rhsTooLarge);
11445
11446 // Call into the VM when the left-hand side operand can't be loaded into a
11447 // pointer-sized register.
11448 masm.loadBigIntAbsolute(lhs, temp1, ool->entry());
11449
11450 // Handle shifts exceeding |BigInt::DigitBits| first.
11451 Label shift, create;
11452 masm.branchPtr(Assembler::Below, temp2, Imm32(BigInt::DigitBits), &shift);
11453 {
11454 masm.bind(&rhsTooLarge);
11455
11456 // x << DigitBits with x != 0n always exceeds pointer-sized storage.
11457 masm.branchIfBigIntIsNonNegative(rhs, ool->entry());
11458
11459 // x << -DigitBits == x >> DigitBits, which is either 0n or -1n.
11460 masm.move32(Imm32(0), temp1);
11461 masm.branchIfBigIntIsNonNegative(lhs, &create);
11462 masm.move32(Imm32(1), temp1);
11463 masm.jump(&create);
11464 }
11465 masm.bind(&shift);
11466
11467 Label nonNegative;
11468 masm.branchIfBigIntIsNonNegative(rhs, &nonNegative);
11469 {
11470 masm.movePtr(temp1, temp3);
11471
11472 // |x << -y| is computed as |x >> y|.
11473 masm.rshiftPtr(temp2, temp1);
11474
11475 // For negative numbers, round down if any bit was shifted out.
11476 masm.branchIfBigIntIsNonNegative(lhs, &create);
11477
11478 // Compute |mask = (static_cast<Digit>(1) << shift) - 1|.
11479 masm.movePtr(ImmWord(-1), output);
11480 masm.lshiftPtr(temp2, output);
11481 masm.notPtr(output);
11482
11483 // Add plus one when |(lhs.digit(0) & mask) != 0|.
11484 masm.branchTestPtr(Assembler::Zero, output, temp3, &create);
11485 masm.addPtr(ImmWord(1), temp1);
11486 masm.jump(&create);
11487 }
11488 masm.bind(&nonNegative);
11489 {
11490 masm.movePtr(temp2, temp3);
11491
11492 // Compute |grow = lhs.digit(0) >> (DigitBits - shift)|.
11493 masm.negPtr(temp2);
11494 masm.addPtr(Imm32(BigInt::DigitBits), temp2);
11495 masm.movePtr(temp1, output);
11496 masm.rshiftPtr(temp2, output);
11497
11498 // Call into the VM when any bit will be shifted out.
11499 masm.branchTestPtr(Assembler::NonZero, output, output, ool->entry());
11500
11501 masm.movePtr(temp3, temp2);
11502 masm.lshiftPtr(temp2, temp1);
11503 }
11504 masm.bind(&create);
11505
11506 // Create and return the result.
11507 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11508 masm.initializeBigIntAbsolute(output, temp1);
11509
11510 // Set the sign bit when the left-hand side is negative.
11511 masm.branchIfBigIntIsNonNegative(lhs, ool->rejoin());
11512 masm.or32(Imm32(BigInt::signBitMask()),
11513 Address(output, BigInt::offsetOfFlags()));
11514
11515 masm.bind(ool->rejoin());
11516}
11517
11518void CodeGenerator::visitBigIntRsh(LBigIntRsh* ins) {
11519 Register lhs = ToRegister(ins->lhs());
11520 Register rhs = ToRegister(ins->rhs());
11521 Register temp1 = ToRegister(ins->temp1());
11522 Register temp2 = ToRegister(ins->temp2());
11523 Register temp3 = ToRegister(ins->temp3());
11524 Register output = ToRegister(ins->output());
11525
11526 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11527 auto* ool = oolCallVM<Fn, BigInt::rsh>(ins, ArgList(lhs, rhs),
11528 StoreRegisterTo(output));
11529
11530 // 0n >> x == 0n
11531 Label lhsNonZero;
11532 masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero);
11533 masm.movePtr(lhs, output);
11534 masm.jump(ool->rejoin());
11535 masm.bind(&lhsNonZero);
11536
11537 // x >> 0n == x
11538 Label rhsNonZero;
11539 masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero);
11540 masm.movePtr(lhs, output);
11541 masm.jump(ool->rejoin());
11542 masm.bind(&rhsNonZero);
11543
11544 // Inline |BigInt::rsh| for the case when |lhs| contains a single digit.
11545
11546 Label rhsTooLarge;
11547 masm.loadBigIntAbsolute(rhs, temp2, &rhsTooLarge);
11548
11549 // Call into the VM when the left-hand side operand can't be loaded into a
11550 // pointer-sized register.
11551 masm.loadBigIntAbsolute(lhs, temp1, ool->entry());
11552
11553 // Handle shifts exceeding |BigInt::DigitBits| first.
11554 Label shift, create;
11555 masm.branchPtr(Assembler::Below, temp2, Imm32(BigInt::DigitBits), &shift);
11556 {
11557 masm.bind(&rhsTooLarge);
11558
11559 // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage.
11560 masm.branchIfBigIntIsNegative(rhs, ool->entry());
11561
11562 // x >> DigitBits is either 0n or -1n.
11563 masm.move32(Imm32(0), temp1);
11564 masm.branchIfBigIntIsNonNegative(lhs, &create);
11565 masm.move32(Imm32(1), temp1);
11566 masm.jump(&create);
11567 }
11568 masm.bind(&shift);
11569
11570 Label nonNegative;
11571 masm.branchIfBigIntIsNonNegative(rhs, &nonNegative);
11572 {
11573 masm.movePtr(temp2, temp3);
11574
11575 // Compute |grow = lhs.digit(0) >> (DigitBits - shift)|.
11576 masm.negPtr(temp2);
11577 masm.addPtr(Imm32(BigInt::DigitBits), temp2);
11578 masm.movePtr(temp1, output);
11579 masm.rshiftPtr(temp2, output);
11580
11581 // Call into the VM when any bit will be shifted out.
11582 masm.branchTestPtr(Assembler::NonZero, output, output, ool->entry());
11583
11584 // |x >> -y| is computed as |x << y|.
11585 masm.movePtr(temp3, temp2);
11586 masm.lshiftPtr(temp2, temp1);
11587 masm.jump(&create);
11588 }
11589 masm.bind(&nonNegative);
11590 {
11591 masm.movePtr(temp1, temp3);
11592
11593 masm.rshiftPtr(temp2, temp1);
11594
11595 // For negative numbers, round down if any bit was shifted out.
11596 masm.branchIfBigIntIsNonNegative(lhs, &create);
11597
11598 // Compute |mask = (static_cast<Digit>(1) << shift) - 1|.
11599 masm.movePtr(ImmWord(-1), output);
11600 masm.lshiftPtr(temp2, output);
11601 masm.notPtr(output);
11602
11603 // Add plus one when |(lhs.digit(0) & mask) != 0|.
11604 masm.branchTestPtr(Assembler::Zero, output, temp3, &create);
11605 masm.addPtr(ImmWord(1), temp1);
11606 }
11607 masm.bind(&create);
11608
11609 // Create and return the result.
11610 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11611 masm.initializeBigIntAbsolute(output, temp1);
11612
11613 // Set the sign bit when the left-hand side is negative.
11614 masm.branchIfBigIntIsNonNegative(lhs, ool->rejoin());
11615 masm.or32(Imm32(BigInt::signBitMask()),
11616 Address(output, BigInt::offsetOfFlags()));
11617
11618 masm.bind(ool->rejoin());
11619}
11620
11621void CodeGenerator::visitBigIntIncrement(LBigIntIncrement* ins) {
11622 Register input = ToRegister(ins->input());
11623 Register temp1 = ToRegister(ins->temp1());
11624 Register temp2 = ToRegister(ins->temp2());
11625 Register output = ToRegister(ins->output());
11626
11627 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
11628 auto* ool =
11629 oolCallVM<Fn, BigInt::inc>(ins, ArgList(input), StoreRegisterTo(output));
11630
11631 // Call into the VM when the input can't be loaded into a pointer-sized
11632 // register.
11633 masm.loadBigInt(input, temp1, ool->entry());
11634 masm.movePtr(ImmWord(1), temp2);
11635
11636 masm.branchAddPtr(Assembler::Overflow, temp2, temp1, ool->entry());
11637
11638 // Create and return the result.
11639 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11640 masm.initializeBigInt(output, temp1);
11641
11642 masm.bind(ool->rejoin());
11643}
11644
11645void CodeGenerator::visitBigIntDecrement(LBigIntDecrement* ins) {
11646 Register input = ToRegister(ins->input());
11647 Register temp1 = ToRegister(ins->temp1());
11648 Register temp2 = ToRegister(ins->temp2());
11649 Register output = ToRegister(ins->output());
11650
11651 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
11652 auto* ool =
11653 oolCallVM<Fn, BigInt::dec>(ins, ArgList(input), StoreRegisterTo(output));
11654
11655 // Call into the VM when the input can't be loaded into a pointer-sized
11656 // register.
11657 masm.loadBigInt(input, temp1, ool->entry());
11658 masm.movePtr(ImmWord(1), temp2);
11659
11660 masm.branchSubPtr(Assembler::Overflow, temp2, temp1, ool->entry());
11661
11662 // Create and return the result.
11663 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11664 masm.initializeBigInt(output, temp1);
11665
11666 masm.bind(ool->rejoin());
11667}
11668
11669void CodeGenerator::visitBigIntNegate(LBigIntNegate* ins) {
11670 Register input = ToRegister(ins->input());
11671 Register temp = ToRegister(ins->temp());
11672 Register output = ToRegister(ins->output());
11673
11674 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
11675 auto* ool =
11676 oolCallVM<Fn, BigInt::neg>(ins, ArgList(input), StoreRegisterTo(output));
11677
11678 // -0n == 0n
11679 Label lhsNonZero;
11680 masm.branchIfBigIntIsNonZero(input, &lhsNonZero);
11681 masm.movePtr(input, output);
11682 masm.jump(ool->rejoin());
11683 masm.bind(&lhsNonZero);
11684
11685 // Call into the VM when the input uses heap digits.
11686 masm.copyBigIntWithInlineDigits(input, output, temp, initialBigIntHeap(),
11687 ool->entry());
11688
11689 // Flip the sign bit.
11690 masm.xor32(Imm32(BigInt::signBitMask()),
11691 Address(output, BigInt::offsetOfFlags()));
11692
11693 masm.bind(ool->rejoin());
11694}
11695
11696void CodeGenerator::visitBigIntBitNot(LBigIntBitNot* ins) {
11697 Register input = ToRegister(ins->input());
11698 Register temp1 = ToRegister(ins->temp1());
11699 Register temp2 = ToRegister(ins->temp2());
11700 Register output = ToRegister(ins->output());
11701
11702 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
11703 auto* ool = oolCallVM<Fn, BigInt::bitNot>(ins, ArgList(input),
11704 StoreRegisterTo(output));
11705
11706 masm.loadBigIntAbsolute(input, temp1, ool->entry());
11707
11708 // This follows the C++ implementation because it let's us support the full
11709 // range [-2^64, 2^64 - 1] on 64-bit resp. [-2^32, 2^32 - 1] on 32-bit.
11710 Label nonNegative, done;
11711 masm.branchIfBigIntIsNonNegative(input, &nonNegative);
11712 {
11713 // ~(-x) == ~(~(x-1)) == x-1
11714 masm.subPtr(Imm32(1), temp1);
11715 masm.jump(&done);
11716 }
11717 masm.bind(&nonNegative);
11718 {
11719 // ~x == -x-1 == -(x+1)
11720 masm.movePtr(ImmWord(1), temp2);
11721 masm.branchAddPtr(Assembler::CarrySet, temp2, temp1, ool->entry());
11722 }
11723 masm.bind(&done);
11724
11725 // Create and return the result.
11726 masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry());
11727 masm.initializeBigIntAbsolute(output, temp1);
11728
11729 // Set the sign bit when the input is positive.
11730 masm.branchIfBigIntIsNegative(input, ool->rejoin());
11731 masm.or32(Imm32(BigInt::signBitMask()),
11732 Address(output, BigInt::offsetOfFlags()));
11733
11734 masm.bind(ool->rejoin());
11735}
11736
11737void CodeGenerator::visitInt32ToStringWithBase(LInt32ToStringWithBase* lir) {
11738 Register input = ToRegister(lir->input());
11739 RegisterOrInt32 base = ToRegisterOrInt32(lir->base());
11740 Register output = ToRegister(lir->output());
11741 Register temp0 = ToRegister(lir->temp0());
11742 Register temp1 = ToRegister(lir->temp1());
11743
11744 bool lowerCase = lir->mir()->lowerCase();
11745
11746 using Fn = JSString* (*)(JSContext*, int32_t, int32_t, bool);
11747 if (base.is<Register>()) {
11748 auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase>(
11749 lir, ArgList(input, base.as<Register>(), Imm32(lowerCase)),
11750 StoreRegisterTo(output));
11751
11752 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
11753 masm.loadInt32ToStringWithBase(input, base.as<Register>(), output, temp0,
11754 temp1, gen->runtime->staticStrings(),
11755 liveRegs, lowerCase, ool->entry());
11756 masm.bind(ool->rejoin());
11757 } else {
11758 auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase>(
11759 lir, ArgList(input, Imm32(base.as<int32_t>()), Imm32(lowerCase)),
11760 StoreRegisterTo(output));
11761
11762 masm.loadInt32ToStringWithBase(input, base.as<int32_t>(), output, temp0,
11763 temp1, gen->runtime->staticStrings(),
11764 lowerCase, ool->entry());
11765 masm.bind(ool->rejoin());
11766 }
11767}
11768
11769void CodeGenerator::visitNumberParseInt(LNumberParseInt* lir) {
11770 Register string = ToRegister(lir->string());
11771 Register radix = ToRegister(lir->radix());
11772 ValueOperand output = ToOutValue(lir);
11773 Register temp = ToRegister(lir->temp0());
11774
11775#ifdef DEBUG1
11776 Label ok;
11777 masm.branch32(Assembler::Equal, radix, Imm32(0), &ok);
11778 masm.branch32(Assembler::Equal, radix, Imm32(10), &ok);
11779 masm.assumeUnreachable("radix must be 0 or 10 for indexed value fast path");
11780 masm.bind(&ok);
11781#endif
11782
11783 // Use indexed value as fast path if possible.
11784 Label vmCall, done;
11785 masm.loadStringIndexValue(string, temp, &vmCall);
11786 masm.tagValue(JSVAL_TYPE_INT32, temp, output);
11787 masm.jump(&done);
11788 {
11789 masm.bind(&vmCall);
11790
11791 pushArg(radix);
11792 pushArg(string);
11793
11794 using Fn = bool (*)(JSContext*, HandleString, int32_t, MutableHandleValue);
11795 callVM<Fn, js::NumberParseInt>(lir);
11796 }
11797 masm.bind(&done);
11798}
11799
11800void CodeGenerator::visitDoubleParseInt(LDoubleParseInt* lir) {
11801 FloatRegister number = ToFloatRegister(lir->number());
11802 Register output = ToRegister(lir->output());
11803 FloatRegister temp = ToFloatRegister(lir->temp0());
11804
11805 Label bail;
11806 masm.branchDouble(Assembler::DoubleUnordered, number, number, &bail);
11807 masm.branchTruncateDoubleToInt32(number, output, &bail);
11808
11809 Label ok;
11810 masm.branch32(Assembler::NotEqual, output, Imm32(0), &ok);
11811 {
11812 // Accept both +0 and -0 and return 0.
11813 masm.loadConstantDouble(0.0, temp);
11814 masm.branchDouble(Assembler::DoubleEqual, number, temp, &ok);
11815
11816 // Fail if a non-zero input is in the exclusive range (-1, 1.0e-6).
11817 masm.loadConstantDouble(DOUBLE_DECIMAL_IN_SHORTEST_LOW, temp);
11818 masm.branchDouble(Assembler::DoubleLessThan, number, temp, &bail);
11819 }
11820 masm.bind(&ok);
11821
11822 bailoutFrom(&bail, lir->snapshot());
11823}
11824
11825void CodeGenerator::visitFloor(LFloor* lir) {
11826 FloatRegister input = ToFloatRegister(lir->input());
11827 Register output = ToRegister(lir->output());
11828
11829 Label bail;
11830 masm.floorDoubleToInt32(input, output, &bail);
11831 bailoutFrom(&bail, lir->snapshot());
11832}
11833
11834void CodeGenerator::visitFloorF(LFloorF* lir) {
11835 FloatRegister input = ToFloatRegister(lir->input());
11836 Register output = ToRegister(lir->output());
11837
11838 Label bail;
11839 masm.floorFloat32ToInt32(input, output, &bail);
11840 bailoutFrom(&bail, lir->snapshot());
11841}
11842
11843void CodeGenerator::visitCeil(LCeil* lir) {
11844 FloatRegister input = ToFloatRegister(lir->input());
11845 Register output = ToRegister(lir->output());
11846
11847 Label bail;
11848 masm.ceilDoubleToInt32(input, output, &bail);
11849 bailoutFrom(&bail, lir->snapshot());
11850}
11851
11852void CodeGenerator::visitCeilF(LCeilF* lir) {
11853 FloatRegister input = ToFloatRegister(lir->input());
11854 Register output = ToRegister(lir->output());
11855
11856 Label bail;
11857 masm.ceilFloat32ToInt32(input, output, &bail);
11858 bailoutFrom(&bail, lir->snapshot());
11859}
11860
11861void CodeGenerator::visitRound(LRound* lir) {
11862 FloatRegister input = ToFloatRegister(lir->input());
11863 FloatRegister temp = ToFloatRegister(lir->temp0());
11864 Register output = ToRegister(lir->output());
11865
11866 Label bail;
11867 masm.roundDoubleToInt32(input, output, temp, &bail);
11868 bailoutFrom(&bail, lir->snapshot());
11869}
11870
11871void CodeGenerator::visitRoundF(LRoundF* lir) {
11872 FloatRegister input = ToFloatRegister(lir->input());
11873 FloatRegister temp = ToFloatRegister(lir->temp0());
11874 Register output = ToRegister(lir->output());
11875
11876 Label bail;
11877 masm.roundFloat32ToInt32(input, output, temp, &bail);
11878 bailoutFrom(&bail, lir->snapshot());
11879}
11880
11881void CodeGenerator::visitTrunc(LTrunc* lir) {
11882 FloatRegister input = ToFloatRegister(lir->input());
11883 Register output = ToRegister(lir->output());
11884
11885 Label bail;
11886 masm.truncDoubleToInt32(input, output, &bail);
11887 bailoutFrom(&bail, lir->snapshot());
11888}
11889
11890void CodeGenerator::visitTruncF(LTruncF* lir) {
11891 FloatRegister input = ToFloatRegister(lir->input());
11892 Register output = ToRegister(lir->output());
11893
11894 Label bail;
11895 masm.truncFloat32ToInt32(input, output, &bail);
11896 bailoutFrom(&bail, lir->snapshot());
11897}
11898
11899void CodeGenerator::visitCompareS(LCompareS* lir) {
11900 JSOp op = lir->mir()->jsop();
11901 Register left = ToRegister(lir->left());
11902 Register right = ToRegister(lir->right());
11903 Register output = ToRegister(lir->output());
11904
11905 OutOfLineCode* ool = nullptr;
11906
11907 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
11908 if (op == JSOp::Eq || op == JSOp::StrictEq) {
11909 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
11910 lir, ArgList(left, right), StoreRegisterTo(output));
11911 } else if (op == JSOp::Ne || op == JSOp::StrictNe) {
11912 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
11913 lir, ArgList(left, right), StoreRegisterTo(output));
11914 } else if (op == JSOp::Lt) {
11915 ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>(
11916 lir, ArgList(left, right), StoreRegisterTo(output));
11917 } else if (op == JSOp::Le) {
11918 // Push the operands in reverse order for JSOp::Le:
11919 // - |left <= right| is implemented as |right >= left|.
11920 ool =
11921 oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>(
11922 lir, ArgList(right, left), StoreRegisterTo(output));
11923 } else if (op == JSOp::Gt) {
11924 // Push the operands in reverse order for JSOp::Gt:
11925 // - |left > right| is implemented as |right < left|.
11926 ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>(
11927 lir, ArgList(right, left), StoreRegisterTo(output));
11928 } else {
11929 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11929); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ge"
")"); do { *((volatile int*)__null) = 11929; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11930 ool =
11931 oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>(
11932 lir, ArgList(left, right), StoreRegisterTo(output));
11933 }
11934
11935 masm.compareStrings(op, left, right, output, ool->entry());
11936
11937 masm.bind(ool->rejoin());
11938}
11939
11940void CodeGenerator::visitCompareSInline(LCompareSInline* lir) {
11941 JSOp op = lir->mir()->jsop();
11942 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11942); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsEqualityOp(op)"
")"); do { *((volatile int*)__null) = 11942; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11943
11944 Register input = ToRegister(lir->input());
11945 Register output = ToRegister(lir->output());
11946
11947 const JSLinearString* str = lir->constant();
11948 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11948); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() > 0"
")"); do { *((volatile int*)__null) = 11948; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11949
11950 OutOfLineCode* ool = nullptr;
11951
11952 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
11953 if (op == JSOp::Eq || op == JSOp::StrictEq) {
11954 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
11955 lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output));
11956 } else {
11957 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11957); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ne || op == JSOp::StrictNe"
")"); do { *((volatile int*)__null) = 11957; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11958 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
11959 lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output));
11960 }
11961
11962 Label compareChars;
11963 {
11964 Label notPointerEqual;
11965
11966 // If operands point to the same instance, the strings are trivially equal.
11967 masm.branchPtr(Assembler::NotEqual, input, ImmGCPtr(str), &notPointerEqual);
11968 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
11969 masm.jump(ool->rejoin());
11970
11971 masm.bind(&notPointerEqual);
11972
11973 Label setNotEqualResult;
11974
11975 if (str->isAtom()) {
11976 // Atoms cannot be equal to each other if they point to different strings.
11977 Imm32 atomBit(JSString::ATOM_BIT);
11978 masm.branchTest32(Assembler::NonZero,
11979 Address(input, JSString::offsetOfFlags()), atomBit,
11980 &setNotEqualResult);
11981 }
11982
11983 if (str->hasTwoByteChars()) {
11984 // Pure two-byte strings can't be equal to Latin-1 strings.
11985 JS::AutoCheckCannotGC nogc;
11986 if (!mozilla::IsUtf16Latin1(str->twoByteRange(nogc))) {
11987 masm.branchLatin1String(input, &setNotEqualResult);
11988 }
11989 }
11990
11991 // Strings of different length can never be equal.
11992 masm.branch32(Assembler::NotEqual,
11993 Address(input, JSString::offsetOfLength()),
11994 Imm32(str->length()), &setNotEqualResult);
11995
11996 if (str->isAtom()) {
11997 Label forwardedPtrEqual;
11998 masm.tryFastAtomize(input, output, output, &compareChars);
11999
12000 // We now have two atoms. Just check pointer equality.
12001 masm.branchPtr(Assembler::Equal, output, ImmGCPtr(str),
12002 &forwardedPtrEqual);
12003
12004 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
12005 masm.jump(ool->rejoin());
12006
12007 masm.bind(&forwardedPtrEqual);
12008 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
12009 masm.jump(ool->rejoin());
12010 } else {
12011 masm.jump(&compareChars);
12012 }
12013
12014 masm.bind(&setNotEqualResult);
12015 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
12016 masm.jump(ool->rejoin());
12017 }
12018
12019 masm.bind(&compareChars);
12020
12021 // Load the input string's characters.
12022 Register stringChars = output;
12023 masm.loadStringCharsForCompare(input, str, stringChars, ool->entry());
12024
12025 // Start comparing character by character.
12026 masm.compareStringChars(op, stringChars, str, output);
12027
12028 masm.bind(ool->rejoin());
12029}
12030
12031void CodeGenerator::visitCompareSSingle(LCompareSSingle* lir) {
12032 JSOp op = lir->jsop();
12033 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12033); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsRelationalOp(op)"
")"); do { *((volatile int*)__null) = 12033; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12034
12035 Register input = ToRegister(lir->input());
12036 Register output = ToRegister(lir->output());
12037 Register temp = ToRegister(lir->temp0());
12038
12039 const JSLinearString* str = lir->constant();
12040 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12040); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() == 1"
")"); do { *((volatile int*)__null) = 12040; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12041
12042 char16_t ch = str->latin1OrTwoByteChar(0);
12043
12044 masm.movePtr(input, temp);
12045
12046 // Check if the string is empty.
12047 Label compareLength;
12048 masm.branch32(Assembler::Equal, Address(temp, JSString::offsetOfLength()),
12049 Imm32(0), &compareLength);
12050
12051 // The first character is in the left-most rope child.
12052 Label notRope;
12053 masm.branchIfNotRope(temp, &notRope);
12054 {
12055 // Unwind ropes at the start if possible.
12056 Label unwindRope;
12057 masm.bind(&unwindRope);
12058 masm.loadRopeLeftChild(temp, output);
12059 masm.movePtr(output, temp);
12060
12061#ifdef DEBUG1
12062 Label notEmpty;
12063 masm.branch32(Assembler::NotEqual,
12064 Address(temp, JSString::offsetOfLength()), Imm32(0),
12065 &notEmpty);
12066 masm.assumeUnreachable("rope children are non-empty");
12067 masm.bind(&notEmpty);
12068#endif
12069
12070 // Otherwise keep unwinding ropes.
12071 masm.branchIfRope(temp, &unwindRope);
12072 }
12073 masm.bind(&notRope);
12074
12075 // Load the first character into |output|.
12076 auto loadFirstChar = [&](auto encoding) {
12077 masm.loadStringChars(temp, output, encoding);
12078 masm.loadChar(Address(output, 0), output, encoding);
12079 };
12080
12081 Label done;
12082 if (ch <= JSString::MAX_LATIN1_CHAR) {
12083 // Handle both encodings when the search character is Latin-1.
12084 Label twoByte, compare;
12085 masm.branchTwoByteString(temp, &twoByte);
12086
12087 loadFirstChar(CharEncoding::Latin1);
12088 masm.jump(&compare);
12089
12090 masm.bind(&twoByte);
12091 loadFirstChar(CharEncoding::TwoByte);
12092
12093 masm.bind(&compare);
12094 } else {
12095 // The search character is a two-byte character, so it can't be equal to any
12096 // character of a Latin-1 string.
12097 masm.move32(Imm32(int32_t(op == JSOp::Lt || op == JSOp::Le)), output);
12098 masm.branchLatin1String(temp, &done);
12099
12100 loadFirstChar(CharEncoding::TwoByte);
12101 }
12102
12103 // Compare the string length when the search character is equal to the
12104 // input's first character.
12105 masm.branch32(Assembler::Equal, output, Imm32(ch), &compareLength);
12106
12107 // Otherwise compute the result and jump to the end.
12108 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), output, Imm32(ch),
12109 output);
12110 masm.jump(&done);
12111
12112 // Compare the string length to compute the overall result.
12113 masm.bind(&compareLength);
12114 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false),
12115 Address(temp, JSString::offsetOfLength()), Imm32(1), output);
12116
12117 masm.bind(&done);
12118}
12119
12120void CodeGenerator::visitCompareBigInt(LCompareBigInt* lir) {
12121 JSOp op = lir->mir()->jsop();
12122 Register left = ToRegister(lir->left());
12123 Register right = ToRegister(lir->right());
12124 Register temp0 = ToRegister(lir->temp0());
12125 Register temp1 = ToRegister(lir->temp1());
12126 Register temp2 = ToRegister(lir->temp2());
12127 Register output = ToRegister(lir->output());
12128
12129 Label notSame;
12130 Label compareSign;
12131 Label compareLength;
12132 Label compareDigit;
12133
12134 Label* notSameSign;
12135 Label* notSameLength;
12136 Label* notSameDigit;
12137 if (IsEqualityOp(op)) {
12138 notSameSign = &notSame;
12139 notSameLength = &notSame;
12140 notSameDigit = &notSame;
12141 } else {
12142 notSameSign = &compareSign;
12143 notSameLength = &compareLength;
12144 notSameDigit = &compareDigit;
12145 }
12146
12147 masm.equalBigInts(left, right, temp0, temp1, temp2, output, notSameSign,
12148 notSameLength, notSameDigit);
12149
12150 Label done;
12151 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le ||
12152 op == JSOp::Ge),
12153 output);
12154 masm.jump(&done);
12155
12156 if (IsEqualityOp(op)) {
12157 masm.bind(&notSame);
12158 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
12159 } else {
12160 Label invertWhenNegative;
12161
12162 // There are two cases when sign(left) != sign(right):
12163 // 1. sign(left) = positive and sign(right) = negative,
12164 // 2. or the dual case with reversed signs.
12165 //
12166 // For case 1, |left| <cmp> |right| is true for cmp=Gt or cmp=Ge and false
12167 // for cmp=Lt or cmp=Le. Initialize the result for case 1 and handle case 2
12168 // with |invertWhenNegative|.
12169 masm.bind(&compareSign);
12170 masm.move32(Imm32(op == JSOp::Gt || op == JSOp::Ge), output);
12171 masm.jump(&invertWhenNegative);
12172
12173 // For sign(left) = sign(right) and len(digits(left)) != len(digits(right)),
12174 // we have to consider the two cases:
12175 // 1. len(digits(left)) < len(digits(right))
12176 // 2. len(digits(left)) > len(digits(right))
12177 //
12178 // For |left| <cmp> |right| with cmp=Lt:
12179 // Assume both BigInts are positive, then |left < right| is true for case 1
12180 // and false for case 2. When both are negative, the result is reversed.
12181 //
12182 // The other comparison operators can be handled similarly.
12183 //
12184 // |temp0| holds the digits length of the right-hand side operand.
12185 masm.bind(&compareLength);
12186 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false),
12187 Address(left, BigInt::offsetOfLength()), temp0, output);
12188 masm.jump(&invertWhenNegative);
12189
12190 // Similar to the case above, compare the current digit to determine the
12191 // overall comparison result.
12192 //
12193 // |temp1| points to the current digit of the left-hand side operand.
12194 // |output| holds the current digit of the right-hand side operand.
12195 masm.bind(&compareDigit);
12196 masm.cmpPtrSet(JSOpToCondition(op, /* isSigned = */ false),
12197 Address(temp1, 0), output, output);
12198
12199 Label nonNegative;
12200 masm.bind(&invertWhenNegative);
12201 masm.branchIfBigIntIsNonNegative(left, &nonNegative);
12202 masm.xor32(Imm32(1), output);
12203 masm.bind(&nonNegative);
12204 }
12205
12206 masm.bind(&done);
12207}
12208
12209void CodeGenerator::visitCompareBigIntInt32(LCompareBigIntInt32* lir) {
12210 JSOp op = lir->mir()->jsop();
12211 Register left = ToRegister(lir->left());
12212 Register right = ToRegister(lir->right());
12213 Register temp0 = ToRegister(lir->temp0());
12214 Register temp1 = ToRegister(lir->temp1());
12215 Register output = ToRegister(lir->output());
12216
12217 Label ifTrue, ifFalse;
12218 masm.compareBigIntAndInt32(op, left, right, temp0, temp1, &ifTrue, &ifFalse);
12219
12220 Label done;
12221 masm.bind(&ifFalse);
12222 masm.move32(Imm32(0), output);
12223 masm.jump(&done);
12224 masm.bind(&ifTrue);
12225 masm.move32(Imm32(1), output);
12226 masm.bind(&done);
12227}
12228
12229void CodeGenerator::visitCompareBigIntDouble(LCompareBigIntDouble* lir) {
12230 JSOp op = lir->mir()->jsop();
12231 Register left = ToRegister(lir->left());
12232 FloatRegister right = ToFloatRegister(lir->right());
12233 Register output = ToRegister(lir->output());
12234
12235 masm.setupAlignedABICall();
12236
12237 // Push the operands in reverse order for JSOp::Le and JSOp::Gt:
12238 // - |left <= right| is implemented as |right >= left|.
12239 // - |left > right| is implemented as |right < left|.
12240 if (op == JSOp::Le || op == JSOp::Gt) {
12241 masm.passABIArg(right, ABIType::Float64);
12242 masm.passABIArg(left);
12243 } else {
12244 masm.passABIArg(left);
12245 masm.passABIArg(right, ABIType::Float64);
12246 }
12247
12248 using FnBigIntNumber = bool (*)(BigInt*, double);
12249 using FnNumberBigInt = bool (*)(double, BigInt*);
12250 switch (op) {
12251 case JSOp::Eq: {
12252 masm.callWithABI<FnBigIntNumber,
12253 jit::BigIntNumberEqual<EqualityKind::Equal>>();
12254 break;
12255 }
12256 case JSOp::Ne: {
12257 masm.callWithABI<FnBigIntNumber,
12258 jit::BigIntNumberEqual<EqualityKind::NotEqual>>();
12259 break;
12260 }
12261 case JSOp::Lt: {
12262 masm.callWithABI<FnBigIntNumber,
12263 jit::BigIntNumberCompare<ComparisonKind::LessThan>>();
12264 break;
12265 }
12266 case JSOp::Gt: {
12267 masm.callWithABI<FnNumberBigInt,
12268 jit::NumberBigIntCompare<ComparisonKind::LessThan>>();
12269 break;
12270 }
12271 case JSOp::Le: {
12272 masm.callWithABI<
12273 FnNumberBigInt,
12274 jit::NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>>();
12275 break;
12276 }
12277 case JSOp::Ge: {
12278 masm.callWithABI<
12279 FnBigIntNumber,
12280 jit::BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>>();
12281 break;
12282 }
12283 default:
12284 MOZ_CRASH("unhandled op")do { do { } while (false); MOZ_ReportCrash("" "unhandled op",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12284); AnnotateMozCrashReason("MOZ_CRASH(" "unhandled op" ")"
); do { *((volatile int*)__null) = 12284; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
12285 }
12286
12287 masm.storeCallBoolResult(output);
12288}
12289
12290void CodeGenerator::visitCompareBigIntString(LCompareBigIntString* lir) {
12291 JSOp op = lir->mir()->jsop();
12292 Register left = ToRegister(lir->left());
12293 Register right = ToRegister(lir->right());
12294
12295 // Push the operands in reverse order for JSOp::Le and JSOp::Gt:
12296 // - |left <= right| is implemented as |right >= left|.
12297 // - |left > right| is implemented as |right < left|.
12298 if (op == JSOp::Le || op == JSOp::Gt) {
12299 pushArg(left);
12300 pushArg(right);
12301 } else {
12302 pushArg(right);
12303 pushArg(left);
12304 }
12305
12306 using FnBigIntString =
12307 bool (*)(JSContext*, HandleBigInt, HandleString, bool*);
12308 using FnStringBigInt =
12309 bool (*)(JSContext*, HandleString, HandleBigInt, bool*);
12310
12311 switch (op) {
12312 case JSOp::Eq: {
12313 constexpr auto Equal = EqualityKind::Equal;
12314 callVM<FnBigIntString, BigIntStringEqual<Equal>>(lir);
12315 break;
12316 }
12317 case JSOp::Ne: {
12318 constexpr auto NotEqual = EqualityKind::NotEqual;
12319 callVM<FnBigIntString, BigIntStringEqual<NotEqual>>(lir);
12320 break;
12321 }
12322 case JSOp::Lt: {
12323 constexpr auto LessThan = ComparisonKind::LessThan;
12324 callVM<FnBigIntString, BigIntStringCompare<LessThan>>(lir);
12325 break;
12326 }
12327 case JSOp::Gt: {
12328 constexpr auto LessThan = ComparisonKind::LessThan;
12329 callVM<FnStringBigInt, StringBigIntCompare<LessThan>>(lir);
12330 break;
12331 }
12332 case JSOp::Le: {
12333 constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual;
12334 callVM<FnStringBigInt, StringBigIntCompare<GreaterThanOrEqual>>(lir);
12335 break;
12336 }
12337 case JSOp::Ge: {
12338 constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual;
12339 callVM<FnBigIntString, BigIntStringCompare<GreaterThanOrEqual>>(lir);
12340 break;
12341 }
12342 default:
12343 MOZ_CRASH("Unexpected compare op")do { do { } while (false); MOZ_ReportCrash("" "Unexpected compare op"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12343); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected compare op"
")"); do { *((volatile int*)__null) = 12343; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
12344 }
12345}
12346
12347void CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir) {
12348 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12349; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
12349 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12349; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12350
12351 JSOp op = lir->mir()->jsop();
12352 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12352); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12352; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12353
12354 const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedV::ValueIndex);
12355 Register output = ToRegister(lir->output());
12356
12357 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
12358 if (!intact) {
12359 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
12360 addOutOfLineCode(ool, lir->mir());
12361
12362 Label* nullOrLikeUndefined = ool->label1();
12363 Label* notNullOrLikeUndefined = ool->label2();
12364
12365 {
12366 ScratchTagScope tag(masm, value);
12367 masm.splitTagForTest(value, tag);
12368
12369 masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
12370 masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
12371
12372 // Check whether it's a truthy object or a falsy object that emulates
12373 // undefined.
12374 masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
12375 }
12376
12377 Register objreg =
12378 masm.extractObject(value, ToTempUnboxRegister(lir->temp0()));
12379 branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined,
12380 notNullOrLikeUndefined, output, ool);
12381 // fall through
12382
12383 Label done;
12384
12385 // It's not null or undefined, and if it's an object it doesn't
12386 // emulate undefined, so it's not like undefined.
12387 masm.move32(Imm32(op == JSOp::Ne), output);
12388 masm.jump(&done);
12389
12390 masm.bind(nullOrLikeUndefined);
12391 masm.move32(Imm32(op == JSOp::Eq), output);
12392
12393 // Both branches meet here.
12394 masm.bind(&done);
12395 } else {
12396 Label nullOrUndefined, notNullOrLikeUndefined;
12397#if defined(DEBUG1) || defined(FUZZING)
12398 Register objreg = Register::Invalid();
12399#endif
12400 {
12401 ScratchTagScope tag(masm, value);
12402 masm.splitTagForTest(value, tag);
12403
12404 masm.branchTestNull(Assembler::Equal, tag, &nullOrUndefined);
12405 masm.branchTestUndefined(Assembler::Equal, tag, &nullOrUndefined);
12406
12407#if defined(DEBUG1) || defined(FUZZING)
12408 // Check whether it's a truthy object or a falsy object that emulates
12409 // undefined.
12410 masm.branchTestObject(Assembler::NotEqual, tag, &notNullOrLikeUndefined);
12411 objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp0()));
12412#endif
12413 }
12414
12415#if defined(DEBUG1) || defined(FUZZING)
12416 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
12417 masm.bind(&notNullOrLikeUndefined);
12418#endif
12419
12420 Label done;
12421
12422 // It's not null or undefined, and if it's an object it doesn't
12423 // emulate undefined.
12424 masm.move32(Imm32(op == JSOp::Ne), output);
12425 masm.jump(&done);
12426
12427 masm.bind(&nullOrUndefined);
12428 masm.move32(Imm32(op == JSOp::Eq), output);
12429
12430 // Both branches meet here.
12431 masm.bind(&done);
12432 }
12433}
12434
12435void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV(
12436 LIsNullOrLikeUndefinedAndBranchV* lir) {
12437 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12438); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12438; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
12438 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12438); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12438; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12439
12440 JSOp op = lir->cmpMir()->jsop();
12441 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12441); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12441; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12442
12443 const ValueOperand value =
12444 ToValue(lir, LIsNullOrLikeUndefinedAndBranchV::Value);
12445
12446 MBasicBlock* ifTrue = lir->ifTrue();
12447 MBasicBlock* ifFalse = lir->ifFalse();
12448
12449 if (op == JSOp::Ne) {
12450 // Swap branches.
12451 std::swap(ifTrue, ifFalse);
12452 }
12453
12454 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
12455
12456 Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
12457 Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
12458
12459 {
12460 ScratchTagScope tag(masm, value);
12461 masm.splitTagForTest(value, tag);
12462
12463 masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
12464 masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
12465
12466 masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
12467 }
12468
12469 bool extractObject = !intact;
Value stored to 'extractObject' during its initialization is never read
12470#if defined(DEBUG1) || defined(FUZZING)
12471 // always extract objreg if we're in debug and
12472 // assertObjectDoesNotEmulateUndefined;
12473 extractObject = true;
12474#endif
12475
12476 Register objreg = Register::Invalid();
12477 Register scratch = ToRegister(lir->temp());
12478 if (extractObject) {
12479 objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
12480 }
12481 if (!intact) {
12482 // Objects that emulate undefined are loosely equal to null/undefined.
12483 OutOfLineTestObject* ool = new (alloc()) OutOfLineTestObject();
12484 addOutOfLineCode(ool, lir->cmpMir());
12485 testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch,
12486 ool);
12487 } else {
12488 assertObjectDoesNotEmulateUndefined(objreg, scratch, lir->cmpMir());
12489 // Bug 1874905. This would be nice to optimize out at the MIR level.
12490 masm.jump(ifFalseLabel);
12491 }
12492}
12493
12494void CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir) {
12495 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12496; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
12496 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12496; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12497 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12497); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->lhs()->type() == MIRType::Object"
")"); do { *((volatile int*)__null) = 12497; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12498
12499 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
12500 JSOp op = lir->mir()->jsop();
12501 Register output = ToRegister(lir->output());
12502 Register objreg = ToRegister(lir->input());
12503 if (!intact) {
12504 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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { *
((volatile int*)__null) = 12505; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
12505 "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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { *
((volatile int*)__null) = 12505; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
12506
12507 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
12508 addOutOfLineCode(ool, lir->mir());
12509
12510 Label* emulatesUndefined = ool->label1();
12511 Label* doesntEmulateUndefined = ool->label2();
12512
12513 branchTestObjectEmulatesUndefined(objreg, emulatesUndefined,
12514 doesntEmulateUndefined, output, ool);
12515
12516 Label done;
12517
12518 masm.move32(Imm32(op == JSOp::Ne), output);
12519 masm.jump(&done);
12520
12521 masm.bind(emulatesUndefined);
12522 masm.move32(Imm32(op == JSOp::Eq), output);
12523 masm.bind(&done);
12524 } else {
12525 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
12526 masm.move32(Imm32(op == JSOp::Ne), output);
12527 }
12528}
12529
12530void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT(
12531 LIsNullOrLikeUndefinedAndBranchT* lir) {
12532 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12533; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
12533 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12533; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12534 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->lhs()->type() == MIRType::Object"
")"); do { *((volatile int*)__null) = 12534; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12535
12536 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
12537
12538 JSOp op = lir->cmpMir()->jsop();
12539 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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12539); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { *
((volatile int*)__null) = 12539; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
12540
12541 MBasicBlock* ifTrue = lir->ifTrue();
12542 MBasicBlock* ifFalse = lir->ifFalse();
12543
12544 if (op == JSOp::Ne) {
12545 // Swap branches.
12546 std::swap(ifTrue, ifFalse);
12547 }
12548
12549 Register input = ToRegister(lir->getOperand(0));
12550 Register scratch = ToRegister(lir->temp());
12551 Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
12552 Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
12553
12554 if (intact) {
12555 // Bug 1874905. Ideally branches like this would be optimized out.
12556 assertObjectDoesNotEmulateUndefined(input, scratch, lir->mir());
12557 masm.jump(ifFalseLabel);
12558 } else {
12559 auto* ool = new (alloc()) OutOfLineTestObject();
12560 addOutOfLineCode(ool, lir->cmpMir());
12561
12562 // Objects that emulate undefined are loosely equal to null/undefined.
12563 testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool);
12564 }
12565}
12566
12567void CodeGenerator::visitIsNull(LIsNull* lir) {
12568 MCompare::CompareType compareType = lir->mir()->compareType();
12569 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12569); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12569; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12570
12571 JSOp op = lir->mir()->jsop();
12572 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12572; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12573
12574 const ValueOperand value = ToValue(lir, LIsNull::ValueIndex);
12575 Register output = ToRegister(lir->output());
12576
12577 Assembler::Condition cond = JSOpToCondition(compareType, op);
12578 masm.testNullSet(cond, value, output);
12579}
12580
12581void CodeGenerator::visitIsUndefined(LIsUndefined* lir) {
12582 MCompare::CompareType compareType = lir->mir()->compareType();
12583 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12583); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined"
")"); do { *((volatile int*)__null) = 12583; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12584
12585 JSOp op = lir->mir()->jsop();
12586 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12586; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12587
12588 const ValueOperand value = ToValue(lir, LIsUndefined::ValueIndex);
12589 Register output = ToRegister(lir->output());
12590
12591 Assembler::Condition cond = JSOpToCondition(compareType, op);
12592 masm.testUndefinedSet(cond, value, output);
12593}
12594
12595void CodeGenerator::visitIsNullAndBranch(LIsNullAndBranch* lir) {
12596 MCompare::CompareType compareType = lir->cmpMir()->compareType();
12597 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12597); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12597; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12598
12599 JSOp op = lir->cmpMir()->jsop();
12600 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12600); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12600; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12601
12602 const ValueOperand value = ToValue(lir, LIsNullAndBranch::Value);
12603
12604 Assembler::Condition cond = JSOpToCondition(compareType, op);
12605 testNullEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
12606}
12607
12608void CodeGenerator::visitIsUndefinedAndBranch(LIsUndefinedAndBranch* lir) {
12609 MCompare::CompareType compareType = lir->cmpMir()->compareType();
12610 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12610); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined"
")"); do { *((volatile int*)__null) = 12610; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12611
12612 JSOp op = lir->cmpMir()->jsop();
12613 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)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12613); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12613; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12614
12615 const ValueOperand value = ToValue(lir, LIsUndefinedAndBranch::Value);
12616
12617 Assembler::Condition cond = JSOpToCondition(compareType, op);
12618 testUndefinedEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse());
12619}
12620
12621void CodeGenerator::visitSameValueDouble(LSameValueDouble* lir) {
12622 FloatRegister left = ToFloatRegister(lir->left());
12623 FloatRegister right = ToFloatRegister(lir->right());
12624 FloatRegister temp = ToFloatRegister(lir->temp0());
12625 Register output = ToRegister(lir->output());
12626
12627 masm.sameValueDouble(left, right, temp, output);
12628}
12629
12630void CodeGenerator::visitSameValue(LSameValue* lir) {
12631 ValueOperand lhs = ToValue(lir, LSameValue::LhsIndex);
12632 ValueOperand rhs = ToValue(lir, LSameValue::RhsIndex);
12633 Register output = ToRegister(lir->output());
12634
12635 using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*);
12636 OutOfLineCode* ool =
12637 oolCallVM<Fn, SameValue>(lir, ArgList(lhs, rhs), StoreRegisterTo(output));
12638
12639 // First check to see if the values have identical bits.
12640 // This is correct for SameValue because SameValue(NaN,NaN) is true,
12641 // and SameValue(0,-0) is false.
12642 masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(),
12643 ool->entry());
12644 masm.move32(Imm32(1), output);
12645
12646 // If this fails, call SameValue.
12647 masm.bind(ool->rejoin());
12648}
12649
12650void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs,
12651 Register output) {
12652 using Fn =
12653 JSString* (*)(JSContext*, HandleString, HandleString, js::gc::Heap);
12654 OutOfLineCode* ool = oolCallVM<Fn, ConcatStrings<CanGC>>(
12655 lir, ArgList(lhs, rhs, static_cast<Imm32>(int32_t(gc::Heap::Default))),
12656 StoreRegisterTo(output));
12657
12658 const JitZone* jitZone = gen->realm->zone()->jitZone();
12659 JitCode* stringConcatStub =
12660 jitZone->stringConcatStubNoBarrier(&zoneStubsToReadBarrier_);
12661 masm.call(stringConcatStub);
12662 masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
12663
12664 masm.bind(ool->rejoin());
12665}
12666
12667void CodeGenerator::visitConcat(LConcat* lir) {
12668 Register lhs = ToRegister(lir->lhs());
12669 Register rhs = ToRegister(lir->rhs());
12670
12671 Register output = ToRegister(lir->output());
12672
12673 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12673); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs == CallTempReg0"
")"); do { *((volatile int*)__null) = 12673; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12674 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs == CallTempReg1"
")"); do { *((volatile int*)__null) = 12674; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12675 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12675); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp0()) == CallTempReg0"
")"); do { *((volatile int*)__null) = 12675; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12676 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12676); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp1()) == CallTempReg1"
")"); do { *((volatile int*)__null) = 12676; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12677 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp2()) == CallTempReg2"
")"); do { *((volatile int*)__null) = 12677; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12678 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12678); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp3()) == CallTempReg3"
")"); do { *((volatile int*)__null) = 12678; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12679 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12679); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp4()) == CallTempReg4"
")"); do { *((volatile int*)__null) = 12679; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12680 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12680); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == CallTempReg5"
")"); do { *((volatile int*)__null) = 12680; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12681
12682 emitConcat(lir, lhs, rhs, output);
12683}
12684
12685static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
12686 Register len, Register byteOpScratch,
12687 CharEncoding fromEncoding, CharEncoding toEncoding,
12688 size_t maximumLength = SIZE_MAX(18446744073709551615UL)) {
12689 // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
12690 // (checked below in debug builds), and when done |to| must point to the
12691 // next available char.
12692
12693#ifdef DEBUG1
12694 Label ok;
12695 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
12696 masm.assumeUnreachable("Length should be greater than 0.");
12697 masm.bind(&ok);
12698
12699 if (maximumLength != SIZE_MAX(18446744073709551615UL)) {
12700 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"
")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12700); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maximumLength <= (2147483647)"
") (" "maximum length fits into int32" ")"); do { *((volatile
int*)__null) = 12700; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
12701
12702 Label ok;
12703 masm.branchPtr(Assembler::BelowOrEqual, len, Imm32(maximumLength), &ok);
12704 masm.assumeUnreachable("Length should not exceed maximum length.");
12705 masm.bind(&ok);
12706 }
12707#endif
12708
12709 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1"
")"); do { *((volatile int*)__null) = 12710; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
12710 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1"
")"); do { *((volatile int*)__null) = 12710; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
12711
12712 size_t fromWidth =
12713 fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
12714 size_t toWidth =
12715 toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
12716
12717 // Try to copy multiple characters at once when both encoding are equal.
12718 if (fromEncoding == toEncoding) {
12719 constexpr size_t ptrWidth = sizeof(uintptr_t);
12720
12721 // Copy |width| bytes and then adjust |from| and |to|.
12722 auto copyCharacters = [&](size_t width) {
12723 static_assert(ptrWidth <= 8, "switch handles only up to eight bytes");
12724
12725 switch (width) {
12726 case 1:
12727 masm.load8ZeroExtend(Address(from, 0), byteOpScratch);
12728 masm.store8(byteOpScratch, Address(to, 0));
12729 break;
12730 case 2:
12731 masm.load16ZeroExtend(Address(from, 0), byteOpScratch);
12732 masm.store16(byteOpScratch, Address(to, 0));
12733 break;
12734 case 4:
12735 masm.load32(Address(from, 0), byteOpScratch);
12736 masm.store32(byteOpScratch, Address(to, 0));
12737 break;
12738 case 8:
12739 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12739); AnnotateMozCrashReason("MOZ_ASSERT" "(" "width == ptrWidth"
")"); do { *((volatile int*)__null) = 12739; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12740 masm.loadPtr(Address(from, 0), byteOpScratch);
12741 masm.storePtr(byteOpScratch, Address(to, 0));
12742 break;
12743 }
12744
12745 masm.addPtr(Imm32(width), from);
12746 masm.addPtr(Imm32(width), to);
12747 };
12748
12749 // First align |len| to pointer width.
12750 Label done;
12751 for (size_t width = fromWidth; width < ptrWidth; width *= 2) {
12752 // Number of characters which fit into |width| bytes.
12753 size_t charsPerWidth = width / fromWidth;
12754
12755 if (charsPerWidth < maximumLength) {
12756 Label next;
12757 masm.branchTest32(Assembler::Zero, len, Imm32(charsPerWidth), &next);
12758
12759 copyCharacters(width);
12760
12761 masm.branchSub32(Assembler::Zero, Imm32(charsPerWidth), len, &done);
12762 masm.bind(&next);
12763 } else if (charsPerWidth == maximumLength) {
12764 copyCharacters(width);
12765 masm.sub32(Imm32(charsPerWidth), len);
12766 }
12767 }
12768
12769 size_t maxInlineLength;
12770 if (fromEncoding == CharEncoding::Latin1) {
12771 maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1;
12772 } else {
12773 maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
12774 }
12775
12776 // Number of characters which fit into a single register.
12777 size_t charsPerPtr = ptrWidth / fromWidth;
12778
12779 // Unroll small loops.
12780 constexpr size_t unrollLoopLimit = 3;
12781 size_t loopCount = std::min(maxInlineLength, maximumLength) / charsPerPtr;
12782
12783#ifdef JS_64BIT1
12784 static constexpr size_t latin1MaxInlineByteLength =
12785 JSFatInlineString::MAX_LENGTH_LATIN1 * sizeof(char);
12786 static constexpr size_t twoByteMaxInlineByteLength =
12787 JSFatInlineString::MAX_LENGTH_TWO_BYTE * sizeof(char16_t);
12788
12789 // |unrollLoopLimit| should be large enough to allow loop unrolling on
12790 // 64-bit targets.
12791 static_assert(latin1MaxInlineByteLength / ptrWidth == unrollLoopLimit,
12792 "Latin-1 loops are unrolled on 64-bit");
12793 static_assert(twoByteMaxInlineByteLength / ptrWidth == unrollLoopLimit,
12794 "Two-byte loops are unrolled on 64-bit");
12795#endif
12796
12797 if (loopCount <= unrollLoopLimit) {
12798 Label labels[unrollLoopLimit];
12799
12800 // Check up front how many characters can be copied.
12801 for (size_t i = 1; i < loopCount; i++) {
12802 masm.branch32(Assembler::Below, len, Imm32((i + 1) * charsPerPtr),
12803 &labels[i]);
12804 }
12805
12806 // Generate the unrolled loop body.
12807 for (size_t i = loopCount; i > 0; i--) {
12808 copyCharacters(ptrWidth);
12809 masm.sub32(Imm32(charsPerPtr), len);
12810
12811 // Jump target for the previous length check.
12812 if (i != 1) {
12813 masm.bind(&labels[i - 1]);
12814 }
12815 }
12816 } else {
12817 Label start;
12818 masm.bind(&start);
12819 copyCharacters(ptrWidth);
12820 masm.branchSub32(Assembler::NonZero, Imm32(charsPerPtr), len, &start);
12821 }
12822
12823 masm.bind(&done);
12824 } else {
12825 Label start;
12826 masm.bind(&start);
12827 masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding);
12828 masm.storeChar(byteOpScratch, Address(to, 0), toEncoding);
12829 masm.addPtr(Imm32(fromWidth), from);
12830 masm.addPtr(Imm32(toWidth), to);
12831 masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
12832 }
12833}
12834
12835static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
12836 Register len, Register byteOpScratch,
12837 CharEncoding encoding, size_t maximumLength) {
12838 CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding,
12839 maximumLength);
12840}
12841
12842static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input,
12843 Register destChars, Register temp1,
12844 Register temp2) {
12845 // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may
12846 // have to inflate.
12847
12848 Label isLatin1, done;
12849 masm.loadStringLength(input, temp1);
12850 masm.branchLatin1String(input, &isLatin1);
12851 {
12852 masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
12853 masm.movePtr(temp2, input);
12854 CopyStringChars(masm, destChars, input, temp1, temp2,
12855 CharEncoding::TwoByte);
12856 masm.jump(&done);
12857 }
12858 masm.bind(&isLatin1);
12859 {
12860 masm.loadStringChars(input, temp2, CharEncoding::Latin1);
12861 masm.movePtr(temp2, input);
12862 CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1,
12863 CharEncoding::TwoByte);
12864 }
12865 masm.bind(&done);
12866}
12867
12868static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output,
12869 Register length, Register temp,
12870 gc::Heap initialStringHeap,
12871 Label* failure,
12872 CharEncoding encoding) {
12873#ifdef DEBUG1
12874 size_t maxInlineLength;
12875 if (encoding == CharEncoding::Latin1) {
12876 maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1;
12877 } else {
12878 maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
12879 }
12880
12881 Label ok;
12882 masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok);
12883 masm.assumeUnreachable("string length too large to be allocated as inline");
12884 masm.bind(&ok);
12885#endif
12886
12887 size_t maxThinInlineLength;
12888 if (encoding == CharEncoding::Latin1) {
12889 maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1;
12890 } else {
12891 maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE;
12892 }
12893
12894 Label isFat, allocDone;
12895 masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat);
12896 {
12897 uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS;
12898 if (encoding == CharEncoding::Latin1) {
12899 flags |= JSString::LATIN1_CHARS_BIT;
12900 }
12901 masm.newGCString(output, temp, initialStringHeap, failure);
12902 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
12903 masm.jump(&allocDone);
12904 }
12905 masm.bind(&isFat);
12906 {
12907 uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS;
12908 if (encoding == CharEncoding::Latin1) {
12909 flags |= JSString::LATIN1_CHARS_BIT;
12910 }
12911 masm.newGCFatInlineString(output, temp, initialStringHeap, failure);
12912 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
12913 }
12914 masm.bind(&allocDone);
12915
12916 // Store length.
12917 masm.store32(length, Address(output, JSString::offsetOfLength()));
12918}
12919
12920static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs,
12921 Register output, Register temp1, Register temp2,
12922 Register temp3, gc::Heap initialStringHeap,
12923 Label* failure, CharEncoding encoding) {
12924 JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)",
12925 (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
12926
12927 // State: result length in temp2.
12928
12929 // Ensure both strings are linear.
12930 masm.branchIfRope(lhs, failure);
12931 masm.branchIfRope(rhs, failure);
12932
12933 // Allocate a JSThinInlineString or JSFatInlineString.
12934 AllocateThinOrFatInlineString(masm, output, temp2, temp1, initialStringHeap,
12935 failure, encoding);
12936
12937 // Load chars pointer in temp2.
12938 masm.loadInlineStringCharsForStore(output, temp2);
12939
12940 auto copyChars = [&](Register src) {
12941 if (encoding == CharEncoding::TwoByte) {
12942 CopyStringCharsMaybeInflate(masm, src, temp2, temp1, temp3);
12943 } else {
12944 masm.loadStringLength(src, temp3);
12945 masm.loadStringChars(src, temp1, CharEncoding::Latin1);
12946 masm.movePtr(temp1, src);
12947 CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1);
12948 }
12949 };
12950
12951 // Copy lhs chars. Note that this advances temp2 to point to the next
12952 // char. This also clobbers the lhs register.
12953 copyChars(lhs);
12954
12955 // Copy rhs chars. Clobbers the rhs register.
12956 copyChars(rhs);
12957}
12958
12959void CodeGenerator::visitSubstr(LSubstr* lir) {
12960 Register string = ToRegister(lir->string());
12961 Register begin = ToRegister(lir->begin());
12962 Register length = ToRegister(lir->length());
12963 Register output = ToRegister(lir->output());
12964 Register temp0 = ToRegister(lir->temp0());
12965 Register temp2 = ToRegister(lir->temp2());
12966
12967 // On x86 there are not enough registers. In that case reuse the string
12968 // register as temporary.
12969 Register temp1 =
12970 lir->temp1()->isBogusTemp() ? string : ToRegister(lir->temp1());
12971
12972 size_t maximumLength = SIZE_MAX(18446744073709551615UL);
12973
12974 Range* range = lir->mir()->length()->range();
12975 if (range && range->hasInt32UpperBound()) {
12976 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "range->upper() >= 0"
")"); do { *((volatile int*)__null) = 12976; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12977 maximumLength = size_t(range->upper());
12978 }
12979
12980 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <=
12981 JSThinInlineString::MAX_LENGTH_LATIN1);
12982
12983 static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE <=
12984 JSFatInlineString::MAX_LENGTH_LATIN1);
12985
12986 bool tryFatInlineOrDependent =
12987 maximumLength > JSThinInlineString::MAX_LENGTH_TWO_BYTE;
12988 bool tryDependent = maximumLength > JSFatInlineString::MAX_LENGTH_TWO_BYTE;
12989
12990#ifdef DEBUG1
12991 if (maximumLength != SIZE_MAX(18446744073709551615UL)) {
12992 Label ok;
12993 masm.branch32(Assembler::BelowOrEqual, length, Imm32(maximumLength), &ok);
12994 masm.assumeUnreachable("length should not exceed maximum length");
12995 masm.bind(&ok);
12996 }
12997#endif
12998
12999 Label nonZero, nonInput;
13000
13001 // For every edge case use the C++ variant.
13002 // Note: we also use this upon allocation failure in newGCString and
13003 // newGCFatInlineString. To squeeze out even more performance those failures
13004 // can be handled by allocate in ool code and returning to jit code to fill
13005 // in all data.
13006 using Fn = JSString* (*)(JSContext* cx, HandleString str, int32_t begin,
13007 int32_t len);
13008 OutOfLineCode* ool = oolCallVM<Fn, SubstringKernel>(
13009 lir, ArgList(string, begin, length), StoreRegisterTo(output));
13010 Label* slowPath = ool->entry();
13011 Label* done = ool->rejoin();
13012
13013 // Zero length, return emptystring.
13014 masm.branchTest32(Assembler::NonZero, length, length, &nonZero);
13015 const JSAtomState& names = gen->runtime->names();
13016 masm.movePtr(ImmGCPtr(names.empty_), output);
13017 masm.jump(done);
13018
13019 // Substring from 0..|str.length|, return str.
13020 masm.bind(&nonZero);
13021 masm.branch32(Assembler::NotEqual,
13022 Address(string, JSString::offsetOfLength()), length, &nonInput);
13023#ifdef DEBUG1
13024 {
13025 Label ok;
13026 masm.branchTest32(Assembler::Zero, begin, begin, &ok);
13027 masm.assumeUnreachable("length == str.length implies begin == 0");
13028 masm.bind(&ok);
13029 }
13030#endif
13031 masm.movePtr(string, output);
13032 masm.jump(done);
13033
13034 // Use slow path for ropes.
13035 masm.bind(&nonInput);
13036 masm.branchIfRope(string, slowPath);
13037
13038 // Optimize one and two character strings.
13039 Label nonStatic;
13040 masm.branch32(Assembler::Above, length, Imm32(2), &nonStatic);
13041 {
13042 Label loadLengthOne, loadLengthTwo;
13043
13044 auto loadChars = [&](CharEncoding encoding, bool fallthru) {
13045 size_t size = encoding == CharEncoding::Latin1 ? sizeof(JS::Latin1Char)
13046 : sizeof(char16_t);
13047
13048 masm.loadStringChars(string, temp0, encoding);
13049 masm.loadChar(temp0, begin, temp2, encoding);
13050 masm.branch32(Assembler::Equal, length, Imm32(1), &loadLengthOne);
13051 masm.loadChar(temp0, begin, temp0, encoding, int32_t(size));
13052 if (!fallthru) {
13053 masm.jump(&loadLengthTwo);
13054 }
13055 };
13056
13057 Label isLatin1;
13058 masm.branchLatin1String(string, &isLatin1);
13059 loadChars(CharEncoding::TwoByte, /* fallthru = */ false);
13060
13061 masm.bind(&isLatin1);
13062 loadChars(CharEncoding::Latin1, /* fallthru = */ true);
13063
13064 // Try to load a length-two static string.
13065 masm.bind(&loadLengthTwo);
13066 masm.lookupStaticString(temp2, temp0, output, gen->runtime->staticStrings(),
13067 &nonStatic);
13068 masm.jump(done);
13069
13070 // Try to load a length-one static string.
13071 masm.bind(&loadLengthOne);
13072 masm.lookupStaticString(temp2, output, gen->runtime->staticStrings(),
13073 &nonStatic);
13074 masm.jump(done);
13075 }
13076 masm.bind(&nonStatic);
13077
13078 // Allocate either a JSThinInlineString or JSFatInlineString, or jump to
13079 // notInline if we need a dependent string.
13080 Label notInline;
13081 {
13082 static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 <
13083 JSFatInlineString::MAX_LENGTH_LATIN1);
13084 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <
13085 JSFatInlineString::MAX_LENGTH_TWO_BYTE);
13086
13087 // Use temp2 to store the JS(Thin|Fat)InlineString flags. This avoids having
13088 // duplicate newGCString/newGCFatInlineString codegen for Latin1 vs TwoByte
13089 // strings.
13090
13091 Label allocFat, allocDone;
13092 if (tryFatInlineOrDependent) {
13093 Label isLatin1, allocThin;
13094 masm.branchLatin1String(string, &isLatin1);
13095 {
13096 if (tryDependent) {
13097 masm.branch32(Assembler::Above, length,
13098 Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
13099 &notInline);
13100 }
13101 masm.move32(Imm32(0), temp2);
13102 masm.branch32(Assembler::Above, length,
13103 Imm32(JSThinInlineString::MAX_LENGTH_TWO_BYTE),
13104 &allocFat);
13105 masm.jump(&allocThin);
13106 }
13107
13108 masm.bind(&isLatin1);
13109 {
13110 if (tryDependent) {
13111 masm.branch32(Assembler::Above, length,
13112 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1),
13113 &notInline);
13114 }
13115 masm.move32(Imm32(JSString::LATIN1_CHARS_BIT), temp2);
13116 masm.branch32(Assembler::Above, length,
13117 Imm32(JSThinInlineString::MAX_LENGTH_LATIN1), &allocFat);
13118 }
13119
13120 masm.bind(&allocThin);
13121 } else {
13122 masm.load32(Address(string, JSString::offsetOfFlags()), temp2);
13123 masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp2);
13124 }
13125
13126 {
13127 masm.newGCString(output, temp0, initialStringHeap(), slowPath);
13128 masm.or32(Imm32(JSString::INIT_THIN_INLINE_FLAGS), temp2);
13129 }
13130
13131 if (tryFatInlineOrDependent) {
13132 masm.jump(&allocDone);
13133
13134 masm.bind(&allocFat);
13135 {
13136 masm.newGCFatInlineString(output, temp0, initialStringHeap(), slowPath);
13137 masm.or32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), temp2);
13138 }
13139
13140 masm.bind(&allocDone);
13141 }
13142
13143 masm.store32(temp2, Address(output, JSString::offsetOfFlags()));
13144 masm.store32(length, Address(output, JSString::offsetOfLength()));
13145
13146 auto initializeInlineString = [&](CharEncoding encoding) {
13147 masm.loadStringChars(string, temp0, encoding);
13148 masm.addToCharPtr(temp0, begin, encoding);
13149 if (temp1 == string) {
13150 masm.push(string);
13151 }
13152 masm.loadInlineStringCharsForStore(output, temp1);
13153 CopyStringChars(masm, temp1, temp0, length, temp2, encoding,
13154 maximumLength);
13155 masm.loadStringLength(output, length);
13156 if (temp1 == string) {
13157 masm.pop(string);
13158 }
13159 };
13160
13161 Label isInlineLatin1;
13162 masm.branchTest32(Assembler::NonZero, temp2,
13163 Imm32(JSString::LATIN1_CHARS_BIT), &isInlineLatin1);
13164 initializeInlineString(CharEncoding::TwoByte);
13165 masm.jump(done);
13166
13167 masm.bind(&isInlineLatin1);
13168 initializeInlineString(CharEncoding::Latin1);
13169 }
13170
13171 // Handle other cases with a DependentString.
13172 if (tryDependent) {
13173 masm.jump(done);
13174
13175 masm.bind(&notInline);
13176 masm.newGCString(output, temp0, gen->initialStringHeap(), slowPath);
13177 masm.store32(length, Address(output, JSString::offsetOfLength()));
13178
13179 // Note: no post barrier is needed because the dependent string is either
13180 // allocated in the nursery or both strings are tenured (if nursery strings
13181 // are disabled for this zone).
13182 EmitInitDependentStringBase(masm, output, string, temp0, temp2,
13183 /* needsPostBarrier = */ false);
13184
13185 auto initializeDependentString = [&](CharEncoding encoding) {
13186 uint32_t flags = JSString::INIT_DEPENDENT_FLAGS;
13187 if (encoding == CharEncoding::Latin1) {
13188 flags |= JSString::LATIN1_CHARS_BIT;
13189 }
13190 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13191 masm.loadNonInlineStringChars(string, temp0, encoding);
13192 masm.addToCharPtr(temp0, begin, encoding);
13193 masm.storeNonInlineStringChars(temp0, output);
13194 };
13195
13196 Label isLatin1;
13197 masm.branchLatin1String(string, &isLatin1);
13198 initializeDependentString(CharEncoding::TwoByte);
13199 masm.jump(done);
13200
13201 masm.bind(&isLatin1);
13202 initializeDependentString(CharEncoding::Latin1);
13203 }
13204
13205 masm.bind(done);
13206}
13207
13208JitCode* JitZone::generateStringConcatStub(JSContext* cx) {
13209 JitSpew(JitSpew_Codegen, "# Emitting StringConcat stub");
13210
13211 TempAllocator temp(&cx->tempLifoAlloc());
13212 JitContext jcx(cx);
13213 StackMacroAssembler masm(cx, temp);
13214 AutoCreatedBy acb(masm, "JitZone::generateStringConcatStub");
13215
13216 Register lhs = CallTempReg0;
13217 Register rhs = CallTempReg1;
13218 Register temp1 = CallTempReg2;
13219 Register temp2 = CallTempReg3;
13220 Register temp3 = CallTempReg4;
13221 Register output = CallTempReg5;
13222
13223 Label failure;
13224#ifdef JS_USE_LINK_REGISTER
13225 masm.pushReturnAddress();
13226#endif
13227 masm.Push(FramePointer);
13228 masm.moveStackPtrTo(FramePointer);
13229
13230 // If lhs is empty, return rhs.
13231 Label leftEmpty;
13232 masm.loadStringLength(lhs, temp1);
13233 masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty);
13234
13235 // If rhs is empty, return lhs.
13236 Label rightEmpty;
13237 masm.loadStringLength(rhs, temp2);
13238 masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty);
13239
13240 masm.add32(temp1, temp2);
13241
13242 // Check if we can use a JSInlineString. The result is a Latin1 string if
13243 // lhs and rhs are both Latin1, so we AND the flags.
13244 Label isInlineTwoByte, isInlineLatin1;
13245 masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1);
13246 masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1);
13247
13248 Label isLatin1, notInline;
13249 masm.branchTest32(Assembler::NonZero, temp1,
13250 Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
13251 {
13252 masm.branch32(Assembler::BelowOrEqual, temp2,
13253 Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
13254 &isInlineTwoByte);
13255 masm.jump(&notInline);
13256 }
13257 masm.bind(&isLatin1);
13258 {
13259 masm.branch32(Assembler::BelowOrEqual, temp2,
13260 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1);
13261 }
13262 masm.bind(&notInline);
13263
13264 // Keep AND'ed flags in temp1.
13265
13266 // Ensure result length <= JSString::MAX_LENGTH.
13267 masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
13268
13269 // Allocate a new rope, guaranteed to be in the nursery if initialStringHeap
13270 // == gc::Heap::Default. (As a result, no post barriers are needed below.)
13271 masm.newGCString(output, temp3, initialStringHeap, &failure);
13272
13273 // Store rope length and flags. temp1 still holds the result of AND'ing the
13274 // lhs and rhs flags, so we just have to clear the other flags to get our rope
13275 // flags (Latin1 if both lhs and rhs are Latin1).
13276 static_assert(JSString::INIT_ROPE_FLAGS == 0,
13277 "Rope type flags must have no bits set");
13278 masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1);
13279 masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
13280 masm.store32(temp2, Address(output, JSString::offsetOfLength()));
13281
13282 // Store left and right nodes.
13283 masm.storeRopeChildren(lhs, rhs, output);
13284 masm.pop(FramePointer);
13285 masm.ret();
13286
13287 masm.bind(&leftEmpty);
13288 masm.mov(rhs, output);
13289 masm.pop(FramePointer);
13290 masm.ret();
13291
13292 masm.bind(&rightEmpty);
13293 masm.mov(lhs, output);
13294 masm.pop(FramePointer);
13295 masm.ret();
13296
13297 masm.bind(&isInlineTwoByte);
13298 ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
13299 initialStringHeap, &failure, CharEncoding::TwoByte);
13300 masm.pop(FramePointer);
13301 masm.ret();
13302
13303 masm.bind(&isInlineLatin1);
13304 ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
13305 initialStringHeap, &failure, CharEncoding::Latin1);
13306 masm.pop(FramePointer);
13307 masm.ret();
13308
13309 masm.bind(&failure);
13310 masm.movePtr(ImmPtr(nullptr), output);
13311 masm.pop(FramePointer);
13312 masm.ret();
13313
13314 Linker linker(masm);
13315 JitCode* code = linker.newCode(cx, CodeKind::Other);
13316
13317 CollectPerfSpewerJitCodeProfile(code, "StringConcatStub");
13318#ifdef MOZ_VTUNE1
13319 vtune::MarkStub(code, "StringConcatStub");
13320#endif
13321
13322 return code;
13323}
13324
13325void JitRuntime::generateFreeStub(MacroAssembler& masm) {
13326 AutoCreatedBy acb(masm, "JitRuntime::generateFreeStub");
13327
13328 const Register regSlots = CallTempReg0;
13329
13330 freeStubOffset_ = startTrampolineCode(masm);
13331
13332#ifdef JS_USE_LINK_REGISTER
13333 masm.pushReturnAddress();
13334#endif
13335 AllocatableRegisterSet regs(RegisterSet::Volatile());
13336 regs.takeUnchecked(regSlots);
13337 LiveRegisterSet save(regs.asLiveSet());
13338 masm.PushRegsInMask(save);
13339
13340 const Register regTemp = regs.takeAnyGeneral();
13341 MOZ_ASSERT(regTemp != regSlots)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(regTemp != regSlots)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(regTemp != regSlots))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("regTemp != regSlots"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13341); AnnotateMozCrashReason("MOZ_ASSERT" "(" "regTemp != regSlots"
")"); do { *((volatile int*)__null) = 13341; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13342
13343 using Fn = void (*)(void* p);
13344 masm.setupUnalignedABICall(regTemp);
13345 masm.passABIArg(regSlots);
13346 masm.callWithABI<Fn, js_free>(ABIType::General,
13347 CheckUnsafeCallWithABI::DontCheckOther);
13348
13349 masm.PopRegsInMask(save);
13350
13351 masm.ret();
13352}
13353
13354void JitRuntime::generateLazyLinkStub(MacroAssembler& masm) {
13355 AutoCreatedBy acb(masm, "JitRuntime::generateLazyLinkStub");
13356
13357 lazyLinkStubOffset_ = startTrampolineCode(masm);
13358
13359#ifdef JS_USE_LINK_REGISTER
13360 masm.pushReturnAddress();
13361#endif
13362 masm.Push(FramePointer);
13363 masm.moveStackPtrTo(FramePointer);
13364
13365 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
13366 Register temp0 = regs.takeAny();
13367 Register temp1 = regs.takeAny();
13368 Register temp2 = regs.takeAny();
13369
13370 masm.loadJSContext(temp0);
13371 masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink);
13372 masm.moveStackPtrTo(temp1);
13373
13374 using Fn = uint8_t* (*)(JSContext* cx, LazyLinkExitFrameLayout* frame);
13375 masm.setupUnalignedABICall(temp2);
13376 masm.passABIArg(temp0);
13377 masm.passABIArg(temp1);
13378 masm.callWithABI<Fn, LazyLinkTopActivation>(
13379 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
13380
13381 // Discard exit frame and restore frame pointer.
13382 masm.leaveExitFrame(0);
13383 masm.pop(FramePointer);
13384
13385#ifdef JS_USE_LINK_REGISTER
13386 // Restore the return address such that the emitPrologue function of the
13387 // CodeGenerator can push it back on the stack with pushReturnAddress.
13388 masm.popReturnAddress();
13389#endif
13390 masm.jump(ReturnReg);
13391}
13392
13393void JitRuntime::generateInterpreterStub(MacroAssembler& masm) {
13394 AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterStub");
13395
13396 interpreterStubOffset_ = startTrampolineCode(masm);
13397
13398#ifdef JS_USE_LINK_REGISTER
13399 masm.pushReturnAddress();
13400#endif
13401 masm.Push(FramePointer);
13402 masm.moveStackPtrTo(FramePointer);
13403
13404 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
13405 Register temp0 = regs.takeAny();
13406 Register temp1 = regs.takeAny();
13407 Register temp2 = regs.takeAny();
13408
13409 masm.loadJSContext(temp0);
13410 masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub);
13411 masm.moveStackPtrTo(temp1);
13412
13413 using Fn = bool (*)(JSContext* cx, InterpreterStubExitFrameLayout* frame);
13414 masm.setupUnalignedABICall(temp2);
13415 masm.passABIArg(temp0);
13416 masm.passABIArg(temp1);
13417 masm.callWithABI<Fn, InvokeFromInterpreterStub>(
13418 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
13419
13420 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
13421
13422 // Discard exit frame and restore frame pointer.
13423 masm.leaveExitFrame(0);
13424 masm.pop(FramePointer);
13425
13426 // InvokeFromInterpreterStub stores the return value in argv[0], where the
13427 // caller stored |this|. Subtract |sizeof(void*)| for the frame pointer we
13428 // just popped.
13429 masm.loadValue(Address(masm.getStackPointer(),
13430 JitFrameLayout::offsetOfThis() - sizeof(void*)),
13431 JSReturnOperand);
13432 masm.ret();
13433}
13434
13435void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) {
13436 AutoCreatedBy acb(masm, "JitRuntime::generateDoubleToInt32ValueStub");
13437 doubleToInt32ValueStubOffset_ = startTrampolineCode(masm);
13438
13439 Label done;
13440 masm.branchTestDouble(Assembler::NotEqual, R0, &done);
13441
13442 masm.unboxDouble(R0, FloatReg0);
13443 masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done,
13444 /* negativeZeroCheck = */ false);
13445 masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0);
13446
13447 masm.bind(&done);
13448 masm.abiret();
13449}
13450
13451void CodeGenerator::visitLinearizeString(LLinearizeString* lir) {
13452 Register str = ToRegister(lir->str());
13453 Register output = ToRegister(lir->output());
13454
13455 using Fn = JSLinearString* (*)(JSContext*, JSString*);
13456 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
13457 lir, ArgList(str), StoreRegisterTo(output));
13458
13459 masm.branchIfRope(str, ool->entry());
13460
13461 masm.movePtr(str, output);
13462 masm.bind(ool->rejoin());
13463}
13464
13465void CodeGenerator::visitLinearizeForCharAccess(LLinearizeForCharAccess* lir) {
13466 Register str = ToRegister(lir->str());
13467 Register index = ToRegister(lir->index());
13468 Register output = ToRegister(lir->output());
13469
13470 using Fn = JSLinearString* (*)(JSContext*, JSString*);
13471 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
13472 lir, ArgList(str), StoreRegisterTo(output));
13473
13474 masm.branchIfNotCanLoadStringChar(str, index, output, ool->entry());
13475
13476 masm.movePtr(str, output);
13477 masm.bind(ool->rejoin());
13478}
13479
13480void CodeGenerator::visitLinearizeForCodePointAccess(
13481 LLinearizeForCodePointAccess* lir) {
13482 Register str = ToRegister(lir->str());
13483 Register index = ToRegister(lir->index());
13484 Register output = ToRegister(lir->output());
13485 Register temp = ToRegister(lir->temp0());
13486
13487 using Fn = JSLinearString* (*)(JSContext*, JSString*);
13488 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
13489 lir, ArgList(str), StoreRegisterTo(output));
13490
13491 masm.branchIfNotCanLoadStringCodePoint(str, index, output, temp,
13492 ool->entry());
13493
13494 masm.movePtr(str, output);
13495 masm.bind(ool->rejoin());
13496}
13497
13498void CodeGenerator::visitToRelativeStringIndex(LToRelativeStringIndex* lir) {
13499 Register index = ToRegister(lir->index());
13500 Register length = ToRegister(lir->length());
13501 Register output = ToRegister(lir->output());
13502
13503 masm.move32(Imm32(0), output);
13504 masm.cmp32Move32(Assembler::LessThan, index, Imm32(0), length, output);
13505 masm.add32(index, output);
13506}
13507
13508void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) {
13509 Register str = ToRegister(lir->str());
13510 Register output = ToRegister(lir->output());
13511 Register temp0 = ToRegister(lir->temp0());
13512 Register temp1 = ToRegister(lir->temp1());
13513
13514 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
13515
13516 if (lir->index()->isBogus()) {
13517 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)),
13518 StoreRegisterTo(output));
13519 masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry());
13520 masm.bind(ool->rejoin());
13521 } else {
13522 Register index = ToRegister(lir->index());
13523
13524 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index),
13525 StoreRegisterTo(output));
13526 masm.loadStringChar(str, index, output, temp0, temp1, ool->entry());
13527 masm.bind(ool->rejoin());
13528 }
13529}
13530
13531void CodeGenerator::visitCharCodeAtOrNegative(LCharCodeAtOrNegative* lir) {
13532 Register str = ToRegister(lir->str());
13533 Register output = ToRegister(lir->output());
13534 Register temp0 = ToRegister(lir->temp0());
13535 Register temp1 = ToRegister(lir->temp1());
13536
13537 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
13538
13539 // Return -1 for out-of-bounds access.
13540 masm.move32(Imm32(-1), output);
13541
13542 if (lir->index()->isBogus()) {
13543 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)),
13544 StoreRegisterTo(output));
13545
13546 masm.branch32(Assembler::Equal, Address(str, JSString::offsetOfLength()),
13547 Imm32(0), ool->rejoin());
13548 masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry());
13549 masm.bind(ool->rejoin());
13550 } else {
13551 Register index = ToRegister(lir->index());
13552
13553 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index),
13554 StoreRegisterTo(output));
13555
13556 masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
13557 temp0, ool->rejoin());
13558 masm.loadStringChar(str, index, output, temp0, temp1, ool->entry());
13559 masm.bind(ool->rejoin());
13560 }
13561}
13562
13563void CodeGenerator::visitCodePointAt(LCodePointAt* lir) {
13564 Register str = ToRegister(lir->str());
13565 Register index = ToRegister(lir->index());
13566 Register output = ToRegister(lir->output());
13567 Register temp0 = ToRegister(lir->temp0());
13568 Register temp1 = ToRegister(lir->temp1());
13569
13570 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
13571 auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index),
13572 StoreRegisterTo(output));
13573
13574 masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry());
13575 masm.bind(ool->rejoin());
13576}
13577
13578void CodeGenerator::visitCodePointAtOrNegative(LCodePointAtOrNegative* lir) {
13579 Register str = ToRegister(lir->str());
13580 Register index = ToRegister(lir->index());
13581 Register output = ToRegister(lir->output());
13582 Register temp0 = ToRegister(lir->temp0());
13583 Register temp1 = ToRegister(lir->temp1());
13584
13585 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
13586 auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index),
13587 StoreRegisterTo(output));
13588
13589 // Return -1 for out-of-bounds access.
13590 masm.move32(Imm32(-1), output);
13591
13592 masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
13593 temp0, ool->rejoin());
13594 masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry());
13595 masm.bind(ool->rejoin());
13596}
13597
13598void CodeGenerator::visitNegativeToNaN(LNegativeToNaN* lir) {
13599 Register input = ToRegister(lir->input());
13600 ValueOperand output = ToOutValue(lir);
13601
13602 masm.tagValue(JSVAL_TYPE_INT32, input, output);
13603
13604 Label done;
13605 masm.branchTest32(Assembler::NotSigned, input, input, &done);
13606 masm.moveValue(JS::NaNValue(), output);
13607 masm.bind(&done);
13608}
13609
13610void CodeGenerator::visitNegativeToUndefined(LNegativeToUndefined* lir) {
13611 Register input = ToRegister(lir->input());
13612 ValueOperand output = ToOutValue(lir);
13613
13614 masm.tagValue(JSVAL_TYPE_INT32, input, output);
13615
13616 Label done;
13617 masm.branchTest32(Assembler::NotSigned, input, input, &done);
13618 masm.moveValue(JS::UndefinedValue(), output);
13619 masm.bind(&done);
13620}
13621
13622void CodeGenerator::visitFromCharCode(LFromCharCode* lir) {
13623 Register code = ToRegister(lir->code());
13624 Register output = ToRegister(lir->output());
13625
13626 using Fn = JSLinearString* (*)(JSContext*, int32_t);
13627 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
13628 StoreRegisterTo(output));
13629
13630 // OOL path if code >= UNIT_STATIC_LIMIT.
13631 masm.lookupStaticString(code, output, gen->runtime->staticStrings(),
13632 ool->entry());
13633
13634 masm.bind(ool->rejoin());
13635}
13636
13637void CodeGenerator::visitFromCharCodeEmptyIfNegative(
13638 LFromCharCodeEmptyIfNegative* lir) {
13639 Register code = ToRegister(lir->code());
13640 Register output = ToRegister(lir->output());
13641
13642 using Fn = JSLinearString* (*)(JSContext*, int32_t);
13643 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
13644 StoreRegisterTo(output));
13645
13646 // Return the empty string for negative inputs.
13647 const JSAtomState& names = gen->runtime->names();
13648 masm.movePtr(ImmGCPtr(names.empty_), output);
13649 masm.branchTest32(Assembler::Signed, code, code, ool->rejoin());
13650
13651 // OOL path if code >= UNIT_STATIC_LIMIT.
13652 masm.lookupStaticString(code, output, gen->runtime->staticStrings(),
13653 ool->entry());
13654
13655 masm.bind(ool->rejoin());
13656}
13657
13658void CodeGenerator::visitFromCharCodeUndefinedIfNegative(
13659 LFromCharCodeUndefinedIfNegative* lir) {
13660 Register code = ToRegister(lir->code());
13661 ValueOperand output = ToOutValue(lir);
13662 Register temp = output.scratchReg();
13663
13664 using Fn = JSLinearString* (*)(JSContext*, int32_t);
13665 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
13666 StoreRegisterTo(temp));
13667
13668 // Return |undefined| for negative inputs.
13669 Label done;
13670 masm.moveValue(UndefinedValue(), output);
13671 masm.branchTest32(Assembler::Signed, code, code, &done);
13672
13673 // OOL path if code >= UNIT_STATIC_LIMIT.
13674 masm.lookupStaticString(code, temp, gen->runtime->staticStrings(),
13675 ool->entry());
13676
13677 masm.bind(ool->rejoin());
13678 masm.tagValue(JSVAL_TYPE_STRING, temp, output);
13679
13680 masm.bind(&done);
13681}
13682
13683void CodeGenerator::visitFromCodePoint(LFromCodePoint* lir) {
13684 Register codePoint = ToRegister(lir->codePoint());
13685 Register output = ToRegister(lir->output());
13686 Register temp0 = ToRegister(lir->temp0());
13687 Register temp1 = ToRegister(lir->temp1());
13688 LSnapshot* snapshot = lir->snapshot();
13689
13690 // The OOL path is only taken when we can't allocate the inline string.
13691 using Fn = JSLinearString* (*)(JSContext*, char32_t);
13692 auto* ool = oolCallVM<Fn, js::StringFromCodePoint>(lir, ArgList(codePoint),
13693 StoreRegisterTo(output));
13694
13695 Label isTwoByte;
13696 Label* done = ool->rejoin();
13697
13698 static_assert(
13699 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
13700 "Latin-1 strings can be loaded from static strings");
13701
13702 {
13703 masm.lookupStaticString(codePoint, output, gen->runtime->staticStrings(),
13704 &isTwoByte);
13705 masm.jump(done);
13706 }
13707 masm.bind(&isTwoByte);
13708 {
13709 // Use a bailout if the input is not a valid code point, because
13710 // MFromCodePoint is movable and it'd be observable when a moved
13711 // fromCodePoint throws an exception before its actual call site.
13712 bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax),
13713 snapshot);
13714
13715 // Allocate a JSThinInlineString.
13716 {
13717 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE >= 2,
13718 "JSThinInlineString can hold a supplementary code point");
13719
13720 uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS;
13721 masm.newGCString(output, temp0, gen->initialStringHeap(), ool->entry());
13722 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13723 }
13724
13725 Label isSupplementary;
13726 masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(unicode::NonBMPMin),
13727 &isSupplementary);
13728 {
13729 // Store length.
13730 masm.store32(Imm32(1), Address(output, JSString::offsetOfLength()));
13731
13732 // Load chars pointer in temp0.
13733 masm.loadInlineStringCharsForStore(output, temp0);
13734
13735 masm.store16(codePoint, Address(temp0, 0));
13736
13737 masm.jump(done);
13738 }
13739 masm.bind(&isSupplementary);
13740 {
13741 // Store length.
13742 masm.store32(Imm32(2), Address(output, JSString::offsetOfLength()));
13743
13744 // Load chars pointer in temp0.
13745 masm.loadInlineStringCharsForStore(output, temp0);
13746
13747 // Inlined unicode::LeadSurrogate(uint32_t).
13748 masm.move32(codePoint, temp1);
13749 masm.rshift32(Imm32(10), temp1);
13750 masm.add32(Imm32(unicode::LeadSurrogateMin - (unicode::NonBMPMin >> 10)),
13751 temp1);
13752
13753 masm.store16(temp1, Address(temp0, 0));
13754
13755 // Inlined unicode::TrailSurrogate(uint32_t).
13756 masm.move32(codePoint, temp1);
13757 masm.and32(Imm32(0x3FF), temp1);
13758 masm.or32(Imm32(unicode::TrailSurrogateMin), temp1);
13759
13760 masm.store16(temp1, Address(temp0, sizeof(char16_t)));
13761 }
13762 }
13763
13764 masm.bind(done);
13765}
13766
13767void CodeGenerator::visitStringIncludes(LStringIncludes* lir) {
13768 pushArg(ToRegister(lir->searchString()));
13769 pushArg(ToRegister(lir->string()));
13770
13771 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
13772 callVM<Fn, js::StringIncludes>(lir);
13773}
13774
13775template <typename LIns>
13776static void CallStringMatch(MacroAssembler& masm, LIns* lir, OutOfLineCode* ool,
13777 LiveRegisterSet volatileRegs) {
13778 Register string = ToRegister(lir->string());
13779 Register output = ToRegister(lir->output());
13780 Register tempLength = ToRegister(lir->temp0());
13781 Register tempChars = ToRegister(lir->temp1());
13782 Register maybeTempPat = ToTempRegisterOrInvalid(lir->temp2());
13783
13784 const JSLinearString* searchString = lir->searchString();
13785 size_t length = searchString->length();
13786 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13786); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length == 1 || length == 2"
")"); do { *((volatile int*)__null) = 13786; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13787
13788 // The additional temp register is only needed when searching for two
13789 // pattern characters.
13790 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13790); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeTempPat != InvalidReg"
")"); do { *((volatile int*)__null) = 13790; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
13791
13792 if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) {
13793 masm.move32(Imm32(0), output);
13794 } else {
13795 masm.move32(Imm32(-1), output);
13796 }
13797
13798 masm.loadStringLength(string, tempLength);
13799
13800 // Can't be a substring when the string is smaller than the search string.
13801 Label done;
13802 masm.branch32(Assembler::Below, tempLength, Imm32(length), ool->rejoin());
13803
13804 bool searchStringIsPureTwoByte = false;
13805 if (searchString->hasTwoByteChars()) {
13806 JS::AutoCheckCannotGC nogc;
13807 searchStringIsPureTwoByte =
13808 !mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc));
13809 }
13810
13811 // Pure two-byte strings can't occur in a Latin-1 string.
13812 if (searchStringIsPureTwoByte) {
13813 masm.branchLatin1String(string, ool->rejoin());
13814 }
13815
13816 // Slow path when we need to linearize the string.
13817 masm.branchIfRope(string, ool->entry());
13818
13819 Label restoreVolatile;
13820
13821 auto callMatcher = [&](CharEncoding encoding) {
13822 masm.loadStringChars(string, tempChars, encoding);
13823
13824 LiveGeneralRegisterSet liveRegs;
13825 if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) {
13826 // Save |tempChars| to compute the result index.
13827 liveRegs.add(tempChars);
13828
13829#ifdef DEBUG1
13830 // Save |tempLength| in debug-mode for assertions.
13831 liveRegs.add(tempLength);
13832#endif
13833
13834 // Exclude non-volatile registers.
13835 liveRegs.set() = GeneralRegisterSet::Intersect(
13836 liveRegs.set(), GeneralRegisterSet::Volatile());
13837
13838 masm.PushRegsInMask(liveRegs);
13839 }
13840
13841 if (length == 1) {
13842 char16_t pat = searchString->latin1OrTwoByteChar(0);
13843 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13844); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 13844; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
13844 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13844); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 13844; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
13845
13846 masm.move32(Imm32(pat), output);
13847
13848 masm.setupAlignedABICall();
13849 masm.passABIArg(tempChars);
13850 masm.passABIArg(output);
13851 masm.passABIArg(tempLength);
13852 if (encoding == CharEncoding::Latin1) {
13853 using Fn = const char* (*)(const char*, char, size_t);
13854 masm.callWithABI<Fn, mozilla::SIMD::memchr8>(
13855 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
13856 } else {
13857 using Fn = const char16_t* (*)(const char16_t*, char16_t, size_t);
13858 masm.callWithABI<Fn, mozilla::SIMD::memchr16>(
13859 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
13860 }
13861 } else {
13862 char16_t pat0 = searchString->latin1OrTwoByteChar(0);
13863 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 13864; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
13864 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 13864; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
13865
13866 char16_t pat1 = searchString->latin1OrTwoByteChar(1);
13867 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13868); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 13868; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
13868 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 13868); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 13868; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
13869
13870 masm.move32(Imm32(pat0), output);
13871 masm.move32(Imm32(pat1), maybeTempPat);
13872
13873 masm.setupAlignedABICall();
13874 masm.passABIArg(tempChars);
13875 masm.passABIArg(output);
13876 masm.passABIArg(maybeTempPat);
13877 masm.passABIArg(tempLength);
13878 if (encoding == CharEncoding::Latin1) {
13879 using Fn = const char* (*)(const char*, char, char, size_t);
13880 masm.callWithABI<Fn, mozilla::SIMD::memchr2x8>(
13881 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
13882 } else {
13883 using Fn =
13884 const char16_t* (*)(const char16_t*, char16_t, char16_t, size_t);
13885 masm.callWithABI<Fn, mozilla::SIMD::memchr2x16>(
13886 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
13887 }
13888 }
13889
13890 masm.storeCallPointerResult(output);
13891
13892 // Convert to string index for `indexOf`.
13893 if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) {
13894 // Restore |tempChars|. (And in debug mode |tempLength|.)
13895 masm.PopRegsInMask(liveRegs);
13896
13897 Label found;
13898 masm.branchPtr(Assembler::NotEqual, output, ImmPtr(nullptr), &found);
13899 {
13900 masm.move32(Imm32(-1), output);
13901 masm.jump(&restoreVolatile);
13902 }
13903 masm.bind(&found);
13904
13905#ifdef DEBUG1
13906 // Check lower bound.
13907 Label lower;
13908 masm.branchPtr(Assembler::AboveOrEqual, output, tempChars, &lower);
13909 masm.assumeUnreachable("result pointer below string chars");
13910 masm.bind(&lower);
13911
13912 // Compute the end position of the characters.
13913 auto scale = encoding == CharEncoding::Latin1 ? TimesOne : TimesTwo;
13914 masm.computeEffectiveAddress(BaseIndex(tempChars, tempLength, scale),
13915 tempLength);
13916
13917 // Check upper bound.
13918 Label upper;
13919 masm.branchPtr(Assembler::Below, output, tempLength, &upper);
13920 masm.assumeUnreachable("result pointer above string chars");
13921 masm.bind(&upper);
13922#endif
13923
13924 masm.subPtr(tempChars, output);
13925
13926 if (encoding == CharEncoding::TwoByte) {
13927 masm.rshiftPtr(Imm32(1), output);
13928 }
13929 }
13930 };
13931
13932 volatileRegs.takeUnchecked(output);
13933 volatileRegs.takeUnchecked(tempLength);
13934 volatileRegs.takeUnchecked(tempChars);
13935 if (maybeTempPat != InvalidReg) {
13936 volatileRegs.takeUnchecked(maybeTempPat);
13937 }
13938 masm.PushRegsInMask(volatileRegs);
13939
13940 // Handle the case when the input is a Latin-1 string.
13941 if (!searchStringIsPureTwoByte) {
13942 Label twoByte;
13943 masm.branchTwoByteString(string, &twoByte);
13944 {
13945 callMatcher(CharEncoding::Latin1);
13946 masm.jump(&restoreVolatile);
13947 }
13948 masm.bind(&twoByte);
13949 }
13950
13951 // Handle the case when the input is a two-byte string.
13952 callMatcher(CharEncoding::TwoByte);
13953
13954 masm.bind(&restoreVolatile);
13955 masm.PopRegsInMask(volatileRegs);
13956
13957 // Convert to bool for `includes`.
13958 if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) {
13959 masm.cmpPtrSet(Assembler::NotEqual, output, ImmPtr(nullptr), output);
13960 }
13961
13962 masm.bind(ool->rejoin());
13963}
13964
13965void CodeGenerator::visitStringIncludesSIMD(LStringIncludesSIMD* lir) {
13966 Register string = ToRegister(lir->string());
13967 Register output = ToRegister(lir->output());
13968 const JSLinearString* searchString = lir->searchString();
13969
13970 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
13971 auto* ool = oolCallVM<Fn, js::StringIncludes>(
13972 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
13973
13974 CallStringMatch(masm, lir, ool, liveVolatileRegs(lir));
13975}
13976
13977void CodeGenerator::visitStringIndexOf(LStringIndexOf* lir) {
13978 pushArg(ToRegister(lir->searchString()));
13979 pushArg(ToRegister(lir->string()));
13980
13981 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
13982 callVM<Fn, js::StringIndexOf>(lir);
13983}
13984
13985void CodeGenerator::visitStringIndexOfSIMD(LStringIndexOfSIMD* lir) {
13986 Register string = ToRegister(lir->string());
13987 Register output = ToRegister(lir->output());
13988 const JSLinearString* searchString = lir->searchString();
13989
13990 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
13991 auto* ool = oolCallVM<Fn, js::StringIndexOf>(
13992 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
13993
13994 CallStringMatch(masm, lir, ool, liveVolatileRegs(lir));
13995}
13996
13997void CodeGenerator::visitStringLastIndexOf(LStringLastIndexOf* lir) {
13998 pushArg(ToRegister(lir->searchString()));
13999 pushArg(ToRegister(lir->string()));
14000
14001 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
14002 callVM<Fn, js::StringLastIndexOf>(lir);
14003}
14004
14005void CodeGenerator::visitStringStartsWith(LStringStartsWith* lir) {
14006 pushArg(ToRegister(lir->searchString()));
14007 pushArg(ToRegister(lir->string()));
14008
14009 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14010 callVM<Fn, js::StringStartsWith>(lir);
14011}
14012
14013void CodeGenerator::visitStringStartsWithInline(LStringStartsWithInline* lir) {
14014 Register string = ToRegister(lir->string());
14015 Register output = ToRegister(lir->output());
14016 Register temp = ToRegister(lir->temp0());
14017
14018 const JSLinearString* searchString = lir->searchString();
14019
14020 size_t length = searchString->length();
14021 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14021); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0"
")"); do { *((volatile int*)__null) = 14021; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14022
14023 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14024 auto* ool = oolCallVM<Fn, js::StringStartsWith>(
14025 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
14026
14027 masm.move32(Imm32(0), output);
14028
14029 // Can't be a prefix when the string is smaller than the search string.
14030 masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()),
14031 Imm32(length), ool->rejoin());
14032
14033 // Unwind ropes at the start if possible.
14034 Label compare;
14035 masm.movePtr(string, temp);
14036 masm.branchIfNotRope(temp, &compare);
14037
14038 Label unwindRope;
14039 masm.bind(&unwindRope);
14040 masm.loadRopeLeftChild(temp, output);
14041 masm.movePtr(output, temp);
14042
14043 // If the left child is smaller than the search string, jump into the VM to
14044 // linearize the string.
14045 masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()),
14046 Imm32(length), ool->entry());
14047
14048 // Otherwise keep unwinding ropes.
14049 masm.branchIfRope(temp, &unwindRope);
14050
14051 masm.bind(&compare);
14052
14053 // If operands point to the same instance, it's trivially a prefix.
14054 Label notPointerEqual;
14055 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString),
14056 &notPointerEqual);
14057 masm.move32(Imm32(1), output);
14058 masm.jump(ool->rejoin());
14059 masm.bind(&notPointerEqual);
14060
14061 if (searchString->hasTwoByteChars()) {
14062 // Pure two-byte strings can't be a prefix of Latin-1 strings.
14063 JS::AutoCheckCannotGC nogc;
14064 if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) {
14065 Label compareChars;
14066 masm.branchTwoByteString(temp, &compareChars);
14067 masm.move32(Imm32(0), output);
14068 masm.jump(ool->rejoin());
14069 masm.bind(&compareChars);
14070 }
14071 }
14072
14073 // Load the input string's characters.
14074 Register stringChars = output;
14075 masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry());
14076
14077 // Start comparing character by character.
14078 masm.compareStringChars(JSOp::Eq, stringChars, searchString, output);
14079
14080 masm.bind(ool->rejoin());
14081}
14082
14083void CodeGenerator::visitStringEndsWith(LStringEndsWith* lir) {
14084 pushArg(ToRegister(lir->searchString()));
14085 pushArg(ToRegister(lir->string()));
14086
14087 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14088 callVM<Fn, js::StringEndsWith>(lir);
14089}
14090
14091void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) {
14092 Register string = ToRegister(lir->string());
14093 Register output = ToRegister(lir->output());
14094 Register temp = ToRegister(lir->temp0());
14095
14096 const JSLinearString* searchString = lir->searchString();
14097
14098 size_t length = searchString->length();
14099 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14099); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0"
")"); do { *((volatile int*)__null) = 14099; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14100
14101 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14102 auto* ool = oolCallVM<Fn, js::StringEndsWith>(
14103 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
14104
14105 masm.move32(Imm32(0), output);
14106
14107 // Can't be a suffix when the string is smaller than the search string.
14108 masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()),
14109 Imm32(length), ool->rejoin());
14110
14111 // Unwind ropes at the end if possible.
14112 Label compare;
14113 masm.movePtr(string, temp);
14114 masm.branchIfNotRope(temp, &compare);
14115
14116 Label unwindRope;
14117 masm.bind(&unwindRope);
14118 masm.loadRopeRightChild(temp, output);
14119 masm.movePtr(output, temp);
14120
14121 // If the right child is smaller than the search string, jump into the VM to
14122 // linearize the string.
14123 masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()),
14124 Imm32(length), ool->entry());
14125
14126 // Otherwise keep unwinding ropes.
14127 masm.branchIfRope(temp, &unwindRope);
14128
14129 masm.bind(&compare);
14130
14131 // If operands point to the same instance, it's trivially a suffix.
14132 Label notPointerEqual;
14133 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString),
14134 &notPointerEqual);
14135 masm.move32(Imm32(1), output);
14136 masm.jump(ool->rejoin());
14137 masm.bind(&notPointerEqual);
14138
14139 CharEncoding encoding = searchString->hasLatin1Chars()
14140 ? CharEncoding::Latin1
14141 : CharEncoding::TwoByte;
14142 if (encoding == CharEncoding::TwoByte) {
14143 // Pure two-byte strings can't be a suffix of Latin-1 strings.
14144 JS::AutoCheckCannotGC nogc;
14145 if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) {
14146 Label compareChars;
14147 masm.branchTwoByteString(temp, &compareChars);
14148 masm.move32(Imm32(0), output);
14149 masm.jump(ool->rejoin());
14150 masm.bind(&compareChars);
14151 }
14152 }
14153
14154 // Load the input string's characters.
14155 Register stringChars = output;
14156 masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry());
14157
14158 // Move string-char pointer to the suffix string.
14159 masm.loadStringLength(temp, temp);
14160 masm.sub32(Imm32(length), temp);
14161 masm.addToCharPtr(stringChars, temp, encoding);
14162
14163 // Start comparing character by character.
14164 masm.compareStringChars(JSOp::Eq, stringChars, searchString, output);
14165
14166 masm.bind(ool->rejoin());
14167}
14168
14169void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) {
14170 Register string = ToRegister(lir->string());
14171 Register output = ToRegister(lir->output());
14172 Register temp0 = ToRegister(lir->temp0());
14173 Register temp1 = ToRegister(lir->temp1());
14174 Register temp2 = ToRegister(lir->temp2());
14175
14176 // On x86 there are not enough registers. In that case reuse the string
14177 // register as a temporary.
14178 Register temp3 =
14179 lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3());
14180 Register temp4 = ToRegister(lir->temp4());
14181
14182 using Fn = JSString* (*)(JSContext*, HandleString);
14183 OutOfLineCode* ool = oolCallVM<Fn, js::StringToLowerCase>(
14184 lir, ArgList(string), StoreRegisterTo(output));
14185
14186 // Take the slow path if the string isn't a linear Latin-1 string.
14187 Imm32 linearLatin1Bits(JSString::LINEAR_BIT | JSString::LATIN1_CHARS_BIT);
14188 Register flags = temp0;
14189 masm.load32(Address(string, JSString::offsetOfFlags()), flags);
14190 masm.and32(linearLatin1Bits, flags);
14191 masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry());
14192
14193 Register length = temp0;
14194 masm.loadStringLength(string, length);
14195
14196 // Return the input if it's the empty string.
14197 Label notEmptyString;
14198 masm.branch32(Assembler::NotEqual, length, Imm32(0), &notEmptyString);
14199 {
14200 masm.movePtr(string, output);
14201 masm.jump(ool->rejoin());
14202 }
14203 masm.bind(&notEmptyString);
14204
14205 Register inputChars = temp1;
14206 masm.loadStringChars(string, inputChars, CharEncoding::Latin1);
14207
14208 Register toLowerCaseTable = temp2;
14209 masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable);
14210
14211 // Single element strings can be directly retrieved from static strings cache.
14212 Label notSingleElementString;
14213 masm.branch32(Assembler::NotEqual, length, Imm32(1), &notSingleElementString);
14214 {
14215 Register current = temp4;
14216
14217 masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1);
14218 masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne),
14219 current);
14220 masm.lookupStaticString(current, output, gen->runtime->staticStrings());
14221
14222 masm.jump(ool->rejoin());
14223 }
14224 masm.bind(&notSingleElementString);
14225
14226 // Use the OOL-path when the string is too long. This prevents scanning long
14227 // strings which have upper case characters only near the end a second time in
14228 // the VM.
14229 constexpr int32_t MaxInlineLength = 64;
14230 masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry());
14231
14232 {
14233 // Check if there are any characters which need to be converted.
14234 //
14235 // This extra loop gives a small performance improvement for strings which
14236 // are already lower cased and lets us avoid calling into the runtime for
14237 // non-inline, all lower case strings. But more importantly it avoids
14238 // repeated inline allocation failures:
14239 // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the
14240 // |js::StringToLowerCase| runtime function when the result string can't be
14241 // allocated inline. And |js::StringToLowerCase| directly returns the input
14242 // string when no characters need to be converted. That means it won't
14243 // trigger GC to clear up the free nursery space, so the next toLowerCase()
14244 // call will again fail to inline allocate the result string.
14245 Label hasUpper;
14246 {
14247 Register checkInputChars = output;
14248 masm.movePtr(inputChars, checkInputChars);
14249
14250 Register current = temp4;
14251
14252 Label start;
14253 masm.bind(&start);
14254 masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1);
14255 masm.branch8(Assembler::NotEqual,
14256 BaseIndex(toLowerCaseTable, current, TimesOne), current,
14257 &hasUpper);
14258 masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars);
14259 masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start);
14260
14261 // Input is already in lower case.
14262 masm.movePtr(string, output);
14263 masm.jump(ool->rejoin());
14264 }
14265 masm.bind(&hasUpper);
14266
14267 // |length| was clobbered above, reload.
14268 masm.loadStringLength(string, length);
14269
14270 // Call into the runtime when we can't create an inline string.
14271 masm.branch32(Assembler::Above, length,
14272 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry());
14273
14274 AllocateThinOrFatInlineString(masm, output, length, temp4,
14275 initialStringHeap(), ool->entry(),
14276 CharEncoding::Latin1);
14277
14278 if (temp3 == string) {
14279 masm.push(string);
14280 }
14281
14282 Register outputChars = temp3;
14283 masm.loadInlineStringCharsForStore(output, outputChars);
14284
14285 {
14286 Register current = temp4;
14287
14288 Label start;
14289 masm.bind(&start);
14290 masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1);
14291 masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne),
14292 current);
14293 masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1);
14294 masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars);
14295 masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars);
14296 masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start);
14297 }
14298
14299 if (temp3 == string) {
14300 masm.pop(string);
14301 }
14302 }
14303
14304 masm.bind(ool->rejoin());
14305}
14306
14307void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) {
14308 pushArg(ToRegister(lir->string()));
14309
14310 using Fn = JSString* (*)(JSContext*, HandleString);
14311 callVM<Fn, js::StringToUpperCase>(lir);
14312}
14313
14314void CodeGenerator::visitCharCodeToLowerCase(LCharCodeToLowerCase* lir) {
14315 Register code = ToRegister(lir->code());
14316 Register output = ToRegister(lir->output());
14317 Register temp = ToRegister(lir->temp0());
14318
14319 using Fn = JSString* (*)(JSContext*, int32_t);
14320 auto* ool = oolCallVM<Fn, jit::CharCodeToLowerCase>(lir, ArgList(code),
14321 StoreRegisterTo(output));
14322
14323 constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1;
14324
14325 // OOL path if code >= NonLatin1Min.
14326 masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry());
14327
14328 // Convert to lower case.
14329 masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), temp);
14330 masm.load8ZeroExtend(BaseIndex(temp, code, TimesOne), temp);
14331
14332 // Load static string for lower case character.
14333 masm.lookupStaticString(temp, output, gen->runtime->staticStrings());
14334
14335 masm.bind(ool->rejoin());
14336}
14337
14338void CodeGenerator::visitCharCodeToUpperCase(LCharCodeToUpperCase* lir) {
14339 Register code = ToRegister(lir->code());
14340 Register output = ToRegister(lir->output());
14341 Register temp = ToRegister(lir->temp0());
14342
14343 using Fn = JSString* (*)(JSContext*, int32_t);
14344 auto* ool = oolCallVM<Fn, jit::CharCodeToUpperCase>(lir, ArgList(code),
14345 StoreRegisterTo(output));
14346
14347 constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1;
14348
14349 // OOL path if code >= NonLatin1Min.
14350 masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry());
14351
14352 // Most one element Latin-1 strings can be directly retrieved from the
14353 // static strings cache, except the following three characters:
14354 //
14355 // 1. ToUpper(U+00B5) = 0+039C
14356 // 2. ToUpper(U+00FF) = 0+0178
14357 // 3. ToUpper(U+00DF) = 0+0053 0+0053
14358 masm.branch32(Assembler::Equal, code, Imm32(unicode::MICRO_SIGN),
14359 ool->entry());
14360 masm.branch32(Assembler::Equal, code,
14361 Imm32(unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS),
14362 ool->entry());
14363 masm.branch32(Assembler::Equal, code,
14364 Imm32(unicode::LATIN_SMALL_LETTER_SHARP_S), ool->entry());
14365
14366 // Inline unicode::ToUpperCase (without the special case for ASCII characters)
14367
14368 constexpr size_t shift = unicode::CharInfoShift;
14369
14370 // code >> shift
14371 masm.move32(code, temp);
14372 masm.rshift32(Imm32(shift), temp);
14373
14374 // index = index1[code >> shift];
14375 masm.movePtr(ImmPtr(unicode::index1), output);
14376 masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp);
14377
14378 // (code & ((1 << shift) - 1)
14379 masm.move32(code, output);
14380 masm.and32(Imm32((1 << shift) - 1), output);
14381
14382 // (index << shift) + (code & ((1 << shift) - 1))
14383 masm.lshift32(Imm32(shift), temp);
14384 masm.add32(output, temp);
14385
14386 // index = index2[(index << shift) + (code & ((1 << shift) - 1))]
14387 masm.movePtr(ImmPtr(unicode::index2), output);
14388 masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp);
14389
14390 // Compute |index * 6| through |(index * 3) * TimesTwo|.
14391 static_assert(sizeof(unicode::CharacterInfo) == 6);
14392 masm.mulBy3(temp, temp);
14393
14394 // upperCase = js_charinfo[index].upperCase
14395 masm.movePtr(ImmPtr(unicode::js_charinfo), output);
14396 masm.load16ZeroExtend(BaseIndex(output, temp, TimesTwo,
14397 offsetof(unicode::CharacterInfo, upperCase)__builtin_offsetof(unicode::CharacterInfo, upperCase)),
14398 temp);
14399
14400 // uint16_t(ch) + upperCase
14401 masm.add32(code, temp);
14402
14403 // Clear any high bits added when performing the unsigned 16-bit addition
14404 // through a signed 32-bit addition.
14405 masm.move8ZeroExtend(temp, temp);
14406
14407 // Load static string for upper case character.
14408 masm.lookupStaticString(temp, output, gen->runtime->staticStrings());
14409
14410 masm.bind(ool->rejoin());
14411}
14412
14413void CodeGenerator::visitStringTrimStartIndex(LStringTrimStartIndex* lir) {
14414 Register string = ToRegister(lir->string());
14415 Register output = ToRegister(lir->output());
14416
14417 auto volatileRegs = liveVolatileRegs(lir);
14418 volatileRegs.takeUnchecked(output);
14419
14420 masm.PushRegsInMask(volatileRegs);
14421
14422 using Fn = int32_t (*)(const JSString*);
14423 masm.setupAlignedABICall();
14424 masm.passABIArg(string);
14425 masm.callWithABI<Fn, jit::StringTrimStartIndex>();
14426 masm.storeCallInt32Result(output);
14427
14428 masm.PopRegsInMask(volatileRegs);
14429}
14430
14431void CodeGenerator::visitStringTrimEndIndex(LStringTrimEndIndex* lir) {
14432 Register string = ToRegister(lir->string());
14433 Register start = ToRegister(lir->start());
14434 Register output = ToRegister(lir->output());
14435
14436 auto volatileRegs = liveVolatileRegs(lir);
14437 volatileRegs.takeUnchecked(output);
14438
14439 masm.PushRegsInMask(volatileRegs);
14440
14441 using Fn = int32_t (*)(const JSString*, int32_t);
14442 masm.setupAlignedABICall();
14443 masm.passABIArg(string);
14444 masm.passABIArg(start);
14445 masm.callWithABI<Fn, jit::StringTrimEndIndex>();
14446 masm.storeCallInt32Result(output);
14447
14448 masm.PopRegsInMask(volatileRegs);
14449}
14450
14451void CodeGenerator::visitStringSplit(LStringSplit* lir) {
14452 pushArg(Imm32(INT32_MAX(2147483647)));
14453 pushArg(ToRegister(lir->separator()));
14454 pushArg(ToRegister(lir->string()));
14455
14456 using Fn = ArrayObject* (*)(JSContext*, HandleString, HandleString, uint32_t);
14457 callVM<Fn, js::StringSplitString>(lir);
14458}
14459
14460void CodeGenerator::visitInitializedLength(LInitializedLength* lir) {
14461 Address initLength(ToRegister(lir->elements()),
14462 ObjectElements::offsetOfInitializedLength());
14463 masm.load32(initLength, ToRegister(lir->output()));
14464}
14465
14466void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) {
14467 Address initLength(ToRegister(lir->elements()),
14468 ObjectElements::offsetOfInitializedLength());
14469 SetLengthFromIndex(masm, lir->index(), initLength);
14470}
14471
14472void CodeGenerator::visitNotBI(LNotBI* lir) {
14473 Register input = ToRegister(lir->input());
14474 Register output = ToRegister(lir->output());
14475
14476 masm.cmp32Set(Assembler::Equal, Address(input, BigInt::offsetOfLength()),
14477 Imm32(0), output);
14478}
14479
14480void CodeGenerator::visitNotO(LNotO* lir) {
14481 Register objreg = ToRegister(lir->input());
14482 Register output = ToRegister(lir->output());
14483
14484 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
14485 if (intact) {
14486 // Bug 1874905: It would be fantastic if this could be optimized out.
14487 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
14488 masm.move32(Imm32(0), output);
14489 } else {
14490 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
14491 addOutOfLineCode(ool, lir->mir());
14492
14493 Label* ifEmulatesUndefined = ool->label1();
14494 Label* ifDoesntEmulateUndefined = ool->label2();
14495
14496 branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined,
14497 ifDoesntEmulateUndefined, output, ool);
14498 // fall through
14499
14500 Label join;
14501
14502 masm.move32(Imm32(0), output);
14503 masm.jump(&join);
14504
14505 masm.bind(ifEmulatesUndefined);
14506 masm.move32(Imm32(1), output);
14507
14508 masm.bind(&join);
14509 }
14510}
14511
14512void CodeGenerator::visitNotV(LNotV* lir) {
14513 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
14514 addOutOfLineCode(ool, lir->mir());
14515
14516 Label* ifTruthy = ool->label1();
14517 Label* ifFalsy = ool->label2();
14518
14519 ValueOperand input = ToValue(lir, LNotV::InputIndex);
14520 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
14521 FloatRegister floatTemp = ToFloatRegister(lir->temp0());
14522 Register output = ToRegister(lir->output());
14523 const TypeDataList& observedTypes = lir->mir()->observedTypes();
14524
14525 testValueTruthy(input, tempToUnbox, output, floatTemp, observedTypes,
14526 ifTruthy, ifFalsy, ool);
14527
14528 Label join;
14529
14530 // Note that the testValueTruthy call above may choose to fall through
14531 // to ifTruthy instead of branching there.
14532 masm.bind(ifTruthy);
14533 masm.move32(Imm32(0), output);
14534 masm.jump(&join);
14535
14536 masm.bind(ifFalsy);
14537 masm.move32(Imm32(1), output);
14538
14539 // both branches meet here.
14540 masm.bind(&join);
14541}
14542
14543void CodeGenerator::visitBoundsCheck(LBoundsCheck* lir) {
14544 const LAllocation* index = lir->index();
14545 const LAllocation* length = lir->length();
14546 LSnapshot* snapshot = lir->snapshot();
14547
14548 MIRType type = lir->mir()->type();
14549
14550 auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) {
14551 if (type == MIRType::Int32) {
14552 bailoutCmp32(cond, lhs, rhs, snapshot);
14553 } else {
14554 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14554); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14554; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14555 bailoutCmpPtr(cond, lhs, rhs, snapshot);
14556 }
14557 };
14558
14559 auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs,
14560 int32_t rhs) {
14561 if (type == MIRType::Int32) {
14562 bailoutCmp32(cond, lhs, Imm32(rhs), snapshot);
14563 } else {
14564 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14564); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14564; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14565 bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot);
14566 }
14567 };
14568
14569 if (index->isConstant()) {
14570 // Use uint32 so that the comparison is unsigned.
14571 uint32_t idx = ToInt32(index);
14572 if (length->isConstant()) {
14573 uint32_t len = ToInt32(lir->length());
14574 if (idx < len) {
14575 return;
14576 }
14577 bailout(snapshot);
14578 return;
14579 }
14580
14581 if (length->isRegister()) {
14582 bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), idx);
14583 } else {
14584 bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), idx);
14585 }
14586 return;
14587 }
14588
14589 Register indexReg = ToRegister(index);
14590 if (length->isConstant()) {
14591 bailoutCmpConstant(Assembler::AboveOrEqual, indexReg, ToInt32(length));
14592 } else if (length->isRegister()) {
14593 bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), indexReg);
14594 } else {
14595 bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), indexReg);
14596 }
14597}
14598
14599void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) {
14600 int32_t min = lir->mir()->minimum();
14601 int32_t max = lir->mir()->maximum();
14602 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14602); AnnotateMozCrashReason("MOZ_ASSERT" "(" "max >= min"
")"); do { *((volatile int*)__null) = 14602; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14603
14604 LSnapshot* snapshot = lir->snapshot();
14605 MIRType type = lir->mir()->type();
14606
14607 const LAllocation* length = lir->length();
14608 Register temp = ToRegister(lir->getTemp(0));
14609
14610 auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) {
14611 if (type == MIRType::Int32) {
14612 bailoutCmp32(cond, lhs, rhs, snapshot);
14613 } else {
14614 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14614); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14614; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14615 bailoutCmpPtr(cond, lhs, rhs, snapshot);
14616 }
14617 };
14618
14619 auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs,
14620 int32_t rhs) {
14621 if (type == MIRType::Int32) {
14622 bailoutCmp32(cond, lhs, Imm32(rhs), snapshot);
14623 } else {
14624 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14624; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14625 bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot);
14626 }
14627 };
14628
14629 if (lir->index()->isConstant()) {
14630 int32_t nmin, nmax;
14631 int32_t index = ToInt32(lir->index());
14632 if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) {
14633 if (length->isRegister()) {
14634 bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax);
14635 } else {
14636 bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), nmax);
14637 }
14638 return;
14639 }
14640 masm.mov(ImmWord(index), temp);
14641 } else {
14642 masm.mov(ToRegister(lir->index()), temp);
14643 }
14644
14645 // If the minimum and maximum differ then do an underflow check first.
14646 // If the two are the same then doing an unsigned comparison on the
14647 // length will also catch a negative index.
14648 if (min != max) {
14649 if (min != 0) {
14650 Label bail;
14651 if (type == MIRType::Int32) {
14652 masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail);
14653 } else {
14654 masm.branchAddPtr(Assembler::Overflow, Imm32(min), temp, &bail);
14655 }
14656 bailoutFrom(&bail, snapshot);
14657 }
14658
14659 bailoutCmpConstant(Assembler::LessThan, temp, 0);
14660
14661 if (min != 0) {
14662 int32_t diff;
14663 if (SafeSub(max, min, &diff)) {
14664 max = diff;
14665 } else {
14666 if (type == MIRType::Int32) {
14667 masm.sub32(Imm32(min), temp);
14668 } else {
14669 masm.subPtr(Imm32(min), temp);
14670 }
14671 }
14672 }
14673 }
14674
14675 // Compute the maximum possible index. No overflow check is needed when
14676 // max > 0. We can only wraparound to a negative number, which will test as
14677 // larger than all nonnegative numbers in the unsigned comparison, and the
14678 // length is required to be nonnegative (else testing a negative length
14679 // would succeed on any nonnegative index).
14680 if (max != 0) {
14681 if (max < 0) {
14682 Label bail;
14683 if (type == MIRType::Int32) {
14684 masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail);
14685 } else {
14686 masm.branchAddPtr(Assembler::Overflow, Imm32(max), temp, &bail);
14687 }
14688 bailoutFrom(&bail, snapshot);
14689 } else {
14690 if (type == MIRType::Int32) {
14691 masm.add32(Imm32(max), temp);
14692 } else {
14693 masm.addPtr(Imm32(max), temp);
14694 }
14695 }
14696 }
14697
14698 if (length->isRegister()) {
14699 bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), temp);
14700 } else {
14701 bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), temp);
14702 }
14703}
14704
14705void CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir) {
14706 int32_t min = lir->mir()->minimum();
14707 bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min),
14708 lir->snapshot());
14709}
14710
14711void CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir) {
14712 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14712); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitOptions.spectreIndexMasking"
")"); do { *((volatile int*)__null) = 14712; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14713
14714 const LAllocation* length = lir->length();
14715 Register index = ToRegister(lir->index());
14716 Register output = ToRegister(lir->output());
14717
14718 if (lir->mir()->type() == MIRType::Int32) {
14719 if (length->isRegister()) {
14720 masm.spectreMaskIndex32(index, ToRegister(length), output);
14721 } else {
14722 masm.spectreMaskIndex32(index, ToAddress(length), output);
14723 }
14724 } else {
14725 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14725); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14725; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14726 if (length->isRegister()) {
14727 masm.spectreMaskIndexPtr(index, ToRegister(length), output);
14728 } else {
14729 masm.spectreMaskIndexPtr(index, ToAddress(length), output);
14730 }
14731 }
14732}
14733
14734class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator> {
14735 LInstruction* ins_;
14736
14737 public:
14738 explicit OutOfLineStoreElementHole(LInstruction* ins) : ins_(ins) {
14739 MOZ_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->isStoreElementHoleV() || ins->isStoreElementHoleT
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ins->isStoreElementHoleV() || ins->isStoreElementHoleT
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ins->isStoreElementHoleV() || ins->isStoreElementHoleT()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14739); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->isStoreElementHoleV() || ins->isStoreElementHoleT()"
")"); do { *((volatile int*)__null) = 14739; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14740 }
14741
14742 void accept(CodeGenerator* codegen) override {
14743 codegen->visitOutOfLineStoreElementHole(this);
14744 }
14745
14746 MStoreElementHole* mir() const {
14747 return ins_->isStoreElementHoleV() ? ins_->toStoreElementHoleV()->mir()
14748 : ins_->toStoreElementHoleT()->mir();
14749 }
14750 LInstruction* ins() const { return ins_; }
14751};
14752
14753void CodeGenerator::emitStoreHoleCheck(Register elements,
14754 const LAllocation* index,
14755 LSnapshot* snapshot) {
14756 Label bail;
14757 if (index->isConstant()) {
14758 Address dest(elements, ToInt32(index) * sizeof(js::Value));
14759 masm.branchTestMagic(Assembler::Equal, dest, &bail);
14760 } else {
14761 BaseObjectElementIndex dest(elements, ToRegister(index));
14762 masm.branchTestMagic(Assembler::Equal, dest, &bail);
14763 }
14764 bailoutFrom(&bail, snapshot);
14765}
14766
14767void CodeGenerator::emitStoreElementTyped(const LAllocation* value,
14768 MIRType valueType, Register elements,
14769 const LAllocation* index) {
14770 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14770); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole"
")"); do { *((volatile int*)__null) = 14770; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14771 ConstantOrRegister v = ToConstantOrRegister(value, valueType);
14772 if (index->isConstant()) {
14773 Address dest(elements, ToInt32(index) * sizeof(js::Value));
14774 masm.storeUnboxedValue(v, valueType, dest);
14775 } else {
14776 BaseObjectElementIndex dest(elements, ToRegister(index));
14777 masm.storeUnboxedValue(v, valueType, dest);
14778 }
14779}
14780
14781void CodeGenerator::visitStoreElementT(LStoreElementT* store) {
14782 Register elements = ToRegister(store->elements());
14783 const LAllocation* index = store->index();
14784
14785 if (store->mir()->needsBarrier()) {
14786 emitPreBarrier(elements, index);
14787 }
14788
14789 if (store->mir()->needsHoleCheck()) {
14790 emitStoreHoleCheck(elements, index, store->snapshot());
14791 }
14792
14793 emitStoreElementTyped(store->value(), store->mir()->value()->type(), elements,
14794 index);
14795}
14796
14797void CodeGenerator::visitStoreElementV(LStoreElementV* lir) {
14798 const ValueOperand value = ToValue(lir, LStoreElementV::Value);
14799 Register elements = ToRegister(lir->elements());
14800 const LAllocation* index = lir->index();
14801
14802 if (lir->mir()->needsBarrier()) {
14803 emitPreBarrier(elements, index);
14804 }
14805
14806 if (lir->mir()->needsHoleCheck()) {
14807 emitStoreHoleCheck(elements, index, lir->snapshot());
14808 }
14809
14810 if (lir->index()->isConstant()) {
14811 Address dest(elements, ToInt32(lir->index()) * sizeof(js::Value));
14812 masm.storeValue(value, dest);
14813 } else {
14814 BaseObjectElementIndex dest(elements, ToRegister(lir->index()));
14815 masm.storeValue(value, dest);
14816 }
14817}
14818
14819void CodeGenerator::visitStoreHoleValueElement(LStoreHoleValueElement* lir) {
14820 Register elements = ToRegister(lir->elements());
14821 Register index = ToRegister(lir->index());
14822
14823 Address elementsFlags(elements, ObjectElements::offsetOfFlags());
14824 masm.or32(Imm32(ObjectElements::NON_PACKED), elementsFlags);
14825
14826 BaseObjectElementIndex element(elements, index);
14827 masm.storeValue(MagicValue(JS_ELEMENTS_HOLE), element);
14828}
14829
14830void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) {
14831 auto* ool = new (alloc()) OutOfLineStoreElementHole(lir);
14832 addOutOfLineCode(ool, lir->mir());
14833
14834 Register obj = ToRegister(lir->object());
14835 Register elements = ToRegister(lir->elements());
14836 Register index = ToRegister(lir->index());
14837 Register temp = ToRegister(lir->temp0());
14838
14839 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
14840 masm.spectreBoundsCheck32(index, initLength, temp, ool->entry());
14841
14842 emitPreBarrier(elements, lir->index());
14843
14844 masm.bind(ool->rejoin());
14845 emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), elements,
14846 lir->index());
14847
14848 if (ValueNeedsPostBarrier(lir->mir()->value())) {
14849 LiveRegisterSet regs = liveVolatileRegs(lir);
14850 ConstantOrRegister val =
14851 ToConstantOrRegister(lir->value(), lir->mir()->value()->type());
14852 emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp, val);
14853 }
14854}
14855
14856void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) {
14857 auto* ool = new (alloc()) OutOfLineStoreElementHole(lir);
14858 addOutOfLineCode(ool, lir->mir());
14859
14860 Register obj = ToRegister(lir->object());
14861 Register elements = ToRegister(lir->elements());
14862 Register index = ToRegister(lir->index());
14863 const ValueOperand value = ToValue(lir, LStoreElementHoleV::ValueIndex);
14864 Register temp = ToRegister(lir->temp0());
14865
14866 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
14867 masm.spectreBoundsCheck32(index, initLength, temp, ool->entry());
14868
14869 emitPreBarrier(elements, lir->index());
14870
14871 masm.bind(ool->rejoin());
14872 masm.storeValue(value, BaseObjectElementIndex(elements, index));
14873
14874 if (ValueNeedsPostBarrier(lir->mir()->value())) {
14875 LiveRegisterSet regs = liveVolatileRegs(lir);
14876 emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp,
14877 ConstantOrRegister(value));
14878 }
14879}
14880
14881void CodeGenerator::visitOutOfLineStoreElementHole(
14882 OutOfLineStoreElementHole* ool) {
14883 Register object, elements, index;
14884 LInstruction* ins = ool->ins();
14885 mozilla::Maybe<ConstantOrRegister> value;
14886 Register temp;
14887
14888 if (ins->isStoreElementHoleV()) {
14889 LStoreElementHoleV* store = ins->toStoreElementHoleV();
14890 object = ToRegister(store->object());
14891 elements = ToRegister(store->elements());
14892 index = ToRegister(store->index());
14893 value.emplace(
14894 TypedOrValueRegister(ToValue(store, LStoreElementHoleV::ValueIndex)));
14895 temp = ToRegister(store->temp0());
14896 } else {
14897 LStoreElementHoleT* store = ins->toStoreElementHoleT();
14898 object = ToRegister(store->object());
14899 elements = ToRegister(store->elements());
14900 index = ToRegister(store->index());
14901 if (store->value()->isConstant()) {
14902 value.emplace(
14903 ConstantOrRegister(store->value()->toConstant()->toJSValue()));
14904 } else {
14905 MIRType valueType = store->mir()->value()->type();
14906 value.emplace(
14907 TypedOrValueRegister(valueType, ToAnyRegister(store->value())));
14908 }
14909 temp = ToRegister(store->temp0());
14910 }
14911
14912 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
14913
14914 // We're out-of-bounds. We only handle the index == initlength case.
14915 // If index > initializedLength, bail out. Note that this relies on the
14916 // condition flags sticking from the incoming branch.
14917 // Also note: this branch does not need Spectre mitigations, doing that for
14918 // the capacity check below is sufficient.
14919 Label allocElement, addNewElement;
14920#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \
14921 defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64)
14922 // Had to reimplement for MIPS because there are no flags.
14923 bailoutCmp32(Assembler::NotEqual, initLength, index, ins->snapshot());
14924#else
14925 bailoutIf(Assembler::NotEqual, ins->snapshot());
14926#endif
14927
14928 // If index < capacity, we can add a dense element inline. If not, we need
14929 // to allocate more elements first.
14930 masm.spectreBoundsCheck32(
14931 index, Address(elements, ObjectElements::offsetOfCapacity()), temp,
14932 &allocElement);
14933 masm.jump(&addNewElement);
14934
14935 masm.bind(&allocElement);
14936
14937 // Save all live volatile registers, except |temp|.
14938 LiveRegisterSet liveRegs = liveVolatileRegs(ins);
14939 liveRegs.takeUnchecked(temp);
14940 masm.PushRegsInMask(liveRegs);
14941
14942 masm.setupAlignedABICall();
14943 masm.loadJSContext(temp);
14944 masm.passABIArg(temp);
14945 masm.passABIArg(object);
14946
14947 using Fn = bool (*)(JSContext*, NativeObject*);
14948 masm.callWithABI<Fn, NativeObject::addDenseElementPure>();
14949 masm.storeCallPointerResult(temp);
14950
14951 masm.PopRegsInMask(liveRegs);
14952 bailoutIfFalseBool(temp, ins->snapshot());
14953
14954 // Load the reallocated elements pointer.
14955 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elements);
14956
14957 masm.bind(&addNewElement);
14958
14959 // Increment initLength
14960 masm.add32(Imm32(1), initLength);
14961
14962 // If length is now <= index, increment length too.
14963 Label skipIncrementLength;
14964 Address length(elements, ObjectElements::offsetOfLength());
14965 masm.branch32(Assembler::Above, length, index, &skipIncrementLength);
14966 masm.add32(Imm32(1), length);
14967 masm.bind(&skipIncrementLength);
14968
14969 // Jump to the inline path where we will store the value.
14970 // We rejoin after the prebarrier, because the memory is uninitialized.
14971 masm.jump(ool->rejoin());
14972}
14973
14974void CodeGenerator::visitArrayPopShift(LArrayPopShift* lir) {
14975 Register obj = ToRegister(lir->object());
14976 Register temp1 = ToRegister(lir->temp0());
14977 Register temp2 = ToRegister(lir->temp1());
14978 ValueOperand out = ToOutValue(lir);
14979
14980 Label bail;
14981 if (lir->mir()->mode() == MArrayPopShift::Pop) {
14982 masm.packedArrayPop(obj, out, temp1, temp2, &bail);
14983 } else {
14984 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 14984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MArrayPopShift::Shift"
")"); do { *((volatile int*)__null) = 14984; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14985 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
14986 masm.packedArrayShift(obj, out, temp1, temp2, volatileRegs, &bail);
14987 }
14988 bailoutFrom(&bail, lir->snapshot());
14989}
14990
14991class OutOfLineArrayPush : public OutOfLineCodeBase<CodeGenerator> {
14992 LArrayPush* ins_;
14993
14994 public:
14995 explicit OutOfLineArrayPush(LArrayPush* ins) : ins_(ins) {}
14996
14997 void accept(CodeGenerator* codegen) override {
14998 codegen->visitOutOfLineArrayPush(this);
14999 }
15000
15001 LArrayPush* ins() const { return ins_; }
15002};
15003
15004void CodeGenerator::visitArrayPush(LArrayPush* lir) {
15005 Register obj = ToRegister(lir->object());
15006 Register elementsTemp = ToRegister(lir->temp0());
15007 Register length = ToRegister(lir->output());
15008 ValueOperand value = ToValue(lir, LArrayPush::ValueIndex);
15009 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
15010
15011 auto* ool = new (alloc()) OutOfLineArrayPush(lir);
15012 addOutOfLineCode(ool, lir->mir());
15013
15014 // Load obj->elements in elementsTemp.
15015 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
15016
15017 Address initLengthAddr(elementsTemp,
15018 ObjectElements::offsetOfInitializedLength());
15019 Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength());
15020 Address capacityAddr(elementsTemp, ObjectElements::offsetOfCapacity());
15021
15022 // Bail out if length != initLength.
15023 masm.load32(lengthAddr, length);
15024 bailoutCmp32(Assembler::NotEqual, initLengthAddr, length, lir->snapshot());
15025
15026 // If length < capacity, we can add a dense element inline. If not, we
15027 // need to allocate more elements.
15028 masm.spectreBoundsCheck32(length, capacityAddr, spectreTemp, ool->entry());
15029 masm.bind(ool->rejoin());
15030
15031 // Store the value.
15032 masm.storeValue(value, BaseObjectElementIndex(elementsTemp, length));
15033
15034 // Update length and initialized length.
15035 masm.add32(Imm32(1), length);
15036 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
15037 masm.store32(length, Address(elementsTemp,
15038 ObjectElements::offsetOfInitializedLength()));
15039
15040 if (ValueNeedsPostBarrier(lir->mir()->value())) {
15041 LiveRegisterSet regs = liveVolatileRegs(lir);
15042 regs.addUnchecked(length);
15043 emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->output()->output(),
15044 elementsTemp, ConstantOrRegister(value),
15045 /* indexDiff = */ -1);
15046 }
15047}
15048
15049void CodeGenerator::visitOutOfLineArrayPush(OutOfLineArrayPush* ool) {
15050 LArrayPush* ins = ool->ins();
15051
15052 Register object = ToRegister(ins->object());
15053 Register temp = ToRegister(ins->temp0());
15054
15055 LiveRegisterSet liveRegs = liveVolatileRegs(ins);
15056 liveRegs.takeUnchecked(temp);
15057 liveRegs.addUnchecked(ToRegister(ins->output()));
15058 liveRegs.addUnchecked(ToValue(ins, LArrayPush::ValueIndex));
15059
15060 masm.PushRegsInMask(liveRegs);
15061
15062 masm.setupAlignedABICall();
15063 masm.loadJSContext(temp);
15064 masm.passABIArg(temp);
15065 masm.passABIArg(object);
15066
15067 using Fn = bool (*)(JSContext*, NativeObject* obj);
15068 masm.callWithABI<Fn, NativeObject::addDenseElementPure>();
15069 masm.storeCallPointerResult(temp);
15070
15071 masm.PopRegsInMask(liveRegs);
15072 bailoutIfFalseBool(temp, ins->snapshot());
15073
15074 // Load the reallocated elements pointer.
15075 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
15076
15077 masm.jump(ool->rejoin());
15078}
15079
15080void CodeGenerator::visitArraySlice(LArraySlice* lir) {
15081 Register object = ToRegister(lir->object());
15082 Register begin = ToRegister(lir->begin());
15083 Register end = ToRegister(lir->end());
15084 Register temp0 = ToRegister(lir->temp0());
15085 Register temp1 = ToRegister(lir->temp1());
15086
15087 Label call, fail;
15088
15089 Label bail;
15090 masm.branchArrayIsNotPacked(object, temp0, temp1, &bail);
15091 bailoutFrom(&bail, lir->snapshot());
15092
15093 // Try to allocate an object.
15094 TemplateObject templateObject(lir->mir()->templateObj());
15095 masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(),
15096 &fail);
15097
15098 masm.jump(&call);
15099 {
15100 masm.bind(&fail);
15101 masm.movePtr(ImmPtr(nullptr), temp0);
15102 }
15103 masm.bind(&call);
15104
15105 pushArg(temp0);
15106 pushArg(end);
15107 pushArg(begin);
15108 pushArg(object);
15109
15110 using Fn =
15111 JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
15112 callVM<Fn, ArraySliceDense>(lir);
15113}
15114
15115void CodeGenerator::visitArgumentsSlice(LArgumentsSlice* lir) {
15116 Register object = ToRegister(lir->object());
15117 Register begin = ToRegister(lir->begin());
15118 Register end = ToRegister(lir->end());
15119 Register temp0 = ToRegister(lir->temp0());
15120 Register temp1 = ToRegister(lir->temp1());
15121
15122 Label call, fail;
15123
15124 // Try to allocate an object.
15125 TemplateObject templateObject(lir->mir()->templateObj());
15126 masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(),
15127 &fail);
15128
15129 masm.jump(&call);
15130 {
15131 masm.bind(&fail);
15132 masm.movePtr(ImmPtr(nullptr), temp0);
15133 }
15134 masm.bind(&call);
15135
15136 pushArg(temp0);
15137 pushArg(end);
15138 pushArg(begin);
15139 pushArg(object);
15140
15141 using Fn =
15142 JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
15143 callVM<Fn, ArgumentsSliceDense>(lir);
15144}
15145
15146#ifdef DEBUG1
15147void CodeGenerator::emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin,
15148 const RegisterOrInt32& count,
15149 Register numActualArgs) {
15150 // |begin| must be positive or zero.
15151 if (begin.is<Register>()) {
15152 Label beginOk;
15153 masm.branch32(Assembler::GreaterThanOrEqual, begin.as<Register>(), Imm32(0),
15154 &beginOk);
15155 masm.assumeUnreachable("begin < 0");
15156 masm.bind(&beginOk);
15157 } else {
15158 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15158); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() >= 0"
")"); do { *((volatile int*)__null) = 15158; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15159 }
15160
15161 // |count| must be positive or zero.
15162 if (count.is<Register>()) {
15163 Label countOk;
15164 masm.branch32(Assembler::GreaterThanOrEqual, count.as<Register>(), Imm32(0),
15165 &countOk);
15166 masm.assumeUnreachable("count < 0");
15167 masm.bind(&countOk);
15168 } else {
15169 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15169); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count.as<int32_t>() >= 0"
")"); do { *((volatile int*)__null) = 15169; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15170 }
15171
15172 // |begin| must be less-or-equal to |numActualArgs|.
15173 Label argsBeginOk;
15174 if (begin.is<Register>()) {
15175 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(),
15176 &argsBeginOk);
15177 } else {
15178 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
15179 Imm32(begin.as<int32_t>()), &argsBeginOk);
15180 }
15181 masm.assumeUnreachable("begin <= numActualArgs");
15182 masm.bind(&argsBeginOk);
15183
15184 // |count| must be less-or-equal to |numActualArgs|.
15185 Label argsCountOk;
15186 if (count.is<Register>()) {
15187 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, count.as<Register>(),
15188 &argsCountOk);
15189 } else {
15190 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
15191 Imm32(count.as<int32_t>()), &argsCountOk);
15192 }
15193 masm.assumeUnreachable("count <= numActualArgs");
15194 masm.bind(&argsCountOk);
15195
15196 // |begin| and |count| must be preserved, but |numActualArgs| can be changed.
15197 //
15198 // Pre-condition: |count| <= |numActualArgs|
15199 // Condition to test: |begin + count| <= |numActualArgs|
15200 // Transform to: |begin| <= |numActualArgs - count|
15201 if (count.is<Register>()) {
15202 masm.subPtr(count.as<Register>(), numActualArgs);
15203 } else {
15204 masm.subPtr(Imm32(count.as<int32_t>()), numActualArgs);
15205 }
15206
15207 // |begin + count| must be less-or-equal to |numActualArgs|.
15208 Label argsBeginCountOk;
15209 if (begin.is<Register>()) {
15210 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(),
15211 &argsBeginCountOk);
15212 } else {
15213 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
15214 Imm32(begin.as<int32_t>()), &argsBeginCountOk);
15215 }
15216 masm.assumeUnreachable("begin + count <= numActualArgs");
15217 masm.bind(&argsBeginCountOk);
15218}
15219#endif
15220
15221template <class ArgumentsSlice>
15222void CodeGenerator::emitNewArray(ArgumentsSlice* lir,
15223 const RegisterOrInt32& count, Register output,
15224 Register temp) {
15225 using Fn = ArrayObject* (*)(JSContext*, int32_t);
15226 auto* ool = count.match(
15227 [&](Register count) {
15228 return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>(
15229 lir, ArgList(count), StoreRegisterTo(output));
15230 },
15231 [&](int32_t count) {
15232 return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>(
15233 lir, ArgList(Imm32(count)), StoreRegisterTo(output));
15234 });
15235
15236 TemplateObject templateObject(lir->mir()->templateObj());
15237 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject.isArrayObject()"
")"); do { *((volatile int*)__null) = 15237; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15238
15239 auto templateNativeObj = templateObject.asTemplateNativeObject();
15240 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15240); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getArrayLength() == 0"
")"); do { *((volatile int*)__null) = 15240; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15241 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15241); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getDenseInitializedLength() == 0"
")"); do { *((volatile int*)__null) = 15241; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15242 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15242); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateNativeObj.hasDynamicElements()"
")"); do { *((volatile int*)__null) = 15242; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15243
15244 // Check array capacity. Call into the VM if the template object's capacity
15245 // is too small.
15246 bool tryAllocate = count.match(
15247 [&](Register count) {
15248 masm.branch32(Assembler::Above, count,
15249 Imm32(templateNativeObj.getDenseCapacity()),
15250 ool->entry());
15251 return true;
15252 },
15253 [&](int32_t count) {
15254 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",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15254); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count >= 0"
")"); do { *((volatile int*)__null) = 15254; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15255 if (uint32_t(count) > templateNativeObj.getDenseCapacity()) {
15256 masm.jump(ool->entry());
15257 return false;
15258 }
15259 return true;
15260 });
15261
15262 if (tryAllocate) {
15263 // Try to allocate an object.
15264 masm.createGCObject(output, temp, templateObject, lir->mir()->initialHeap(),
15265 ool->entry());
15266
15267 auto setInitializedLengthAndLength = [&](auto count) {
15268 const int elementsOffset = NativeObject::offsetOfFixedElements();
15269
15270 // Update initialized length.
15271 Address initLength(
15272 output, elementsOffset + ObjectElements::offsetOfInitializedLength());
15273 masm.store32(count, initLength);
15274
15275 // Update length.
15276 Address length(output, elementsOffset + ObjectElements::offsetOfLength());
15277 masm.store32(count, length);
15278 };
15279
15280 // The array object was successfully created. Set the length and initialized
15281 // length and then proceed to fill the elements.
15282 count.match([&](Register count) { setInitializedLengthAndLength(count); },
15283 [&](int32_t count) {
15284 if (count > 0) {
15285 setInitializedLengthAndLength(Imm32(count));
15286 }
15287 });
15288 }
15289
15290 masm.bind(ool->rejoin());
15291}
15292
15293void CodeGenerator::visitFrameArgumentsSlice(LFrameArgumentsSlice* lir) {
15294 Register begin = ToRegister(lir->begin());
15295 Register count = ToRegister(lir->count());
15296 Register temp = ToRegister(lir->temp0());
15297 Register output = ToRegister(lir->output());
15298
15299#ifdef DEBUG1
15300 masm.loadNumActualArgs(FramePointer, temp);
15301 emitAssertArgumentsSliceBounds(RegisterOrInt32(begin), RegisterOrInt32(count),
15302 temp);
15303#endif
15304
15305 emitNewArray(lir, RegisterOrInt32(count), output, temp);
15306
15307 Label done;
15308 masm.branch32(Assembler::Equal, count, Imm32(0), &done);
15309 {
15310 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
15311 allRegs.take(begin);
15312 allRegs.take(count);
15313 allRegs.take(temp);
15314 allRegs.take(output);
15315
15316 ValueOperand value = allRegs.takeAnyValue();
15317
15318 LiveRegisterSet liveRegs;
15319 liveRegs.add(output);
15320 liveRegs.add(begin);
15321 liveRegs.add(value);
15322
15323 masm.PushRegsInMask(liveRegs);
15324
15325 // Initialize all elements.
15326
15327 Register elements = output;
15328 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
15329
15330 Register argIndex = begin;
15331
15332 Register index = temp;
15333 masm.move32(Imm32(0), index);
15334
15335 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
15336 BaseValueIndex argPtr(FramePointer, argIndex, argvOffset);
15337
15338 Label loop;
15339 masm.bind(&loop);
15340
15341 masm.loadValue(argPtr, value);
15342
15343 // We don't need a pre-barrier, because the element at |index| is guaranteed
15344 // to be a non-GC thing (either uninitialized memory or the magic hole
15345 // value).
15346 masm.storeValue(value, BaseObjectElementIndex(elements, index));
15347
15348 masm.add32(Imm32(1), index);
15349 masm.add32(Imm32(1), argIndex);
15350
15351 masm.branch32(Assembler::LessThan, index, count, &loop);
15352
15353 masm.PopRegsInMask(liveRegs);
15354
15355 // Emit a post-write barrier if |output| is tenured.
15356 //
15357 // We expect that |output| is nursery allocated, so it isn't worth the
15358 // trouble to check if no frame argument is a nursery thing, which would
15359 // allow to omit the post-write barrier.
15360 masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done);
15361
15362 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
15363 volatileRegs.takeUnchecked(temp);
15364 if (output.volatile_()) {
15365 volatileRegs.addUnchecked(output);
15366 }
15367
15368 masm.PushRegsInMask(volatileRegs);
15369 emitPostWriteBarrier(output);
15370 masm.PopRegsInMask(volatileRegs);
15371 }
15372 masm.bind(&done);
15373}
15374
15375CodeGenerator::RegisterOrInt32 CodeGenerator::ToRegisterOrInt32(
15376 const LAllocation* allocation) {
15377 if (allocation->isConstant()) {
15378 return RegisterOrInt32(allocation->toConstant()->toInt32());
15379 }
15380 return RegisterOrInt32(ToRegister(allocation));
15381}
15382
15383void CodeGenerator::visitInlineArgumentsSlice(LInlineArgumentsSlice* lir) {
15384 RegisterOrInt32 begin = ToRegisterOrInt32(lir->begin());
15385 RegisterOrInt32 count = ToRegisterOrInt32(lir->count());
15386 Register temp = ToRegister(lir->temp());
15387 Register output = ToRegister(lir->output());
15388
15389 uint32_t numActuals = lir->mir()->numActuals();
15390
15391#ifdef DEBUG1
15392 masm.move32(Imm32(numActuals), temp);
15393
15394 emitAssertArgumentsSliceBounds(begin, count, temp);
15395#endif
15396
15397 emitNewArray(lir, count, output, temp);
15398
15399 // We're done if there are no actual arguments.
15400 if (numActuals == 0) {
15401 return;
15402 }
15403
15404 // Check if any arguments have to be copied.
15405 Label done;
15406 if (count.is<Register>()) {
15407 masm.branch32(Assembler::Equal, count.as<Register>(), Imm32(0), &done);
15408 } else if (count.as<int32_t>() == 0) {
15409 return;
15410 }
15411
15412 auto getArg = [&](uint32_t i) {
15413 return toConstantOrRegister(lir, LInlineArgumentsSlice::ArgIndex(i),
15414 lir->mir()->getArg(i)->type());
15415 };
15416
15417 auto storeArg = [&](uint32_t i, auto dest) {
15418 // We don't need a pre-barrier because the element at |index| is guaranteed
15419 // to be a non-GC thing (either uninitialized memory or the magic hole
15420 // value).
15421 masm.storeConstantOrRegister(getArg(i), dest);
15422 };
15423
15424 // Initialize all elements.
15425 if (numActuals == 1) {
15426 // There's exactly one argument. We've checked that |count| is non-zero,
15427 // which implies that |begin| must be zero.
15428 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15428); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() == 0"
")"); do { *((volatile int*)__null) = 15428; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
15429
15430 Register elements = temp;
15431 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
15432
15433 storeArg(0, Address(elements, 0));
15434 } else if (begin.is<Register>()) {
15435 // There is more than one argument and |begin| isn't a compile-time
15436 // constant. Iterate through 0..numActuals to search for |begin| and then
15437 // start copying |count| arguments from that index.
15438
15439 LiveGeneralRegisterSet liveRegs;
15440 liveRegs.add(output);
15441 liveRegs.add(begin.as<Register>());
15442
15443 masm.PushRegsInMask(liveRegs);
15444
15445 Register elements = output;
15446 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
15447
15448 Register argIndex = begin.as<Register>();
15449
15450 Register index = temp;
15451 masm.move32(Imm32(0), index);
15452
15453 Label doneLoop;
15454 for (uint32_t i = 0; i < numActuals; ++i) {
15455 Label next;
15456 masm.branch32(Assembler::NotEqual, argIndex, Imm32(i), &next);
15457
15458 storeArg(i, BaseObjectElementIndex(elements, index));
15459
15460 masm.add32(Imm32(1), index);
15461 masm.add32(Imm32(1), argIndex);
15462
15463 if (count.is<Register>()) {
15464 masm.branch32(Assembler::GreaterThanOrEqual, index,
15465 count.as<Register>(), &doneLoop);
15466 } else {
15467 masm.branch32(Assembler::GreaterThanOrEqual, index,
15468 Imm32(count.as<int32_t>()), &doneLoop);
15469 }
15470
15471 masm.bind(&next);
15472 }
15473 masm.bind(&doneLoop);
15474
15475 masm.PopRegsInMask(liveRegs);
15476 } else {
15477 // There is more than one argument and |begin| is a compile-time constant.
15478
15479 Register elements = temp;
15480 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
15481
15482 int32_t argIndex = begin.as<int32_t>();
15483
15484 int32_t index = 0;
15485
15486 Label doneLoop;
15487 for (uint32_t i = argIndex; i < numActuals; ++i) {
15488 storeArg(i, Address(elements, index * sizeof(Value)));
15489
15490 index += 1;
15491
15492 if (count.is<Register>()) {
15493 masm.branch32(Assembler::LessThanOrEqual, count.as<Register>(),
15494 Imm32(index), &doneLoop);
15495 } else {
15496 if (index >= count.as<int32_t>()) {
15497 break;
15498 }
15499 }
15500 }
15501 masm.bind(&doneLoop);
15502 }
15503
15504 // Determine if we have to emit post-write barrier.
15505 //
15506 // If either |begin| or |count| is a constant, use their value directly.
15507 // Otherwise assume we copy all inline arguments from 0..numActuals.
15508 bool postWriteBarrier = false;
15509 uint32_t actualBegin = begin.match([](Register) { return 0; },
15510 [](int32_t value) { return value; });
15511 uint32_t actualCount =
15512 count.match([=](Register) { return numActuals; },
15513 [](int32_t value) -> uint32_t { return value; });
15514 for (uint32_t i = 0; i < actualCount; ++i) {
15515 ConstantOrRegister arg = getArg(actualBegin + i);
15516 if (arg.constant()) {
15517 Value v = arg.value();
15518 if (v.isGCThing() && IsInsideNursery(v.toGCThing())) {
15519 postWriteBarrier = true;
15520 }
15521 } else {
15522 MIRType type = arg.reg().type();
15523 if (type == MIRType::Value || NeedsPostBarrier(type)) {
15524 postWriteBarrier = true;
15525 }
15526 }
15527 }
15528
15529 // Emit a post-write barrier if |output| is tenured and we couldn't
15530 // determine at compile-time that no barrier is needed.
15531 if (postWriteBarrier) {
15532 masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done);
15533
15534 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
15535 volatileRegs.takeUnchecked(temp);
15536 if (output.volatile_()) {
15537 volatileRegs.addUnchecked(output);
15538 }
15539
15540 masm.PushRegsInMask(volatileRegs);
15541 emitPostWriteBarrier(output);
15542 masm.PopRegsInMask(volatileRegs);
15543 }
15544
15545 masm.bind(&done);
15546}
15547
15548void CodeGenerator::visitNormalizeSliceTerm(LNormalizeSliceTerm* lir) {
15549 Register value = ToRegister(lir->value());
15550 Register length = ToRegister(lir->length());
15551 Register output = ToRegister(lir->output());
15552
15553 masm.move32(value, output);
15554
15555 Label positive;
15556 masm.branch32(Assembler::GreaterThanOrEqual, value, Imm32(0), &positive);
15557
15558 Label done;
15559 masm.add32(length, output);
15560 masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(0), &done);
15561 masm.move32(Imm32(0), output);
15562 masm.jump(&done);
15563
15564 masm.bind(&positive);
15565 masm.cmp32Move32(Assembler::LessThan, length, value, length, output);
15566
15567 masm.bind(&done);
15568}
15569
15570void CodeGenerator::visitArrayJoin(LArrayJoin* lir) {
15571 Label skipCall;
15572
15573 Register output = ToRegister(lir->output());
15574 Register sep = ToRegister(lir->separator());
15575 Register array = ToRegister(lir->array());
15576 Register temp = ToRegister(lir->temp0());
15577
15578 // Fast path for simple length <= 1 cases.
15579 {
15580 masm.loadPtr(Address(array, NativeObject::offsetOfElements()), temp);
15581 Address length(temp, ObjectElements::offsetOfLength());
15582 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
15583
15584 // Check for length == 0
15585 Label notEmpty;
15586 masm.branch32(Assembler::NotEqual, length, Imm32(0), &notEmpty);
15587 const JSAtomState& names = gen->runtime->names();
15588 masm.movePtr(ImmGCPtr(names.empty_), output);
15589 masm.jump(&skipCall);
15590
15591 masm.bind(&notEmpty);
15592 Label notSingleString;
15593 // Check for length == 1, initializedLength >= 1, arr[0].isString()
15594 masm.branch32(Assembler::NotEqual, length, Imm32(1), &notSingleString);
15595 masm.branch32(Assembler::LessThan, initLength, Imm32(1), &notSingleString);
15596
15597 Address elem0(temp, 0);
15598 masm.branchTestString(Assembler::NotEqual, elem0, &notSingleString);
15599
15600 // At this point, 'output' can be used as a scratch register, since we're
15601 // guaranteed to succeed.
15602 masm.unboxString(elem0, output);
15603 masm.jump(&skipCall);
15604 masm.bind(&notSingleString);
15605 }
15606
15607 pushArg(sep);
15608 pushArg(array);
15609
15610 using Fn = JSString* (*)(JSContext*, HandleObject, HandleString);
15611 callVM<Fn, jit::ArrayJoin>(lir);
15612 masm.bind(&skipCall);
15613}
15614
15615void CodeGenerator::visitObjectKeys(LObjectKeys* lir) {
15616 Register object = ToRegister(lir->object());
15617
15618 pushArg(object);
15619
15620 using Fn = JSObject* (*)(JSContext*, HandleObject);
15621 callVM<Fn, jit::ObjectKeys>(lir);
15622}
15623
15624void CodeGenerator::visitObjectKeysLength(LObjectKeysLength* lir) {
15625 Register object = ToRegister(lir->object());
15626
15627 pushArg(object);
15628
15629 using Fn = bool (*)(JSContext*, HandleObject, int32_t*);
15630 callVM<Fn, jit::ObjectKeysLength>(lir);
15631}
15632
15633void CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir) {
15634 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
15635 TypedOrValueRegister val =
15636 toConstantOrRegister(lir, LGetIteratorCache::ValueIndex,
15637 lir->mir()->value()->type())
15638 .reg();
15639 Register output = ToRegister(lir->output());
15640 Register temp0 = ToRegister(lir->temp0());
15641 Register temp1 = ToRegister(lir->temp1());
15642
15643 IonGetIteratorIC ic(liveRegs, val, output, temp0, temp1);
15644 addIC(lir, allocateIC(ic));
15645}
15646
15647void CodeGenerator::visitOptimizeSpreadCallCache(
15648 LOptimizeSpreadCallCache* lir) {
15649 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
15650 ValueOperand val = ToValue(lir, LOptimizeSpreadCallCache::ValueIndex);
15651 ValueOperand output = ToOutValue(lir);
15652 Register temp = ToRegister(lir->temp0());
15653
15654 IonOptimizeSpreadCallIC ic(liveRegs, val, output, temp);
15655 addIC(lir, allocateIC(ic));
15656}
15657
15658void CodeGenerator::visitCloseIterCache(LCloseIterCache* lir) {
15659 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
15660 Register iter = ToRegister(lir->iter());
15661 Register temp = ToRegister(lir->temp0());
15662 CompletionKind kind = CompletionKind(lir->mir()->completionKind());
15663
15664 IonCloseIterIC ic(liveRegs, iter, temp, kind);
15665 addIC(lir, allocateIC(ic));
15666}
15667
15668void CodeGenerator::visitOptimizeGetIteratorCache(
15669 LOptimizeGetIteratorCache* lir) {
15670 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
15671 ValueOperand val = ToValue(lir, LOptimizeGetIteratorCache::ValueIndex);
15672 Register output = ToRegister(lir->output());
15673 Register temp = ToRegister(lir->temp0());
15674
15675 IonOptimizeGetIteratorIC ic(liveRegs, val, output, temp);
15676 addIC(lir, allocateIC(ic));
15677}
15678
15679void CodeGenerator::visitIteratorMore(LIteratorMore* lir) {
15680 const Register obj = ToRegister(lir->iterator());
15681 const ValueOperand output = ToOutValue(lir);
15682 const Register temp = ToRegister(lir->temp0());
15683
15684 masm.iteratorMore(obj, output, temp);
15685}
15686
15687void CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch* lir) {
15688 ValueOperand input = ToValue(lir, LIsNoIterAndBranch::Input);
15689 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
15690 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
15691
15692 masm.branchTestMagic(Assembler::Equal, input, ifTrue);
15693
15694 if (!isNextBlock(lir->ifFalse()->lir())) {
15695 masm.jump(ifFalse);
15696 }
15697}
15698
15699void CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) {
15700 const Register obj = ToRegister(lir->object());
15701 const Register temp0 = ToRegister(lir->temp0());
15702 const Register temp1 = ToRegister(lir->temp1());
15703 const Register temp2 = ToRegister(lir->temp2());
15704
15705 masm.iteratorClose(obj, temp0, temp1, temp2);
15706}
15707
15708void CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) {
15709 // read number of actual arguments from the JS frame.
15710 Register argc = ToRegister(lir->output());
15711 masm.loadNumActualArgs(FramePointer, argc);
15712}
15713
15714void CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) {
15715 ValueOperand result = ToOutValue(lir);
15716 const LAllocation* index = lir->index();
15717 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
15718
15719 // This instruction is used to access actual arguments and formal arguments.
15720 // The number of Values on the stack is |max(numFormals, numActuals)|, so we
15721 // assert |index < numFormals || index < numActuals| in debug builds.
15722 DebugOnly<size_t> numFormals = gen->outerInfo().script()->function()->nargs();
15723
15724 if (index->isConstant()) {
15725 int32_t i = index->toConstant()->toInt32();
15726#ifdef DEBUG1
15727 if (uint32_t(i) >= numFormals) {
15728 Label ok;
15729 Register argc = result.scratchReg();
15730 masm.loadNumActualArgs(FramePointer, argc);
15731 masm.branch32(Assembler::Above, argc, Imm32(i), &ok);
15732 masm.assumeUnreachable("Invalid argument index");
15733 masm.bind(&ok);
15734 }
15735#endif
15736 Address argPtr(FramePointer, sizeof(Value) * i + argvOffset);
15737 masm.loadValue(argPtr, result);
15738 } else {
15739 Register i = ToRegister(index);
15740#ifdef DEBUG1
15741 Label ok;
15742 Register argc = result.scratchReg();
15743 masm.branch32(Assembler::Below, i, Imm32(numFormals), &ok);
15744 masm.loadNumActualArgs(FramePointer, argc);
15745 masm.branch32(Assembler::Above, argc, i, &ok);
15746 masm.assumeUnreachable("Invalid argument index");
15747 masm.bind(&ok);
15748#endif
15749 BaseValueIndex argPtr(FramePointer, i, argvOffset);
15750 masm.loadValue(argPtr, result);
15751 }
15752}
15753
15754void CodeGenerator::visitGetFrameArgumentHole(LGetFrameArgumentHole* lir) {
15755 ValueOperand result = ToOutValue(lir);
15756 Register index = ToRegister(lir->index());
15757 Register length = ToRegister(lir->length());
15758 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0());
15759 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
15760
15761 Label outOfBounds, done;
15762 masm.spectreBoundsCheck32(index, length, spectreTemp, &outOfBounds);
15763
15764 BaseValueIndex argPtr(FramePointer, index, argvOffset);
15765 masm.loadValue(argPtr, result);
15766 masm.jump(&done);
15767
15768 masm.bind(&outOfBounds);
15769 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
15770 masm.moveValue(UndefinedValue(), result);
15771
15772 masm.bind(&done);
15773}
15774
15775void CodeGenerator::visitRest(LRest* lir) {
15776 Register numActuals = ToRegister(lir->numActuals());
15777 Register temp0 = ToRegister(lir->temp0());
15778 Register temp1 = ToRegister(lir->temp1());
15779 Register temp2 = ToRegister(lir->temp2());
15780 Register temp3 = ToRegister(lir->temp3());
15781 unsigned numFormals = lir->mir()->numFormals();
15782
15783 constexpr uint32_t arrayCapacity = 2;
15784
15785 if (Shape* shape = lir->mir()->shape()) {
15786 uint32_t arrayLength = 0;
15787 gc::AllocKind allocKind = GuessArrayGCKind(arrayCapacity);
15788 MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject
::class_))>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(allocKind
, &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15788); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)"
")"); do { *((volatile int*)__null) = 15788; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15789 allocKind = ForegroundToBackgroundAllocKind(allocKind);
15790 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
")"); do { *((volatile int*)__null) = 15791; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
15791 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
")"); do { *((volatile int*)__null) = 15791; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15792
15793 Label joinAlloc, failAlloc;
15794 masm.movePtr(ImmGCPtr(shape), temp0);
15795 masm.createArrayWithFixedElements(temp2, temp0, temp1, InvalidReg,
15796 arrayLength, arrayCapacity, 0, 0,
15797 allocKind, gc::Heap::Default, &failAlloc);
15798 masm.jump(&joinAlloc);
15799 {
15800 masm.bind(&failAlloc);
15801 masm.movePtr(ImmPtr(nullptr), temp2);
15802 }
15803 masm.bind(&joinAlloc);
15804 } else {
15805 masm.movePtr(ImmPtr(nullptr), temp2);
15806 }
15807
15808 // Set temp1 to the address of the first actual argument.
15809 size_t actualsOffset = JitFrameLayout::offsetOfActualArgs();
15810 masm.computeEffectiveAddress(Address(FramePointer, actualsOffset), temp1);
15811
15812 // Compute array length: max(numActuals - numFormals, 0).
15813 Register lengthReg;
15814 if (numFormals) {
15815 lengthReg = temp0;
15816 Label emptyLength, joinLength;
15817 masm.branch32(Assembler::LessThanOrEqual, numActuals, Imm32(numFormals),
15818 &emptyLength);
15819 {
15820 masm.move32(numActuals, lengthReg);
15821 masm.sub32(Imm32(numFormals), lengthReg);
15822
15823 // Skip formal arguments.
15824 masm.addPtr(Imm32(sizeof(Value) * numFormals), temp1);
15825
15826 masm.jump(&joinLength);
15827 }
15828 masm.bind(&emptyLength);
15829 {
15830 masm.move32(Imm32(0), lengthReg);
15831
15832 // Leave temp1 pointed to the start of actuals() when the rest-array
15833 // length is zero. We don't use |actuals() + numFormals| because
15834 // |numFormals| can be any non-negative int32 value when this MRest was
15835 // created from scalar replacement optimizations. And it seems
15836 // questionable to compute a Value* pointer which points to who knows
15837 // where.
15838 }
15839 masm.bind(&joinLength);
15840 } else {
15841 // Use numActuals directly when there are no formals.
15842 lengthReg = numActuals;
15843 }
15844
15845 // Try to initialize the array elements.
15846 Label vmCall, done;
15847 if (lir->mir()->shape()) {
15848 // Call into C++ if we failed to allocate an array or there are more than
15849 // |arrayCapacity| elements.
15850 masm.branchTestPtr(Assembler::Zero, temp2, temp2, &vmCall);
15851 masm.branch32(Assembler::Above, lengthReg, Imm32(arrayCapacity), &vmCall);
15852
15853 // The array must be nursery allocated so no post barrier is needed.
15854#ifdef DEBUG1
15855 Label ok;
15856 masm.branchPtrInNurseryChunk(Assembler::Equal, temp2, temp3, &ok);
15857 masm.assumeUnreachable("Unexpected tenured object for LRest");
15858 masm.bind(&ok);
15859#endif
15860
15861 Label initialized;
15862 masm.branch32(Assembler::Equal, lengthReg, Imm32(0), &initialized);
15863
15864 // Store length and initializedLength.
15865 Register elements = temp3;
15866 masm.loadPtr(Address(temp2, NativeObject::offsetOfElements()), elements);
15867 Address lengthAddr(elements, ObjectElements::offsetOfLength());
15868 Address initLengthAddr(elements,
15869 ObjectElements::offsetOfInitializedLength());
15870 masm.store32(lengthReg, lengthAddr);
15871 masm.store32(lengthReg, initLengthAddr);
15872
15873 // Store either one or two elements. This may clobber lengthReg (temp0).
15874 static_assert(arrayCapacity == 2, "code handles 1 or 2 elements");
15875 Label storeFirst;
15876 masm.branch32(Assembler::Equal, lengthReg, Imm32(1), &storeFirst);
15877 masm.storeValue(Address(temp1, sizeof(Value)),
15878 Address(elements, sizeof(Value)), temp0);
15879 masm.bind(&storeFirst);
15880 masm.storeValue(Address(temp1, 0), Address(elements, 0), temp0);
15881
15882 // Done.
15883 masm.bind(&initialized);
15884 masm.movePtr(temp2, ReturnReg);
15885 masm.jump(&done);
15886 }
15887
15888 masm.bind(&vmCall);
15889
15890 pushArg(temp2);
15891 pushArg(temp1);
15892 pushArg(lengthReg);
15893
15894 using Fn =
15895 ArrayObject* (*)(JSContext*, uint32_t, Value*, Handle<ArrayObject*>);
15896 callVM<Fn, InitRestParameter>(lir);
15897
15898 masm.bind(&done);
15899}
15900
15901// Create a stackmap from the given safepoint, with the structure:
15902//
15903// <reg dump, if any>
15904// | ++ <body (general spill)>
15905// | | ++ <space for Frame>
15906// | | ++ <inbound args>
15907// | | |
15908// Lowest Addr Highest Addr
15909// |
15910// framePushedAtStackMapBase
15911//
15912// The caller owns the resulting stackmap. This assumes a grow-down stack.
15913//
15914// For non-debug builds, if the stackmap would contain no pointers, no
15915// stackmap is created, and nullptr is returned. For a debug build, a
15916// stackmap is always created and returned.
15917//
15918// Depending on the type of safepoint, the stackmap may need to account for
15919// spilled registers. WasmSafepointKind::LirCall corresponds to LIR nodes where
15920// isCall() == true, for which the register allocator will spill/restore all
15921// live registers at the LIR level - in this case, the LSafepoint sees only live
15922// values on the stack, never in registers. WasmSafepointKind::CodegenCall, on
15923// the other hand, is for LIR nodes which may manually spill/restore live
15924// registers in codegen, in which case the stackmap must account for this. Traps
15925// also require tracking of live registers, but spilling is handled by the trap
15926// mechanism.
15927static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint,
15928 const RegisterOffsets& trapExitLayout,
15929 size_t trapExitLayoutNumWords,
15930 size_t nInboundStackArgBytes,
15931 wasm::StackMap** result) {
15932 // Ensure this is defined on all return paths.
15933 *result = nullptr;
15934
15935 // The size of the wasm::Frame itself.
15936 const size_t nFrameBytes = sizeof(wasm::Frame);
15937
15938 // This is the number of bytes spilled for live registers, outside of a trap.
15939 // For traps, trapExitLayout and trapExitLayoutNumWords will be used.
15940 const size_t nRegisterDumpBytes =
15941 MacroAssembler::PushRegsInMaskSizeInBytes(safepoint.liveRegs());
15942
15943 // As mentioned above, for WasmSafepointKind::LirCall, register spills and
15944 // restores are handled at the LIR level and there should therefore be no live
15945 // registers to handle here.
15946 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15947); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0"
")"); do { *((volatile int*)__null) = 15947; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
15947 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15947); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0"
")"); do { *((volatile int*)__null) = 15947; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
15948 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15948); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 15948; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15949
15950 // This is the number of bytes in the general spill area, below the Frame.
15951 const size_t nBodyBytes = safepoint.framePushedAtStackMapBase();
15952
15953 // The stack map owns any alignment padding around inbound stack args.
15954 const size_t nInboundStackArgBytesAligned =
15955 wasm::AlignStackArgAreaSize(nInboundStackArgBytes);
15956
15957 // This is the number of bytes in the general spill area, the Frame, and the
15958 // incoming args, but not including any register dump area.
15959 const size_t nNonRegisterBytes =
15960 nBodyBytes + nFrameBytes + nInboundStackArgBytesAligned;
15961 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 15961); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nNonRegisterBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 15961; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15962
15963 // This is the number of bytes in the register dump area, if any, below the
15964 // general spill area.
15965 const size_t nRegisterBytes =
15966 (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap)
15967 ? (trapExitLayoutNumWords * sizeof(void*))
15968 : nRegisterDumpBytes;
15969
15970 // This is the total number of bytes covered by the map.
15971 const size_t nTotalBytes = nNonRegisterBytes + nRegisterBytes;
15972
15973#ifndef DEBUG1
15974 bool needStackMap = !(safepoint.wasmAnyRefRegs().empty() &&
15975 safepoint.wasmAnyRefSlots().empty() &&
15976 safepoint.slotsOrElementsSlots().empty());
15977
15978 // There are no references, and this is a non-debug build, so don't bother
15979 // building the stackmap.
15980 if (!needStackMap) {
15981 return true;
15982 }
15983#endif
15984
15985 wasm::StackMap* stackMap =
15986 wasm::StackMap::create(nTotalBytes / sizeof(void*));
15987 if (!stackMap) {
15988 return false;
15989 }
15990 if (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) {
15991 stackMap->setExitStubWords(trapExitLayoutNumWords);
15992 }
15993
15994 // REG DUMP AREA, if any.
15995 size_t regDumpWords = 0;
15996 const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs();
15997 const LiveGeneralRegisterSet slotsOrElementsRegs =
15998 safepoint.slotsOrElementsRegs();
15999 const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union(
16000 wasmAnyRefRegs.set(), slotsOrElementsRegs.set()));
16001 GeneralRegisterForwardIterator refRegsIter(refRegs);
16002 switch (safepoint.wasmSafepointKind()) {
16003 case WasmSafepointKind::LirCall:
16004 case WasmSafepointKind::StackSwitch:
16005 case WasmSafepointKind::CodegenCall: {
16006 size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*);
16007 regDumpWords += spilledNumWords;
16008
16009 for (; refRegsIter.more(); ++refRegsIter) {
16010 Register reg = *refRegsIter;
16011 size_t offsetFromSpillBase =
16012 safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) /
16013 sizeof(void*);
16014 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16015); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
")"); do { *((volatile int*)__null) = 16015; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16015 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16015); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
")"); do { *((volatile int*)__null) = 16015; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16016 size_t index = spilledNumWords - offsetFromSpillBase;
16017
16018 if (wasmAnyRefRegs.has(reg)) {
16019 stackMap->set(index, wasm::StackMap::AnyRef);
16020 } else {
16021 MOZ_ASSERT(slotsOrElementsRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slotsOrElementsRegs.has(reg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slotsOrElementsRegs.has(reg)
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"slotsOrElementsRegs.has(reg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16021); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)"
")"); do { *((volatile int*)__null) = 16021; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16022 stackMap->set(index, wasm::StackMap::ArrayDataPointer);
16023 }
16024 }
16025 // Float and vector registers do not have to be handled; they cannot
16026 // contain wasm anyrefs, and they are spilled after general-purpose
16027 // registers. Gprs are therefore closest to the spill base and thus their
16028 // offset calculation does not need to account for other spills.
16029 } break;
16030 case WasmSafepointKind::Trap: {
16031 regDumpWords += trapExitLayoutNumWords;
16032
16033 for (; refRegsIter.more(); ++refRegsIter) {
16034 Register reg = *refRegsIter;
16035 size_t offsetFromTop = trapExitLayout.getOffset(reg);
16036
16037 // If this doesn't hold, the associated register wasn't saved by
16038 // the trap exit stub. Better to crash now than much later, in
16039 // some obscure place, and possibly with security consequences.
16040 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16040); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "offsetFromTop < trapExitLayoutNumWords"
")"); do { *((volatile int*)__null) = 16040; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16041
16042 // offsetFromTop is an offset in words down from the highest
16043 // address in the exit stub save area. Switch it around to be an
16044 // offset up from the bottom of the (integer register) save area.
16045 size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop;
16046
16047 if (wasmAnyRefRegs.has(reg)) {
16048 stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef);
16049 } else {
16050 MOZ_ASSERT(slotsOrElementsRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(slotsOrElementsRegs.has(reg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(slotsOrElementsRegs.has(reg)
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"slotsOrElementsRegs.has(reg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16050); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)"
")"); do { *((volatile int*)__null) = 16050; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16051 stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer);
16052 }
16053 }
16054 } break;
16055 default:
16056 MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16056); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")"
); do { *((volatile int*)__null) = 16056; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
16057 }
16058
16059 // Ensure other reg/slot collections on LSafepoint are empty.
16060 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16060); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.gcRegs().empty() && safepoint.gcSlots().empty()"
")"); do { *((volatile int*)__null) = 16060; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16061#ifdef JS_NUNBOX32
16062 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16062); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.nunboxParts().empty()"
")"); do { *((volatile int*)__null) = 16062; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16063#elif JS_PUNBOX641
16064 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16064); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.valueRegs().empty() && safepoint.valueSlots().empty()"
")"); do { *((volatile int*)__null) = 16064; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16065#endif
16066
16067 // BODY (GENERAL SPILL) AREA and FRAME and INCOMING ARGS
16068 // Deal with roots on the stack.
16069 const LSafepoint::SlotList& wasmAnyRefSlots = safepoint.wasmAnyRefSlots();
16070 for (SafepointSlotEntry wasmAnyRefSlot : wasmAnyRefSlots) {
16071 // The following needs to correspond with JitFrameLayout::slotRef
16072 // wasmAnyRefSlot.stack == 0 means the slot is in the args area
16073 if (wasmAnyRefSlot.stack) {
16074 // It's a slot in the body allocation, so .slot is interpreted
16075 // as an index downwards from the Frame*
16076 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16076); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot <= nBodyBytes"
")"); do { *((volatile int*)__null) = 16076; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16077 uint32_t offsetInBytes = nBodyBytes - wasmAnyRefSlot.slot;
16078 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 16078; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16079 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
16080 wasm::StackMap::AnyRef);
16081 } else {
16082 // It's an argument slot
16083 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16083); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot < nInboundStackArgBytes"
")"); do { *((volatile int*)__null) = 16083; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16084 uint32_t offsetInBytes = nBodyBytes + nFrameBytes + wasmAnyRefSlot.slot;
16085 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16085); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 16085; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16086 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
16087 wasm::StackMap::AnyRef);
16088 }
16089 }
16090
16091 // Track array data pointers on the stack
16092 const LSafepoint::SlotList& slots = safepoint.slotsOrElementsSlots();
16093 for (SafepointSlotEntry slot : slots) {
16094 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16094); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack"
")"); do { *((volatile int*)__null) = 16094; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16095
16096 // It's a slot in the body allocation, so .slot is interpreted
16097 // as an index downwards from the Frame*
16098 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16098); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes"
")"); do { *((volatile int*)__null) = 16098; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16099 uint32_t offsetInBytes = nBodyBytes - slot.slot;
16100 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16100); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 16100; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16101 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
16102 wasm::StackMap::Kind::ArrayDataPointer);
16103 }
16104
16105 // Record in the map, how far down from the highest address the Frame* is.
16106 // Take the opportunity to check that we haven't marked any part of the
16107 // Frame itself as a pointer.
16108 stackMap->setFrameOffsetFromTop((nInboundStackArgBytesAligned + nFrameBytes) /
16109 sizeof(void*));
16110#ifdef DEBUG1
16111 for (uint32_t i = 0; i < nFrameBytes / sizeof(void*); i++) {
16112 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16114); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { *((volatile int*)__null) = 16114; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16113 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16114); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { *((volatile int*)__null) = 16114; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16114 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16114); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { *((volatile int*)__null) = 16114; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16115 }
16116#endif
16117
16118 *result = stackMap;
16119 return true;
16120}
16121
16122bool CodeGenerator::generateWasm(
16123 wasm::CallIndirectId callIndirectId, wasm::BytecodeOffset trapOffset,
16124 const wasm::ArgTypeVector& argTypes, const RegisterOffsets& trapExitLayout,
16125 size_t trapExitLayoutNumWords, wasm::FuncOffsets* offsets,
16126 wasm::StackMaps* stackMaps, wasm::Decoder* decoder) {
16127 AutoCreatedBy acb(masm, "CodeGenerator::generateWasm");
16128
16129 JitSpew(JitSpew_Codegen, "# Emitting wasm code");
16130
16131 size_t nInboundStackArgBytes = StackArgAreaSizeUnaligned(argTypes);
16132 inboundStackArgBytes_ = nInboundStackArgBytes;
16133
16134 wasm::GenerateFunctionPrologue(masm, callIndirectId, mozilla::Nothing(),
16135 offsets);
16136
16137 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0"
")"); do { *((volatile int*)__null) = 16137; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16138
16139 // Very large frames are implausible, probably an attack.
16140 if (frameSize() > wasm::MaxFrameSize) {
16141 return decoder->fail(decoder->beginOffset(), "stack frame is too large");
16142 }
16143
16144 if (omitOverRecursedCheck()) {
16145 masm.reserveStack(frameSize());
16146 } else {
16147 std::pair<CodeOffset, uint32_t> pair =
16148 masm.wasmReserveStackChecked(frameSize(), trapOffset);
16149 CodeOffset trapInsnOffset = pair.first;
16150 size_t nBytesReservedBeforeTrap = pair.second;
16151
16152 wasm::StackMap* functionEntryStackMap = nullptr;
16153 if (!CreateStackMapForFunctionEntryTrap(
16154 argTypes, trapExitLayout, trapExitLayoutNumWords,
16155 nBytesReservedBeforeTrap, nInboundStackArgBytes,
16156 &functionEntryStackMap)) {
16157 return false;
16158 }
16159
16160 // In debug builds, we'll always have a stack map, even if there are no
16161 // refs to track.
16162 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16162); AnnotateMozCrashReason("MOZ_ASSERT" "(" "functionEntryStackMap"
")"); do { *((volatile int*)__null) = 16162; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16163
16164 if (functionEntryStackMap &&
16165 !stackMaps->add((uint8_t*)(uintptr_t)trapInsnOffset.offset(),
16166 functionEntryStackMap)) {
16167 functionEntryStackMap->destroy();
16168 return false;
16169 }
16170 }
16171
16172 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16172); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 16172; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16173
16174 if (!generateBody()) {
16175 return false;
16176 }
16177
16178 masm.bind(&returnLabel_);
16179 wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets);
16180
16181 if (!generateOutOfLineCode()) {
16182 return false;
16183 }
16184
16185 masm.flush();
16186 if (masm.oom()) {
16187 return false;
16188 }
16189
16190 offsets->end = masm.currentOffset();
16191
16192 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!masm.failureLabel()->used()"
")"); do { *((volatile int*)__null) = 16192; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16193 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.listSize() == 0"
")"); do { *((volatile int*)__null) = 16193; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16194 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16194); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.RVATableSize() == 0"
")"); do { *((volatile int*)__null) = 16194; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16195 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16195); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size() == 0"
")"); do { *((volatile int*)__null) = 16195; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16196 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16196); AnnotateMozCrashReason("MOZ_ASSERT" "(" "graph.numConstants() == 0"
")"); do { *((volatile int*)__null) = 16196; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16197 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16197); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osiIndices_.empty()"
")"); do { *((volatile int*)__null) = 16197; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16198 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "icList_.empty()"
")"); do { *((volatile int*)__null) = 16198; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16199 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16199); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoints_.size() == 0"
")"); do { *((volatile int*)__null) = 16199; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16200 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_"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16200); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!scriptCounts_"
")"); do { *((volatile int*)__null) = 16200; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16201
16202 // Convert the safepoints to stackmaps and add them to our running
16203 // collection thereof.
16204 for (CodegenSafepointIndex& index : safepointIndices_) {
16205 wasm::StackMap* stackMap = nullptr;
16206 if (!CreateStackMapFromLSafepoint(*index.safepoint(), trapExitLayout,
16207 trapExitLayoutNumWords,
16208 nInboundStackArgBytes, &stackMap)) {
16209 return false;
16210 }
16211
16212 // In debug builds, we'll always have a stack map.
16213 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16213); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap" ")"
); do { *((volatile int*)__null) = 16213; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
16214 if (!stackMap) {
16215 continue;
16216 }
16217
16218 if (!stackMaps->add((uint8_t*)(uintptr_t)index.displacement(), stackMap)) {
16219 stackMap->destroy();
16220 return false;
16221 }
16222 }
16223
16224 return true;
16225}
16226
16227bool CodeGenerator::generate() {
16228 AutoCreatedBy acb(masm, "CodeGenerator::generate");
16229
16230 JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%u:%u",
16231 gen->outerInfo().script()->filename(),
16232 gen->outerInfo().script()->lineno(),
16233 gen->outerInfo().script()->column().oneOriginValue());
16234
16235 // Initialize native code table with an entry to the start of
16236 // top-level script.
16237 InlineScriptTree* tree = gen->outerInfo().inlineScriptTree();
16238 jsbytecode* startPC = tree->script()->code();
16239 BytecodeSite* startSite = new (gen->alloc()) BytecodeSite(tree, startPC);
16240 if (!addNativeToBytecodeEntry(startSite)) {
16241 return false;
16242 }
16243
16244 if (!safepoints_.init(gen->alloc())) {
16245 return false;
16246 }
16247
16248 perfSpewer_.recordOffset(masm, "Prologue");
16249 if (!generatePrologue()) {
16250 return false;
16251 }
16252
16253 // Reset native => bytecode map table with top-level script and startPc.
16254 if (!addNativeToBytecodeEntry(startSite)) {
16255 return false;
16256 }
16257
16258 if (!generateBody()) {
16259 return false;
16260 }
16261
16262 // Reset native => bytecode map table with top-level script and startPc.
16263 if (!addNativeToBytecodeEntry(startSite)) {
16264 return false;
16265 }
16266
16267 perfSpewer_.recordOffset(masm, "Epilogue");
16268 if (!generateEpilogue()) {
16269 return false;
16270 }
16271
16272 // Reset native => bytecode map table with top-level script and startPc.
16273 if (!addNativeToBytecodeEntry(startSite)) {
16274 return false;
16275 }
16276
16277 perfSpewer_.recordOffset(masm, "InvalidateEpilogue");
16278 generateInvalidateEpilogue();
16279
16280 // native => bytecode entries for OOL code will be added
16281 // by CodeGeneratorShared::generateOutOfLineCode
16282 perfSpewer_.recordOffset(masm, "OOLCode");
16283 if (!generateOutOfLineCode()) {
16284 return false;
16285 }
16286
16287 // Add terminal entry.
16288 if (!addNativeToBytecodeEntry(startSite)) {
16289 return false;
16290 }
16291
16292 // Dump Native to bytecode entries to spew.
16293 dumpNativeToBytecodeEntries();
16294
16295 // We encode safepoints after the OSI-point offsets have been determined.
16296 if (!encodeSafepoints()) {
16297 return false;
16298 }
16299
16300 return !masm.oom();
16301}
16302
16303static bool AddInlinedCompilations(JSContext* cx, HandleScript script,
16304 IonCompilationId compilationId,
16305 const WarpSnapshot* snapshot,
16306 bool* isValid) {
16307 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16307); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*isValid"
")"); do { *((volatile int*)__null) = 16307; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16308 RecompileInfo recompileInfo(script, compilationId);
16309
16310 JitZone* jitZone = cx->zone()->jitZone();
16311
16312 for (const auto* scriptSnapshot : snapshot->scripts()) {
16313 JSScript* inlinedScript = scriptSnapshot->script();
16314 if (inlinedScript == script) {
16315 continue;
16316 }
16317
16318 // TODO(post-Warp): This matches FinishCompilation and is necessary to
16319 // ensure in-progress compilations are canceled when an inlined functon
16320 // becomes a debuggee. See the breakpoint-14.js jit-test.
16321 // When TI is gone, try to clean this up by moving AddInlinedCompilations to
16322 // WarpOracle so that we can handle this as part of addPendingRecompile
16323 // instead of requiring this separate check.
16324 if (inlinedScript->isDebuggee()) {
16325 *isValid = false;
16326 return true;
16327 }
16328
16329 if (!jitZone->addInlinedCompilation(recompileInfo, inlinedScript)) {
16330 return false;
16331 }
16332 }
16333
16334 *isValid = true;
16335 return true;
16336}
16337
16338void CodeGenerator::validateAndRegisterFuseDependencies(JSContext* cx,
16339 HandleScript script,
16340 bool* isValid) {
16341 // No need to validate as we will toss this compilation anyhow.
16342 if (!*isValid) {
16343 return;
16344 }
16345
16346 for (auto dependency : fuseDependencies) {
16347 switch (dependency) {
16348 case FuseDependencyKind::HasSeenObjectEmulateUndefinedFuse: {
16349 auto& hasSeenObjectEmulateUndefinedFuse =
16350 cx->runtime()->hasSeenObjectEmulateUndefinedFuse.ref();
16351
16352 if (!hasSeenObjectEmulateUndefinedFuse.intact()) {
16353 JitSpew(JitSpew_Codegen,
16354 "tossing compilation; hasSeenObjectEmulateUndefinedFuse fuse "
16355 "dependency no longer valid\n");
16356 *isValid = false;
16357 return;
16358 }
16359
16360 if (!hasSeenObjectEmulateUndefinedFuse.addFuseDependency(cx, script)) {
16361 JitSpew(JitSpew_Codegen,
16362 "tossing compilation; failed to register "
16363 "hasSeenObjectEmulateUndefinedFuse script dependency\n");
16364 *isValid = false;
16365 return;
16366 }
16367 break;
16368 }
16369
16370 case FuseDependencyKind::OptimizeGetIteratorFuse: {
16371 auto& optimizeGetIteratorFuse =
16372 cx->realm()->realmFuses.optimizeGetIteratorFuse;
16373 if (!optimizeGetIteratorFuse.intact()) {
16374 JitSpew(JitSpew_Codegen,
16375 "tossing compilation; optimizeGetIteratorFuse fuse "
16376 "dependency no longer valid\n");
16377 *isValid = false;
16378 return;
16379 }
16380
16381 if (!optimizeGetIteratorFuse.addFuseDependency(cx, script)) {
16382 JitSpew(JitSpew_Codegen,
16383 "tossing compilation; failed to register "
16384 "optimizeGetIteratorFuse script dependency\n");
16385 *isValid = false;
16386 return;
16387 }
16388 break;
16389 }
16390
16391 default:
16392 MOZ_CRASH("Unknown Dependency Kind")do { do { } while (false); MOZ_ReportCrash("" "Unknown Dependency Kind"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16392); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown Dependency Kind"
")"); do { *((volatile int*)__null) = 16392; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
16393 }
16394 }
16395}
16396
16397bool CodeGenerator::link(JSContext* cx, const WarpSnapshot* snapshot) {
16398 AutoCreatedBy acb(masm, "CodeGenerator::link");
16399
16400 // We cancel off-thread Ion compilations in a few places during GC, but if
16401 // this compilation was performed off-thread it will already have been
16402 // removed from the relevant lists by this point. Don't allow GC here.
16403 JS::AutoAssertNoGC nogc(cx);
16404
16405 RootedScript script(cx, gen->outerInfo().script());
16406 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16406); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!script->hasIonScript()"
")"); do { *((volatile int*)__null) = 16406; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16407
16408 // Perform any read barriers which were skipped while compiling the
16409 // script, which may have happened off-thread.
16410 JitZone* jitZone = cx->zone()->jitZone();
16411 jitZone->performStubReadBarriers(zoneStubsToReadBarrier_);
16412
16413 if (scriptCounts_ && !script->hasScriptCounts() &&
16414 !script->initScriptCounts(cx)) {
16415 return false;
16416 }
16417
16418 IonCompilationId compilationId =
16419 cx->runtime()->jitRuntime()->nextCompilationId();
16420 jitZone->currentCompilationIdRef().emplace(compilationId);
16421 auto resetCurrentId = mozilla::MakeScopeExit(
16422 [jitZone] { jitZone->currentCompilationIdRef().reset(); });
16423
16424 // Record constraints. If an error occured, returns false and potentially
16425 // prevent future compilations. Otherwise, if an invalidation occured, then
16426 // skip the current compilation.
16427 bool isValid = false;
16428
16429 // If an inlined script is invalidated (for example, by attaching
16430 // a debugger), we must also invalidate the parent IonScript.
16431 if (!AddInlinedCompilations(cx, script, compilationId, snapshot, &isValid)) {
16432 return false;
16433 }
16434
16435 // Validate fuse dependencies here; if a fuse has popped since we registered a
16436 // dependency then we need to toss this compilation as it assumes things which
16437 // are not valid.
16438 //
16439 // Eagerly register a fuse dependency here too; this way if we OOM we can
16440 // instead simply remove the compilation and move on with our lives.
16441 validateAndRegisterFuseDependencies(cx, script, &isValid);
16442
16443 // This compilation is no longer valid; don't proceed, but return true as this
16444 // isn't an error case either.
16445 if (!isValid) {
16446 return true;
16447 }
16448
16449 uint32_t argumentSlots = (gen->outerInfo().nargs() + 1) * sizeof(Value);
16450
16451 size_t numNurseryObjects = snapshot->nurseryObjects().length();
16452
16453 IonScript* ionScript = IonScript::New(
16454 cx, compilationId, graph.localSlotsSize(), argumentSlots, frameDepth_,
16455 snapshots_.listSize(), snapshots_.RVATableSize(), recovers_.size(),
16456 graph.numConstants(), numNurseryObjects, safepointIndices_.length(),
16457 osiIndices_.length(), icList_.length(), runtimeData_.length(),
16458 safepoints_.size());
16459 if (!ionScript) {
16460 return false;
16461 }
16462#ifdef DEBUG1
16463 ionScript->setICHash(snapshot->icHash());
16464#endif
16465
16466 auto freeIonScript = mozilla::MakeScopeExit([&ionScript] {
16467 // Use js_free instead of IonScript::Destroy: the cache list is still
16468 // uninitialized.
16469 js_free(ionScript);
16470 });
16471
16472 Linker linker(masm);
16473 JitCode* code = linker.newCode(cx, CodeKind::Ion);
16474 if (!code) {
16475 return false;
16476 }
16477
16478 // Encode native to bytecode map if profiling is enabled.
16479 if (isProfilerInstrumentationEnabled()) {
16480 // Generate native-to-bytecode main table.
16481 IonEntry::ScriptList scriptList;
16482 if (!generateCompactNativeToBytecodeMap(cx, code, scriptList)) {
16483 return false;
16484 }
16485
16486 uint8_t* ionTableAddr =
16487 ((uint8_t*)nativeToBytecodeMap_.get()) + nativeToBytecodeTableOffset_;
16488 JitcodeIonTable* ionTable = (JitcodeIonTable*)ionTableAddr;
16489
16490 // Construct the IonEntry that will go into the global table.
16491 auto entry = MakeJitcodeGlobalEntry<IonEntry>(
16492 cx, code, code->raw(), code->rawEnd(), std::move(scriptList), ionTable);
16493 if (!entry) {
16494 return false;
16495 }
16496 (void)nativeToBytecodeMap_.release(); // Table is now owned by |entry|.
16497
16498 // Add entry to the global table.
16499 JitcodeGlobalTable* globalTable =
16500 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
16501 if (!globalTable->addEntry(std::move(entry))) {
16502 return false;
16503 }
16504
16505 // Mark the jitcode as having a bytecode map.
16506 code->setHasBytecodeMap();
16507 } else {
16508 // Add a dumy jitcodeGlobalTable entry.
16509 auto entry = MakeJitcodeGlobalEntry<DummyEntry>(cx, code, code->raw(),
16510 code->rawEnd());
16511 if (!entry) {
16512 return false;
16513 }
16514
16515 // Add entry to the global table.
16516 JitcodeGlobalTable* globalTable =
16517 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
16518 if (!globalTable->addEntry(std::move(entry))) {
16519 return false;
16520 }
16521
16522 // Mark the jitcode as having a bytecode map.
16523 code->setHasBytecodeMap();
16524 }
16525
16526 ionScript->setMethod(code);
16527
16528 // If the Gecko Profiler is enabled, mark IonScript as having been
16529 // instrumented accordingly.
16530 if (isProfilerInstrumentationEnabled()) {
16531 ionScript->setHasProfilingInstrumentation();
16532 }
16533
16534 Assembler::PatchDataWithValueCheck(
16535 CodeLocationLabel(code, invalidateEpilogueData_), ImmPtr(ionScript),
16536 ImmPtr((void*)-1));
16537
16538 for (CodeOffset offset : ionScriptLabels_) {
16539 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, offset),
16540 ImmPtr(ionScript), ImmPtr((void*)-1));
16541 }
16542
16543 for (NurseryObjectLabel label : ionNurseryObjectLabels_) {
16544 void* entry = ionScript->addressOfNurseryObject(label.nurseryIndex);
16545 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset),
16546 ImmPtr(entry), ImmPtr((void*)-1));
16547 }
16548
16549 // for generating inline caches during the execution.
16550 if (runtimeData_.length()) {
16551 ionScript->copyRuntimeData(&runtimeData_[0]);
16552 }
16553 if (icList_.length()) {
16554 ionScript->copyICEntries(&icList_[0]);
16555 }
16556
16557 for (size_t i = 0; i < icInfo_.length(); i++) {
16558 IonIC& ic = ionScript->getICFromIndex(i);
16559 Assembler::PatchDataWithValueCheck(
16560 CodeLocationLabel(code, icInfo_[i].icOffsetForJump),
16561 ImmPtr(ic.codeRawPtr()), ImmPtr((void*)-1));
16562 Assembler::PatchDataWithValueCheck(
16563 CodeLocationLabel(code, icInfo_[i].icOffsetForPush), ImmPtr(&ic),
16564 ImmPtr((void*)-1));
16565 }
16566
16567 JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)", (void*)ionScript,
16568 (void*)code->raw());
16569
16570 ionScript->setInvalidationEpilogueDataOffset(
16571 invalidateEpilogueData_.offset());
16572 if (jsbytecode* osrPc = gen->outerInfo().osrPc()) {
16573 ionScript->setOsrPc(osrPc);
16574 ionScript->setOsrEntryOffset(getOsrEntryOffset());
16575 }
16576 ionScript->setInvalidationEpilogueOffset(invalidate_.offset());
16577
16578 perfSpewer_.saveProfile(cx, script, code);
16579
16580#ifdef MOZ_VTUNE1
16581 vtune::MarkScript(code, script, "ion");
16582#endif
16583
16584 // Set a Ion counter hint for this script.
16585 if (cx->runtime()->jitRuntime()->hasJitHintsMap()) {
16586 JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap();
16587 jitHints->recordIonCompilation(script);
16588 }
16589
16590 // for marking during GC.
16591 if (safepointIndices_.length()) {
16592 ionScript->copySafepointIndices(&safepointIndices_[0]);
16593 }
16594 if (safepoints_.size()) {
16595 ionScript->copySafepoints(&safepoints_);
16596 }
16597
16598 // for recovering from an Ion Frame.
16599 if (osiIndices_.length()) {
16600 ionScript->copyOsiIndices(&osiIndices_[0]);
16601 }
16602 if (snapshots_.listSize()) {
16603 ionScript->copySnapshots(&snapshots_);
16604 }
16605 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size()"
")"); do { *((volatile int*)__null) = 16605; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
16606 if (recovers_.size()) {
16607 ionScript->copyRecovers(&recovers_);
16608 }
16609 if (graph.numConstants()) {
16610 const Value* vp = graph.constantPool();
16611 ionScript->copyConstants(vp);
16612 for (size_t i = 0; i < graph.numConstants(); i++) {
16613 const Value& v = vp[i];
16614 if (v.isGCThing()) {
16615 if (gc::StoreBuffer* sb = v.toGCThing()->storeBuffer()) {
16616 sb->putWholeCell(script);
16617 break;
16618 }
16619 }
16620 }
16621 }
16622
16623 // Attach any generated script counts to the script.
16624 if (IonScriptCounts* counts = extractScriptCounts()) {
16625 script->addIonCounts(counts);
16626 }
16627 // WARNING: Code after this point must be infallible!
16628
16629 // Copy the list of nursery objects. Note that the store buffer can add
16630 // HeapPtr edges that must be cleared in IonScript::Destroy. See the
16631 // infallibility warning above.
16632 const auto& nurseryObjects = snapshot->nurseryObjects();
16633 for (size_t i = 0; i < nurseryObjects.length(); i++) {
16634 ionScript->nurseryObjects()[i].init(nurseryObjects[i]);
16635 }
16636
16637 // Transfer ownership of the IonScript to the JitScript. At this point enough
16638 // of the IonScript must be initialized for IonScript::Destroy to work.
16639 freeIonScript.release();
16640 script->jitScript()->setIonScript(script, ionScript);
16641
16642 return true;
16643}
16644
16645// An out-of-line path to convert a boxed int32 to either a float or double.
16646class OutOfLineUnboxFloatingPoint : public OutOfLineCodeBase<CodeGenerator> {
16647 LUnboxFloatingPoint* unboxFloatingPoint_;
16648
16649 public:
16650 explicit OutOfLineUnboxFloatingPoint(LUnboxFloatingPoint* unboxFloatingPoint)
16651 : unboxFloatingPoint_(unboxFloatingPoint) {}
16652
16653 void accept(CodeGenerator* codegen) override {
16654 codegen->visitOutOfLineUnboxFloatingPoint(this);
16655 }
16656
16657 LUnboxFloatingPoint* unboxFloatingPoint() const {
16658 return unboxFloatingPoint_;
16659 }
16660};
16661
16662void CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint* lir) {
16663 const ValueOperand box = ToValue(lir, LUnboxFloatingPoint::Input);
16664 const LDefinition* result = lir->output();
16665
16666 // Out-of-line path to convert int32 to double or bailout
16667 // if this instruction is fallible.
16668 OutOfLineUnboxFloatingPoint* ool =
16669 new (alloc()) OutOfLineUnboxFloatingPoint(lir);
16670 addOutOfLineCode(ool, lir->mir());
16671
16672 FloatRegister resultReg = ToFloatRegister(result);
16673 masm.branchTestDouble(Assembler::NotEqual, box, ool->entry());
16674 masm.unboxDouble(box, resultReg);
16675 if (lir->type() == MIRType::Float32) {
16676 masm.convertDoubleToFloat32(resultReg, resultReg);
16677 }
16678 masm.bind(ool->rejoin());
16679}
16680
16681void CodeGenerator::visitOutOfLineUnboxFloatingPoint(
16682 OutOfLineUnboxFloatingPoint* ool) {
16683 LUnboxFloatingPoint* ins = ool->unboxFloatingPoint();
16684 const ValueOperand value = ToValue(ins, LUnboxFloatingPoint::Input);
16685
16686 if (ins->mir()->fallible()) {
16687 Label bail;
16688 masm.branchTestInt32(Assembler::NotEqual, value, &bail);
16689 bailoutFrom(&bail, ins->snapshot());
16690 }
16691 if (ins->type() == MIRType::Float32) {
16692 masm.convertInt32ToFloat32(value.payloadOrValueReg(),
16693 ToFloatRegister(ins->output()));
16694 } else {
16695 masm.convertInt32ToDouble(value.payloadOrValueReg(),
16696 ToFloatRegister(ins->output()));
16697 }
16698 masm.jump(ool->rejoin());
16699}
16700
16701void CodeGenerator::visitCallBindVar(LCallBindVar* lir) {
16702 pushArg(ToRegister(lir->environmentChain()));
16703
16704 using Fn = JSObject* (*)(JSContext*, JSObject*);
16705 callVM<Fn, BindVarOperation>(lir);
16706}
16707
16708void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) {
16709 Register obj = ToRegister(lir->getOperand(0));
16710 ValueOperand idVal = ToValue(lir, LMegamorphicSetElement::IndexIndex);
16711 ValueOperand value = ToValue(lir, LMegamorphicSetElement::ValueIndex);
16712
16713 Register temp0 = ToRegister(lir->temp0());
16714 // See comment in LIROps.yaml (x86 is short on registers)
16715#ifndef JS_CODEGEN_X86
16716 Register temp1 = ToRegister(lir->temp1());
16717 Register temp2 = ToRegister(lir->temp2());
16718#endif
16719
16720 Label cacheHit, done;
16721#ifdef JS_CODEGEN_X86
16722 masm.emitMegamorphicCachedSetSlot(
16723 idVal, obj, temp0, value, &cacheHit,
16724 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
16725 EmitPreBarrier(masm, addr, mirType);
16726 });
16727#else
16728 masm.emitMegamorphicCachedSetSlot(
16729 idVal, obj, temp0, temp1, temp2, value, &cacheHit,
16730 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
16731 EmitPreBarrier(masm, addr, mirType);
16732 });
16733#endif
16734
16735 pushArg(Imm32(lir->mir()->strict()));
16736 pushArg(ToValue(lir, LMegamorphicSetElement::ValueIndex));
16737 pushArg(ToValue(lir, LMegamorphicSetElement::IndexIndex));
16738 pushArg(obj);
16739
16740 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
16741 callVM<Fn, js::jit::SetElementMegamorphic<true>>(lir);
16742
16743 masm.jump(&done);
16744 masm.bind(&cacheHit);
16745
16746 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
16747 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
16748
16749 saveVolatile(temp0);
16750 emitPostWriteBarrier(obj);
16751 restoreVolatile(temp0);
16752
16753 masm.bind(&done);
16754}
16755
16756void CodeGenerator::visitLoadScriptedProxyHandler(
16757 LLoadScriptedProxyHandler* ins) {
16758 Register obj = ToRegister(ins->getOperand(0));
16759 Register output = ToRegister(ins->output());
16760
16761 masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), output);
16762
16763 Label bail;
16764 Address handlerAddr(output, js::detail::ProxyReservedSlots::offsetOfSlot(
16765 ScriptedProxyHandler::HANDLER_EXTRA));
16766 masm.fallibleUnboxObject(handlerAddr, output, &bail);
16767 bailoutFrom(&bail, ins->snapshot());
16768}
16769
16770#ifdef JS_PUNBOX641
16771void CodeGenerator::visitCheckScriptedProxyGetResult(
16772 LCheckScriptedProxyGetResult* ins) {
16773 ValueOperand target = ToValue(ins, LCheckScriptedProxyGetResult::TargetIndex);
16774 ValueOperand value = ToValue(ins, LCheckScriptedProxyGetResult::ValueIndex);
16775 ValueOperand id = ToValue(ins, LCheckScriptedProxyGetResult::IdIndex);
16776 Register scratch = ToRegister(ins->temp0());
16777 Register scratch2 = ToRegister(ins->temp1());
16778
16779 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue,
16780 MutableHandleValue);
16781 OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>(
16782 ins, ArgList(scratch, id, value), StoreValueTo(value));
16783
16784 masm.unboxObject(target, scratch);
16785 masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch,
16786 scratch2, ool->entry());
16787 masm.bind(ool->rejoin());
16788}
16789#endif
16790
16791void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) {
16792 ValueOperand id = ToValue(ins, LIdToStringOrSymbol::IdIndex);
16793 ValueOperand output = ToOutValue(ins);
16794 Register scratch = ToRegister(ins->temp0());
16795
16796 masm.moveValue(id, output);
16797
16798 Label done, callVM;
16799 Label bail;
16800 {
16801 ScratchTagScope tag(masm, output);
16802 masm.splitTagForTest(output, tag);
16803 masm.branchTestString(Assembler::Equal, tag, &done);
16804 masm.branchTestSymbol(Assembler::Equal, tag, &done);
16805 masm.branchTestInt32(Assembler::NotEqual, tag, &bail);
16806 }
16807
16808 masm.unboxInt32(output, scratch);
16809
16810 using Fn = JSLinearString* (*)(JSContext*, int);
16811 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
16812 ins, ArgList(scratch), StoreRegisterTo(output.scratchReg()));
16813
16814 masm.lookupStaticIntString(scratch, output.scratchReg(),
16815 gen->runtime->staticStrings(), ool->entry());
16816
16817 masm.bind(ool->rejoin());
16818 masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
16819 masm.bind(&done);
16820
16821 bailoutFrom(&bail, ins->snapshot());
16822}
16823
16824void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) {
16825 const Register obj = ToRegister(ins->getOperand(0));
16826 size_t slot = ins->mir()->slot();
16827 ValueOperand result = ToOutValue(ins);
16828
16829 masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result);
16830}
16831
16832void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) {
16833 const Register obj = ToRegister(ins->getOperand(0));
16834 size_t slot = ins->mir()->slot();
16835 AnyRegister result = ToAnyRegister(ins->getDef(0));
16836 MIRType type = ins->mir()->type();
16837
16838 masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)),
16839 type, result);
16840}
16841
16842template <typename T>
16843static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type,
16844 bool fallible, AnyRegister dest, Label* fail) {
16845 if (type == MIRType::Double) {
16846 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16846); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.isFloat()"
")"); do { *((volatile int*)__null) = 16846; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16847 masm.ensureDouble(src, dest.fpu(), fail);
16848 return;
16849 }
16850 if (fallible) {
16851 switch (type) {
16852 case MIRType::Int32:
16853 masm.fallibleUnboxInt32(src, dest.gpr(), fail);
16854 break;
16855 case MIRType::Boolean:
16856 masm.fallibleUnboxBoolean(src, dest.gpr(), fail);
16857 break;
16858 case MIRType::Object:
16859 masm.fallibleUnboxObject(src, dest.gpr(), fail);
16860 break;
16861 case MIRType::String:
16862 masm.fallibleUnboxString(src, dest.gpr(), fail);
16863 break;
16864 case MIRType::Symbol:
16865 masm.fallibleUnboxSymbol(src, dest.gpr(), fail);
16866 break;
16867 case MIRType::BigInt:
16868 masm.fallibleUnboxBigInt(src, dest.gpr(), fail);
16869 break;
16870 default:
16871 MOZ_CRASH("Unexpected MIRType")do { do { } while (false); MOZ_ReportCrash("" "Unexpected MIRType"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16871); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected MIRType"
")"); do { *((volatile int*)__null) = 16871; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
16872 }
16873 return;
16874 }
16875 masm.loadUnboxedValue(src, type, dest);
16876}
16877
16878void CodeGenerator::visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* ins) {
16879 const MLoadFixedSlotAndUnbox* mir = ins->mir();
16880 MIRType type = mir->type();
16881 Register input = ToRegister(ins->object());
16882 AnyRegister result = ToAnyRegister(ins->output());
16883 size_t slot = mir->slot();
16884
16885 Address address(input, NativeObject::getFixedSlotOffset(slot));
16886
16887 Label bail;
16888 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail);
16889 if (mir->fallible()) {
16890 bailoutFrom(&bail, ins->snapshot());
16891 }
16892}
16893
16894void CodeGenerator::visitLoadDynamicSlotAndUnbox(
16895 LLoadDynamicSlotAndUnbox* ins) {
16896 const MLoadDynamicSlotAndUnbox* mir = ins->mir();
16897 MIRType type = mir->type();
16898 Register input = ToRegister(ins->slots());
16899 AnyRegister result = ToAnyRegister(ins->output());
16900 size_t slot = mir->slot();
16901
16902 Address address(input, slot * sizeof(JS::Value));
16903
16904 Label bail;
16905 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail);
16906 if (mir->fallible()) {
16907 bailoutFrom(&bail, ins->snapshot());
16908 }
16909}
16910
16911void CodeGenerator::visitLoadElementAndUnbox(LLoadElementAndUnbox* ins) {
16912 const MLoadElementAndUnbox* mir = ins->mir();
16913 MIRType type = mir->type();
16914 Register elements = ToRegister(ins->elements());
16915 AnyRegister result = ToAnyRegister(ins->output());
16916
16917 Label bail;
16918 if (ins->index()->isConstant()) {
16919 NativeObject::elementsSizeMustNotOverflow();
16920 int32_t offset = ToInt32(ins->index()) * sizeof(Value);
16921 Address address(elements, offset);
16922 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail);
16923 } else {
16924 BaseObjectElementIndex address(elements, ToRegister(ins->index()));
16925 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail);
16926 }
16927
16928 if (mir->fallible()) {
16929 bailoutFrom(&bail, ins->snapshot());
16930 }
16931}
16932
16933class OutOfLineAtomizeSlot : public OutOfLineCodeBase<CodeGenerator> {
16934 LInstruction* lir_;
16935 Register stringReg_;
16936 Address slotAddr_;
16937 TypedOrValueRegister dest_;
16938
16939 public:
16940 OutOfLineAtomizeSlot(LInstruction* lir, Register stringReg, Address slotAddr,
16941 TypedOrValueRegister dest)
16942 : lir_(lir), stringReg_(stringReg), slotAddr_(slotAddr), dest_(dest) {}
16943
16944 void accept(CodeGenerator* codegen) final {
16945 codegen->visitOutOfLineAtomizeSlot(this);
16946 }
16947 LInstruction* lir() const { return lir_; }
16948 Register stringReg() const { return stringReg_; }
16949 Address slotAddr() const { return slotAddr_; }
16950 TypedOrValueRegister dest() const { return dest_; }
16951};
16952
16953void CodeGenerator::visitOutOfLineAtomizeSlot(OutOfLineAtomizeSlot* ool) {
16954 LInstruction* lir = ool->lir();
16955 Register stringReg = ool->stringReg();
16956 Address slotAddr = ool->slotAddr();
16957 TypedOrValueRegister dest = ool->dest();
16958
16959 // This code is called with a non-atomic string in |stringReg|.
16960 // When it returns, |stringReg| contains an unboxed pointer to an
16961 // atomized version of that string, and |slotAddr| contains a
16962 // StringValue pointing to that atom. If |dest| is a ValueOperand,
16963 // it contains the same StringValue; otherwise we assert that |dest|
16964 // is |stringReg|.
16965
16966 saveLive(lir);
16967 pushArg(stringReg);
16968
16969 using Fn = JSAtom* (*)(JSContext*, JSString*);
16970 callVM<Fn, js::AtomizeString>(lir);
16971 StoreRegisterTo(stringReg).generate(this);
16972 restoreLiveIgnore(lir, StoreRegisterTo(stringReg).clobbered());
16973
16974 if (dest.hasValue()) {
16975 masm.moveValue(
16976 TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)),
16977 dest.valueReg());
16978 } else {
16979 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16979); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg"
")"); do { *((volatile int*)__null) = 16979; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16980 }
16981
16982 emitPreBarrier(slotAddr);
16983 masm.storeTypedOrValue(dest, slotAddr);
16984
16985 // We don't need a post-barrier because atoms aren't nursery-allocated.
16986#ifdef DEBUG1
16987 // We need a temp register for the nursery check. Spill something.
16988 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
16989 allRegs.take(stringReg);
16990 Register temp = allRegs.takeAny();
16991 masm.push(temp);
16992
16993 Label tenured;
16994 masm.branchPtrInNurseryChunk(Assembler::NotEqual, stringReg, temp, &tenured);
16995 masm.assumeUnreachable("AtomizeString returned a nursery pointer");
16996 masm.bind(&tenured);
16997
16998 masm.pop(temp);
16999#endif
17000
17001 masm.jump(ool->rejoin());
17002}
17003
17004void CodeGenerator::emitMaybeAtomizeSlot(LInstruction* ins, Register stringReg,
17005 Address slotAddr,
17006 TypedOrValueRegister dest) {
17007 OutOfLineAtomizeSlot* ool =
17008 new (alloc()) OutOfLineAtomizeSlot(ins, stringReg, slotAddr, dest);
17009 addOutOfLineCode(ool, ins->mirRaw()->toInstruction());
17010 masm.branchTest32(Assembler::NonZero,
17011 Address(stringReg, JSString::offsetOfFlags()),
17012 Imm32(JSString::ATOM_BIT), ool->rejoin());
17013
17014 masm.branchTest32(Assembler::Zero,
17015 Address(stringReg, JSString::offsetOfFlags()),
17016 Imm32(JSString::ATOM_REF_BIT), ool->entry());
17017 masm.loadPtr(Address(stringReg, JSAtomRefString::offsetOfAtom()), stringReg);
17018
17019 if (dest.hasValue()) {
17020 masm.moveValue(
17021 TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)),
17022 dest.valueReg());
17023 } else {
17024 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 17024); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg"
")"); do { *((volatile int*)__null) = 17024; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17025 }
17026
17027 emitPreBarrier(slotAddr);
17028 masm.storeTypedOrValue(dest, slotAddr);
17029
17030 masm.bind(ool->rejoin());
17031}
17032
17033void CodeGenerator::visitLoadFixedSlotAndAtomize(
17034 LLoadFixedSlotAndAtomize* ins) {
17035 Register obj = ToRegister(ins->getOperand(0));
17036 Register temp = ToRegister(ins->temp0());
17037 size_t slot = ins->mir()->slot();
17038 ValueOperand result = ToOutValue(ins);
17039
17040 Address slotAddr(obj, NativeObject::getFixedSlotOffset(slot));
17041 masm.loadValue(slotAddr, result);
17042
17043 Label notString;
17044 masm.branchTestString(Assembler::NotEqual, result, &notString);
17045 masm.unboxString(result, temp);
17046 emitMaybeAtomizeSlot(ins, temp, slotAddr, result);
17047 masm.bind(&notString);
17048}
17049
17050void CodeGenerator::visitLoadDynamicSlotAndAtomize(
17051 LLoadDynamicSlotAndAtomize* ins) {
17052 ValueOperand result = ToOutValue(ins);
17053 Register temp = ToRegister(ins->temp0());
17054 Register base = ToRegister(ins->input());
17055 int32_t offset = ins->mir()->slot() * sizeof(js::Value);
17056
17057 Address slotAddr(base, offset);
17058 masm.loadValue(slotAddr, result);
17059
17060 Label notString;
17061 masm.branchTestString(Assembler::NotEqual, result, &notString);
17062 masm.unboxString(result, temp);
17063 emitMaybeAtomizeSlot(ins, temp, slotAddr, result);
17064 masm.bind(&notString);
17065}
17066
17067void CodeGenerator::visitLoadFixedSlotUnboxAndAtomize(
17068 LLoadFixedSlotUnboxAndAtomize* ins) {
17069 const MLoadFixedSlotAndUnbox* mir = ins->mir();
17070 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 17070); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String"
")"); do { *((volatile int*)__null) = 17070; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17071 Register input = ToRegister(ins->object());
17072 AnyRegister result = ToAnyRegister(ins->output());
17073 size_t slot = mir->slot();
17074
17075 Address slotAddr(input, NativeObject::getFixedSlotOffset(slot));
17076
17077 Label bail;
17078 EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result,
17079 &bail);
17080 emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr,
17081 TypedOrValueRegister(MIRType::String, result));
17082
17083 if (mir->fallible()) {
17084 bailoutFrom(&bail, ins->snapshot());
17085 }
17086}
17087
17088void CodeGenerator::visitLoadDynamicSlotUnboxAndAtomize(
17089 LLoadDynamicSlotUnboxAndAtomize* ins) {
17090 const MLoadDynamicSlotAndUnbox* mir = ins->mir();
17091 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 17091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String"
")"); do { *((volatile int*)__null) = 17091; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17092 Register input = ToRegister(ins->slots());
17093 AnyRegister result = ToAnyRegister(ins->output());
17094 size_t slot = mir->slot();
17095
17096 Address slotAddr(input, slot * sizeof(JS::Value));
17097
17098 Label bail;
17099 EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result,
17100 &bail);
17101 emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr,
17102 TypedOrValueRegister(MIRType::String, result));
17103
17104 if (mir->fallible()) {
17105 bailoutFrom(&bail, ins->snapshot());
17106 }
17107}
17108
17109void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) {
17110 const Register obj = ToRegister(ins->getOperand(0));
17111 const ValueOperand value = ToValue(ins, LAddAndStoreSlot::ValueIndex);
17112 const Register maybeTemp = ToTempRegisterOrInvalid(ins->temp0());
17113
17114 Shape* shape = ins->mir()->shape();
17115 masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) {
17116 EmitPreBarrier(masm, addr, MIRType::Shape);
17117 });
17118
17119 // Perform the store. No pre-barrier required since this is a new
17120 // initialization.
17121
17122 uint32_t offset = ins->mir()->slotOffset();
17123 if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) {
17124 Address slot(obj, offset);
17125 masm.storeValue(value, slot);
17126 } else {
17127 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), maybeTemp);
17128 Address slot(maybeTemp, offset);
17129 masm.storeValue(value, slot);
17130 }
17131}
17132
17133void CodeGenerator::visitAllocateAndStoreSlot(LAllocateAndStoreSlot* ins) {
17134 const Register obj = ToRegister(ins->getOperand(0));
17135 const ValueOperand value = ToValue(ins, LAllocateAndStoreSlot::ValueIndex);
17136 const Register temp0 = ToRegister(ins->temp0());
17137 const Register temp1 = ToRegister(ins->temp1());
17138
17139 masm.Push(obj);
17140 masm.Push(value);
17141
17142 using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount);
17143 masm.setupAlignedABICall();
17144 masm.loadJSContext(temp0);
17145 masm.passABIArg(temp0);
17146 masm.passABIArg(obj);
17147 masm.move32(Imm32(ins->mir()->numNewSlots()), temp1);
17148 masm.passABIArg(temp1);
17149 masm.callWithABI<Fn, NativeObject::growSlotsPure>();
17150 masm.storeCallPointerResult(temp0);
17151
17152 masm.Pop(value);
17153 masm.Pop(obj);
17154
17155 bailoutIfFalseBool(temp0, ins->snapshot());
17156
17157 masm.storeObjShape(ins->mir()->shape(), obj,
17158 [](MacroAssembler& masm, const Address& addr) {
17159 EmitPreBarrier(masm, addr, MIRType::Shape);
17160 });
17161
17162 // Perform the store. No pre-barrier required since this is a new
17163 // initialization.
17164 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0);
17165 Address slot(temp0, ins->mir()->slotOffset());
17166 masm.storeValue(value, slot);
17167}
17168
17169void CodeGenerator::visitAddSlotAndCallAddPropHook(
17170 LAddSlotAndCallAddPropHook* ins) {
17171 const Register obj = ToRegister(ins->object());
17172 const ValueOperand value =
17173 ToValue(ins, LAddSlotAndCallAddPropHook::ValueIndex);
17174
17175 pushArg(ImmGCPtr(ins->mir()->shape()));
17176 pushArg(value);
17177 pushArg(obj);
17178
17179 using Fn =
17180 bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, Handle<Shape*>);
17181 callVM<Fn, AddSlotAndCallAddPropHook>(ins);
17182}
17183
17184void CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV* ins) {
17185 const Register obj = ToRegister(ins->getOperand(0));
17186 size_t slot = ins->mir()->slot();
17187
17188 const ValueOperand value = ToValue(ins, LStoreFixedSlotV::ValueIndex);
17189
17190 Address address(obj, NativeObject::getFixedSlotOffset(slot));
17191 if (ins->mir()->needsBarrier()) {
17192 emitPreBarrier(address);
17193 }
17194
17195 masm.storeValue(value, address);
17196}
17197
17198void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) {
17199 const Register obj = ToRegister(ins->getOperand(0));
17200 size_t slot = ins->mir()->slot();
17201
17202 const LAllocation* value = ins->value();
17203 MIRType valueType = ins->mir()->value()->type();
17204
17205 Address address(obj, NativeObject::getFixedSlotOffset(slot));
17206 if (ins->mir()->needsBarrier()) {
17207 emitPreBarrier(address);
17208 }
17209
17210 ConstantOrRegister nvalue =
17211 value->isConstant()
17212 ? ConstantOrRegister(value->toConstant()->toJSValue())
17213 : TypedOrValueRegister(valueType, ToAnyRegister(value));
17214 masm.storeConstantOrRegister(nvalue, address);
17215}
17216
17217void CodeGenerator::visitGetNameCache(LGetNameCache* ins) {
17218 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17219 Register envChain = ToRegister(ins->envObj());
17220 ValueOperand output = ToOutValue(ins);
17221 Register temp = ToRegister(ins->temp0());
17222
17223 IonGetNameIC ic(liveRegs, envChain, output, temp);
17224 addIC(ins, allocateIC(ic));
17225}
17226
17227void CodeGenerator::addGetPropertyCache(LInstruction* ins,
17228 LiveRegisterSet liveRegs,
17229 TypedOrValueRegister value,
17230 const ConstantOrRegister& id,
17231 ValueOperand output) {
17232 CacheKind kind = CacheKind::GetElem;
17233 if (id.constant() && id.value().isString()) {
17234 JSString* idString = id.value().toString();
17235 if (idString->isAtom() && !idString->asAtom().isIndex()) {
17236 kind = CacheKind::GetProp;
17237 }
17238 }
17239 IonGetPropertyIC cache(kind, liveRegs, value, id, output);
17240 addIC(ins, allocateIC(cache));
17241}
17242
17243void CodeGenerator::addSetPropertyCache(LInstruction* ins,
17244 LiveRegisterSet liveRegs,
17245 Register objReg, Register temp,
17246 const ConstantOrRegister& id,
17247 const ConstantOrRegister& value,
17248 bool strict) {
17249 CacheKind kind = CacheKind::SetElem;
17250 if (id.constant() && id.value().isString()) {
17251 JSString* idString = id.value().toString();
17252 if (idString->isAtom() && !idString->asAtom().isIndex()) {
17253 kind = CacheKind::SetProp;
17254 }
17255 }
17256 IonSetPropertyIC cache(kind, liveRegs, objReg, temp, id, value, strict);
17257 addIC(ins, allocateIC(cache));
17258}
17259
17260ConstantOrRegister CodeGenerator::toConstantOrRegister(LInstruction* lir,
17261 size_t n, MIRType type) {
17262 if (type == MIRType::Value) {
17263 return TypedOrValueRegister(ToValue(lir, n));
17264 }
17265
17266 const LAllocation* value = lir->getOperand(n);
17267 if (value->isConstant()) {
17268 return ConstantOrRegister(value->toConstant()->toJSValue());
17269 }
17270
17271 return TypedOrValueRegister(type, ToAnyRegister(value));
17272}
17273
17274void CodeGenerator::visitGetPropertyCache(LGetPropertyCache* ins) {
17275 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17276 TypedOrValueRegister value =
17277 toConstantOrRegister(ins, LGetPropertyCache::ValueIndex,
17278 ins->mir()->value()->type())
17279 .reg();
17280 ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCache::IdIndex,
17281 ins->mir()->idval()->type());
17282 ValueOperand output = ToOutValue(ins);
17283 addGetPropertyCache(ins, liveRegs, value, id, output);
17284}
17285
17286void CodeGenerator::visitGetPropSuperCache(LGetPropSuperCache* ins) {
17287 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17288 Register obj = ToRegister(ins->obj());
17289 TypedOrValueRegister receiver =
17290 toConstantOrRegister(ins, LGetPropSuperCache::ReceiverIndex,
17291 ins->mir()->receiver()->type())
17292 .reg();
17293 ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCache::IdIndex,
17294 ins->mir()->idval()->type());
17295 ValueOperand output = ToOutValue(ins);
17296
17297 CacheKind kind = CacheKind::GetElemSuper;
17298 if (id.constant() && id.value().isString()) {
17299 JSString* idString = id.value().toString();
17300 if (idString->isAtom() && !idString->asAtom().isIndex()) {
17301 kind = CacheKind::GetPropSuper;
17302 }
17303 }
17304
17305 IonGetPropSuperIC cache(kind, liveRegs, obj, receiver, id, output);
17306 addIC(ins, allocateIC(cache));
17307}
17308
17309void CodeGenerator::visitBindNameCache(LBindNameCache* ins) {
17310 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17311 Register envChain = ToRegister(ins->environmentChain());
17312 Register output = ToRegister(ins->output());
17313 Register temp = ToRegister(ins->temp0());
17314
17315 IonBindNameIC ic(liveRegs, envChain, output, temp);
17316 addIC(ins, allocateIC(ic));
17317}
17318
17319void CodeGenerator::visitHasOwnCache(LHasOwnCache* ins) {
17320 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17321 TypedOrValueRegister value =
17322 toConstantOrRegister(ins, LHasOwnCache::ValueIndex,
17323 ins->mir()->value()->type())
17324 .reg();
17325 TypedOrValueRegister id = toConstantOrRegister(ins, LHasOwnCache::IdIndex,
17326 ins->mir()->idval()->type())
17327 .reg();
17328 Register output = ToRegister(ins->output());
17329
17330 IonHasOwnIC cache(liveRegs, value, id, output);
17331 addIC(ins, allocateIC(cache));
17332}
17333
17334void CodeGenerator::visitCheckPrivateFieldCache(LCheckPrivateFieldCache* ins) {
17335 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17336 TypedOrValueRegister value =
17337 toConstantOrRegister(ins, LCheckPrivateFieldCache::ValueIndex,
17338 ins->mir()->value()->type())
17339 .reg();
17340 TypedOrValueRegister id =
17341 toConstantOrRegister(ins, LCheckPrivateFieldCache::IdIndex,
17342 ins->mir()->idval()->type())
17343 .reg();
17344 Register output = ToRegister(ins->output());
17345
17346 IonCheckPrivateFieldIC cache(liveRegs, value, id, output);
17347 addIC(ins, allocateIC(cache));
17348}
17349
17350void CodeGenerator::visitNewPrivateName(LNewPrivateName* ins) {
17351 pushArg(ImmGCPtr(ins->mir()->name()));
17352
17353 using Fn = JS::Symbol* (*)(JSContext*, Handle<JSAtom*>);
17354 callVM<Fn, NewPrivateName>(ins);
17355}
17356
17357void CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty* lir) {
17358 pushArg(ImmGCPtr(lir->mir()->name()));
17359 pushArg(ToValue(lir, LCallDeleteProperty::ValueIndex));
17360
17361 using Fn = bool (*)(JSContext*, HandleValue, Handle<PropertyName*>, bool*);
17362 if (lir->mir()->strict()) {
17363 callVM<Fn, DelPropOperation<true>>(lir);
17364 } else {
17365 callVM<Fn, DelPropOperation<false>>(lir);
17366 }
17367}
17368
17369void CodeGenerator::visitCallDeleteElement(LCallDeleteElement* lir) {
17370 pushArg(ToValue(lir, LCallDeleteElement::IndexIndex));
17371 pushArg(ToValue(lir, LCallDeleteElement::ValueIndex));
17372
17373 using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*);
17374 if (lir->mir()->strict()) {
17375 callVM<Fn, DelElemOperation<true>>(lir);
17376 } else {
17377 callVM<Fn, DelElemOperation<false>>(lir);
17378 }
17379}
17380
17381void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) {
17382 Register obj = ToRegister(lir->object());
17383 Register iterObj = ToRegister(lir->output());
17384 Register temp = ToRegister(lir->temp0());
17385 Register temp2 = ToRegister(lir->temp1());
17386 Register temp3 = ToRegister(lir->temp2());
17387
17388 using Fn = PropertyIteratorObject* (*)(JSContext*, HandleObject);
17389 OutOfLineCode* ool = (lir->mir()->wantsIndices())
17390 ? oolCallVM<Fn, GetIteratorWithIndices>(
17391 lir, ArgList(obj), StoreRegisterTo(iterObj))
17392 : oolCallVM<Fn, GetIterator>(
17393 lir, ArgList(obj), StoreRegisterTo(iterObj));
17394
17395 masm.maybeLoadIteratorFromShape(obj, iterObj, temp, temp2, temp3,
17396 ool->entry());
17397
17398 Register nativeIter = temp;
17399 masm.loadPrivate(
17400 Address(iterObj, PropertyIteratorObject::offsetOfIteratorSlot()),
17401 nativeIter);
17402
17403 if (lir->mir()->wantsIndices()) {
17404 // At least one consumer of the output of this iterator has been optimized
17405 // to use iterator indices. If the cached iterator doesn't include indices,
17406 // but it was marked to indicate that we can create them if needed, then we
17407 // do a VM call to replace the cached iterator with a fresh iterator
17408 // including indices.
17409 masm.branchNativeIteratorIndices(Assembler::Equal, nativeIter, temp2,
17410 NativeIteratorIndices::AvailableOnRequest,
17411 ool->entry());
17412 }
17413
17414 Address iterFlagsAddr(nativeIter, NativeIterator::offsetOfFlagsAndCount());
17415 masm.storePtr(
17416 obj, Address(nativeIter, NativeIterator::offsetOfObjectBeingIterated()));
17417 masm.or32(Imm32(NativeIterator::Flags::Active), iterFlagsAddr);
17418
17419 Register enumeratorsAddr = temp2;
17420 masm.movePtr(ImmPtr(lir->mir()->enumeratorsAddr()), enumeratorsAddr);
17421 masm.registerIterator(enumeratorsAddr, nativeIter, temp3);
17422
17423 // Generate post-write barrier for storing to |iterObj->objectBeingIterated_|.
17424 // We already know that |iterObj| is tenured, so we only have to check |obj|.
17425 Label skipBarrier;
17426 masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp2, &skipBarrier);
17427 {
17428 LiveRegisterSet save = liveVolatileRegs(lir);
17429 save.takeUnchecked(temp);
17430 save.takeUnchecked(temp2);
17431 save.takeUnchecked(temp3);
17432 if (iterObj.volatile_()) {
17433 save.addUnchecked(iterObj);
17434 }
17435
17436 masm.PushRegsInMask(save);
17437 emitPostWriteBarrier(iterObj);
17438 masm.PopRegsInMask(save);
17439 }
17440 masm.bind(&skipBarrier);
17441
17442 masm.bind(ool->rejoin());
17443}
17444
17445void CodeGenerator::visitValueToIterator(LValueToIterator* lir) {
17446 pushArg(ToValue(lir, LValueToIterator::ValueIndex));
17447
17448 using Fn = PropertyIteratorObject* (*)(JSContext*, HandleValue);
17449 callVM<Fn, ValueToIterator>(lir);
17450}
17451
17452void CodeGenerator::visitIteratorHasIndicesAndBranch(
17453 LIteratorHasIndicesAndBranch* lir) {
17454 Register iterator = ToRegister(lir->iterator());
17455 Register object = ToRegister(lir->object());
17456 Register temp = ToRegister(lir->temp());
17457 Register temp2 = ToRegister(lir->temp2());
17458 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
17459 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
17460
17461 // Check that the iterator has indices available.
17462 Address nativeIterAddr(iterator,
17463 PropertyIteratorObject::offsetOfIteratorSlot());
17464 masm.loadPrivate(nativeIterAddr, temp);
17465 masm.branchNativeIteratorIndices(Assembler::NotEqual, temp, temp2,
17466 NativeIteratorIndices::Valid, ifFalse);
17467
17468 // Guard that the first shape stored in the iterator matches the current
17469 // shape of the iterated object.
17470 Address firstShapeAddr(temp, NativeIterator::offsetOfFirstShape());
17471 masm.loadPtr(firstShapeAddr, temp);
17472 masm.branchTestObjShape(Assembler::NotEqual, object, temp, temp2, object,
17473 ifFalse);
17474
17475 if (!isNextBlock(lir->ifTrue()->lir())) {
17476 masm.jump(ifTrue);
17477 }
17478}
17479
17480void CodeGenerator::visitLoadSlotByIteratorIndex(
17481 LLoadSlotByIteratorIndex* lir) {
17482 Register object = ToRegister(lir->object());
17483 Register iterator = ToRegister(lir->iterator());
17484 Register temp = ToRegister(lir->temp0());
17485 Register temp2 = ToRegister(lir->temp1());
17486 ValueOperand result = ToOutValue(lir);
17487
17488 masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2);
17489
17490 Label notDynamicSlot, notFixedSlot, done;
17491 masm.branch32(Assembler::NotEqual, temp2,
17492 Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)),
17493 &notDynamicSlot);
17494 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
17495 masm.loadValue(BaseValueIndex(temp2, temp), result);
17496 masm.jump(&done);
17497
17498 masm.bind(&notDynamicSlot);
17499 masm.branch32(Assembler::NotEqual, temp2,
17500 Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot);
17501 // Fixed slot
17502 masm.loadValue(BaseValueIndex(object, temp, sizeof(NativeObject)), result);
17503 masm.jump(&done);
17504 masm.bind(&notFixedSlot);
17505
17506#ifdef DEBUG1
17507 Label kindOkay;
17508 masm.branch32(Assembler::Equal, temp2,
17509 Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay);
17510 masm.assumeUnreachable("Invalid PropertyIndex::Kind");
17511 masm.bind(&kindOkay);
17512#endif
17513
17514 // Dense element
17515 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2);
17516 Label indexOkay;
17517 Address initLength(temp2, ObjectElements::offsetOfInitializedLength());
17518 masm.branch32(Assembler::Above, initLength, temp, &indexOkay);
17519 masm.assumeUnreachable("Dense element out of bounds");
17520 masm.bind(&indexOkay);
17521
17522 masm.loadValue(BaseObjectElementIndex(temp2, temp), result);
17523 masm.bind(&done);
17524}
17525
17526void CodeGenerator::visitStoreSlotByIteratorIndex(
17527 LStoreSlotByIteratorIndex* lir) {
17528 Register object = ToRegister(lir->object());
17529 Register iterator = ToRegister(lir->iterator());
17530 ValueOperand value = ToValue(lir, LStoreSlotByIteratorIndex::ValueIndex);
17531 Register temp = ToRegister(lir->temp0());
17532 Register temp2 = ToRegister(lir->temp1());
17533
17534 masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2);
17535
17536 Label notDynamicSlot, notFixedSlot, done, doStore;
17537 masm.branch32(Assembler::NotEqual, temp2,
17538 Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)),
17539 &notDynamicSlot);
17540 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
17541 masm.computeEffectiveAddress(BaseValueIndex(temp2, temp), temp);
17542 masm.jump(&doStore);
17543
17544 masm.bind(&notDynamicSlot);
17545 masm.branch32(Assembler::NotEqual, temp2,
17546 Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot);
17547 // Fixed slot
17548 masm.computeEffectiveAddress(
17549 BaseValueIndex(object, temp, sizeof(NativeObject)), temp);
17550 masm.jump(&doStore);
17551 masm.bind(&notFixedSlot);
17552
17553#ifdef DEBUG1
17554 Label kindOkay;
17555 masm.branch32(Assembler::Equal, temp2,
17556 Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay);
17557 masm.assumeUnreachable("Invalid PropertyIndex::Kind");
17558 masm.bind(&kindOkay);
17559#endif
17560
17561 // Dense element
17562 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2);
17563 Label indexOkay;
17564 Address initLength(temp2, ObjectElements::offsetOfInitializedLength());
17565 masm.branch32(Assembler::Above, initLength, temp, &indexOkay);
17566 masm.assumeUnreachable("Dense element out of bounds");
17567 masm.bind(&indexOkay);
17568
17569 BaseObjectElementIndex elementAddress(temp2, temp);
17570 masm.computeEffectiveAddress(elementAddress, temp);
17571
17572 masm.bind(&doStore);
17573 Address storeAddress(temp, 0);
17574 emitPreBarrier(storeAddress);
17575 masm.storeValue(value, storeAddress);
17576
17577 masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp2, &done);
17578 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp2, &done);
17579
17580 saveVolatile(temp2);
17581 emitPostWriteBarrier(object);
17582 restoreVolatile(temp2);
17583
17584 masm.bind(&done);
17585}
17586
17587void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) {
17588 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17589 Register objReg = ToRegister(ins->object());
17590 Register temp = ToRegister(ins->temp0());
17591
17592 ConstantOrRegister id = toConstantOrRegister(ins, LSetPropertyCache::IdIndex,
17593 ins->mir()->idval()->type());
17594 ConstantOrRegister value = toConstantOrRegister(
17595 ins, LSetPropertyCache::ValueIndex, ins->mir()->value()->type());
17596
17597 addSetPropertyCache(ins, liveRegs, objReg, temp, id, value,
17598 ins->mir()->strict());
17599}
17600
17601void CodeGenerator::visitThrow(LThrow* lir) {
17602 pushArg(ToValue(lir, LThrow::ValueIndex));
17603
17604 using Fn = bool (*)(JSContext*, HandleValue);
17605 callVM<Fn, js::ThrowOperation>(lir);
17606}
17607
17608void CodeGenerator::visitThrowWithStack(LThrowWithStack* lir) {
17609 pushArg(ToValue(lir, LThrowWithStack::StackIndex));
17610 pushArg(ToValue(lir, LThrowWithStack::ValueIndex));
17611
17612 using Fn = bool (*)(JSContext*, HandleValue, HandleValue);
17613 callVM<Fn, js::ThrowWithStackOperation>(lir);
17614}
17615
17616class OutOfLineTypeOfV : public OutOfLineCodeBase<CodeGenerator> {
17617 LTypeOfV* ins_;
17618
17619 public:
17620 explicit OutOfLineTypeOfV(LTypeOfV* ins) : ins_(ins) {}
17621
17622 void accept(CodeGenerator* codegen) override {
17623 codegen->visitOutOfLineTypeOfV(this);
17624 }
17625 LTypeOfV* ins() const { return ins_; }
17626};
17627
17628void CodeGenerator::emitTypeOfJSType(JSValueType type, Register output) {
17629 switch (type) {
17630 case JSVAL_TYPE_OBJECT:
17631 masm.move32(Imm32(JSTYPE_OBJECT), output);
17632 break;
17633 case JSVAL_TYPE_DOUBLE:
17634 case JSVAL_TYPE_INT32:
17635 masm.move32(Imm32(JSTYPE_NUMBER), output);
17636 break;
17637 case JSVAL_TYPE_BOOLEAN:
17638 masm.move32(Imm32(JSTYPE_BOOLEAN), output);
17639 break;
17640 case JSVAL_TYPE_UNDEFINED:
17641 masm.move32(Imm32(JSTYPE_UNDEFINED), output);
17642 break;
17643 case JSVAL_TYPE_NULL:
17644 masm.move32(Imm32(JSTYPE_OBJECT), output);
17645 break;
17646 case JSVAL_TYPE_STRING:
17647 masm.move32(Imm32(JSTYPE_STRING), output);
17648 break;
17649 case JSVAL_TYPE_SYMBOL:
17650 masm.move32(Imm32(JSTYPE_SYMBOL), output);
17651 break;
17652 case JSVAL_TYPE_BIGINT:
17653 masm.move32(Imm32(JSTYPE_BIGINT), output);
17654 break;
17655 default:
17656 MOZ_CRASH("Unsupported JSValueType")do { do { } while (false); MOZ_ReportCrash("" "Unsupported JSValueType"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 17656); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported JSValueType"
")"); do { *((volatile int*)__null) = 17656; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
17657 }
17658}
17659
17660void CodeGenerator::emitTypeOfCheck(JSValueType type, Register tag,
17661 Register output, Label* done,
17662 Label* oolObject) {
17663 Label notMatch;
17664 switch (type) {
17665 case JSVAL_TYPE_OBJECT:
17666 // The input may be a callable object (result is "function") or
17667 // may emulate undefined (result is "undefined"). Use an OOL path.
17668 masm.branchTestObject(Assembler::Equal, tag, oolObject);
17669 return;
17670 case JSVAL_TYPE_DOUBLE:
17671 case JSVAL_TYPE_INT32:
17672 masm.branchTestNumber(Assembler::NotEqual, tag, &notMatch);
17673 break;
17674 default:
17675 masm.branchTestType(Assembler::NotEqual, tag, type, &notMatch);
17676 break;
17677 }
17678
17679 emitTypeOfJSType(type, output);
17680 masm.jump(done);
17681 masm.bind(&notMatch);
17682}
17683
17684void CodeGenerator::visitTypeOfV(LTypeOfV* lir) {
17685 const ValueOperand value = ToValue(lir, LTypeOfV::InputIndex);
17686 Register output = ToRegister(lir->output());
17687 Register tag = masm.extractTag(value, output);
17688
17689 Label done;
17690
17691 auto* ool = new (alloc()) OutOfLineTypeOfV(lir);
17692 addOutOfLineCode(ool, lir->mir());
17693
17694 const std::initializer_list<JSValueType> defaultOrder = {
17695 JSVAL_TYPE_OBJECT, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_UNDEFINED,
17696 JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_STRING,
17697 JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
17698
17699 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
17700
17701 // Generate checks for previously observed types first.
17702 // The TypeDataList is sorted by descending frequency.
17703 for (auto& observed : lir->mir()->observedTypes()) {
17704 JSValueType type = observed.type();
17705
17706 // Unify number types.
17707 if (type == JSVAL_TYPE_INT32) {
17708 type = JSVAL_TYPE_DOUBLE;
17709 }
17710
17711 remaining -= type;
17712
17713 emitTypeOfCheck(type, tag, output, &done, ool->entry());
17714 }
17715
17716 // Generate checks for remaining types.
17717 for (auto type : defaultOrder) {
17718 if (!remaining.contains(type)) {
17719 continue;
17720 }
17721 remaining -= type;
17722
17723 if (remaining.isEmpty() && type != JSVAL_TYPE_OBJECT) {
17724 // We can skip the check for the last remaining type, unless the type is
17725 // JSVAL_TYPE_OBJECT, which may have to go through the OOL path.
17726#ifdef DEBUG1
17727 emitTypeOfCheck(type, tag, output, &done, ool->entry());
17728 masm.assumeUnreachable("Unexpected Value type in visitTypeOfV");
17729#else
17730 emitTypeOfJSType(type, output);
17731#endif
17732 } else {
17733 emitTypeOfCheck(type, tag, output, &done, ool->entry());
17734 }
17735 }
17736 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 17736); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()"
")"); do { *((volatile int*)__null) = 17736; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17737
17738 masm.bind(&done);
17739 masm.bind(ool->rejoin());
17740}
17741
17742void CodeGenerator::emitTypeOfObject(Register obj, Register output,
17743 Label* done) {
17744 Label slowCheck, isObject, isCallable, isUndefined;
17745 masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable,
17746 &isUndefined);
17747
17748 masm.bind(&isCallable);
17749 masm.move32(Imm32(JSTYPE_FUNCTION), output);
17750 masm.jump(done);
17751
17752 masm.bind(&isUndefined);
17753 masm.move32(Imm32(JSTYPE_UNDEFINED), output);
17754 masm.jump(done);
17755
17756 masm.bind(&isObject);
17757 masm.move32(Imm32(JSTYPE_OBJECT), output);
17758 masm.jump(done);
17759
17760 masm.bind(&slowCheck);
17761
17762 saveVolatile(output);
17763 using Fn = JSType (*)(JSObject*);
17764 masm.setupAlignedABICall();
17765 masm.passABIArg(obj);
17766 masm.callWithABI<Fn, js::TypeOfObject>();
17767 masm.storeCallInt32Result(output);
17768 restoreVolatile(output);
17769}
17770
17771void CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) {
17772 LTypeOfV* ins = ool->ins();
17773
17774 ValueOperand input = ToValue(ins, LTypeOfV::InputIndex);
17775 Register temp = ToTempUnboxRegister(ins->temp0());
17776 Register output = ToRegister(ins->output());
17777
17778 Register obj = masm.extractObject(input, temp);
17779 emitTypeOfObject(obj, output, ool->rejoin());
17780 masm.jump(ool->rejoin());
17781}
17782
17783void CodeGenerator::visitTypeOfO(LTypeOfO* lir) {
17784 Register obj = ToRegister(lir->object());
17785 Register output = ToRegister(lir->output());
17786
17787 Label done;
17788 emitTypeOfObject(obj, output, &done);
17789 masm.bind(&done);
17790}
17791
17792void CodeGenerator::visitTypeOfName(LTypeOfName* lir) {
17793 Register input = ToRegister(lir->input());
17794 Register output = ToRegister(lir->output());
17795
17796#ifdef DEBUG1
17797 Label ok;
17798 masm.branch32(Assembler::Below, input, Imm32(JSTYPE_LIMIT), &ok);
17799 masm.assumeUnreachable("bad JSType");
17800 masm.bind(&ok);
17801#endif
17802
17803 static_assert(JSTYPE_UNDEFINED == 0);
17804
17805 masm.movePtr(ImmPtr(&gen->runtime->names().undefined), output);
17806 masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
17807}
17808
17809class OutOfLineTypeOfIsNonPrimitiveV : public OutOfLineCodeBase<CodeGenerator> {
17810 LTypeOfIsNonPrimitiveV* ins_;
17811
17812 public:
17813 explicit OutOfLineTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* ins)
17814 : ins_(ins) {}
17815
17816 void accept(CodeGenerator* codegen) override {
17817 codegen->visitOutOfLineTypeOfIsNonPrimitiveV(this);
17818 }
17819 auto* ins() const { return ins_; }
17820};
17821
17822class OutOfLineTypeOfIsNonPrimitiveO : public OutOfLineCodeBase<CodeGenerator> {
17823 LTypeOfIsNonPrimitiveO* ins_;
17824
17825 public:
17826 explicit OutOfLineTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* ins)
17827 : ins_(ins) {}
17828
17829 void accept(CodeGenerator* codegen) override {
17830 codegen->visitOutOfLineTypeOfIsNonPrimitiveO(this);
17831 }
17832 auto* ins() const { return ins_; }
17833};
17834
17835void CodeGenerator::emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj,
17836 Register output) {
17837 saveVolatile(output);
17838 using Fn = JSType (*)(JSObject*);
17839 masm.setupAlignedABICall();
17840 masm.passABIArg(obj);
17841 masm.callWithABI<Fn, js::TypeOfObject>();
17842 masm.storeCallInt32Result(output);
17843 restoreVolatile(output);
17844
17845 auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false);
17846 masm.cmp32Set(cond, output, Imm32(mir->jstype()), output);
17847}
17848
17849void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveV(
17850 OutOfLineTypeOfIsNonPrimitiveV* ool) {
17851 auto* ins = ool->ins();
17852 ValueOperand input = ToValue(ins, LTypeOfIsNonPrimitiveV::InputIndex);
17853 Register output = ToRegister(ins->output());
17854 Register temp = ToTempUnboxRegister(ins->temp0());
17855
17856 Register obj = masm.extractObject(input, temp);
17857
17858 emitTypeOfIsObjectOOL(ins->mir(), obj, output);
17859
17860 masm.jump(ool->rejoin());
17861}
17862
17863void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveO(
17864 OutOfLineTypeOfIsNonPrimitiveO* ool) {
17865 auto* ins = ool->ins();
17866 Register input = ToRegister(ins->input());
17867 Register output = ToRegister(ins->output());
17868
17869 emitTypeOfIsObjectOOL(ins->mir(), input, output);
17870
17871 masm.jump(ool->rejoin());
17872}
17873
17874void CodeGenerator::emitTypeOfIsObject(MTypeOfIs* mir, Register obj,
17875 Register output, Label* success,
17876 Label* fail, Label* slowCheck) {
17877 Label* isObject = fail;
17878 Label* isFunction = fail;
17879 Label* isUndefined = fail;
17880
17881 switch (mir->jstype()) {
17882 case JSTYPE_UNDEFINED:
17883 isUndefined = success;
17884 break;
17885
17886 case JSTYPE_OBJECT:
17887 isObject = success;
17888 break;
17889
17890 case JSTYPE_FUNCTION:
17891 isFunction = success;
17892 break;
17893
17894 case JSTYPE_STRING:
17895 case JSTYPE_NUMBER:
17896 case JSTYPE_BOOLEAN:
17897 case JSTYPE_SYMBOL:
17898 case JSTYPE_BIGINT:
17899#ifdef ENABLE_RECORD_TUPLE
17900 case JSTYPE_RECORD:
17901 case JSTYPE_TUPLE:
17902#endif
17903 case JSTYPE_LIMIT:
17904 MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 17904); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type"
")"); do { *((volatile int*)__null) = 17904; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
17905 }
17906
17907 masm.typeOfObject(obj, output, slowCheck, isObject, isFunction, isUndefined);
17908
17909 auto op = mir->jsop();
17910
17911 Label done;
17912 masm.bind(fail);
17913 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
17914 masm.jump(&done);
17915 masm.bind(success);
17916 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
17917 masm.bind(&done);
17918}
17919
17920void CodeGenerator::visitTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* lir) {
17921 ValueOperand input = ToValue(lir, LTypeOfIsNonPrimitiveV::InputIndex);
17922 Register output = ToRegister(lir->output());
17923 Register temp = ToTempUnboxRegister(lir->temp0());
17924
17925 auto* mir = lir->mir();
17926
17927 auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveV(lir);
17928 addOutOfLineCode(ool, mir);
17929
17930 Label success, fail;
17931
17932 switch (mir->jstype()) {
17933 case JSTYPE_UNDEFINED: {
17934 ScratchTagScope tag(masm, input);
17935 masm.splitTagForTest(input, tag);
17936
17937 masm.branchTestUndefined(Assembler::Equal, tag, &success);
17938 masm.branchTestObject(Assembler::NotEqual, tag, &fail);
17939 break;
17940 }
17941
17942 case JSTYPE_OBJECT: {
17943 ScratchTagScope tag(masm, input);
17944 masm.splitTagForTest(input, tag);
17945
17946 masm.branchTestNull(Assembler::Equal, tag, &success);
17947 masm.branchTestObject(Assembler::NotEqual, tag, &fail);
17948 break;
17949 }
17950
17951 case JSTYPE_FUNCTION: {
17952 masm.branchTestObject(Assembler::NotEqual, input, &fail);
17953 break;
17954 }
17955
17956 case JSTYPE_STRING:
17957 case JSTYPE_NUMBER:
17958 case JSTYPE_BOOLEAN:
17959 case JSTYPE_SYMBOL:
17960 case JSTYPE_BIGINT:
17961#ifdef ENABLE_RECORD_TUPLE
17962 case JSTYPE_RECORD:
17963 case JSTYPE_TUPLE:
17964#endif
17965 case JSTYPE_LIMIT:
17966 MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 17966); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type"
")"); do { *((volatile int*)__null) = 17966; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
17967 }
17968
17969 Register obj = masm.extractObject(input, temp);
17970
17971 emitTypeOfIsObject(mir, obj, output, &success, &fail, ool->entry());
17972
17973 masm.bind(ool->rejoin());
17974}
17975
17976void CodeGenerator::visitTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* lir) {
17977 Register input = ToRegister(lir->input());
17978 Register output = ToRegister(lir->output());
17979
17980 auto* mir = lir->mir();
17981
17982 auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveO(lir);
17983 addOutOfLineCode(ool, mir);
17984
17985 Label success, fail;
17986 emitTypeOfIsObject(mir, input, output, &success, &fail, ool->entry());
17987
17988 masm.bind(ool->rejoin());
17989}
17990
17991void CodeGenerator::visitTypeOfIsPrimitive(LTypeOfIsPrimitive* lir) {
17992 ValueOperand input = ToValue(lir, LTypeOfIsPrimitive::InputIndex);
17993 Register output = ToRegister(lir->output());
17994
17995 auto* mir = lir->mir();
17996 auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false);
17997
17998 switch (mir->jstype()) {
17999 case JSTYPE_STRING:
18000 masm.testStringSet(cond, input, output);
18001 break;
18002 case JSTYPE_NUMBER:
18003 masm.testNumberSet(cond, input, output);
18004 break;
18005 case JSTYPE_BOOLEAN:
18006 masm.testBooleanSet(cond, input, output);
18007 break;
18008 case JSTYPE_SYMBOL:
18009 masm.testSymbolSet(cond, input, output);
18010 break;
18011 case JSTYPE_BIGINT:
18012 masm.testBigIntSet(cond, input, output);
18013 break;
18014
18015 case JSTYPE_UNDEFINED:
18016 case JSTYPE_OBJECT:
18017 case JSTYPE_FUNCTION:
18018#ifdef ENABLE_RECORD_TUPLE
18019 case JSTYPE_RECORD:
18020 case JSTYPE_TUPLE:
18021#endif
18022 case JSTYPE_LIMIT:
18023 MOZ_CRASH("Non-primitive type")do { do { } while (false); MOZ_ReportCrash("" "Non-primitive type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18023); AnnotateMozCrashReason("MOZ_CRASH(" "Non-primitive type"
")"); do { *((volatile int*)__null) = 18023; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18024 }
18025}
18026
18027void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) {
18028 pushArg(ToValue(lir, LToAsyncIter::NextMethodIndex));
18029 pushArg(ToRegister(lir->iterator()));
18030
18031 using Fn = JSObject* (*)(JSContext*, HandleObject, HandleValue);
18032 callVM<Fn, js::CreateAsyncFromSyncIterator>(lir);
18033}
18034
18035void CodeGenerator::visitToPropertyKeyCache(LToPropertyKeyCache* lir) {
18036 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
18037 ValueOperand input = ToValue(lir, LToPropertyKeyCache::InputIndex);
18038 ValueOperand output = ToOutValue(lir);
18039
18040 IonToPropertyKeyIC ic(liveRegs, input, output);
18041 addIC(lir, allocateIC(ic));
18042}
18043
18044void CodeGenerator::visitLoadElementV(LLoadElementV* load) {
18045 Register elements = ToRegister(load->elements());
18046 const ValueOperand out = ToOutValue(load);
18047
18048 if (load->index()->isConstant()) {
18049 NativeObject::elementsSizeMustNotOverflow();
18050 int32_t offset = ToInt32(load->index()) * sizeof(Value);
18051 masm.loadValue(Address(elements, offset), out);
18052 } else {
18053 masm.loadValue(BaseObjectElementIndex(elements, ToRegister(load->index())),
18054 out);
18055 }
18056
18057 Label testMagic;
18058 masm.branchTestMagic(Assembler::Equal, out, &testMagic);
18059 bailoutFrom(&testMagic, load->snapshot());
18060}
18061
18062void CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) {
18063 Register elements = ToRegister(lir->elements());
18064 Register index = ToRegister(lir->index());
18065 Register initLength = ToRegister(lir->initLength());
18066 const ValueOperand out = ToOutValue(lir);
18067
18068 const MLoadElementHole* mir = lir->mir();
18069
18070 // If the index is out of bounds, load |undefined|. Otherwise, load the
18071 // value.
18072 Label outOfBounds, done;
18073 masm.spectreBoundsCheck32(index, initLength, out.scratchReg(), &outOfBounds);
18074
18075 masm.loadValue(BaseObjectElementIndex(elements, index), out);
18076
18077 // If the value wasn't a hole, we're done. Otherwise, we'll load undefined.
18078 masm.branchTestMagic(Assembler::NotEqual, out, &done);
18079
18080 if (mir->needsNegativeIntCheck()) {
18081 Label loadUndefined;
18082 masm.jump(&loadUndefined);
18083
18084 masm.bind(&outOfBounds);
18085
18086 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
18087
18088 masm.bind(&loadUndefined);
18089 } else {
18090 masm.bind(&outOfBounds);
18091 }
18092 masm.moveValue(UndefinedValue(), out);
18093
18094 masm.bind(&done);
18095}
18096
18097void CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) {
18098 Register elements = ToRegister(lir->elements());
18099 Register temp0 = ToTempRegisterOrInvalid(lir->temp0());
18100 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
18101 AnyRegister out = ToAnyRegister(lir->output());
18102
18103 const MLoadUnboxedScalar* mir = lir->mir();
18104
18105 Scalar::Type storageType = mir->storageType();
18106
18107 LiveRegisterSet volatileRegs;
18108 if (MacroAssembler::LoadRequiresCall(storageType)) {
18109 volatileRegs = liveVolatileRegs(lir);
18110 }
18111
18112 Label fail;
18113 if (lir->index()->isConstant()) {
18114 Address source =
18115 ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment());
18116 masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail,
18117 volatileRegs);
18118 } else {
18119 BaseIndex source(elements, ToRegister(lir->index()),
18120 ScaleFromScalarType(storageType), mir->offsetAdjustment());
18121 masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail,
18122 volatileRegs);
18123 }
18124
18125 if (fail.used()) {
18126 bailoutFrom(&fail, lir->snapshot());
18127 }
18128}
18129
18130void CodeGenerator::visitLoadUnboxedBigInt(LLoadUnboxedBigInt* lir) {
18131 Register elements = ToRegister(lir->elements());
18132 Register temp = ToRegister(lir->temp());
18133 Register64 temp64 = ToRegister64(lir->temp64());
18134 Register out = ToRegister(lir->output());
18135
18136 const MLoadUnboxedScalar* mir = lir->mir();
18137
18138 Scalar::Type storageType = mir->storageType();
18139
18140 if (lir->index()->isConstant()) {
18141 Address source =
18142 ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment());
18143 masm.load64(source, temp64);
18144 } else {
18145 BaseIndex source(elements, ToRegister(lir->index()),
18146 ScaleFromScalarType(storageType), mir->offsetAdjustment());
18147 masm.load64(source, temp64);
18148 }
18149
18150 emitCreateBigInt(lir, storageType, temp64, out, temp);
18151}
18152
18153void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) {
18154 Register elements = ToRegister(lir->elements());
18155 const LAllocation* littleEndian = lir->littleEndian();
18156 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
18157 Register temp2 = ToTempRegisterOrInvalid(lir->temp2());
18158 Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64());
18159 AnyRegister out = ToAnyRegister(lir->output());
18160
18161 const MLoadDataViewElement* mir = lir->mir();
18162 Scalar::Type storageType = mir->storageType();
18163
18164 LiveRegisterSet volatileRegs;
18165 if (MacroAssembler::LoadRequiresCall(storageType)) {
18166 volatileRegs = liveVolatileRegs(lir);
18167 }
18168
18169 BaseIndex source(elements, ToRegister(lir->index()), TimesOne);
18170
18171 bool noSwap = littleEndian->isConstant() &&
18172 ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1;
18173
18174 // Directly load if no byte swap is needed and the platform supports unaligned
18175 // accesses for the access. (Such support is assumed for integer types.)
18176 if (noSwap && (!Scalar::isFloatingType(storageType) ||
18177 MacroAssembler::SupportsFastUnalignedFPAccesses())) {
18178 if (!Scalar::isBigIntType(storageType)) {
18179 Label fail;
18180 masm.loadFromTypedArray(storageType, source, out, temp1, temp2, &fail,
18181 volatileRegs);
18182
18183 if (fail.used()) {
18184 bailoutFrom(&fail, lir->snapshot());
18185 }
18186 } else {
18187 masm.load64(source, temp64);
18188
18189 emitCreateBigInt(lir, storageType, temp64, out.gpr(), temp1);
18190 }
18191 return;
18192 }
18193
18194 // Load the value into a gpr register.
18195 switch (storageType) {
18196 case Scalar::Int16:
18197 masm.load16UnalignedSignExtend(source, out.gpr());
18198 break;
18199 case Scalar::Uint16:
18200 masm.load16UnalignedZeroExtend(source, out.gpr());
18201 break;
18202 case Scalar::Int32:
18203 masm.load32Unaligned(source, out.gpr());
18204 break;
18205 case Scalar::Uint32:
18206 masm.load32Unaligned(source, out.isFloat() ? temp1 : out.gpr());
18207 break;
18208 case Scalar::Float16:
18209 masm.load16UnalignedZeroExtend(source, temp1);
18210 break;
18211 case Scalar::Float32:
18212 masm.load32Unaligned(source, temp1);
18213 break;
18214 case Scalar::Float64:
18215 case Scalar::BigInt64:
18216 case Scalar::BigUint64:
18217 masm.load64Unaligned(source, temp64);
18218 break;
18219 case Scalar::Int8:
18220 case Scalar::Uint8:
18221 case Scalar::Uint8Clamped:
18222 default:
18223 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18223); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18223; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18224 }
18225
18226 if (!noSwap) {
18227 // Swap the bytes in the loaded value.
18228 Label skip;
18229 if (!littleEndian->isConstant()) {
18230 masm.branch32(
18231 MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal,
18232 ToRegister(littleEndian), Imm32(0), &skip);
18233 }
18234
18235 switch (storageType) {
18236 case Scalar::Int16:
18237 masm.byteSwap16SignExtend(out.gpr());
18238 break;
18239 case Scalar::Uint16:
18240 masm.byteSwap16ZeroExtend(out.gpr());
18241 break;
18242 case Scalar::Int32:
18243 masm.byteSwap32(out.gpr());
18244 break;
18245 case Scalar::Uint32:
18246 masm.byteSwap32(out.isFloat() ? temp1 : out.gpr());
18247 break;
18248 case Scalar::Float16:
18249 masm.byteSwap16ZeroExtend(temp1);
18250 break;
18251 case Scalar::Float32:
18252 masm.byteSwap32(temp1);
18253 break;
18254 case Scalar::Float64:
18255 case Scalar::BigInt64:
18256 case Scalar::BigUint64:
18257 masm.byteSwap64(temp64);
18258 break;
18259 case Scalar::Int8:
18260 case Scalar::Uint8:
18261 case Scalar::Uint8Clamped:
18262 default:
18263 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18263); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18263; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18264 }
18265
18266 if (skip.used()) {
18267 masm.bind(&skip);
18268 }
18269 }
18270
18271 // Move the value into the output register.
18272 switch (storageType) {
18273 case Scalar::Int16:
18274 case Scalar::Uint16:
18275 case Scalar::Int32:
18276 break;
18277 case Scalar::Uint32:
18278 if (out.isFloat()) {
18279 masm.convertUInt32ToDouble(temp1, out.fpu());
18280 } else {
18281 // Bail out if the value doesn't fit into a signed int32 value. This
18282 // is what allows MLoadDataViewElement to have a type() of
18283 // MIRType::Int32 for UInt32 array loads.
18284 bailoutTest32(Assembler::Signed, out.gpr(), out.gpr(), lir->snapshot());
18285 }
18286 break;
18287 case Scalar::Float16:
18288 masm.moveGPRToFloat16(temp1, out.fpu(), temp2, volatileRegs);
18289 masm.canonicalizeFloat(out.fpu());
18290 break;
18291 case Scalar::Float32:
18292 masm.moveGPRToFloat32(temp1, out.fpu());
18293 masm.canonicalizeFloat(out.fpu());
18294 break;
18295 case Scalar::Float64:
18296 masm.moveGPR64ToDouble(temp64, out.fpu());
18297 masm.canonicalizeDouble(out.fpu());
18298 break;
18299 case Scalar::BigInt64:
18300 case Scalar::BigUint64:
18301 emitCreateBigInt(lir, storageType, temp64, out.gpr(), temp1);
18302 break;
18303 case Scalar::Int8:
18304 case Scalar::Uint8:
18305 case Scalar::Uint8Clamped:
18306 default:
18307 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18307); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18307; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18308 }
18309}
18310
18311void CodeGenerator::visitLoadTypedArrayElementHole(
18312 LLoadTypedArrayElementHole* lir) {
18313 Register elements = ToRegister(lir->elements());
18314 Register index = ToRegister(lir->index());
18315 Register length = ToRegister(lir->length());
18316 Register temp = ToTempRegisterOrInvalid(lir->temp0());
18317 const ValueOperand out = ToOutValue(lir);
18318
18319 Register scratch = out.scratchReg();
18320
18321 // Load undefined if index >= length.
18322 Label outOfBounds, done;
18323 masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds);
18324
18325 Scalar::Type arrayType = lir->mir()->arrayType();
18326
18327 LiveRegisterSet volatileRegs;
18328 if (MacroAssembler::LoadRequiresCall(arrayType)) {
18329 volatileRegs = liveVolatileRegs(lir);
18330 }
18331
18332 Label fail;
18333 BaseIndex source(elements, index, ScaleFromScalarType(arrayType));
18334 MacroAssembler::Uint32Mode uint32Mode =
18335 lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble
18336 : MacroAssembler::Uint32Mode::FailOnDouble;
18337 masm.loadFromTypedArray(arrayType, source, out, uint32Mode, temp, &fail,
18338 volatileRegs);
18339 masm.jump(&done);
18340
18341 masm.bind(&outOfBounds);
18342 masm.moveValue(UndefinedValue(), out);
18343
18344 if (fail.used()) {
18345 bailoutFrom(&fail, lir->snapshot());
18346 }
18347
18348 masm.bind(&done);
18349}
18350
18351void CodeGenerator::visitLoadTypedArrayElementHoleBigInt(
18352 LLoadTypedArrayElementHoleBigInt* lir) {
18353 Register elements = ToRegister(lir->elements());
18354 Register index = ToRegister(lir->index());
18355 Register length = ToRegister(lir->length());
18356 const ValueOperand out = ToOutValue(lir);
18357
18358 Register temp = ToRegister(lir->temp());
18359
18360 // On x86 there are not enough registers. In that case reuse the output
18361 // registers as temporaries.
18362#ifdef JS_CODEGEN_X86
18363 MOZ_ASSERT(lir->temp64().isBogusTemp())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->temp64().isBogusTemp())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->temp64().isBogusTemp
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->temp64().isBogusTemp()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18363); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->temp64().isBogusTemp()"
")"); do { *((volatile int*)__null) = 18363; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18364 Register64 temp64 = out.toRegister64();
18365#else
18366 Register64 temp64 = ToRegister64(lir->temp64());
18367#endif
18368
18369 // Load undefined if index >= length.
18370 Label outOfBounds, done;
18371 masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds);
18372
18373 Scalar::Type arrayType = lir->mir()->arrayType();
18374 BaseIndex source(elements, index, ScaleFromScalarType(arrayType));
18375 masm.load64(source, temp64);
18376
18377#ifdef JS_CODEGEN_X86
18378 Register bigInt = temp;
18379 Register maybeTemp = InvalidReg;
18380#else
18381 Register bigInt = out.scratchReg();
18382 Register maybeTemp = temp;
18383#endif
18384 emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp);
18385
18386 masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out);
18387 masm.jump(&done);
18388
18389 masm.bind(&outOfBounds);
18390 masm.moveValue(UndefinedValue(), out);
18391
18392 masm.bind(&done);
18393}
18394
18395template <SwitchTableType tableType>
18396class OutOfLineSwitch : public OutOfLineCodeBase<CodeGenerator> {
18397 using LabelsVector = Vector<Label, 0, JitAllocPolicy>;
18398 using CodeLabelsVector = Vector<CodeLabel, 0, JitAllocPolicy>;
18399 LabelsVector labels_;
18400 CodeLabelsVector codeLabels_;
18401 CodeLabel start_;
18402 bool isOutOfLine_;
18403
18404 void accept(CodeGenerator* codegen) override {
18405 codegen->visitOutOfLineSwitch(this);
18406 }
18407
18408 public:
18409 explicit OutOfLineSwitch(TempAllocator& alloc)
18410 : labels_(alloc), codeLabels_(alloc), isOutOfLine_(false) {}
18411
18412 CodeLabel* start() { return &start_; }
18413
18414 CodeLabelsVector& codeLabels() { return codeLabels_; }
18415 LabelsVector& labels() { return labels_; }
18416
18417 void jumpToCodeEntries(MacroAssembler& masm, Register index, Register temp) {
18418 Register base;
18419 if (tableType == SwitchTableType::Inline) {
18420#if defined(JS_CODEGEN_ARM)
18421 base = ::js::jit::pc;
18422#else
18423 MOZ_CRASH("NYI: SwitchTableType::Inline")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::Inline"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18423); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::Inline"
")"); do { *((volatile int*)__null) = 18423; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18424#endif
18425 } else {
18426#if defined(JS_CODEGEN_ARM)
18427 MOZ_CRASH("NYI: SwitchTableType::OutOfLine")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::OutOfLine"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18427); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine"
")"); do { *((volatile int*)__null) = 18427; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18428#else
18429 masm.mov(start(), temp);
18430 base = temp;
18431#endif
18432 }
18433 BaseIndex jumpTarget(base, index, ScalePointer);
18434 masm.branchToComputedAddress(jumpTarget);
18435 }
18436
18437 // Register an entry in the switch table.
18438 void addTableEntry(MacroAssembler& masm) {
18439 if ((!isOutOfLine_ && tableType == SwitchTableType::Inline) ||
18440 (isOutOfLine_ && tableType == SwitchTableType::OutOfLine)) {
18441 CodeLabel cl;
18442 masm.writeCodePointer(&cl);
18443 masm.propagateOOM(codeLabels_.append(std::move(cl)));
18444 }
18445 }
18446 // Register the code, to which the table will jump to.
18447 void addCodeEntry(MacroAssembler& masm) {
18448 Label entry;
18449 masm.bind(&entry);
18450 masm.propagateOOM(labels_.append(std::move(entry)));
18451 }
18452
18453 void setOutOfLine() { isOutOfLine_ = true; }
18454};
18455
18456template <SwitchTableType tableType>
18457void CodeGenerator::visitOutOfLineSwitch(
18458 OutOfLineSwitch<tableType>* jumpTable) {
18459 jumpTable->setOutOfLine();
18460 auto& labels = jumpTable->labels();
18461
18462 if (tableType == SwitchTableType::OutOfLine) {
18463#if defined(JS_CODEGEN_ARM)
18464 MOZ_CRASH("NYI: SwitchTableType::OutOfLine")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::OutOfLine"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18464); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine"
")"); do { *((volatile int*)__null) = 18464; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18465#elif defined(JS_CODEGEN_NONE)
18466 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18466); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 18466; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
18467#else
18468
18469# if defined(JS_CODEGEN_ARM64)
18470 AutoForbidPoolsAndNops afp(
18471 &masm,
18472 (labels.length() + 1) * (sizeof(void*) / vixl::kInstructionSize));
18473# endif
18474
18475 masm.haltingAlign(sizeof(void*));
18476
18477 // Bind the address of the jump table and reserve the space for code
18478 // pointers to jump in the newly generated code.
18479 masm.bind(jumpTable->start());
18480 masm.addCodeLabel(*jumpTable->start());
18481 for (size_t i = 0, e = labels.length(); i < e; i++) {
18482 jumpTable->addTableEntry(masm);
18483 }
18484#endif
18485 }
18486
18487 // Register all reserved pointers of the jump table to target labels. The
18488 // entries of the jump table need to be absolute addresses and thus must be
18489 // patched after codegen is finished.
18490 auto& codeLabels = jumpTable->codeLabels();
18491 for (size_t i = 0, e = codeLabels.length(); i < e; i++) {
18492 auto& cl = codeLabels[i];
18493 cl.target()->bind(labels[i].offset());
18494 masm.addCodeLabel(cl);
18495 }
18496}
18497
18498template void CodeGenerator::visitOutOfLineSwitch(
18499 OutOfLineSwitch<SwitchTableType::Inline>* jumpTable);
18500template void CodeGenerator::visitOutOfLineSwitch(
18501 OutOfLineSwitch<SwitchTableType::OutOfLine>* jumpTable);
18502
18503template <typename T>
18504static inline void StoreToTypedArray(MacroAssembler& masm,
18505 Scalar::Type writeType,
18506 const LAllocation* value, const T& dest,
18507 Register temp,
18508 LiveRegisterSet volatileRegs) {
18509 if (Scalar::isFloatingType(writeType)) {
18510 masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, temp,
18511 volatileRegs);
18512 } else {
18513 if (value->isConstant()) {
18514 masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest);
18515 } else {
18516 masm.storeToTypedIntArray(writeType, ToRegister(value), dest);
18517 }
18518 }
18519}
18520
18521void CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) {
18522 Register elements = ToRegister(lir->elements());
18523 Register temp = ToTempRegisterOrInvalid(lir->temp0());
18524 const LAllocation* value = lir->value();
18525
18526 const MStoreUnboxedScalar* mir = lir->mir();
18527
18528 Scalar::Type writeType = mir->writeType();
18529
18530 LiveRegisterSet volatileRegs;
18531 if (MacroAssembler::StoreRequiresCall(writeType)) {
18532 volatileRegs = liveVolatileRegs(lir);
18533 }
18534
18535 if (lir->index()->isConstant()) {
18536 Address dest = ToAddress(elements, lir->index(), writeType);
18537 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
18538 } else {
18539 BaseIndex dest(elements, ToRegister(lir->index()),
18540 ScaleFromScalarType(writeType));
18541 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
18542 }
18543}
18544
18545void CodeGenerator::visitStoreUnboxedBigInt(LStoreUnboxedBigInt* lir) {
18546 Register elements = ToRegister(lir->elements());
18547 Register value = ToRegister(lir->value());
18548 Register64 temp = ToRegister64(lir->temp());
18549
18550 Scalar::Type writeType = lir->mir()->writeType();
18551
18552 masm.loadBigInt64(value, temp);
18553
18554 if (lir->index()->isConstant()) {
18555 Address dest = ToAddress(elements, lir->index(), writeType);
18556 masm.storeToTypedBigIntArray(writeType, temp, dest);
18557 } else {
18558 BaseIndex dest(elements, ToRegister(lir->index()),
18559 ScaleFromScalarType(writeType));
18560 masm.storeToTypedBigIntArray(writeType, temp, dest);
18561 }
18562}
18563
18564void CodeGenerator::visitStoreDataViewElement(LStoreDataViewElement* lir) {
18565 Register elements = ToRegister(lir->elements());
18566 const LAllocation* value = lir->value();
18567 const LAllocation* littleEndian = lir->littleEndian();
18568 Register temp = ToTempRegisterOrInvalid(lir->temp());
18569 Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64());
18570
18571 const MStoreDataViewElement* mir = lir->mir();
18572 Scalar::Type writeType = mir->writeType();
18573
18574 LiveRegisterSet volatileRegs;
18575 if (MacroAssembler::StoreRequiresCall(writeType)) {
18576 volatileRegs = liveVolatileRegs(lir);
18577 }
18578
18579 BaseIndex dest(elements, ToRegister(lir->index()), TimesOne);
18580
18581 bool noSwap = littleEndian->isConstant() &&
18582 ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1;
18583
18584 // Directly store if no byte swap is needed and the platform supports
18585 // unaligned accesses for the access. (Such support is assumed for integer
18586 // types.)
18587 if (noSwap && (!Scalar::isFloatingType(writeType) ||
18588 MacroAssembler::SupportsFastUnalignedFPAccesses())) {
18589 if (!Scalar::isBigIntType(writeType)) {
18590 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
18591 } else {
18592 masm.loadBigInt64(ToRegister(value), temp64);
18593 masm.storeToTypedBigIntArray(writeType, temp64, dest);
18594 }
18595 return;
18596 }
18597
18598 // Load the value into a gpr register.
18599 switch (writeType) {
18600 case Scalar::Int16:
18601 case Scalar::Uint16:
18602 case Scalar::Int32:
18603 case Scalar::Uint32:
18604 if (value->isConstant()) {
18605 masm.move32(Imm32(ToInt32(value)), temp);
18606 } else {
18607 masm.move32(ToRegister(value), temp);
18608 }
18609 break;
18610 case Scalar::Float16: {
18611 FloatRegister fvalue = ToFloatRegister(value);
18612 masm.canonicalizeFloatIfDeterministic(fvalue);
18613 masm.moveFloat16ToGPR(fvalue, temp, volatileRegs);
18614 break;
18615 }
18616 case Scalar::Float32: {
18617 FloatRegister fvalue = ToFloatRegister(value);
18618 masm.canonicalizeFloatIfDeterministic(fvalue);
18619 masm.moveFloat32ToGPR(fvalue, temp);
18620 break;
18621 }
18622 case Scalar::Float64: {
18623 FloatRegister fvalue = ToFloatRegister(value);
18624 masm.canonicalizeDoubleIfDeterministic(fvalue);
18625 masm.moveDoubleToGPR64(fvalue, temp64);
18626 break;
18627 }
18628 case Scalar::BigInt64:
18629 case Scalar::BigUint64:
18630 masm.loadBigInt64(ToRegister(value), temp64);
18631 break;
18632 case Scalar::Int8:
18633 case Scalar::Uint8:
18634 case Scalar::Uint8Clamped:
18635 default:
18636 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18636); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18636; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18637 }
18638
18639 if (!noSwap) {
18640 // Swap the bytes in the loaded value.
18641 Label skip;
18642 if (!littleEndian->isConstant()) {
18643 masm.branch32(
18644 MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal,
18645 ToRegister(littleEndian), Imm32(0), &skip);
18646 }
18647
18648 switch (writeType) {
18649 case Scalar::Int16:
18650 masm.byteSwap16SignExtend(temp);
18651 break;
18652 case Scalar::Uint16:
18653 case Scalar::Float16:
18654 masm.byteSwap16ZeroExtend(temp);
18655 break;
18656 case Scalar::Int32:
18657 case Scalar::Uint32:
18658 case Scalar::Float32:
18659 masm.byteSwap32(temp);
18660 break;
18661 case Scalar::Float64:
18662 case Scalar::BigInt64:
18663 case Scalar::BigUint64:
18664 masm.byteSwap64(temp64);
18665 break;
18666 case Scalar::Int8:
18667 case Scalar::Uint8:
18668 case Scalar::Uint8Clamped:
18669 default:
18670 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18670); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18670; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18671 }
18672
18673 if (skip.used()) {
18674 masm.bind(&skip);
18675 }
18676 }
18677
18678 // Store the value into the destination.
18679 switch (writeType) {
18680 case Scalar::Int16:
18681 case Scalar::Uint16:
18682 case Scalar::Float16:
18683 masm.store16Unaligned(temp, dest);
18684 break;
18685 case Scalar::Int32:
18686 case Scalar::Uint32:
18687 case Scalar::Float32:
18688 masm.store32Unaligned(temp, dest);
18689 break;
18690 case Scalar::Float64:
18691 case Scalar::BigInt64:
18692 case Scalar::BigUint64:
18693 masm.store64Unaligned(temp64, dest);
18694 break;
18695 case Scalar::Int8:
18696 case Scalar::Uint8:
18697 case Scalar::Uint8Clamped:
18698 default:
18699 MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18699); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18699; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18700 }
18701}
18702
18703void CodeGenerator::visitStoreTypedArrayElementHole(
18704 LStoreTypedArrayElementHole* lir) {
18705 Register elements = ToRegister(lir->elements());
18706 const LAllocation* value = lir->value();
18707
18708 Scalar::Type arrayType = lir->mir()->arrayType();
18709
18710 Register index = ToRegister(lir->index());
18711 const LAllocation* length = lir->length();
18712 Register temp = ToTempRegisterOrInvalid(lir->temp0());
18713
18714 LiveRegisterSet volatileRegs;
18715 if (MacroAssembler::StoreRequiresCall(arrayType)) {
18716 volatileRegs = liveVolatileRegs(lir);
18717 }
18718
18719 Label skip;
18720 if (length->isRegister()) {
18721 masm.spectreBoundsCheckPtr(index, ToRegister(length), temp, &skip);
18722 } else {
18723 masm.spectreBoundsCheckPtr(index, ToAddress(length), temp, &skip);
18724 }
18725
18726 BaseIndex dest(elements, index, ScaleFromScalarType(arrayType));
18727 StoreToTypedArray(masm, arrayType, value, dest, temp, volatileRegs);
18728
18729 masm.bind(&skip);
18730}
18731
18732void CodeGenerator::visitStoreTypedArrayElementHoleBigInt(
18733 LStoreTypedArrayElementHoleBigInt* lir) {
18734 Register elements = ToRegister(lir->elements());
18735 Register value = ToRegister(lir->value());
18736 Register64 temp = ToRegister64(lir->temp());
18737
18738 Scalar::Type arrayType = lir->mir()->arrayType();
18739
18740 Register index = ToRegister(lir->index());
18741 const LAllocation* length = lir->length();
18742 Register spectreTemp = temp.scratchReg();
18743
18744 Label skip;
18745 if (length->isRegister()) {
18746 masm.spectreBoundsCheckPtr(index, ToRegister(length), spectreTemp, &skip);
18747 } else {
18748 masm.spectreBoundsCheckPtr(index, ToAddress(length), spectreTemp, &skip);
18749 }
18750
18751 masm.loadBigInt64(value, temp);
18752
18753 BaseIndex dest(elements, index, ScaleFromScalarType(arrayType));
18754 masm.storeToTypedBigIntArray(arrayType, temp, dest);
18755
18756 masm.bind(&skip);
18757}
18758
18759void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) {
18760 masm.memoryBarrier(ins->type());
18761}
18762
18763void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) {
18764 Register value = ToRegister(lir->value());
18765 Register output = ToRegister(lir->output());
18766
18767 masm.atomicIsLockFreeJS(value, output);
18768}
18769
18770void CodeGenerator::visitClampIToUint8(LClampIToUint8* lir) {
18771 Register output = ToRegister(lir->output());
18772 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())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18772); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == ToRegister(lir->input())"
")"); do { *((volatile int*)__null) = 18772; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18773 masm.clampIntToUint8(output);
18774}
18775
18776void CodeGenerator::visitClampDToUint8(LClampDToUint8* lir) {
18777 FloatRegister input = ToFloatRegister(lir->input());
18778 Register output = ToRegister(lir->output());
18779 masm.clampDoubleToUint8(input, output);
18780}
18781
18782void CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) {
18783 ValueOperand operand = ToValue(lir, LClampVToUint8::InputIndex);
18784 FloatRegister tempFloat = ToFloatRegister(lir->temp0());
18785 Register output = ToRegister(lir->output());
18786
18787 using Fn = bool (*)(JSContext*, JSString*, double*);
18788 OutOfLineCode* oolString = oolCallVM<Fn, StringToNumber>(
18789 lir, ArgList(output), StoreFloatRegisterTo(tempFloat));
18790 Label* stringEntry = oolString->entry();
18791 Label* stringRejoin = oolString->rejoin();
18792
18793 Label fails;
18794 masm.clampValueToUint8(operand, stringEntry, stringRejoin, output, tempFloat,
18795 output, &fails);
18796
18797 bailoutFrom(&fails, lir->snapshot());
18798}
18799
18800void CodeGenerator::visitInCache(LInCache* ins) {
18801 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18802
18803 ConstantOrRegister key =
18804 toConstantOrRegister(ins, LInCache::LhsIndex, ins->mir()->key()->type());
18805 Register object = ToRegister(ins->rhs());
18806 Register output = ToRegister(ins->output());
18807 Register temp = ToRegister(ins->temp0());
18808
18809 IonInIC cache(liveRegs, key, object, output, temp);
18810 addIC(ins, allocateIC(cache));
18811}
18812
18813void CodeGenerator::visitInArray(LInArray* lir) {
18814 const MInArray* mir = lir->mir();
18815 Register elements = ToRegister(lir->elements());
18816 Register initLength = ToRegister(lir->initLength());
18817 Register output = ToRegister(lir->output());
18818
18819 Label falseBranch, done, trueBranch;
18820
18821 if (lir->index()->isConstant()) {
18822 int32_t index = ToInt32(lir->index());
18823
18824 if (index < 0) {
18825 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18825); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->needsNegativeIntCheck()"
")"); do { *((volatile int*)__null) = 18825; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18826 bailout(lir->snapshot());
18827 return;
18828 }
18829
18830 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index),
18831 &falseBranch);
18832
18833 NativeObject::elementsSizeMustNotOverflow();
18834 Address address = Address(elements, index * sizeof(Value));
18835 masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
18836 } else {
18837 Register index = ToRegister(lir->index());
18838
18839 Label negativeIntCheck;
18840 Label* failedInitLength = &falseBranch;
18841 if (mir->needsNegativeIntCheck()) {
18842 failedInitLength = &negativeIntCheck;
18843 }
18844
18845 masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
18846
18847 BaseObjectElementIndex address(elements, index);
18848 masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
18849
18850 if (mir->needsNegativeIntCheck()) {
18851 masm.jump(&trueBranch);
18852 masm.bind(&negativeIntCheck);
18853
18854 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
18855
18856 masm.jump(&falseBranch);
18857 }
18858 }
18859
18860 masm.bind(&trueBranch);
18861 masm.move32(Imm32(1), output);
18862 masm.jump(&done);
18863
18864 masm.bind(&falseBranch);
18865 masm.move32(Imm32(0), output);
18866 masm.bind(&done);
18867}
18868
18869void CodeGenerator::visitGuardElementNotHole(LGuardElementNotHole* lir) {
18870 Register elements = ToRegister(lir->elements());
18871 const LAllocation* index = lir->index();
18872
18873 Label testMagic;
18874 if (index->isConstant()) {
18875 Address address(elements, ToInt32(index) * sizeof(js::Value));
18876 masm.branchTestMagic(Assembler::Equal, address, &testMagic);
18877 } else {
18878 BaseObjectElementIndex address(elements, ToRegister(index));
18879 masm.branchTestMagic(Assembler::Equal, address, &testMagic);
18880 }
18881 bailoutFrom(&testMagic, lir->snapshot());
18882}
18883
18884void CodeGenerator::visitInstanceOfO(LInstanceOfO* ins) {
18885 Register protoReg = ToRegister(ins->rhs());
18886 emitInstanceOf(ins, protoReg);
18887}
18888
18889void CodeGenerator::visitInstanceOfV(LInstanceOfV* ins) {
18890 Register protoReg = ToRegister(ins->rhs());
18891 emitInstanceOf(ins, protoReg);
18892}
18893
18894void CodeGenerator::emitInstanceOf(LInstruction* ins, Register protoReg) {
18895 // This path implements fun_hasInstance when the function's prototype is
18896 // known to be the object in protoReg
18897
18898 Label done;
18899 Register output = ToRegister(ins->getDef(0));
18900
18901 // If the lhs is a primitive, the result is false.
18902 Register objReg;
18903 if (ins->isInstanceOfV()) {
18904 Label isObject;
18905 ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex);
18906 masm.branchTestObject(Assembler::Equal, lhsValue, &isObject);
18907 masm.mov(ImmWord(0), output);
18908 masm.jump(&done);
18909 masm.bind(&isObject);
18910 objReg = masm.extractObject(lhsValue, output);
18911 } else {
18912 objReg = ToRegister(ins->toInstanceOfO()->lhs());
18913 }
18914
18915 // Crawl the lhs's prototype chain in a loop to search for prototypeObject.
18916 // This follows the main loop of js::IsPrototypeOf, though additionally breaks
18917 // out of the loop on Proxy::LazyProto.
18918
18919 // Load the lhs's prototype.
18920 masm.loadObjProto(objReg, output);
18921
18922 Label testLazy;
18923 {
18924 Label loopPrototypeChain;
18925 masm.bind(&loopPrototypeChain);
18926
18927 // Test for the target prototype object.
18928 Label notPrototypeObject;
18929 masm.branchPtr(Assembler::NotEqual, output, protoReg, &notPrototypeObject);
18930 masm.mov(ImmWord(1), output);
18931 masm.jump(&done);
18932 masm.bind(&notPrototypeObject);
18933
18934 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 18934; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18935
18936 // Test for nullptr or Proxy::LazyProto
18937 masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy);
18938
18939 // Load the current object's prototype.
18940 masm.loadObjProto(output, output);
18941
18942 masm.jump(&loopPrototypeChain);
18943 }
18944
18945 // Make a VM call if an object with a lazy proto was found on the prototype
18946 // chain. This currently occurs only for cross compartment wrappers, which
18947 // we do not expect to be compared with non-wrapper functions from this
18948 // compartment. Otherwise, we stopped on a nullptr prototype and the output
18949 // register is already correct.
18950
18951 using Fn = bool (*)(JSContext*, HandleObject, JSObject*, bool*);
18952 auto* ool = oolCallVM<Fn, IsPrototypeOf>(ins, ArgList(protoReg, objReg),
18953 StoreRegisterTo(output));
18954
18955 // Regenerate the original lhs object for the VM call.
18956 Label regenerate, *lazyEntry;
18957 if (objReg != output) {
18958 lazyEntry = ool->entry();
18959 } else {
18960 masm.bind(&regenerate);
18961 lazyEntry = &regenerate;
18962 if (ins->isInstanceOfV()) {
18963 ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex);
18964 objReg = masm.extractObject(lhsValue, output);
18965 } else {
18966 objReg = ToRegister(ins->toInstanceOfO()->lhs());
18967 }
18968 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18968); AnnotateMozCrashReason("MOZ_ASSERT" "(" "objReg == output"
")"); do { *((volatile int*)__null) = 18968; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18969 masm.jump(ool->entry());
18970 }
18971
18972 masm.bind(&testLazy);
18973 masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry);
18974
18975 masm.bind(&done);
18976 masm.bind(ool->rejoin());
18977}
18978
18979void CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins) {
18980 // The Lowering ensures that RHS is an object, and that LHS is a value.
18981 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
18982 TypedOrValueRegister lhs =
18983 TypedOrValueRegister(ToValue(ins, LInstanceOfCache::LHS));
18984 Register rhs = ToRegister(ins->rhs());
18985 Register output = ToRegister(ins->output());
18986
18987 IonInstanceOfIC ic(liveRegs, lhs, rhs, output);
18988 addIC(ins, allocateIC(ic));
18989}
18990
18991void CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) {
18992 const Register JSContextReg = ToRegister(ins->getJSContextReg());
18993 const Register ObjectReg = ToRegister(ins->getObjectReg());
18994 const Register PrivateReg = ToRegister(ins->getPrivReg());
18995 const Register ValueReg = ToRegister(ins->getValueReg());
18996
18997 Label haveValue;
18998 if (ins->mir()->valueMayBeInSlot()) {
18999 size_t slot = ins->mir()->domMemberSlotIndex();
19000 // It's a bit annoying to redo these slot calculations, which duplcate
19001 // LSlots and a few other things like that, but I'm not sure there's a
19002 // way to reuse those here.
19003 //
19004 // If this ever gets fixed to work with proxies (by not assuming that
19005 // reserved slot indices, which is what domMemberSlotIndex() returns,
19006 // match fixed slot indices), we can reenable MGetDOMProperty for
19007 // proxies in IonBuilder.
19008 if (slot < NativeObject::MAX_FIXED_SLOTS) {
19009 masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)),
19010 JSReturnOperand);
19011 } else {
19012 // It's a dynamic slot.
19013 slot -= NativeObject::MAX_FIXED_SLOTS;
19014 // Use PrivateReg as a scratch register for the slots pointer.
19015 masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()),
19016 PrivateReg);
19017 masm.loadValue(Address(PrivateReg, slot * sizeof(js::Value)),
19018 JSReturnOperand);
19019 }
19020 masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue);
19021 }
19022
19023 DebugOnly<uint32_t> initialStack = masm.framePushed();
19024
19025 masm.checkStackAlignment();
19026
19027 // Make space for the outparam. Pre-initialize it to UndefinedValue so we
19028 // can trace it at GC time.
19029 masm.Push(UndefinedValue());
19030 // We pass the pointer to our out param as an instance of
19031 // JSJitGetterCallArgs, since on the binary level it's the same thing.
19032 static_assert(sizeof(JSJitGetterCallArgs) == sizeof(Value*));
19033 masm.moveStackPtrTo(ValueReg);
19034
19035 masm.Push(ObjectReg);
19036
19037 LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
19038
19039 // Rooting will happen at GC time.
19040 masm.moveStackPtrTo(ObjectReg);
19041
19042 Realm* getterRealm = ins->mir()->getterRealm();
19043 if (gen->realm->realmPtr() != getterRealm) {
19044 // We use JSContextReg as scratch register here.
19045 masm.switchToRealm(getterRealm, JSContextReg);
19046 }
19047
19048 uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
19049 masm.loadJSContext(JSContextReg);
19050 masm.enterFakeExitFrame(JSContextReg, JSContextReg,
19051 ExitFrameType::IonDOMGetter);
19052
19053 markSafepointAt(safepointOffset, ins);
19054
19055 masm.setupAlignedABICall();
19056 masm.loadJSContext(JSContextReg);
19057 masm.passABIArg(JSContextReg);
19058 masm.passABIArg(ObjectReg);
19059 masm.passABIArg(PrivateReg);
19060 masm.passABIArg(ValueReg);
19061 ensureOsiSpace();
19062 masm.callWithABI(DynamicFunction<JSJitGetterOp>(ins->mir()->fun()),
19063 ABIType::General,
19064 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
19065
19066 if (ins->mir()->isInfallible()) {
19067 masm.loadValue(Address(masm.getStackPointer(),
19068 IonDOMExitFrameLayout::offsetOfResult()),
19069 JSReturnOperand);
19070 } else {
19071 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
19072
19073 masm.loadValue(Address(masm.getStackPointer(),
19074 IonDOMExitFrameLayout::offsetOfResult()),
19075 JSReturnOperand);
19076 }
19077
19078 // Switch back to the current realm if needed. Note: if the getter threw an
19079 // exception, the exception handler will do this.
19080 if (gen->realm->realmPtr() != getterRealm) {
19081 static_assert(!JSReturnOperand.aliases(ReturnReg),
19082 "Clobbering ReturnReg should not affect the return value");
19083 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
19084 }
19085
19086 // Until C++ code is instrumented against Spectre, prevent speculative
19087 // execution from returning any private data.
19088 if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) {
19089 masm.speculationBarrier();
19090 }
19091
19092 masm.adjustStack(IonDOMExitFrameLayout::Size());
19093
19094 masm.bind(&haveValue);
19095
19096 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 19096; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19097}
19098
19099void CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins) {
19100 // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to
19101 // use an LLoadFixedSlotV or some subclass of it for this case: that would
19102 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
19103 // we'd have to duplicate a bunch of stuff we now get for free from
19104 // MGetDOMProperty.
19105 //
19106 // If this ever gets fixed to work with proxies (by not assuming that
19107 // reserved slot indices, which is what domMemberSlotIndex() returns,
19108 // match fixed slot indices), we can reenable MGetDOMMember for
19109 // proxies in IonBuilder.
19110 Register object = ToRegister(ins->object());
19111 size_t slot = ins->mir()->domMemberSlotIndex();
19112 ValueOperand result = ToOutValue(ins);
19113
19114 masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)),
19115 result);
19116}
19117
19118void CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT* ins) {
19119 // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to
19120 // use an LLoadFixedSlotT or some subclass of it for this case: that would
19121 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
19122 // we'd have to duplicate a bunch of stuff we now get for free from
19123 // MGetDOMProperty.
19124 //
19125 // If this ever gets fixed to work with proxies (by not assuming that
19126 // reserved slot indices, which is what domMemberSlotIndex() returns,
19127 // match fixed slot indices), we can reenable MGetDOMMember for
19128 // proxies in IonBuilder.
19129 Register object = ToRegister(ins->object());
19130 size_t slot = ins->mir()->domMemberSlotIndex();
19131 AnyRegister result = ToAnyRegister(ins->getDef(0));
19132 MIRType type = ins->mir()->type();
19133
19134 masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)),
19135 type, result);
19136}
19137
19138void CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) {
19139 const Register JSContextReg = ToRegister(ins->getJSContextReg());
19140 const Register ObjectReg = ToRegister(ins->getObjectReg());
19141 const Register PrivateReg = ToRegister(ins->getPrivReg());
19142 const Register ValueReg = ToRegister(ins->getValueReg());
19143
19144 DebugOnly<uint32_t> initialStack = masm.framePushed();
19145
19146 masm.checkStackAlignment();
19147
19148 // Push the argument. Rooting will happen at GC time.
19149 ValueOperand argVal = ToValue(ins, LSetDOMProperty::Value);
19150 masm.Push(argVal);
19151 // We pass the pointer to our out param as an instance of
19152 // JSJitGetterCallArgs, since on the binary level it's the same thing.
19153 static_assert(sizeof(JSJitSetterCallArgs) == sizeof(Value*));
19154 masm.moveStackPtrTo(ValueReg);
19155
19156 masm.Push(ObjectReg);
19157
19158 LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
19159
19160 // Rooting will happen at GC time.
19161 masm.moveStackPtrTo(ObjectReg);
19162
19163 Realm* setterRealm = ins->mir()->setterRealm();
19164 if (gen->realm->realmPtr() != setterRealm) {
19165 // We use JSContextReg as scratch register here.
19166 masm.switchToRealm(setterRealm, JSContextReg);
19167 }
19168
19169 uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
19170 masm.loadJSContext(JSContextReg);
19171 masm.enterFakeExitFrame(JSContextReg, JSContextReg,
19172 ExitFrameType::IonDOMSetter);
19173
19174 markSafepointAt(safepointOffset, ins);
19175
19176 masm.setupAlignedABICall();
19177 masm.loadJSContext(JSContextReg);
19178 masm.passABIArg(JSContextReg);
19179 masm.passABIArg(ObjectReg);
19180 masm.passABIArg(PrivateReg);
19181 masm.passABIArg(ValueReg);
19182 ensureOsiSpace();
19183 masm.callWithABI(DynamicFunction<JSJitSetterOp>(ins->mir()->fun()),
19184 ABIType::General,
19185 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
19186
19187 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
19188
19189 // Switch back to the current realm if needed. Note: if the setter threw an
19190 // exception, the exception handler will do this.
19191 if (gen->realm->realmPtr() != setterRealm) {
19192 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
19193 }
19194
19195 masm.adjustStack(IonDOMExitFrameLayout::Size());
19196
19197 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19197); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 19197; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19198}
19199
19200void CodeGenerator::visitLoadDOMExpandoValue(LLoadDOMExpandoValue* ins) {
19201 Register proxy = ToRegister(ins->proxy());
19202 ValueOperand out = ToOutValue(ins);
19203
19204 masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()),
19205 out.scratchReg());
19206 masm.loadValue(Address(out.scratchReg(),
19207 js::detail::ProxyReservedSlots::offsetOfPrivateSlot()),
19208 out);
19209}
19210
19211void CodeGenerator::visitLoadDOMExpandoValueGuardGeneration(
19212 LLoadDOMExpandoValueGuardGeneration* ins) {
19213 Register proxy = ToRegister(ins->proxy());
19214 ValueOperand out = ToOutValue(ins);
19215
19216 Label bail;
19217 masm.loadDOMExpandoValueGuardGeneration(proxy, out,
19218 ins->mir()->expandoAndGeneration(),
19219 ins->mir()->generation(), &bail);
19220 bailoutFrom(&bail, ins->snapshot());
19221}
19222
19223void CodeGenerator::visitLoadDOMExpandoValueIgnoreGeneration(
19224 LLoadDOMExpandoValueIgnoreGeneration* ins) {
19225 Register proxy = ToRegister(ins->proxy());
19226 ValueOperand out = ToOutValue(ins);
19227
19228 masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()),
19229 out.scratchReg());
19230
19231 // Load the ExpandoAndGeneration* from the PrivateValue.
19232 masm.loadPrivate(
19233 Address(out.scratchReg(),
19234 js::detail::ProxyReservedSlots::offsetOfPrivateSlot()),
19235 out.scratchReg());
19236
19237 // Load expandoAndGeneration->expando into the output Value register.
19238 masm.loadValue(
19239 Address(out.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), out);
19240}
19241
19242void CodeGenerator::visitGuardDOMExpandoMissingOrGuardShape(
19243 LGuardDOMExpandoMissingOrGuardShape* ins) {
19244 Register temp = ToRegister(ins->temp0());
19245 ValueOperand input =
19246 ToValue(ins, LGuardDOMExpandoMissingOrGuardShape::InputIndex);
19247
19248 Label done;
19249 masm.branchTestUndefined(Assembler::Equal, input, &done);
19250
19251 masm.debugAssertIsObject(input);
19252 masm.unboxObject(input, temp);
19253 // The expando object is not used in this case, so we don't need Spectre
19254 // mitigations.
19255 Label bail;
19256 masm.branchTestObjShapeNoSpectreMitigations(Assembler::NotEqual, temp,
19257 ins->mir()->shape(), &bail);
19258 bailoutFrom(&bail, ins->snapshot());
19259
19260 masm.bind(&done);
19261}
19262
19263class OutOfLineIsCallable : public OutOfLineCodeBase<CodeGenerator> {
19264 Register object_;
19265 Register output_;
19266
19267 public:
19268 OutOfLineIsCallable(Register object, Register output)
19269 : object_(object), output_(output) {}
19270
19271 void accept(CodeGenerator* codegen) override {
19272 codegen->visitOutOfLineIsCallable(this);
19273 }
19274 Register object() const { return object_; }
19275 Register output() const { return output_; }
19276};
19277
19278void CodeGenerator::visitIsCallableO(LIsCallableO* ins) {
19279 Register object = ToRegister(ins->object());
19280 Register output = ToRegister(ins->output());
19281
19282 OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(object, output);
19283 addOutOfLineCode(ool, ins->mir());
19284
19285 masm.isCallable(object, output, ool->entry());
19286
19287 masm.bind(ool->rejoin());
19288}
19289
19290void CodeGenerator::visitIsCallableV(LIsCallableV* ins) {
19291 ValueOperand val = ToValue(ins, LIsCallableV::ObjectIndex);
19292 Register output = ToRegister(ins->output());
19293 Register temp = ToRegister(ins->temp0());
19294
19295 Label notObject;
19296 masm.fallibleUnboxObject(val, temp, &notObject);
19297
19298 OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(temp, output);
19299 addOutOfLineCode(ool, ins->mir());
19300
19301 masm.isCallable(temp, output, ool->entry());
19302 masm.jump(ool->rejoin());
19303
19304 masm.bind(&notObject);
19305 masm.move32(Imm32(0), output);
19306
19307 masm.bind(ool->rejoin());
19308}
19309
19310void CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool) {
19311 Register object = ool->object();
19312 Register output = ool->output();
19313
19314 saveVolatile(output);
19315 using Fn = bool (*)(JSObject* obj);
19316 masm.setupAlignedABICall();
19317 masm.passABIArg(object);
19318 masm.callWithABI<Fn, ObjectIsCallable>();
19319 masm.storeCallBoolResult(output);
19320 restoreVolatile(output);
19321 masm.jump(ool->rejoin());
19322}
19323
19324class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator> {
19325 LIsConstructor* ins_;
19326
19327 public:
19328 explicit OutOfLineIsConstructor(LIsConstructor* ins) : ins_(ins) {}
19329
19330 void accept(CodeGenerator* codegen) override {
19331 codegen->visitOutOfLineIsConstructor(this);
19332 }
19333 LIsConstructor* ins() const { return ins_; }
19334};
19335
19336void CodeGenerator::visitIsConstructor(LIsConstructor* ins) {
19337 Register object = ToRegister(ins->object());
19338 Register output = ToRegister(ins->output());
19339
19340 OutOfLineIsConstructor* ool = new (alloc()) OutOfLineIsConstructor(ins);
19341 addOutOfLineCode(ool, ins->mir());
19342
19343 masm.isConstructor(object, output, ool->entry());
19344
19345 masm.bind(ool->rejoin());
19346}
19347
19348void CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool) {
19349 LIsConstructor* ins = ool->ins();
19350 Register object = ToRegister(ins->object());
19351 Register output = ToRegister(ins->output());
19352
19353 saveVolatile(output);
19354 using Fn = bool (*)(JSObject* obj);
19355 masm.setupAlignedABICall();
19356 masm.passABIArg(object);
19357 masm.callWithABI<Fn, ObjectIsConstructor>();
19358 masm.storeCallBoolResult(output);
19359 restoreVolatile(output);
19360 masm.jump(ool->rejoin());
19361}
19362
19363void CodeGenerator::visitIsCrossRealmArrayConstructor(
19364 LIsCrossRealmArrayConstructor* ins) {
19365 Register object = ToRegister(ins->object());
19366 Register output = ToRegister(ins->output());
19367
19368 masm.setIsCrossRealmArrayConstructor(object, output);
19369}
19370
19371static void EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool,
19372 Register obj, Register output,
19373 Label* notArray = nullptr) {
19374 masm.loadObjClassUnsafe(obj, output);
19375
19376 Label isArray;
19377 masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_),
19378 &isArray);
19379
19380 // Branch to OOL path if it's a proxy.
19381 masm.branchTestClassIsProxy(true, output, ool->entry());
19382
19383 if (notArray) {
19384 masm.bind(notArray);
19385 }
19386 masm.move32(Imm32(0), output);
19387 masm.jump(ool->rejoin());
19388
19389 masm.bind(&isArray);
19390 masm.move32(Imm32(1), output);
19391
19392 masm.bind(ool->rejoin());
19393}
19394
19395void CodeGenerator::visitIsArrayO(LIsArrayO* lir) {
19396 Register object = ToRegister(lir->object());
19397 Register output = ToRegister(lir->output());
19398
19399 using Fn = bool (*)(JSContext*, HandleObject, bool*);
19400 OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>(
19401 lir, ArgList(object), StoreRegisterTo(output));
19402 EmitObjectIsArray(masm, ool, object, output);
19403}
19404
19405void CodeGenerator::visitIsArrayV(LIsArrayV* lir) {
19406 ValueOperand val = ToValue(lir, LIsArrayV::ValueIndex);
19407 Register output = ToRegister(lir->output());
19408 Register temp = ToRegister(lir->temp0());
19409
19410 Label notArray;
19411 masm.fallibleUnboxObject(val, temp, &notArray);
19412
19413 using Fn = bool (*)(JSContext*, HandleObject, bool*);
19414 OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>(
19415 lir, ArgList(temp), StoreRegisterTo(output));
19416 EmitObjectIsArray(masm, ool, temp, output, &notArray);
19417}
19418
19419void CodeGenerator::visitIsTypedArray(LIsTypedArray* lir) {
19420 Register object = ToRegister(lir->object());
19421 Register output = ToRegister(lir->output());
19422
19423 OutOfLineCode* ool = nullptr;
19424 if (lir->mir()->isPossiblyWrapped()) {
19425 using Fn = bool (*)(JSContext*, JSObject*, bool*);
19426 ool = oolCallVM<Fn, jit::IsPossiblyWrappedTypedArray>(
19427 lir, ArgList(object), StoreRegisterTo(output));
19428 }
19429
19430 Label notTypedArray;
19431 Label done;
19432
19433 masm.loadObjClassUnsafe(object, output);
19434 masm.branchIfClassIsNotTypedArray(output, &notTypedArray);
19435
19436 masm.move32(Imm32(1), output);
19437 masm.jump(&done);
19438 masm.bind(&notTypedArray);
19439 if (ool) {
19440 masm.branchTestClassIsProxy(true, output, ool->entry());
19441 }
19442 masm.move32(Imm32(0), output);
19443 masm.bind(&done);
19444 if (ool) {
19445 masm.bind(ool->rejoin());
19446 }
19447}
19448
19449void CodeGenerator::visitIsObject(LIsObject* ins) {
19450 Register output = ToRegister(ins->output());
19451 ValueOperand value = ToValue(ins, LIsObject::ObjectIndex);
19452 masm.testObjectSet(Assembler::Equal, value, output);
19453}
19454
19455void CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins) {
19456 ValueOperand value = ToValue(ins, LIsObjectAndBranch::Input);
19457 testObjectEmitBranch(Assembler::Equal, value, ins->ifTrue(), ins->ifFalse());
19458}
19459
19460void CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins) {
19461 Register output = ToRegister(ins->output());
19462 ValueOperand value = ToValue(ins, LIsNullOrUndefined::InputIndex);
19463
19464 Label isNotNull, done;
19465 masm.branchTestNull(Assembler::NotEqual, value, &isNotNull);
19466
19467 masm.move32(Imm32(1), output);
19468 masm.jump(&done);
19469
19470 masm.bind(&isNotNull);
19471 masm.testUndefinedSet(Assembler::Equal, value, output);
19472
19473 masm.bind(&done);
19474}
19475
19476void CodeGenerator::visitIsNullOrUndefinedAndBranch(
19477 LIsNullOrUndefinedAndBranch* ins) {
19478 Label* ifTrue = getJumpLabelForBranch(ins->ifTrue());
19479 Label* ifFalse = getJumpLabelForBranch(ins->ifFalse());
19480 ValueOperand value = ToValue(ins, LIsNullOrUndefinedAndBranch::Input);
19481
19482 ScratchTagScope tag(masm, value);
19483 masm.splitTagForTest(value, tag);
19484
19485 masm.branchTestNull(Assembler::Equal, tag, ifTrue);
19486 masm.branchTestUndefined(Assembler::Equal, tag, ifTrue);
19487
19488 if (!isNextBlock(ins->ifFalse()->lir())) {
19489 masm.jump(ifFalse);
19490 }
19491}
19492
19493void CodeGenerator::loadOutermostJSScript(Register reg) {
19494 // The "outermost" JSScript means the script that we are compiling
19495 // basically; this is not always the script associated with the
19496 // current basic block, which might be an inlined script.
19497
19498 MIRGraph& graph = current->mir()->graph();
19499 MBasicBlock* entryBlock = graph.entryBlock();
19500 masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg);
19501}
19502
19503void CodeGenerator::loadJSScriptForBlock(MBasicBlock* block, Register reg) {
19504 // The current JSScript means the script for the current
19505 // basic block. This may be an inlined script.
19506
19507 JSScript* script = block->info().script();
19508 masm.movePtr(ImmGCPtr(script), reg);
19509}
19510
19511void CodeGenerator::visitHasClass(LHasClass* ins) {
19512 Register lhs = ToRegister(ins->lhs());
19513 Register output = ToRegister(ins->output());
19514
19515 masm.loadObjClassUnsafe(lhs, output);
19516 masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()),
19517 output);
19518}
19519
19520void CodeGenerator::visitGuardToClass(LGuardToClass* ins) {
19521 Register lhs = ToRegister(ins->lhs());
19522 Register temp = ToRegister(ins->temp0());
19523
19524 // branchTestObjClass may zero the object register on speculative paths
19525 // (we should have a defineReuseInput allocation in this case).
19526 Register spectreRegToZero = lhs;
19527
19528 Label notEqual;
19529
19530 masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(),
19531 temp, spectreRegToZero, &notEqual);
19532
19533 // Can't return null-return here, so bail.
19534 bailoutFrom(&notEqual, ins->snapshot());
19535}
19536
19537void CodeGenerator::visitGuardToEitherClass(LGuardToEitherClass* ins) {
19538 Register lhs = ToRegister(ins->lhs());
19539 Register temp = ToRegister(ins->temp0());
19540
19541 // branchTestObjClass may zero the object register on speculative paths
19542 // (we should have a defineReuseInput allocation in this case).
19543 Register spectreRegToZero = lhs;
19544
19545 Label notEqual;
19546
19547 masm.branchTestObjClass(Assembler::NotEqual, lhs,
19548 {ins->mir()->getClass1(), ins->mir()->getClass2()},
19549 temp, spectreRegToZero, &notEqual);
19550
19551 // Can't return null-return here, so bail.
19552 bailoutFrom(&notEqual, ins->snapshot());
19553}
19554
19555void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) {
19556 Register lhs = ToRegister(ins->lhs());
19557 Register temp = ToRegister(ins->temp0());
19558
19559 // branchTestObjClass may zero the object register on speculative paths
19560 // (we should have a defineReuseInput allocation in this case).
19561 Register spectreRegToZero = lhs;
19562
19563 Label notEqual;
19564
19565 masm.branchTestObjIsFunction(Assembler::NotEqual, lhs, temp, spectreRegToZero,
19566 &notEqual);
19567
19568 // Can't return null-return here, so bail.
19569 bailoutFrom(&notEqual, ins->snapshot());
19570}
19571
19572void CodeGenerator::visitObjectClassToString(LObjectClassToString* lir) {
19573 Register obj = ToRegister(lir->lhs());
19574 Register temp = ToRegister(lir->temp0());
19575
19576 using Fn = JSString* (*)(JSContext*, JSObject*);
19577 masm.setupAlignedABICall();
19578 masm.loadJSContext(temp);
19579 masm.passABIArg(temp);
19580 masm.passABIArg(obj);
19581 masm.callWithABI<Fn, js::ObjectClassToString>();
19582
19583 bailoutCmpPtr(Assembler::Equal, ReturnReg, ImmWord(0), lir->snapshot());
19584}
19585
19586void CodeGenerator::visitWasmParameter(LWasmParameter* lir) {}
19587
19588void CodeGenerator::visitWasmParameterI64(LWasmParameterI64* lir) {}
19589
19590void CodeGenerator::visitWasmReturn(LWasmReturn* lir) {
19591 // Don't emit a jump to the return label if this is the last block.
19592 if (current->mir() != *gen->graph().poBegin()) {
19593 masm.jump(&returnLabel_);
19594 }
19595}
19596
19597void CodeGenerator::visitWasmReturnI64(LWasmReturnI64* lir) {
19598 // Don't emit a jump to the return label if this is the last block.
19599 if (current->mir() != *gen->graph().poBegin()) {
19600 masm.jump(&returnLabel_);
19601 }
19602}
19603
19604void CodeGenerator::visitWasmReturnVoid(LWasmReturnVoid* lir) {
19605 // Don't emit a jump to the return label if this is the last block.
19606 if (current->mir() != *gen->graph().poBegin()) {
19607 masm.jump(&returnLabel_);
19608 }
19609}
19610
19611void CodeGenerator::emitAssertRangeI(MIRType type, const Range* r,
19612 Register input) {
19613 // Check the lower bound.
19614 if (r->hasInt32LowerBound() && r->lower() > INT32_MIN(-2147483647-1)) {
19615 Label success;
19616 if (type == MIRType::Int32 || type == MIRType::Boolean) {
19617 masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()),
19618 &success);
19619 } else {
19620 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19620); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 19620; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19621 masm.branchPtr(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()),
19622 &success);
19623 }
19624 masm.assumeUnreachable(
19625 "Integer input should be equal or higher than Lowerbound.");
19626 masm.bind(&success);
19627 }
19628
19629 // Check the upper bound.
19630 if (r->hasInt32UpperBound() && r->upper() < INT32_MAX(2147483647)) {
19631 Label success;
19632 if (type == MIRType::Int32 || type == MIRType::Boolean) {
19633 masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()),
19634 &success);
19635 } else {
19636 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 19636; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19637 masm.branchPtr(Assembler::LessThanOrEqual, input, Imm32(r->upper()),
19638 &success);
19639 }
19640 masm.assumeUnreachable(
19641 "Integer input should be lower or equal than Upperbound.");
19642 masm.bind(&success);
19643 }
19644
19645 // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and
19646 // r->exponent(), there's nothing to check, because if we ended up in the
19647 // integer range checking code, the value is already in an integer register
19648 // in the integer range.
19649}
19650
19651void CodeGenerator::emitAssertRangeD(const Range* r, FloatRegister input,
19652 FloatRegister temp) {
19653 // Check the lower bound.
19654 if (r->hasInt32LowerBound()) {
19655 Label success;
19656 masm.loadConstantDouble(r->lower(), temp);
19657 if (r->canBeNaN()) {
19658 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
19659 }
19660 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp,
19661 &success);
19662 masm.assumeUnreachable(
19663 "Double input should be equal or higher than Lowerbound.");
19664 masm.bind(&success);
19665 }
19666 // Check the upper bound.
19667 if (r->hasInt32UpperBound()) {
19668 Label success;
19669 masm.loadConstantDouble(r->upper(), temp);
19670 if (r->canBeNaN()) {
19671 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
19672 }
19673 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
19674 masm.assumeUnreachable(
19675 "Double input should be lower or equal than Upperbound.");
19676 masm.bind(&success);
19677 }
19678
19679 // This code does not yet check r->canHaveFractionalPart(). This would require
19680 // new assembler interfaces to make rounding instructions available.
19681
19682 if (!r->canBeNegativeZero()) {
19683 Label success;
19684
19685 // First, test for being equal to 0.0, which also includes -0.0.
19686 masm.loadConstantDouble(0.0, temp);
19687 masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp,
19688 &success);
19689
19690 // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is
19691 // -Infinity instead of Infinity.
19692 masm.loadConstantDouble(1.0, temp);
19693 masm.divDouble(input, temp);
19694 masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success);
19695
19696 masm.assumeUnreachable("Input shouldn't be negative zero.");
19697
19698 masm.bind(&success);
19699 }
19700
19701 if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() &&
19702 r->exponent() < FloatingPoint<double>::kExponentBias) {
19703 // Check the bounds implied by the maximum exponent.
19704 Label exponentLoOk;
19705 masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp);
19706 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
19707 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp,
19708 &exponentLoOk);
19709 masm.assumeUnreachable("Check for exponent failed.");
19710 masm.bind(&exponentLoOk);
19711
19712 Label exponentHiOk;
19713 masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp);
19714 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk);
19715 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp,
19716 &exponentHiOk);
19717 masm.assumeUnreachable("Check for exponent failed.");
19718 masm.bind(&exponentHiOk);
19719 } else if (!r->hasInt32Bounds() && !r->canBeNaN()) {
19720 // If we think the value can't be NaN, check that it isn't.
19721 Label notnan;
19722 masm.branchDouble(Assembler::DoubleOrdered, input, input, &notnan);
19723 masm.assumeUnreachable("Input shouldn't be NaN.");
19724 masm.bind(&notnan);
19725
19726 // If we think the value also can't be an infinity, check that it isn't.
19727 if (!r->canBeInfiniteOrNaN()) {
19728 Label notposinf;
19729 masm.loadConstantDouble(PositiveInfinity<double>(), temp);
19730 masm.branchDouble(Assembler::DoubleLessThan, input, temp, &notposinf);
19731 masm.assumeUnreachable("Input shouldn't be +Inf.");
19732 masm.bind(&notposinf);
19733
19734 Label notneginf;
19735 masm.loadConstantDouble(NegativeInfinity<double>(), temp);
19736 masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, &notneginf);
19737 masm.assumeUnreachable("Input shouldn't be -Inf.");
19738 masm.bind(&notneginf);
19739 }
19740 }
19741}
19742
19743void CodeGenerator::visitAssertClass(LAssertClass* ins) {
19744 Register obj = ToRegister(ins->input());
19745 Register temp = ToRegister(ins->getTemp(0));
19746
19747 Label success;
19748 if (ins->mir()->getClass() == &FunctionClass) {
19749 // Allow both possible function classes here.
19750 masm.branchTestObjIsFunctionNoSpectreMitigations(Assembler::Equal, obj,
19751 temp, &success);
19752 } else {
19753 masm.branchTestObjClassNoSpectreMitigations(
19754 Assembler::Equal, obj, ins->mir()->getClass(), temp, &success);
19755 }
19756 masm.assumeUnreachable("Wrong KnownClass during run-time");
19757 masm.bind(&success);
19758}
19759
19760void CodeGenerator::visitAssertShape(LAssertShape* ins) {
19761 Register obj = ToRegister(ins->input());
19762
19763 Label success;
19764 masm.branchTestObjShapeNoSpectreMitigations(Assembler::Equal, obj,
19765 ins->mir()->shape(), &success);
19766 masm.assumeUnreachable("Wrong Shape during run-time");
19767 masm.bind(&success);
19768}
19769
19770void CodeGenerator::visitAssertRangeI(LAssertRangeI* ins) {
19771 Register input = ToRegister(ins->input());
19772 const Range* r = ins->range();
19773
19774 emitAssertRangeI(ins->mir()->input()->type(), r, input);
19775}
19776
19777void CodeGenerator::visitAssertRangeD(LAssertRangeD* ins) {
19778 FloatRegister input = ToFloatRegister(ins->input());
19779 FloatRegister temp = ToFloatRegister(ins->temp());
19780 const Range* r = ins->range();
19781
19782 emitAssertRangeD(r, input, temp);
19783}
19784
19785void CodeGenerator::visitAssertRangeF(LAssertRangeF* ins) {
19786 FloatRegister input = ToFloatRegister(ins->input());
19787 FloatRegister temp = ToFloatRegister(ins->temp());
19788 FloatRegister temp2 = ToFloatRegister(ins->temp2());
19789
19790 const Range* r = ins->range();
19791
19792 masm.convertFloat32ToDouble(input, temp);
19793 emitAssertRangeD(r, temp, temp2);
19794}
19795
19796void CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) {
19797 const Range* r = ins->range();
19798 const ValueOperand value = ToValue(ins, LAssertRangeV::Input);
19799 Label done;
19800
19801 {
19802 ScratchTagScope tag(masm, value);
19803 masm.splitTagForTest(value, tag);
19804
19805 {
19806 Label isNotInt32;
19807 masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32);
19808 {
19809 ScratchTagScopeRelease _(&tag);
19810 Register unboxInt32 = ToTempUnboxRegister(ins->temp());
19811 Register input = masm.extractInt32(value, unboxInt32);
19812 emitAssertRangeI(MIRType::Int32, r, input);
19813 masm.jump(&done);
19814 }
19815 masm.bind(&isNotInt32);
19816 }
19817
19818 {
19819 Label isNotDouble;
19820 masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble);
19821 {
19822 ScratchTagScopeRelease _(&tag);
19823 FloatRegister input = ToFloatRegister(ins->floatTemp1());
19824 FloatRegister temp = ToFloatRegister(ins->floatTemp2());
19825 masm.unboxDouble(value, input);
19826 emitAssertRangeD(r, input, temp);
19827 masm.jump(&done);
19828 }
19829 masm.bind(&isNotDouble);
19830 }
19831 }
19832
19833 masm.assumeUnreachable("Incorrect range for Value.");
19834 masm.bind(&done);
19835}
19836
19837void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) {
19838 using Fn = bool (*)(JSContext*);
19839 OutOfLineCode* ool =
19840 oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing());
19841
19842 const void* interruptAddr = gen->runtime->addressOfInterruptBits();
19843 masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0),
19844 ool->entry());
19845 masm.bind(ool->rejoin());
19846}
19847
19848void CodeGenerator::visitOutOfLineResumableWasmTrap(
19849 OutOfLineResumableWasmTrap* ool) {
19850 LInstruction* lir = ool->lir();
19851 masm.wasmTrap(ool->trap(), ool->bytecodeOffset());
19852
19853 markSafepointAt(masm.currentOffset(), lir);
19854
19855 // Note that masm.framePushed() doesn't include the register dump area.
19856 // That will be taken into account when the StackMap is created from the
19857 // LSafepoint.
19858 lir->safepoint()->setFramePushedAtStackMapBase(ool->framePushed());
19859 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::Trap);
19860
19861 masm.jump(ool->rejoin());
19862}
19863
19864void CodeGenerator::visitOutOfLineAbortingWasmTrap(
19865 OutOfLineAbortingWasmTrap* ool) {
19866 masm.wasmTrap(ool->trap(), ool->bytecodeOffset());
19867}
19868
19869void CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir) {
19870 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19870); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 19870; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19871
19872 OutOfLineResumableWasmTrap* ool = new (alloc()) OutOfLineResumableWasmTrap(
19873 lir, masm.framePushed(), lir->mir()->bytecodeOffset(),
19874 wasm::Trap::CheckInterrupt);
19875 addOutOfLineCode(ool, lir->mir());
19876 masm.branch32(
19877 Assembler::NotEqual,
19878 Address(ToRegister(lir->instance()), wasm::Instance::offsetOfInterrupt()),
19879 Imm32(0), ool->entry());
19880 masm.bind(ool->rejoin());
19881}
19882
19883void CodeGenerator::visitWasmTrap(LWasmTrap* lir) {
19884 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19884); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 19884; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19885 const MWasmTrap* mir = lir->mir();
19886
19887 masm.wasmTrap(mir->trap(), mir->bytecodeOffset());
19888}
19889
19890void CodeGenerator::visitWasmTrapIfNull(LWasmTrapIfNull* lir) {
19891 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 19891; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19892 const MWasmTrapIfNull* mir = lir->mir();
19893 Label nonNull;
19894 Register ref = ToRegister(lir->ref());
19895
19896 masm.branchWasmAnyRefIsNull(false, ref, &nonNull);
19897 masm.wasmTrap(mir->trap(), mir->bytecodeOffset());
19898 masm.bind(&nonNull);
19899}
19900
19901void CodeGenerator::visitWasmRefIsSubtypeOfAbstract(
19902 LWasmRefIsSubtypeOfAbstract* ins) {
19903 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19903); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 19903; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19904
19905 const MWasmRefIsSubtypeOfAbstract* mir = ins->mir();
19906 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19906); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()"
")"); do { *((volatile int*)__null) = 19906; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19907
19908 Register ref = ToRegister(ins->ref());
19909 Register superSTV = Register::Invalid();
19910 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
19911 Register scratch2 = Register::Invalid();
19912 Register result = ToRegister(ins->output());
19913 Label onSuccess;
19914 Label onFail;
19915 Label join;
19916 masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(),
19917 &onSuccess, /*onSuccess=*/true, superSTV,
19918 scratch1, scratch2);
19919 masm.bind(&onFail);
19920 masm.xor32(result, result);
19921 masm.jump(&join);
19922 masm.bind(&onSuccess);
19923 masm.move32(Imm32(1), result);
19924 masm.bind(&join);
19925}
19926
19927void CodeGenerator::visitWasmRefIsSubtypeOfConcrete(
19928 LWasmRefIsSubtypeOfConcrete* ins) {
19929 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19929); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 19929; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19930
19931 const MWasmRefIsSubtypeOfConcrete* mir = ins->mir();
19932 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19932); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()"
")"); do { *((volatile int*)__null) = 19932; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19933
19934 Register ref = ToRegister(ins->ref());
19935 Register superSTV = ToRegister(ins->superSTV());
19936 Register scratch1 = ToRegister(ins->temp0());
19937 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
19938 Register result = ToRegister(ins->output());
19939 Label onSuccess;
19940 Label join;
19941 masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(),
19942 &onSuccess, /*onSuccess=*/true, superSTV,
19943 scratch1, scratch2);
19944 masm.move32(Imm32(0), result);
19945 masm.jump(&join);
19946 masm.bind(&onSuccess);
19947 masm.move32(Imm32(1), result);
19948 masm.bind(&join);
19949}
19950
19951void CodeGenerator::visitWasmRefIsSubtypeOfAbstractAndBranch(
19952 LWasmRefIsSubtypeOfAbstractAndBranch* ins) {
19953 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19953); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 19953; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19954 Register ref = ToRegister(ins->ref());
19955 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
19956 Label* onSuccess = getJumpLabelForBranch(ins->ifTrue());
19957 Label* onFail = getJumpLabelForBranch(ins->ifFalse());
19958 masm.branchWasmRefIsSubtype(
19959 ref, ins->sourceType(), ins->destType(), onSuccess, /*onSuccess=*/true,
19960 Register::Invalid(), scratch1, Register::Invalid());
19961 masm.jump(onFail);
19962}
19963
19964void CodeGenerator::visitWasmRefIsSubtypeOfConcreteAndBranch(
19965 LWasmRefIsSubtypeOfConcreteAndBranch* ins) {
19966 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 19966; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19967 Register ref = ToRegister(ins->ref());
19968 Register superSTV = ToRegister(ins->superSTV());
19969 Register scratch1 = ToRegister(ins->temp0());
19970 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
19971 Label* onSuccess = getJumpLabelForBranch(ins->ifTrue());
19972 Label* onFail = getJumpLabelForBranch(ins->ifFalse());
19973 masm.branchWasmRefIsSubtype(ref, ins->sourceType(), ins->destType(),
19974 onSuccess, /*onSuccess=*/true, superSTV, scratch1,
19975 scratch2);
19976 masm.jump(onFail);
19977}
19978
19979void CodeGenerator::callWasmStructAllocFun(LInstruction* lir,
19980 wasm::SymbolicAddress fun,
19981 Register typeDefData,
19982 Register output) {
19983 masm.Push(InstanceReg);
19984 int32_t framePushedAfterInstance = masm.framePushed();
19985 saveLive(lir);
19986
19987 masm.setupWasmABICall();
19988 masm.passABIArg(InstanceReg);
19989 masm.passABIArg(typeDefData);
19990 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
19991 CodeOffset offset =
19992 masm.callWithABI(wasm::BytecodeOffset(0), fun,
19993 mozilla::Some(instanceOffset), ABIType::General);
19994 masm.storeCallPointerResult(output);
19995
19996 markSafepointAt(offset.offset(), lir);
19997 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance);
19998 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall);
19999
20000 restoreLive(lir);
20001 masm.Pop(InstanceReg);
20002#if JS_CODEGEN_ARM64
20003 masm.syncStackPtr();
20004#endif
20005}
20006
20007// Out-of-line path to allocate wasm GC structs
20008class OutOfLineWasmNewStruct : public OutOfLineCodeBase<CodeGenerator> {
20009 LInstruction* lir_;
20010 wasm::SymbolicAddress fun_;
20011 Register typeDefData_;
20012 Register output_;
20013
20014 public:
20015 OutOfLineWasmNewStruct(LInstruction* lir, wasm::SymbolicAddress fun,
20016 Register typeDefData, Register output)
20017 : lir_(lir), fun_(fun), typeDefData_(typeDefData), output_(output) {}
20018
20019 void accept(CodeGenerator* codegen) override {
20020 codegen->visitOutOfLineWasmNewStruct(this);
20021 }
20022
20023 LInstruction* lir() const { return lir_; }
20024 wasm::SymbolicAddress fun() const { return fun_; }
20025 Register typeDefData() const { return typeDefData_; }
20026 Register output() const { return output_; }
20027};
20028
20029void CodeGenerator::visitOutOfLineWasmNewStruct(OutOfLineWasmNewStruct* ool) {
20030 callWasmStructAllocFun(ool->lir(), ool->fun(), ool->typeDefData(),
20031 ool->output());
20032 masm.jump(ool->rejoin());
20033}
20034
20035void CodeGenerator::visitWasmNewStructObject(LWasmNewStructObject* lir) {
20036 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20036); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20036; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20037
20038 MWasmNewStructObject* mir = lir->mir();
20039
20040 Register typeDefData = ToRegister(lir->typeDefData());
20041 Register output = ToRegister(lir->output());
20042
20043 if (mir->isOutline()) {
20044 wasm::SymbolicAddress fun = mir->zeroFields()
20045 ? wasm::SymbolicAddress::StructNewOOL_true
20046 : wasm::SymbolicAddress::StructNewOOL_false;
20047 callWasmStructAllocFun(lir, fun, typeDefData, output);
20048 } else {
20049 wasm::SymbolicAddress fun = mir->zeroFields()
20050 ? wasm::SymbolicAddress::StructNewIL_true
20051 : wasm::SymbolicAddress::StructNewIL_false;
20052
20053 Register instance = ToRegister(lir->instance());
20054 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20054); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg"
")"); do { *((volatile int*)__null) = 20054; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20055
20056 auto ool =
20057 new (alloc()) OutOfLineWasmNewStruct(lir, fun, typeDefData, output);
20058 addOutOfLineCode(ool, lir->mir());
20059
20060 Register temp1 = ToRegister(lir->temp0());
20061 Register temp2 = ToRegister(lir->temp1());
20062 masm.wasmNewStructObject(instance, output, typeDefData, temp1, temp2,
20063 ool->entry(), mir->allocKind(), mir->zeroFields());
20064
20065 masm.bind(ool->rejoin());
20066 }
20067}
20068
20069void CodeGenerator::callWasmArrayAllocFun(LInstruction* lir,
20070 wasm::SymbolicAddress fun,
20071 Register numElements,
20072 Register typeDefData, Register output,
20073 wasm::BytecodeOffset bytecodeOffset) {
20074 masm.Push(InstanceReg);
20075 int32_t framePushedAfterInstance = masm.framePushed();
20076 saveLive(lir);
20077
20078 masm.setupWasmABICall();
20079 masm.passABIArg(InstanceReg);
20080 masm.passABIArg(numElements);
20081 masm.passABIArg(typeDefData);
20082 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
20083 CodeOffset offset = masm.callWithABI(
20084 bytecodeOffset, fun, mozilla::Some(instanceOffset), ABIType::General);
20085 masm.storeCallPointerResult(output);
20086
20087 markSafepointAt(offset.offset(), lir);
20088 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance);
20089 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall);
20090
20091 restoreLive(lir);
20092 masm.Pop(InstanceReg);
20093#if JS_CODEGEN_ARM64
20094 masm.syncStackPtr();
20095#endif
20096
20097 Label ok;
20098 masm.branchPtr(Assembler::NonZero, output, ImmWord(0), &ok);
20099 masm.wasmTrap(wasm::Trap::ThrowReported, bytecodeOffset);
20100 masm.bind(&ok);
20101}
20102
20103// Out-of-line path to allocate wasm GC arrays
20104class OutOfLineWasmNewArray : public OutOfLineCodeBase<CodeGenerator> {
20105 LInstruction* lir_;
20106 wasm::SymbolicAddress fun_;
20107 Register numElementsReg_;
20108 mozilla::Maybe<uint32_t> numElements_;
20109 Register typeDefData_;
20110 Register output_;
20111 wasm::BytecodeOffset bytecodeOffset_;
20112
20113 public:
20114 OutOfLineWasmNewArray(LInstruction* lir, wasm::SymbolicAddress fun,
20115 Register numElementsReg,
20116 mozilla::Maybe<uint32_t> numElements,
20117 Register typeDefData, Register output,
20118 wasm::BytecodeOffset bytecodeOffset)
20119 : lir_(lir),
20120 fun_(fun),
20121 numElementsReg_(numElementsReg),
20122 numElements_(numElements),
20123 typeDefData_(typeDefData),
20124 output_(output),
20125 bytecodeOffset_(bytecodeOffset) {}
20126
20127 void accept(CodeGenerator* codegen) override {
20128 codegen->visitOutOfLineWasmNewArray(this);
20129 }
20130
20131 LInstruction* lir() const { return lir_; }
20132 wasm::SymbolicAddress fun() const { return fun_; }
20133 Register numElementsReg() const { return numElementsReg_; }
20134 mozilla::Maybe<uint32_t> numElements() const { return numElements_; }
20135 Register typeDefData() const { return typeDefData_; }
20136 Register output() const { return output_; }
20137 wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; }
20138};
20139
20140void CodeGenerator::visitOutOfLineWasmNewArray(OutOfLineWasmNewArray* ool) {
20141 if (ool->numElements().isSome()) {
20142 masm.move32(Imm32(ool->numElements().value()), ool->numElementsReg());
20143 }
20144 callWasmArrayAllocFun(ool->lir(), ool->fun(), ool->numElementsReg(),
20145 ool->typeDefData(), ool->output(),
20146 ool->bytecodeOffset());
20147 masm.jump(ool->rejoin());
20148}
20149
20150void CodeGenerator::visitWasmNewArrayObject(LWasmNewArrayObject* lir) {
20151 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20151); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20151; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20152
20153 MWasmNewArrayObject* mir = lir->mir();
20154
20155 Register typeDefData = ToRegister(lir->typeDefData());
20156 Register output = ToRegister(lir->output());
20157 Register temp1 = ToRegister(lir->temp0());
20158 Register temp2 = ToRegister(lir->temp1());
20159
20160 wasm::SymbolicAddress fun = mir->zeroFields()
20161 ? wasm::SymbolicAddress::ArrayNew_true
20162 : wasm::SymbolicAddress::ArrayNew_false;
20163
20164 if (lir->numElements()->isConstant()) {
20165 // numElements is constant, so we can do optimized code generation.
20166 uint32_t numElements = lir->numElements()->toConstant()->toInt32();
20167 CheckedUint32 storageBytes =
20168 WasmArrayObject::calcStorageBytesChecked(mir->elemSize(), numElements);
20169 if (!storageBytes.isValid() ||
20170 storageBytes.value() > WasmArrayObject_MaxInlineBytes) {
20171 // Too much array data to store inline. Immediately perform an instance
20172 // call to handle the out-of-line storage (or the trap).
20173 masm.move32(Imm32(numElements), temp1);
20174 callWasmArrayAllocFun(lir, fun, temp1, typeDefData, output,
20175 mir->bytecodeOffset());
20176 } else {
20177 // storageBytes is small enough to be stored inline in WasmArrayObject.
20178 // Attempt a nursery allocation and fall back to an instance call if it
20179 // fails.
20180 Register instance = ToRegister(lir->instance());
20181 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg"
")"); do { *((volatile int*)__null) = 20181; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20182
20183 auto ool = new (alloc())
20184 OutOfLineWasmNewArray(lir, fun, temp1, mozilla::Some(numElements),
20185 typeDefData, output, mir->bytecodeOffset());
20186 addOutOfLineCode(ool, lir->mir());
20187
20188 masm.wasmNewArrayObjectFixed(instance, output, typeDefData, temp1, temp2,
20189 ool->entry(), numElements,
20190 storageBytes.value(), mir->zeroFields());
20191
20192 masm.bind(ool->rejoin());
20193 }
20194 } else {
20195 // numElements is dynamic. Attempt a dynamic inline-storage nursery
20196 // allocation and fall back to an instance call if it fails.
20197 Register instance = ToRegister(lir->instance());
20198 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg"
")"); do { *((volatile int*)__null) = 20198; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20199 Register numElements = ToRegister(lir->numElements());
20200
20201 auto ool = new (alloc())
20202 OutOfLineWasmNewArray(lir, fun, numElements, mozilla::Nothing(),
20203 typeDefData, output, mir->bytecodeOffset());
20204 addOutOfLineCode(ool, lir->mir());
20205
20206 masm.wasmNewArrayObject(instance, output, numElements, typeDefData, temp1,
20207 ool->entry(), mir->elemSize(), mir->zeroFields());
20208
20209 masm.bind(ool->rejoin());
20210 }
20211}
20212
20213void CodeGenerator::visitWasmHeapReg(LWasmHeapReg* ins) {
20214#ifdef WASM_HAS_HEAPREG1
20215 masm.movePtr(HeapReg, ToRegister(ins->output()));
20216#else
20217 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20217); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 20217; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
20218#endif
20219}
20220
20221void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) {
20222 const MWasmBoundsCheck* mir = ins->mir();
20223 Register ptr = ToRegister(ins->ptr());
20224 Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit());
20225 // When there are no spectre mitigations in place, branching out-of-line to
20226 // the trap is a big performance win, but with mitigations it's trickier. See
20227 // bug 1680243.
20228 if (JitOptions.spectreIndexMasking) {
20229 Label ok;
20230 masm.wasmBoundsCheck32(Assembler::Below, ptr, boundsCheckLimit, &ok);
20231 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset());
20232 masm.bind(&ok);
20233 } else {
20234 OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap(
20235 mir->bytecodeOffset(), wasm::Trap::OutOfBounds);
20236 addOutOfLineCode(ool, mir);
20237 masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
20238 ool->entry());
20239 }
20240}
20241
20242void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) {
20243 const MWasmBoundsCheck* mir = ins->mir();
20244 Register64 ptr = ToRegister64(ins->ptr());
20245 Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit());
20246 // See above.
20247 if (JitOptions.spectreIndexMasking) {
20248 Label ok;
20249 masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok);
20250 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset());
20251 masm.bind(&ok);
20252 } else {
20253 OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap(
20254 mir->bytecodeOffset(), wasm::Trap::OutOfBounds);
20255 addOutOfLineCode(ool, mir);
20256 masm.wasmBoundsCheck64(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
20257 ool->entry());
20258 }
20259}
20260
20261void CodeGenerator::visitWasmBoundsCheckRange32(LWasmBoundsCheckRange32* ins) {
20262 const MWasmBoundsCheckRange32* mir = ins->mir();
20263 Register index = ToRegister(ins->index());
20264 Register length = ToRegister(ins->length());
20265 Register limit = ToRegister(ins->limit());
20266 Register tmp = ToRegister(ins->temp0());
20267
20268 masm.wasmBoundsCheckRange32(index, length, limit, tmp, mir->bytecodeOffset());
20269}
20270
20271void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) {
20272 const MWasmAlignmentCheck* mir = ins->mir();
20273 Register ptr = ToRegister(ins->ptr());
20274 OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap(
20275 mir->bytecodeOffset(), wasm::Trap::UnalignedAccess);
20276 addOutOfLineCode(ool, mir);
20277 masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1),
20278 ool->entry());
20279}
20280
20281void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) {
20282 const MWasmAlignmentCheck* mir = ins->mir();
20283 Register64 ptr = ToRegister64(ins->ptr());
20284#ifdef JS_64BIT1
20285 Register r = ptr.reg;
20286#else
20287 Register r = ptr.low;
20288#endif
20289 OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap(
20290 mir->bytecodeOffset(), wasm::Trap::UnalignedAccess);
20291 addOutOfLineCode(ool, mir);
20292 masm.branchTestPtr(Assembler::NonZero, r, Imm32(mir->byteSize() - 1),
20293 ool->entry());
20294}
20295
20296void CodeGenerator::visitWasmLoadInstance(LWasmLoadInstance* ins) {
20297 switch (ins->mir()->type()) {
20298 case MIRType::WasmAnyRef:
20299 case MIRType::Pointer:
20300 masm.loadPtr(Address(ToRegister(ins->instance()), ins->mir()->offset()),
20301 ToRegister(ins->output()));
20302 break;
20303 case MIRType::Int32:
20304 masm.load32(Address(ToRegister(ins->instance()), ins->mir()->offset()),
20305 ToRegister(ins->output()));
20306 break;
20307 default:
20308 MOZ_CRASH("MIRType not supported in WasmLoadInstance")do { do { } while (false); MOZ_ReportCrash("" "MIRType not supported in WasmLoadInstance"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20308); AnnotateMozCrashReason("MOZ_CRASH(" "MIRType not supported in WasmLoadInstance"
")"); do { *((volatile int*)__null) = 20308; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
20309 }
20310}
20311
20312void CodeGenerator::visitWasmLoadInstance64(LWasmLoadInstance64* ins) {
20313 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 20313; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20314 masm.load64(Address(ToRegister(ins->instance()), ins->mir()->offset()),
20315 ToOutRegister64(ins));
20316}
20317
20318void CodeGenerator::incrementWarmUpCounter(AbsoluteAddress warmUpCount,
20319 JSScript* script, Register tmp) {
20320 // The code depends on the JitScript* not being discarded without also
20321 // invalidating Ion code. Assert this.
20322#ifdef DEBUG1
20323 Label ok;
20324 masm.movePtr(ImmGCPtr(script), tmp);
20325 masm.loadJitScript(tmp, tmp);
20326 masm.branchPtr(Assembler::Equal, tmp, ImmPtr(script->jitScript()), &ok);
20327 masm.assumeUnreachable("Didn't find JitScript?");
20328 masm.bind(&ok);
20329#endif
20330
20331 masm.load32(warmUpCount, tmp);
20332 masm.add32(Imm32(1), tmp);
20333 masm.store32(tmp, warmUpCount);
20334}
20335
20336void CodeGenerator::visitIncrementWarmUpCounter(LIncrementWarmUpCounter* ins) {
20337 Register tmp = ToRegister(ins->temp0());
20338
20339 AbsoluteAddress warmUpCount =
20340 AbsoluteAddress(ins->mir()->script()->jitScript())
20341 .offset(JitScript::offsetOfWarmUpCount());
20342 incrementWarmUpCounter(warmUpCount, ins->mir()->script(), tmp);
20343}
20344
20345void CodeGenerator::visitLexicalCheck(LLexicalCheck* ins) {
20346 ValueOperand inputValue = ToValue(ins, LLexicalCheck::InputIndex);
20347 Label bail;
20348 masm.branchTestMagicValue(Assembler::Equal, inputValue,
20349 JS_UNINITIALIZED_LEXICAL, &bail);
20350 bailoutFrom(&bail, ins->snapshot());
20351}
20352
20353void CodeGenerator::visitThrowRuntimeLexicalError(
20354 LThrowRuntimeLexicalError* ins) {
20355 pushArg(Imm32(ins->mir()->errorNumber()));
20356
20357 using Fn = bool (*)(JSContext*, unsigned);
20358 callVM<Fn, jit::ThrowRuntimeLexicalError>(ins);
20359}
20360
20361void CodeGenerator::visitThrowMsg(LThrowMsg* ins) {
20362 pushArg(Imm32(static_cast<int32_t>(ins->mir()->throwMsgKind())));
20363
20364 using Fn = bool (*)(JSContext*, unsigned);
20365 callVM<Fn, js::ThrowMsgOperation>(ins);
20366}
20367
20368void CodeGenerator::visitGlobalDeclInstantiation(
20369 LGlobalDeclInstantiation* ins) {
20370 pushArg(ImmPtr(ins->mir()->resumePoint()->pc()));
20371 pushArg(ImmGCPtr(ins->mir()->block()->info().script()));
20372
20373 using Fn = bool (*)(JSContext*, HandleScript, const jsbytecode*);
20374 callVM<Fn, GlobalDeclInstantiationFromIon>(ins);
20375}
20376
20377void CodeGenerator::visitDebugger(LDebugger* ins) {
20378 Register cx = ToRegister(ins->temp0());
20379
20380 masm.loadJSContext(cx);
20381 using Fn = bool (*)(JSContext* cx);
20382 masm.setupAlignedABICall();
20383 masm.passABIArg(cx);
20384 masm.callWithABI<Fn, GlobalHasLiveOnDebuggerStatement>();
20385
20386 Label bail;
20387 masm.branchIfTrueBool(ReturnReg, &bail);
20388 bailoutFrom(&bail, ins->snapshot());
20389}
20390
20391void CodeGenerator::visitNewTarget(LNewTarget* ins) {
20392 ValueOperand output = ToOutValue(ins);
20393
20394 // if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)]
20395 Label notConstructing, done;
20396 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
20397 masm.branchTestPtr(Assembler::Zero, calleeToken,
20398 Imm32(CalleeToken_FunctionConstructing), &notConstructing);
20399
20400 Register argvLen = output.scratchReg();
20401 masm.loadNumActualArgs(FramePointer, argvLen);
20402
20403 Label useNFormals;
20404
20405 size_t numFormalArgs = ins->mirRaw()->block()->info().nargs();
20406 masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs), &useNFormals);
20407
20408 size_t argsOffset = JitFrameLayout::offsetOfActualArgs();
20409 {
20410 BaseValueIndex newTarget(FramePointer, argvLen, argsOffset);
20411 masm.loadValue(newTarget, output);
20412 masm.jump(&done);
20413 }
20414
20415 masm.bind(&useNFormals);
20416
20417 {
20418 Address newTarget(FramePointer,
20419 argsOffset + (numFormalArgs * sizeof(Value)));
20420 masm.loadValue(newTarget, output);
20421 masm.jump(&done);
20422 }
20423
20424 // else output = undefined
20425 masm.bind(&notConstructing);
20426 masm.moveValue(UndefinedValue(), output);
20427 masm.bind(&done);
20428}
20429
20430void CodeGenerator::visitCheckReturn(LCheckReturn* ins) {
20431 ValueOperand returnValue = ToValue(ins, LCheckReturn::ReturnValueIndex);
20432 ValueOperand thisValue = ToValue(ins, LCheckReturn::ThisValueIndex);
20433 ValueOperand output = ToOutValue(ins);
20434
20435 using Fn = bool (*)(JSContext*, HandleValue);
20436 OutOfLineCode* ool = oolCallVM<Fn, ThrowBadDerivedReturnOrUninitializedThis>(
20437 ins, ArgList(returnValue), StoreNothing());
20438
20439 Label noChecks;
20440 masm.branchTestObject(Assembler::Equal, returnValue, &noChecks);
20441 masm.branchTestUndefined(Assembler::NotEqual, returnValue, ool->entry());
20442 masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry());
20443 masm.moveValue(thisValue, output);
20444 masm.jump(ool->rejoin());
20445 masm.bind(&noChecks);
20446 masm.moveValue(returnValue, output);
20447 masm.bind(ool->rejoin());
20448}
20449
20450void CodeGenerator::visitCheckIsObj(LCheckIsObj* ins) {
20451 ValueOperand value = ToValue(ins, LCheckIsObj::ValueIndex);
20452 Register output = ToRegister(ins->output());
20453
20454 using Fn = bool (*)(JSContext*, CheckIsObjectKind);
20455 OutOfLineCode* ool = oolCallVM<Fn, ThrowCheckIsObject>(
20456 ins, ArgList(Imm32(ins->mir()->checkKind())), StoreNothing());
20457
20458 masm.fallibleUnboxObject(value, output, ool->entry());
20459 masm.bind(ool->rejoin());
20460}
20461
20462void CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) {
20463 ValueOperand checkValue = ToValue(ins, LCheckObjCoercible::ValueIndex);
20464
20465 using Fn = bool (*)(JSContext*, HandleValue);
20466 OutOfLineCode* ool = oolCallVM<Fn, ThrowObjectCoercible>(
20467 ins, ArgList(checkValue), StoreNothing());
20468 masm.branchTestNull(Assembler::Equal, checkValue, ool->entry());
20469 masm.branchTestUndefined(Assembler::Equal, checkValue, ool->entry());
20470 masm.bind(ool->rejoin());
20471}
20472
20473void CodeGenerator::visitCheckClassHeritage(LCheckClassHeritage* ins) {
20474 ValueOperand heritage = ToValue(ins, LCheckClassHeritage::HeritageIndex);
20475 Register temp0 = ToRegister(ins->temp0());
20476 Register temp1 = ToRegister(ins->temp1());
20477
20478 using Fn = bool (*)(JSContext*, HandleValue);
20479 OutOfLineCode* ool = oolCallVM<Fn, CheckClassHeritageOperation>(
20480 ins, ArgList(heritage), StoreNothing());
20481
20482 masm.branchTestNull(Assembler::Equal, heritage, ool->rejoin());
20483 masm.fallibleUnboxObject(heritage, temp0, ool->entry());
20484
20485 masm.isConstructor(temp0, temp1, ool->entry());
20486 masm.branchTest32(Assembler::Zero, temp1, temp1, ool->entry());
20487
20488 masm.bind(ool->rejoin());
20489}
20490
20491void CodeGenerator::visitCheckThis(LCheckThis* ins) {
20492 ValueOperand thisValue = ToValue(ins, LCheckThis::ValueIndex);
20493
20494 using Fn = bool (*)(JSContext*);
20495 OutOfLineCode* ool =
20496 oolCallVM<Fn, ThrowUninitializedThis>(ins, ArgList(), StoreNothing());
20497 masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry());
20498 masm.bind(ool->rejoin());
20499}
20500
20501void CodeGenerator::visitCheckThisReinit(LCheckThisReinit* ins) {
20502 ValueOperand thisValue = ToValue(ins, LCheckThisReinit::ThisValueIndex);
20503
20504 using Fn = bool (*)(JSContext*);
20505 OutOfLineCode* ool =
20506 oolCallVM<Fn, ThrowInitializedThis>(ins, ArgList(), StoreNothing());
20507 masm.branchTestMagic(Assembler::NotEqual, thisValue, ool->entry());
20508 masm.bind(ool->rejoin());
20509}
20510
20511void CodeGenerator::visitGenerator(LGenerator* lir) {
20512 Register callee = ToRegister(lir->callee());
20513 Register environmentChain = ToRegister(lir->environmentChain());
20514 Register argsObject = ToRegister(lir->argsObject());
20515
20516 pushArg(argsObject);
20517 pushArg(environmentChain);
20518 pushArg(ImmGCPtr(current->mir()->info().script()));
20519 pushArg(callee);
20520
20521 using Fn = JSObject* (*)(JSContext* cx, HandleFunction, HandleScript,
20522 HandleObject, HandleObject);
20523 callVM<Fn, CreateGenerator>(lir);
20524}
20525
20526void CodeGenerator::visitAsyncResolve(LAsyncResolve* lir) {
20527 Register generator = ToRegister(lir->generator());
20528 ValueOperand value = ToValue(lir, LAsyncResolve::ValueIndex);
20529
20530 pushArg(value);
20531 pushArg(generator);
20532
20533 using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>,
20534 HandleValue);
20535 callVM<Fn, js::AsyncFunctionResolve>(lir);
20536}
20537
20538void CodeGenerator::visitAsyncReject(LAsyncReject* lir) {
20539 Register generator = ToRegister(lir->generator());
20540 ValueOperand reason = ToValue(lir, LAsyncReject::ReasonIndex);
20541 ValueOperand stack = ToValue(lir, LAsyncReject::StackIndex);
20542
20543 pushArg(stack);
20544 pushArg(reason);
20545 pushArg(generator);
20546
20547 using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>,
20548 HandleValue, HandleValue);
20549 callVM<Fn, js::AsyncFunctionReject>(lir);
20550}
20551
20552void CodeGenerator::visitAsyncAwait(LAsyncAwait* lir) {
20553 ValueOperand value = ToValue(lir, LAsyncAwait::ValueIndex);
20554 Register generator = ToRegister(lir->generator());
20555
20556 pushArg(value);
20557 pushArg(generator);
20558
20559 using Fn =
20560 JSObject* (*)(JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj,
20561 HandleValue value);
20562 callVM<Fn, js::AsyncFunctionAwait>(lir);
20563}
20564
20565void CodeGenerator::visitCanSkipAwait(LCanSkipAwait* lir) {
20566 ValueOperand value = ToValue(lir, LCanSkipAwait::ValueIndex);
20567
20568 pushArg(value);
20569
20570 using Fn = bool (*)(JSContext*, HandleValue, bool* canSkip);
20571 callVM<Fn, js::CanSkipAwait>(lir);
20572}
20573
20574void CodeGenerator::visitMaybeExtractAwaitValue(LMaybeExtractAwaitValue* lir) {
20575 ValueOperand value = ToValue(lir, LMaybeExtractAwaitValue::ValueIndex);
20576 ValueOperand output = ToOutValue(lir);
20577 Register canSkip = ToRegister(lir->canSkip());
20578
20579 Label cantExtract, finished;
20580 masm.branchIfFalseBool(canSkip, &cantExtract);
20581
20582 pushArg(value);
20583
20584 using Fn = bool (*)(JSContext*, HandleValue, MutableHandleValue);
20585 callVM<Fn, js::ExtractAwaitValue>(lir);
20586 masm.jump(&finished);
20587 masm.bind(&cantExtract);
20588
20589 masm.moveValue(value, output);
20590
20591 masm.bind(&finished);
20592}
20593
20594void CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) {
20595 ValueOperand checkValue = ToValue(ins, LDebugCheckSelfHosted::ValueIndex);
20596 pushArg(checkValue);
20597 using Fn = bool (*)(JSContext*, HandleValue);
20598 callVM<Fn, js::Debug_CheckSelfHosted>(ins);
20599}
20600
20601void CodeGenerator::visitRandom(LRandom* ins) {
20602 using mozilla::non_crypto::XorShift128PlusRNG;
20603
20604 FloatRegister output = ToFloatRegister(ins->output());
20605 Register rngReg = ToRegister(ins->temp0());
20606
20607 Register64 temp1 = ToRegister64(ins->temp1());
20608 Register64 temp2 = ToRegister64(ins->temp2());
20609
20610 const XorShift128PlusRNG* rng = gen->realm->addressOfRandomNumberGenerator();
20611 masm.movePtr(ImmPtr(rng), rngReg);
20612
20613 masm.randomDouble(rngReg, output, temp1, temp2);
20614 if (js::SupportDifferentialTesting()) {
20615 masm.loadConstantDouble(0.0, output);
20616 }
20617}
20618
20619void CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins) {
20620 Register input = ToRegister(ins->input());
20621 Register output = ToRegister(ins->output());
20622
20623 switch (ins->mode()) {
20624 case MSignExtendInt32::Byte:
20625 masm.move8SignExtend(input, output);
20626 break;
20627 case MSignExtendInt32::Half:
20628 masm.move16SignExtend(input, output);
20629 break;
20630 }
20631}
20632
20633void CodeGenerator::visitRotate(LRotate* ins) {
20634 MRotate* mir = ins->mir();
20635 Register input = ToRegister(ins->input());
20636 Register dest = ToRegister(ins->output());
20637
20638 const LAllocation* count = ins->count();
20639 if (count->isConstant()) {
20640 int32_t c = ToInt32(count) & 0x1F;
20641 if (mir->isLeftRotate()) {
20642 masm.rotateLeft(Imm32(c), input, dest);
20643 } else {
20644 masm.rotateRight(Imm32(c), input, dest);
20645 }
20646 } else {
20647 Register creg = ToRegister(count);
20648 if (mir->isLeftRotate()) {
20649 masm.rotateLeft(creg, input, dest);
20650 } else {
20651 masm.rotateRight(creg, input, dest);
20652 }
20653 }
20654}
20655
20656class OutOfLineNaNToZero : public OutOfLineCodeBase<CodeGenerator> {
20657 LNaNToZero* lir_;
20658
20659 public:
20660 explicit OutOfLineNaNToZero(LNaNToZero* lir) : lir_(lir) {}
20661
20662 void accept(CodeGenerator* codegen) override {
20663 codegen->visitOutOfLineNaNToZero(this);
20664 }
20665 LNaNToZero* lir() const { return lir_; }
20666};
20667
20668void CodeGenerator::visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool) {
20669 FloatRegister output = ToFloatRegister(ool->lir()->output());
20670 masm.loadConstantDouble(0.0, output);
20671 masm.jump(ool->rejoin());
20672}
20673
20674void CodeGenerator::visitNaNToZero(LNaNToZero* lir) {
20675 FloatRegister input = ToFloatRegister(lir->input());
20676
20677 OutOfLineNaNToZero* ool = new (alloc()) OutOfLineNaNToZero(lir);
20678 addOutOfLineCode(ool, lir->mir());
20679
20680 if (lir->mir()->operandIsNeverNegativeZero()) {
20681 masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry());
20682 } else {
20683 FloatRegister scratch = ToFloatRegister(lir->temp0());
20684 masm.loadConstantDouble(0.0, scratch);
20685 masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch,
20686 ool->entry());
20687 }
20688 masm.bind(ool->rejoin());
20689}
20690
20691void CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) {
20692 Register obj = ToRegister(lir->object());
20693 Register output = ToRegister(lir->output());
20694 Register temp = ToRegister(lir->temp0());
20695
20696 masm.setIsPackedArray(obj, output, temp);
20697}
20698
20699void CodeGenerator::visitGuardArrayIsPacked(LGuardArrayIsPacked* lir) {
20700 Register array = ToRegister(lir->array());
20701 Register temp0 = ToRegister(lir->temp0());
20702 Register temp1 = ToRegister(lir->temp1());
20703
20704 Label bail;
20705 masm.branchArrayIsNotPacked(array, temp0, temp1, &bail);
20706 bailoutFrom(&bail, lir->snapshot());
20707}
20708
20709void CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) {
20710 Register target = ToRegister(lir->target());
20711 ValueOperand out = ToOutValue(lir);
20712 Register scratch = out.scratchReg();
20713
20714 using Fn = bool (*)(JSContext*, HandleObject, MutableHandleValue);
20715 OutOfLineCode* ool = oolCallVM<Fn, jit::GetPrototypeOf>(lir, ArgList(target),
20716 StoreValueTo(out));
20717
20718 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20718); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 20718; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20719
20720 masm.loadObjProto(target, scratch);
20721
20722 Label hasProto;
20723 masm.branchPtr(Assembler::Above, scratch, ImmWord(1), &hasProto);
20724
20725 // Call into the VM for lazy prototypes.
20726 masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), ool->entry());
20727
20728 masm.moveValue(NullValue(), out);
20729 masm.jump(ool->rejoin());
20730
20731 masm.bind(&hasProto);
20732 masm.tagValue(JSVAL_TYPE_OBJECT, scratch, out);
20733
20734 masm.bind(ool->rejoin());
20735}
20736
20737void CodeGenerator::visitObjectWithProto(LObjectWithProto* lir) {
20738 pushArg(ToValue(lir, LObjectWithProto::PrototypeIndex));
20739
20740 using Fn = PlainObject* (*)(JSContext*, HandleValue);
20741 callVM<Fn, js::ObjectWithProtoOperation>(lir);
20742}
20743
20744void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) {
20745 Register obj = ToRegister(lir->input());
20746 Register output = ToRegister(lir->output());
20747
20748 masm.loadObjProto(obj, output);
20749
20750#ifdef DEBUG1
20751 // We shouldn't encounter a null or lazy proto.
20752 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20752); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 20752; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20753
20754 Label done;
20755 masm.branchPtr(Assembler::Above, output, ImmWord(1), &done);
20756 masm.assumeUnreachable("Unexpected null or lazy proto in MObjectStaticProto");
20757 masm.bind(&done);
20758#endif
20759}
20760
20761void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) {
20762 pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind())));
20763
20764 using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind);
20765 callVM<Fn, js::BuiltinObjectOperation>(lir);
20766}
20767
20768void CodeGenerator::visitSuperFunction(LSuperFunction* lir) {
20769 Register callee = ToRegister(lir->callee());
20770 ValueOperand out = ToOutValue(lir);
20771 Register temp = ToRegister(lir->temp0());
20772
20773#ifdef DEBUG1
20774 Label classCheckDone;
20775 masm.branchTestObjIsFunction(Assembler::Equal, callee, temp, callee,
20776 &classCheckDone);
20777 masm.assumeUnreachable("Unexpected non-JSFunction callee in JSOp::SuperFun");
20778 masm.bind(&classCheckDone);
20779#endif
20780
20781 // Load prototype of callee
20782 masm.loadObjProto(callee, temp);
20783
20784#ifdef DEBUG1
20785 // We won't encounter a lazy proto, because |callee| is guaranteed to be a
20786 // JSFunction and only proxy objects can have a lazy proto.
20787 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20787); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 20787; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20788
20789 Label proxyCheckDone;
20790 masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone);
20791 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperFun");
20792 masm.bind(&proxyCheckDone);
20793#endif
20794
20795 Label nullProto, done;
20796 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
20797
20798 // Box prototype and return
20799 masm.tagValue(JSVAL_TYPE_OBJECT, temp, out);
20800 masm.jump(&done);
20801
20802 masm.bind(&nullProto);
20803 masm.moveValue(NullValue(), out);
20804
20805 masm.bind(&done);
20806}
20807
20808void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) {
20809 Register func = ToRegister(lir->function());
20810 ValueOperand homeObject = ToValue(lir, LInitHomeObject::HomeObjectIndex);
20811
20812 masm.assertFunctionIsExtended(func);
20813
20814 Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
20815
20816 emitPreBarrier(addr);
20817 masm.storeValue(homeObject, addr);
20818}
20819
20820void CodeGenerator::visitIsTypedArrayConstructor(
20821 LIsTypedArrayConstructor* lir) {
20822 Register object = ToRegister(lir->object());
20823 Register output = ToRegister(lir->output());
20824
20825 masm.setIsDefinitelyTypedArrayConstructor(object, output);
20826}
20827
20828void CodeGenerator::visitLoadValueTag(LLoadValueTag* lir) {
20829 ValueOperand value = ToValue(lir, LLoadValueTag::ValueIndex);
20830 Register output = ToRegister(lir->output());
20831
20832 Register tag = masm.extractTag(value, output);
20833 if (tag != output) {
20834 masm.mov(tag, output);
20835 }
20836}
20837
20838void CodeGenerator::visitGuardTagNotEqual(LGuardTagNotEqual* lir) {
20839 Register lhs = ToRegister(lir->lhs());
20840 Register rhs = ToRegister(lir->rhs());
20841
20842 bailoutCmp32(Assembler::Equal, lhs, rhs, lir->snapshot());
20843
20844 // If both lhs and rhs are numbers, can't use tag comparison to do inequality
20845 // comparison
20846 Label done;
20847 masm.branchTestNumber(Assembler::NotEqual, lhs, &done);
20848 masm.branchTestNumber(Assembler::NotEqual, rhs, &done);
20849 bailout(lir->snapshot());
20850
20851 masm.bind(&done);
20852}
20853
20854void CodeGenerator::visitLoadWrapperTarget(LLoadWrapperTarget* lir) {
20855 Register object = ToRegister(lir->object());
20856 Register output = ToRegister(lir->output());
20857
20858 masm.loadPtr(Address(object, ProxyObject::offsetOfReservedSlots()), output);
20859
20860 // Bail for revoked proxies.
20861 Label bail;
20862 Address targetAddr(output,
20863 js::detail::ProxyReservedSlots::offsetOfPrivateSlot());
20864 if (lir->mir()->fallible()) {
20865 masm.fallibleUnboxObject(targetAddr, output, &bail);
20866 bailoutFrom(&bail, lir->snapshot());
20867 } else {
20868 masm.unboxObject(targetAddr, output);
20869 }
20870}
20871
20872void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) {
20873 Register object = ToRegister(lir->object());
20874 Register temp0 = ToRegister(lir->temp0());
20875 Register temp1 = ToRegister(lir->temp1());
20876 Register temp2 = ToRegister(lir->temp2());
20877
20878 masm.movePropertyKey(lir->mir()->propId(), temp1);
20879 masm.movePtr(ImmGCPtr(lir->mir()->getterSetter()), temp2);
20880
20881 using Fn = bool (*)(JSContext* cx, JSObject* obj, jsid id,
20882 GetterSetter* getterSetter);
20883 masm.setupAlignedABICall();
20884 masm.loadJSContext(temp0);
20885 masm.passABIArg(temp0);
20886 masm.passABIArg(object);
20887 masm.passABIArg(temp1);
20888 masm.passABIArg(temp2);
20889 masm.callWithABI<Fn, ObjectHasGetterSetterPure>();
20890
20891 bailoutIfFalseBool(ReturnReg, lir->snapshot());
20892}
20893
20894void CodeGenerator::visitGuardIsExtensible(LGuardIsExtensible* lir) {
20895 Register object = ToRegister(lir->object());
20896 Register temp = ToRegister(lir->temp0());
20897
20898 Label bail;
20899 masm.branchIfObjectNotExtensible(object, temp, &bail);
20900 bailoutFrom(&bail, lir->snapshot());
20901}
20902
20903void CodeGenerator::visitGuardInt32IsNonNegative(
20904 LGuardInt32IsNonNegative* lir) {
20905 Register index = ToRegister(lir->index());
20906
20907 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
20908}
20909
20910void CodeGenerator::visitGuardInt32Range(LGuardInt32Range* lir) {
20911 Register input = ToRegister(lir->input());
20912
20913 bailoutCmp32(Assembler::LessThan, input, Imm32(lir->mir()->minimum()),
20914 lir->snapshot());
20915 bailoutCmp32(Assembler::GreaterThan, input, Imm32(lir->mir()->maximum()),
20916 lir->snapshot());
20917}
20918
20919void CodeGenerator::visitGuardIndexIsNotDenseElement(
20920 LGuardIndexIsNotDenseElement* lir) {
20921 Register object = ToRegister(lir->object());
20922 Register index = ToRegister(lir->index());
20923 Register temp = ToRegister(lir->temp0());
20924 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
20925
20926 // Load obj->elements.
20927 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
20928
20929 // Ensure index >= initLength or the element is a hole.
20930 Label notDense;
20931 Address capacity(temp, ObjectElements::offsetOfInitializedLength());
20932 masm.spectreBoundsCheck32(index, capacity, spectreTemp, &notDense);
20933
20934 BaseValueIndex element(temp, index);
20935 masm.branchTestMagic(Assembler::Equal, element, &notDense);
20936
20937 bailout(lir->snapshot());
20938
20939 masm.bind(&notDense);
20940}
20941
20942void CodeGenerator::visitGuardIndexIsValidUpdateOrAdd(
20943 LGuardIndexIsValidUpdateOrAdd* lir) {
20944 Register object = ToRegister(lir->object());
20945 Register index = ToRegister(lir->index());
20946 Register temp = ToRegister(lir->temp0());
20947 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
20948
20949 // Load obj->elements.
20950 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
20951
20952 Label success;
20953
20954 // If length is writable, branch to &success. All indices are writable.
20955 Address flags(temp, ObjectElements::offsetOfFlags());
20956 masm.branchTest32(Assembler::Zero, flags,
20957 Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH),
20958 &success);
20959
20960 // Otherwise, ensure index is in bounds.
20961 Label bail;
20962 Address length(temp, ObjectElements::offsetOfLength());
20963 masm.spectreBoundsCheck32(index, length, spectreTemp, &bail);
20964 masm.bind(&success);
20965
20966 bailoutFrom(&bail, lir->snapshot());
20967}
20968
20969void CodeGenerator::visitCallAddOrUpdateSparseElement(
20970 LCallAddOrUpdateSparseElement* lir) {
20971 Register object = ToRegister(lir->object());
20972 Register index = ToRegister(lir->index());
20973 ValueOperand value = ToValue(lir, LCallAddOrUpdateSparseElement::ValueIndex);
20974
20975 pushArg(Imm32(lir->mir()->strict()));
20976 pushArg(value);
20977 pushArg(index);
20978 pushArg(object);
20979
20980 using Fn =
20981 bool (*)(JSContext*, Handle<NativeObject*>, int32_t, HandleValue, bool);
20982 callVM<Fn, js::AddOrUpdateSparseElementHelper>(lir);
20983}
20984
20985void CodeGenerator::visitCallGetSparseElement(LCallGetSparseElement* lir) {
20986 Register object = ToRegister(lir->object());
20987 Register index = ToRegister(lir->index());
20988
20989 pushArg(index);
20990 pushArg(object);
20991
20992 using Fn =
20993 bool (*)(JSContext*, Handle<NativeObject*>, int32_t, MutableHandleValue);
20994 callVM<Fn, js::GetSparseElementHelper>(lir);
20995}
20996
20997void CodeGenerator::visitCallNativeGetElement(LCallNativeGetElement* lir) {
20998 Register object = ToRegister(lir->object());
20999 Register index = ToRegister(lir->index());
21000
21001 pushArg(index);
21002 pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
21003 pushArg(object);
21004
21005 using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t,
21006 MutableHandleValue);
21007 callVM<Fn, js::NativeGetElement>(lir);
21008}
21009
21010void CodeGenerator::visitCallNativeGetElementSuper(
21011 LCallNativeGetElementSuper* lir) {
21012 Register object = ToRegister(lir->object());
21013 Register index = ToRegister(lir->index());
21014 ValueOperand receiver =
21015 ToValue(lir, LCallNativeGetElementSuper::ReceiverIndex);
21016
21017 pushArg(index);
21018 pushArg(receiver);
21019 pushArg(object);
21020
21021 using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t,
21022 MutableHandleValue);
21023 callVM<Fn, js::NativeGetElement>(lir);
21024}
21025
21026void CodeGenerator::visitCallObjectHasSparseElement(
21027 LCallObjectHasSparseElement* lir) {
21028 Register object = ToRegister(lir->object());
21029 Register index = ToRegister(lir->index());
21030 Register temp0 = ToRegister(lir->temp0());
21031 Register temp1 = ToRegister(lir->temp1());
21032 Register output = ToRegister(lir->output());
21033
21034 masm.reserveStack(sizeof(Value));
21035 masm.moveStackPtrTo(temp1);
21036
21037 using Fn = bool (*)(JSContext*, NativeObject*, int32_t, Value*);
21038 masm.setupAlignedABICall();
21039 masm.loadJSContext(temp0);
21040 masm.passABIArg(temp0);
21041 masm.passABIArg(object);
21042 masm.passABIArg(index);
21043 masm.passABIArg(temp1);
21044 masm.callWithABI<Fn, HasNativeElementPure>();
21045 masm.storeCallPointerResult(temp0);
21046
21047 Label bail, ok;
21048 uint32_t framePushed = masm.framePushed();
21049 masm.branchIfTrueBool(temp0, &ok);
21050 masm.adjustStack(sizeof(Value));
21051 masm.jump(&bail);
21052
21053 masm.bind(&ok);
21054 masm.setFramePushed(framePushed);
21055 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
21056 masm.adjustStack(sizeof(Value));
21057
21058 bailoutFrom(&bail, lir->snapshot());
21059}
21060
21061void CodeGenerator::visitBigIntAsIntN(LBigIntAsIntN* ins) {
21062 Register bits = ToRegister(ins->bits());
21063 Register input = ToRegister(ins->input());
21064
21065 pushArg(bits);
21066 pushArg(input);
21067
21068 using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t);
21069 callVM<Fn, jit::BigIntAsIntN>(ins);
21070}
21071
21072void CodeGenerator::visitBigIntAsIntN64(LBigIntAsIntN64* ins) {
21073 Register input = ToRegister(ins->input());
21074 Register temp = ToRegister(ins->temp());
21075 Register64 temp64 = ToRegister64(ins->temp64());
21076 Register output = ToRegister(ins->output());
21077
21078 Label done, create;
21079
21080 masm.movePtr(input, output);
21081
21082 // Load the BigInt value as an int64.
21083 masm.loadBigInt64(input, temp64);
21084
21085 // Create a new BigInt when the input exceeds the int64 range.
21086 masm.branch32(Assembler::Above, Address(input, BigInt::offsetOfLength()),
21087 Imm32(64 / BigInt::DigitBits), &create);
21088
21089 // And create a new BigInt when the value and the BigInt have different signs.
21090 Label nonNegative;
21091 masm.branchIfBigIntIsNonNegative(input, &nonNegative);
21092 masm.branchTest64(Assembler::NotSigned, temp64, temp64, temp, &create);
21093 masm.jump(&done);
21094
21095 masm.bind(&nonNegative);
21096 masm.branchTest64(Assembler::NotSigned, temp64, temp64, temp, &done);
21097
21098 masm.bind(&create);
21099 emitCreateBigInt(ins, Scalar::BigInt64, temp64, output, temp);
21100
21101 masm.bind(&done);
21102}
21103
21104void CodeGenerator::visitBigIntAsIntN32(LBigIntAsIntN32* ins) {
21105 Register input = ToRegister(ins->input());
21106 Register temp = ToRegister(ins->temp());
21107 Register64 temp64 = ToRegister64(ins->temp64());
21108 Register output = ToRegister(ins->output());
21109
21110 Label done, create;
21111
21112 masm.movePtr(input, output);
21113
21114 // Load the absolute value of the first digit.
21115 masm.loadFirstBigIntDigitOrZero(input, temp);
21116
21117 // If the absolute value exceeds the int32 range, create a new BigInt.
21118 masm.branchPtr(Assembler::Above, temp, Imm32(INT32_MAX(2147483647)), &create);
21119
21120 // Also create a new BigInt if we have more than one digit.
21121 masm.branch32(Assembler::BelowOrEqual,
21122 Address(input, BigInt::offsetOfLength()), Imm32(1), &done);
21123
21124 masm.bind(&create);
21125
21126 // |temp| stores the absolute value, negate it when the sign flag is set.
21127 Label nonNegative;
21128 masm.branchIfBigIntIsNonNegative(input, &nonNegative);
21129 masm.negPtr(temp);
21130 masm.bind(&nonNegative);
21131
21132 masm.move32To64SignExtend(temp, temp64);
21133 emitCreateBigInt(ins, Scalar::BigInt64, temp64, output, temp);
21134
21135 masm.bind(&done);
21136}
21137
21138void CodeGenerator::visitBigIntAsUintN(LBigIntAsUintN* ins) {
21139 Register bits = ToRegister(ins->bits());
21140 Register input = ToRegister(ins->input());
21141
21142 pushArg(bits);
21143 pushArg(input);
21144
21145 using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t);
21146 callVM<Fn, jit::BigIntAsUintN>(ins);
21147}
21148
21149void CodeGenerator::visitBigIntAsUintN64(LBigIntAsUintN64* ins) {
21150 Register input = ToRegister(ins->input());
21151 Register temp = ToRegister(ins->temp());
21152 Register64 temp64 = ToRegister64(ins->temp64());
21153 Register output = ToRegister(ins->output());
21154
21155 Label done, create;
21156
21157 masm.movePtr(input, output);
21158
21159 // Load the BigInt value as an uint64.
21160 masm.loadBigInt64(input, temp64);
21161
21162 // Create a new BigInt when the input exceeds the uint64 range.
21163 masm.branch32(Assembler::Above, Address(input, BigInt::offsetOfLength()),
21164 Imm32(64 / BigInt::DigitBits), &create);
21165
21166 // And create a new BigInt when the input has the sign flag set.
21167 masm.branchIfBigIntIsNonNegative(input, &done);
21168
21169 masm.bind(&create);
21170 emitCreateBigInt(ins, Scalar::BigUint64, temp64, output, temp);
21171
21172 masm.bind(&done);
21173}
21174
21175void CodeGenerator::visitBigIntAsUintN32(LBigIntAsUintN32* ins) {
21176 Register input = ToRegister(ins->input());
21177 Register temp = ToRegister(ins->temp());
21178 Register64 temp64 = ToRegister64(ins->temp64());
21179 Register output = ToRegister(ins->output());
21180
21181 Label done, create;
21182
21183 masm.movePtr(input, output);
21184
21185 // Load the absolute value of the first digit.
21186 masm.loadFirstBigIntDigitOrZero(input, temp);
21187
21188 // If the absolute value exceeds the uint32 range, create a new BigInt.
21189#if JS_PUNBOX641
21190 masm.branchPtr(Assembler::Above, temp, ImmWord(UINT32_MAX(4294967295U)), &create);
21191#endif
21192
21193 // Also create a new BigInt if we have more than one digit.
21194 masm.branch32(Assembler::Above, Address(input, BigInt::offsetOfLength()),
21195 Imm32(1), &create);
21196
21197 // And create a new BigInt when the input has the sign flag set.
21198 masm.branchIfBigIntIsNonNegative(input, &done);
21199
21200 masm.bind(&create);
21201
21202 // |temp| stores the absolute value, negate it when the sign flag is set.
21203 Label nonNegative;
21204 masm.branchIfBigIntIsNonNegative(input, &nonNegative);
21205 masm.negPtr(temp);
21206 masm.bind(&nonNegative);
21207
21208 masm.move32To64ZeroExtend(temp, temp64);
21209 emitCreateBigInt(ins, Scalar::BigUint64, temp64, output, temp);
21210
21211 masm.bind(&done);
21212}
21213
21214void CodeGenerator::visitGuardNonGCThing(LGuardNonGCThing* ins) {
21215 ValueOperand input = ToValue(ins, LGuardNonGCThing::InputIndex);
21216
21217 Label bail;
21218 masm.branchTestGCThing(Assembler::Equal, input, &bail);
21219 bailoutFrom(&bail, ins->snapshot());
21220}
21221
21222void CodeGenerator::visitToHashableNonGCThing(LToHashableNonGCThing* ins) {
21223 ValueOperand input = ToValue(ins, LToHashableNonGCThing::InputIndex);
21224 FloatRegister tempFloat = ToFloatRegister(ins->temp0());
21225 ValueOperand output = ToOutValue(ins);
21226
21227 masm.toHashableNonGCThing(input, output, tempFloat);
21228}
21229
21230void CodeGenerator::visitToHashableString(LToHashableString* ins) {
21231 Register input = ToRegister(ins->input());
21232 Register output = ToRegister(ins->output());
21233
21234 using Fn = JSAtom* (*)(JSContext*, JSString*);
21235 auto* ool = oolCallVM<Fn, js::AtomizeString>(ins, ArgList(input),
21236 StoreRegisterTo(output));
21237
21238 Label isAtom;
21239 masm.branchTest32(Assembler::NonZero,
21240 Address(input, JSString::offsetOfFlags()),
21241 Imm32(JSString::ATOM_BIT), &isAtom);
21242
21243 masm.tryFastAtomize(input, output, output, ool->entry());
21244 masm.jump(ool->rejoin());
21245 masm.bind(&isAtom);
21246 masm.movePtr(input, output);
21247 masm.bind(ool->rejoin());
21248}
21249
21250void CodeGenerator::visitToHashableValue(LToHashableValue* ins) {
21251 ValueOperand input = ToValue(ins, LToHashableValue::InputIndex);
21252 FloatRegister tempFloat = ToFloatRegister(ins->temp0());
21253 ValueOperand output = ToOutValue(ins);
21254
21255 Register str = output.scratchReg();
21256
21257 using Fn = JSAtom* (*)(JSContext*, JSString*);
21258 auto* ool =
21259 oolCallVM<Fn, js::AtomizeString>(ins, ArgList(str), StoreRegisterTo(str));
21260
21261 masm.toHashableValue(input, output, tempFloat, ool->entry(), ool->rejoin());
21262}
21263
21264void CodeGenerator::visitHashNonGCThing(LHashNonGCThing* ins) {
21265 ValueOperand input = ToValue(ins, LHashNonGCThing::InputIndex);
21266 Register temp = ToRegister(ins->temp0());
21267 Register output = ToRegister(ins->output());
21268
21269 masm.prepareHashNonGCThing(input, output, temp);
21270}
21271
21272void CodeGenerator::visitHashString(LHashString* ins) {
21273 Register input = ToRegister(ins->input());
21274 Register temp = ToRegister(ins->temp0());
21275 Register output = ToRegister(ins->output());
21276
21277 masm.prepareHashString(input, output, temp);
21278}
21279
21280void CodeGenerator::visitHashSymbol(LHashSymbol* ins) {
21281 Register input = ToRegister(ins->input());
21282 Register output = ToRegister(ins->output());
21283
21284 masm.prepareHashSymbol(input, output);
21285}
21286
21287void CodeGenerator::visitHashBigInt(LHashBigInt* ins) {
21288 Register input = ToRegister(ins->input());
21289 Register temp0 = ToRegister(ins->temp0());
21290 Register temp1 = ToRegister(ins->temp1());
21291 Register temp2 = ToRegister(ins->temp2());
21292 Register output = ToRegister(ins->output());
21293
21294 masm.prepareHashBigInt(input, output, temp0, temp1, temp2);
21295}
21296
21297void CodeGenerator::visitHashObject(LHashObject* ins) {
21298 Register setObj = ToRegister(ins->setObject());
21299 ValueOperand input = ToValue(ins, LHashObject::InputIndex);
21300 Register temp0 = ToRegister(ins->temp0());
21301 Register temp1 = ToRegister(ins->temp1());
21302 Register temp2 = ToRegister(ins->temp2());
21303 Register temp3 = ToRegister(ins->temp3());
21304 Register output = ToRegister(ins->output());
21305
21306 masm.prepareHashObject(setObj, input, output, temp0, temp1, temp2, temp3);
21307}
21308
21309void CodeGenerator::visitHashValue(LHashValue* ins) {
21310 Register setObj = ToRegister(ins->setObject());
21311 ValueOperand input = ToValue(ins, LHashValue::InputIndex);
21312 Register temp0 = ToRegister(ins->temp0());
21313 Register temp1 = ToRegister(ins->temp1());
21314 Register temp2 = ToRegister(ins->temp2());
21315 Register temp3 = ToRegister(ins->temp3());
21316 Register output = ToRegister(ins->output());
21317
21318 masm.prepareHashValue(setObj, input, output, temp0, temp1, temp2, temp3);
21319}
21320
21321void CodeGenerator::visitSetObjectHasNonBigInt(LSetObjectHasNonBigInt* ins) {
21322 Register setObj = ToRegister(ins->setObject());
21323 ValueOperand input = ToValue(ins, LSetObjectHasNonBigInt::InputIndex);
21324 Register hash = ToRegister(ins->hash());
21325 Register temp0 = ToRegister(ins->temp0());
21326 Register temp1 = ToRegister(ins->temp1());
21327 Register output = ToRegister(ins->output());
21328
21329 masm.setObjectHasNonBigInt(setObj, input, hash, output, temp0, temp1);
21330}
21331
21332void CodeGenerator::visitSetObjectHasBigInt(LSetObjectHasBigInt* ins) {
21333 Register setObj = ToRegister(ins->setObject());
21334 ValueOperand input = ToValue(ins, LSetObjectHasBigInt::InputIndex);
21335 Register hash = ToRegister(ins->hash());
21336 Register temp0 = ToRegister(ins->temp0());
21337 Register temp1 = ToRegister(ins->temp1());
21338 Register temp2 = ToRegister(ins->temp2());
21339 Register temp3 = ToRegister(ins->temp3());
21340 Register output = ToRegister(ins->output());
21341
21342 masm.setObjectHasBigInt(setObj, input, hash, output, temp0, temp1, temp2,
21343 temp3);
21344}
21345
21346void CodeGenerator::visitSetObjectHasValue(LSetObjectHasValue* ins) {
21347 Register setObj = ToRegister(ins->setObject());
21348 ValueOperand input = ToValue(ins, LSetObjectHasValue::InputIndex);
21349 Register hash = ToRegister(ins->hash());
21350 Register temp0 = ToRegister(ins->temp0());
21351 Register temp1 = ToRegister(ins->temp1());
21352 Register temp2 = ToRegister(ins->temp2());
21353 Register temp3 = ToRegister(ins->temp3());
21354 Register output = ToRegister(ins->output());
21355
21356 masm.setObjectHasValue(setObj, input, hash, output, temp0, temp1, temp2,
21357 temp3);
21358}
21359
21360void CodeGenerator::visitSetObjectHasValueVMCall(
21361 LSetObjectHasValueVMCall* ins) {
21362 pushArg(ToValue(ins, LSetObjectHasValueVMCall::InputIndex));
21363 pushArg(ToRegister(ins->setObject()));
21364
21365 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*);
21366 callVM<Fn, jit::SetObjectHas>(ins);
21367}
21368
21369void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) {
21370 Register setObj = ToRegister(ins->setObject());
21371 Register output = ToRegister(ins->output());
21372
21373 masm.loadSetObjectSize(setObj, output);
21374}
21375
21376void CodeGenerator::visitMapObjectHasNonBigInt(LMapObjectHasNonBigInt* ins) {
21377 Register mapObj = ToRegister(ins->mapObject());
21378 ValueOperand input = ToValue(ins, LMapObjectHasNonBigInt::InputIndex);
21379 Register hash = ToRegister(ins->hash());
21380 Register temp0 = ToRegister(ins->temp0());
21381 Register temp1 = ToRegister(ins->temp1());
21382 Register output = ToRegister(ins->output());
21383
21384 masm.mapObjectHasNonBigInt(mapObj, input, hash, output, temp0, temp1);
21385}
21386
21387void CodeGenerator::visitMapObjectHasBigInt(LMapObjectHasBigInt* ins) {
21388 Register mapObj = ToRegister(ins->mapObject());
21389 ValueOperand input = ToValue(ins, LMapObjectHasBigInt::InputIndex);
21390 Register hash = ToRegister(ins->hash());
21391 Register temp0 = ToRegister(ins->temp0());
21392 Register temp1 = ToRegister(ins->temp1());
21393 Register temp2 = ToRegister(ins->temp2());
21394 Register temp3 = ToRegister(ins->temp3());
21395 Register output = ToRegister(ins->output());
21396
21397 masm.mapObjectHasBigInt(mapObj, input, hash, output, temp0, temp1, temp2,
21398 temp3);
21399}
21400
21401void CodeGenerator::visitMapObjectHasValue(LMapObjectHasValue* ins) {
21402 Register mapObj = ToRegister(ins->mapObject());
21403 ValueOperand input = ToValue(ins, LMapObjectHasValue::InputIndex);
21404 Register hash = ToRegister(ins->hash());
21405 Register temp0 = ToRegister(ins->temp0());
21406 Register temp1 = ToRegister(ins->temp1());
21407 Register temp2 = ToRegister(ins->temp2());
21408 Register temp3 = ToRegister(ins->temp3());
21409 Register output = ToRegister(ins->output());
21410
21411 masm.mapObjectHasValue(mapObj, input, hash, output, temp0, temp1, temp2,
21412 temp3);
21413}
21414
21415void CodeGenerator::visitMapObjectHasValueVMCall(
21416 LMapObjectHasValueVMCall* ins) {
21417 pushArg(ToValue(ins, LMapObjectHasValueVMCall::InputIndex));
21418 pushArg(ToRegister(ins->mapObject()));
21419
21420 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*);
21421 callVM<Fn, jit::MapObjectHas>(ins);
21422}
21423
21424void CodeGenerator::visitMapObjectGetNonBigInt(LMapObjectGetNonBigInt* ins) {
21425 Register mapObj = ToRegister(ins->mapObject());
21426 ValueOperand input = ToValue(ins, LMapObjectGetNonBigInt::InputIndex);
21427 Register hash = ToRegister(ins->hash());
21428 Register temp0 = ToRegister(ins->temp0());
21429 Register temp1 = ToRegister(ins->temp1());
21430 ValueOperand output = ToOutValue(ins);
21431
21432 masm.mapObjectGetNonBigInt(mapObj, input, hash, output, temp0, temp1,
21433 output.scratchReg());
21434}
21435
21436void CodeGenerator::visitMapObjectGetBigInt(LMapObjectGetBigInt* ins) {
21437 Register mapObj = ToRegister(ins->mapObject());
21438 ValueOperand input = ToValue(ins, LMapObjectGetBigInt::InputIndex);
21439 Register hash = ToRegister(ins->hash());
21440 Register temp0 = ToRegister(ins->temp0());
21441 Register temp1 = ToRegister(ins->temp1());
21442 Register temp2 = ToRegister(ins->temp2());
21443 Register temp3 = ToRegister(ins->temp3());
21444 ValueOperand output = ToOutValue(ins);
21445
21446 masm.mapObjectGetBigInt(mapObj, input, hash, output, temp0, temp1, temp2,
21447 temp3, output.scratchReg());
21448}
21449
21450void CodeGenerator::visitMapObjectGetValue(LMapObjectGetValue* ins) {
21451 Register mapObj = ToRegister(ins->mapObject());
21452 ValueOperand input = ToValue(ins, LMapObjectGetValue::InputIndex);
21453 Register hash = ToRegister(ins->hash());
21454 Register temp0 = ToRegister(ins->temp0());
21455 Register temp1 = ToRegister(ins->temp1());
21456 Register temp2 = ToRegister(ins->temp2());
21457 Register temp3 = ToRegister(ins->temp3());
21458 ValueOperand output = ToOutValue(ins);
21459
21460 masm.mapObjectGetValue(mapObj, input, hash, output, temp0, temp1, temp2,
21461 temp3, output.scratchReg());
21462}
21463
21464void CodeGenerator::visitMapObjectGetValueVMCall(
21465 LMapObjectGetValueVMCall* ins) {
21466 pushArg(ToValue(ins, LMapObjectGetValueVMCall::InputIndex));
21467 pushArg(ToRegister(ins->mapObject()));
21468
21469 using Fn =
21470 bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
21471 callVM<Fn, jit::MapObjectGet>(ins);
21472}
21473
21474void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) {
21475 Register mapObj = ToRegister(ins->mapObject());
21476 Register output = ToRegister(ins->output());
21477
21478 masm.loadMapObjectSize(mapObj, output);
21479}
21480
21481template <size_t NumDefs>
21482void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) {
21483 wasm::JitCallStackArgVector stackArgs;
21484 masm.propagateOOM(stackArgs.reserve(lir->numOperands()));
21485 if (masm.oom()) {
21486 return;
21487 }
21488
21489 MIonToWasmCall* mir = lir->mir();
21490 const wasm::FuncExport& funcExport = mir->funcExport();
21491 const wasm::FuncType& sig =
21492 mir->instance()->code().codeMeta().getFuncType(funcExport.funcIndex());
21493
21494 WasmABIArgGenerator abi;
21495 for (size_t i = 0; i < lir->numOperands(); i++) {
21496 MIRType argMir;
21497 switch (sig.args()[i].kind()) {
21498 case wasm::ValType::I32:
21499 case wasm::ValType::I64:
21500 case wasm::ValType::F32:
21501 case wasm::ValType::F64:
21502 argMir = sig.args()[i].toMIRType();
21503 break;
21504 case wasm::ValType::V128:
21505 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21505); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected argument type when calling from ion to wasm"
")"); do { *((volatile int*)__null) = 21505; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
21506 case wasm::ValType::Ref:
21507 // temporarilyUnsupportedReftypeForEntry() restricts args to externref
21508 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()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21508); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "sig.args()[i].refType().isExtern()"
")"); do { *((volatile int*)__null) = 21508; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21509 // Argument is boxed on the JS side to an anyref, so passed as a
21510 // pointer here.
21511 argMir = sig.args()[i].toMIRType();
21512 break;
21513 }
21514
21515 ABIArg arg = abi.next(argMir);
21516 switch (arg.kind()) {
21517 case ABIArg::GPR:
21518 case ABIArg::FPU: {
21519 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21519); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToAnyRegister(lir->getOperand(i)) == arg.reg()"
")"); do { *((volatile int*)__null) = 21519; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21520 stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg());
21521 break;
21522 }
21523 case ABIArg::Stack: {
21524 const LAllocation* larg = lir->getOperand(i);
21525 if (larg->isConstant()) {
21526 stackArgs.infallibleEmplaceBack(ToInt32(larg));
21527 } else if (larg->isGeneralReg()) {
21528 stackArgs.infallibleEmplaceBack(ToRegister(larg));
21529 } else if (larg->isFloatReg()) {
21530 stackArgs.infallibleEmplaceBack(ToFloatRegister(larg));
21531 } else {
21532 // Always use the stack pointer here because GenerateDirectCallFromJit
21533 // depends on this.
21534 Address addr = ToAddress<BaseRegForAddress::SP>(larg);
21535 stackArgs.infallibleEmplaceBack(addr);
21536 }
21537 break;
21538 }
21539#ifdef JS_CODEGEN_REGISTER_PAIR
21540 case ABIArg::GPR_PAIR: {
21541 MOZ_CRASH(do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21542); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls"
")"); do { *((volatile int*)__null) = 21542; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
21542 "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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21542); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls"
")"); do { *((volatile int*)__null) = 21542; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
21543 }
21544#endif
21545 case ABIArg::Uninitialized: {
21546 MOZ_CRASH("Uninitialized ABIArg kind")do { do { } while (false); MOZ_ReportCrash("" "Uninitialized ABIArg kind"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21546); AnnotateMozCrashReason("MOZ_CRASH(" "Uninitialized ABIArg kind"
")"); do { *((volatile int*)__null) = 21546; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
21547 }
21548 }
21549 }
21550
21551 const wasm::ValTypeVector& results = sig.results();
21552 if (results.length() == 0) {
21553 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21553); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value"
")"); do { *((volatile int*)__null) = 21553; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21554 } else {
21555 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" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21555); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 1"
") (" "multi-value return unimplemented" ")"); do { *((volatile
int*)__null) = 21555; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
21556 switch (results[0].kind()) {
21557 case wasm::ValType::I32:
21558 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21558); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 21558; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21559 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21559); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { *((volatile int*)__null) = 21559; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21560 break;
21561 case wasm::ValType::I64:
21562 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21562); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 21562; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21563 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21563); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutRegister64(lir) == ReturnReg64"
")"); do { *((volatile int*)__null) = 21563; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21564 break;
21565 case wasm::ValType::F32:
21566 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32"
")"); do { *((volatile int*)__null) = 21566; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21567 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg"
")"); do { *((volatile int*)__null) = 21567; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21568 break;
21569 case wasm::ValType::F64:
21570 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double"
")"); do { *((volatile int*)__null) = 21570; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21571 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", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 21571; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21572 break;
21573 case wasm::ValType::V128:
21574 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21574); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected return type when calling from ion to wasm"
")"); do { *((volatile int*)__null) = 21574; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
21575 case wasm::ValType::Ref:
21576 // The wasm stubs layer unboxes anything that needs to be unboxed
21577 // and leaves it in a Value. A FuncRef/EqRef we could in principle
21578 // leave it as a raw object pointer but for now it complicates the
21579 // API to do so.
21580 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value"
")"); do { *((volatile int*)__null) = 21580; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21581 break;
21582 }
21583 }
21584
21585 WasmInstanceObject* instObj = lir->mir()->instanceObject();
21586
21587 Register scratch = ToRegister(lir->temp());
21588
21589 uint32_t callOffset;
21590 ensureOsiSpace();
21591 GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs,
21592 scratch, &callOffset);
21593
21594 // Add the instance object to the constant pool, so it is transferred to
21595 // the owning IonScript and so that it gets traced as long as the IonScript
21596 // lives.
21597
21598 uint32_t unused;
21599 masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused));
21600
21601 markSafepointAt(callOffset, lir);
21602}
21603
21604void CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) {
21605 emitIonToWasmCallBase(lir);
21606}
21607void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) {
21608 emitIonToWasmCallBase(lir);
21609}
21610void CodeGenerator::visitIonToWasmCallI64(LIonToWasmCallI64* lir) {
21611 emitIonToWasmCallBase(lir);
21612}
21613
21614void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) {
21615 masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output()));
21616}
21617
21618void CodeGenerator::visitWasmFence(LWasmFence* lir) {
21619 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()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21619); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 21619; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21620 masm.memoryBarrier(MembarFull);
21621}
21622
21623void CodeGenerator::visitWasmAnyRefFromJSValue(LWasmAnyRefFromJSValue* lir) {
21624 ValueOperand input = ToValue(lir, LWasmAnyRefFromJSValue::InputIndex);
21625 Register output = ToRegister(lir->output());
21626 FloatRegister tempFloat = ToFloatRegister(lir->temp0());
21627
21628 using Fn = JSObject* (*)(JSContext* cx, HandleValue value);
21629 OutOfLineCode* oolBoxValue = oolCallVM<Fn, wasm::AnyRef::boxValue>(
21630 lir, ArgList(input), StoreRegisterTo(output));
21631 masm.convertValueToWasmAnyRef(input, output, tempFloat, oolBoxValue->entry());
21632 masm.bind(oolBoxValue->rejoin());
21633}
21634
21635void CodeGenerator::visitWasmAnyRefFromJSObject(LWasmAnyRefFromJSObject* lir) {
21636 Register input = ToRegister(lir->input());
21637 Register output = ToRegister(lir->output());
21638 masm.convertObjectToWasmAnyRef(input, output);
21639}
21640
21641void CodeGenerator::visitWasmAnyRefFromJSString(LWasmAnyRefFromJSString* lir) {
21642 Register input = ToRegister(lir->input());
21643 Register output = ToRegister(lir->output());
21644 masm.convertStringToWasmAnyRef(input, output);
21645}
21646
21647void CodeGenerator::visitWasmNewI31Ref(LWasmNewI31Ref* lir) {
21648 if (lir->value()->isConstant()) {
21649 // i31ref are often created with constants. If that's the case we will
21650 // do the operation statically here. This is similar to what is done
21651 // in masm.truncate32ToWasmI31Ref.
21652 Register output = ToRegister(lir->output());
21653 uint32_t value =
21654 static_cast<uint32_t>(lir->value()->toConstant()->toInt32());
21655 uintptr_t ptr = wasm::AnyRef::fromUint32Truncate(value).rawValue();
21656 masm.movePtr(ImmWord(ptr), output);
21657 } else {
21658 Register value = ToRegister(lir->value());
21659 Register output = ToRegister(lir->output());
21660 masm.truncate32ToWasmI31Ref(value, output);
21661 }
21662}
21663
21664void CodeGenerator::visitWasmI31RefGet(LWasmI31RefGet* lir) {
21665 Register value = ToRegister(lir->value());
21666 Register output = ToRegister(lir->output());
21667 if (lir->mir()->wideningOp() == wasm::FieldWideningOp::Signed) {
21668 masm.convertWasmI31RefTo32Signed(value, output);
21669 } else {
21670 masm.convertWasmI31RefTo32Unsigned(value, output);
21671 }
21672}
21673
21674#ifdef FUZZING_JS_FUZZILLI
21675void CodeGenerator::emitFuzzilliHashObject(LInstruction* lir, Register obj,
21676 Register output) {
21677 using Fn = void (*)(JSContext* cx, JSObject* obj, uint32_t* out);
21678 OutOfLineCode* ool = oolCallVM<Fn, FuzzilliHashObjectInl>(
21679 lir, ArgList(obj), StoreRegisterTo(output));
21680
21681 masm.jump(ool->entry());
21682 masm.bind(ool->rejoin());
21683}
21684
21685void CodeGenerator::emitFuzzilliHashBigInt(LInstruction* lir, Register bigInt,
21686 Register output) {
21687 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
21688 volatileRegs.takeUnchecked(output);
21689
21690 masm.PushRegsInMask(volatileRegs);
21691
21692 using Fn = uint32_t (*)(BigInt* bigInt);
21693 masm.setupUnalignedABICall(output);
21694 masm.passABIArg(bigInt);
21695 masm.callWithABI<Fn, js::FuzzilliHashBigInt>();
21696 masm.storeCallInt32Result(output);
21697
21698 masm.PopRegsInMask(volatileRegs);
21699}
21700
21701void CodeGenerator::visitFuzzilliHashV(LFuzzilliHashV* ins) {
21702 ValueOperand value = ToValue(ins, 0);
21703
21704 FloatRegister scratchFloat = ToFloatRegister(ins->getTemp(1));
21705 Register scratch = ToRegister(ins->getTemp(0));
21706 Register output = ToRegister(ins->output());
21707 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21707); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output"
")"); do { *((volatile int*)__null) = 21707; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21708
21709 Label hashDouble, done;
21710
21711 Label isInt32, isDouble, isNull, isUndefined, isBoolean, isBigInt, isObject;
21712 {
21713 ScratchTagScope tag(masm, value);
21714 masm.splitTagForTest(value, tag);
21715
21716 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
21717 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
21718 masm.branchTestNull(Assembler::Equal, tag, &isNull);
21719 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
21720 masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean);
21721 masm.branchTestBigInt(Assembler::Equal, tag, &isBigInt);
21722 masm.branchTestObject(Assembler::Equal, tag, &isObject);
21723
21724 // Symbol or String.
21725 masm.move32(Imm32(0), output);
21726 masm.jump(&done);
21727 }
21728
21729 masm.bind(&isInt32);
21730 {
21731 masm.unboxInt32(value, scratch);
21732 masm.convertInt32ToDouble(scratch, scratchFloat);
21733 masm.jump(&hashDouble);
21734 }
21735
21736 masm.bind(&isDouble);
21737 {
21738 masm.unboxDouble(value, scratchFloat);
21739 masm.jump(&hashDouble);
21740 }
21741
21742 masm.bind(&isNull);
21743 {
21744 masm.loadConstantDouble(1.0, scratchFloat);
21745 masm.jump(&hashDouble);
21746 }
21747
21748 masm.bind(&isUndefined);
21749 {
21750 masm.loadConstantDouble(2.0, scratchFloat);
21751 masm.jump(&hashDouble);
21752 }
21753
21754 masm.bind(&isBoolean);
21755 {
21756 masm.unboxBoolean(value, scratch);
21757 masm.add32(Imm32(3), scratch);
21758 masm.convertInt32ToDouble(scratch, scratchFloat);
21759 masm.jump(&hashDouble);
21760 }
21761
21762 masm.bind(&isBigInt);
21763 {
21764 masm.unboxBigInt(value, scratch);
21765 emitFuzzilliHashBigInt(ins, scratch, output);
21766 masm.jump(&done);
21767 }
21768
21769 masm.bind(&isObject);
21770 {
21771 masm.unboxObject(value, scratch);
21772 emitFuzzilliHashObject(ins, scratch, output);
21773 masm.jump(&done);
21774 }
21775
21776 masm.bind(&hashDouble);
21777 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
21778
21779 masm.bind(&done);
21780}
21781
21782void CodeGenerator::visitFuzzilliHashT(LFuzzilliHashT* ins) {
21783 const LAllocation* value = ins->value();
21784 MIRType mirType = ins->mir()->getOperand(0)->type();
21785
21786 Register scratch = ToTempRegisterOrInvalid(ins->getTemp(0));
21787 FloatRegister scratchFloat = ToTempFloatRegisterOrInvalid(ins->getTemp(1));
21788
21789 Register output = ToRegister(ins->output());
21790 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"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21790); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output"
")"); do { *((volatile int*)__null) = 21790; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21791
21792 switch (mirType) {
21793 case MIRType::Undefined: {
21794 masm.loadConstantDouble(2.0, scratchFloat);
21795 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
21796 break;
21797 }
21798
21799 case MIRType::Null: {
21800 masm.loadConstantDouble(1.0, scratchFloat);
21801 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
21802 break;
21803 }
21804
21805 case MIRType::Int32: {
21806 masm.move32(ToRegister(value), scratch);
21807 masm.convertInt32ToDouble(scratch, scratchFloat);
21808 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
21809 break;
21810 }
21811
21812 case MIRType::Double: {
21813 masm.moveDouble(ToFloatRegister(value), scratchFloat);
21814 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
21815 break;
21816 }
21817
21818 case MIRType::Float32: {
21819 masm.convertFloat32ToDouble(ToFloatRegister(value), scratchFloat);
21820 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
21821 break;
21822 }
21823
21824 case MIRType::Boolean: {
21825 masm.move32(ToRegister(value), scratch);
21826 masm.add32(Imm32(3), scratch);
21827 masm.convertInt32ToDouble(scratch, scratchFloat);
21828 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
21829 break;
21830 }
21831
21832 case MIRType::BigInt: {
21833 emitFuzzilliHashBigInt(ins, ToRegister(value), output);
21834 break;
21835 }
21836
21837 case MIRType::Object: {
21838 emitFuzzilliHashObject(ins, ToRegister(value), output);
21839 break;
21840 }
21841
21842 default:
21843 MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21843); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type"
")"); do { *((volatile int*)__null) = 21843; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
21844 }
21845}
21846
21847void CodeGenerator::visitFuzzilliHashStore(LFuzzilliHashStore* ins) {
21848 Register value = ToRegister(ins->value());
21849 Register temp0 = ToRegister(ins->getTemp(0));
21850 Register temp1 = ToRegister(ins->getTemp(1));
21851
21852 masm.fuzzilliStoreHash(value, temp0, temp1);
21853}
21854#endif
21855
21856static_assert(!std::is_polymorphic_v<CodeGenerator>,
21857 "CodeGenerator should not have any virtual methods");
21858
21859} // namespace jit
21860} // namespace js