Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp
Warning:line 12828, 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-20/lib/clang/20 -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 _GLIBCXX_ASSERTIONS -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-20/lib/clang/20/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-01-20-090804-167946-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/BytecodeUtil-inl.h"
96#include "vm/JSScript-inl.h"
97#include "wasm/WasmInstance-inl.h"
98
99using namespace js;
100using namespace js::jit;
101
102using mozilla::CheckedUint32;
103using mozilla::DebugOnly;
104using mozilla::FloatingPoint;
105using mozilla::NegativeInfinity;
106using mozilla::PositiveInfinity;
107
108using JS::ExpandoAndGeneration;
109
110namespace js {
111namespace jit {
112
113#ifdef CHECK_OSIPOINT_REGISTERS1
114template <class Op>
115static void HandleRegisterDump(Op op, MacroAssembler& masm,
116 LiveRegisterSet liveRegs, Register activation,
117 Register scratch) {
118 const size_t baseOffset = JitActivation::offsetOfRegs();
119
120 // Handle live GPRs.
121 for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) {
122 Register reg = *iter;
123 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
124
125 if (reg == activation) {
126 // To use the original value of the activation register (that's
127 // now on top of the stack), we need the scratch register.
128 masm.push(scratch);
129 masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch);
130 op(scratch, dump);
131 masm.pop(scratch);
132 } else {
133 op(reg, dump);
134 }
135 }
136
137 // Handle live FPRs.
138 for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) {
139 FloatRegister reg = *iter;
140 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
141 op(reg, dump);
142 }
143}
144
145class StoreOp {
146 MacroAssembler& masm;
147
148 public:
149 explicit StoreOp(MacroAssembler& masm) : masm(masm) {}
150
151 void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); }
152 void operator()(FloatRegister reg, Address dump) {
153 if (reg.isDouble()) {
154 masm.storeDouble(reg, dump);
155 } else if (reg.isSingle()) {
156 masm.storeFloat32(reg, dump);
157 } else if (reg.isSimd128()) {
158 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"
, 158); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD"
")"); do { *((volatile int*)__null) = 158; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
159 } else {
160 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"
, 160); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type."
")"); do { *((volatile int*)__null) = 160; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
161 }
162 }
163};
164
165class VerifyOp {
166 MacroAssembler& masm;
167 Label* failure_;
168
169 public:
170 VerifyOp(MacroAssembler& masm, Label* failure)
171 : masm(masm), failure_(failure) {}
172
173 void operator()(Register reg, Address dump) {
174 masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
175 }
176 void operator()(FloatRegister reg, Address dump) {
177 if (reg.isDouble()) {
178 ScratchDoubleScope scratch(masm);
179 masm.loadDouble(dump, scratch);
180 masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_);
181 } else if (reg.isSingle()) {
182 ScratchFloat32Scope scratch(masm);
183 masm.loadFloat32(dump, scratch);
184 masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_);
185 } else if (reg.isSimd128()) {
186 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"
, 186); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD"
")"); do { *((volatile int*)__null) = 186; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
187 } else {
188 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"
, 188); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type."
")"); do { *((volatile int*)__null) = 188; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
189 }
190 }
191};
192
193void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) {
194 // Ensure the live registers stored by callVM did not change between
195 // the call and this OsiPoint. Try-catch relies on this invariant.
196
197 // Load pointer to the JitActivation in a scratch register.
198 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
199 Register scratch = allRegs.takeAny();
200 masm.push(scratch);
201 masm.loadJitActivation(scratch);
202
203 // If we should not check registers (because the instruction did not call
204 // into the VM, or a GC happened), we're done.
205 Label failure, done;
206 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
207 masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
208
209 // Having more than one VM function call made in one visit function at
210 // runtime is a sec-ciritcal error, because if we conservatively assume that
211 // one of the function call can re-enter Ion, then the invalidation process
212 // will potentially add a call at a random location, by patching the code
213 // before the return address.
214 masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
215
216 // Set checkRegs to 0, so that we don't try to verify registers after we
217 // return from this script to the caller.
218 masm.store32(Imm32(0), checkRegs);
219
220 // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
221 // temps after calling into the VM. This is fine because no other
222 // instructions (including this OsiPoint) will depend on them. Also
223 // backtracking can also use the same register for an input and an output.
224 // These are marked as clobbered and shouldn't get checked.
225 LiveRegisterSet liveRegs;
226 liveRegs.set() = RegisterSet::Intersect(
227 safepoint->liveRegs().set(),
228 RegisterSet::Not(safepoint->clobberedRegs().set()));
229
230 VerifyOp op(masm, &failure);
231 HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
232
233 masm.jump(&done);
234
235 // Do not profile the callWithABI that occurs below. This is to avoid a
236 // rare corner case that occurs when profiling interacts with itself:
237 //
238 // When slow profiling assertions are turned on, FunctionBoundary ops
239 // (which update the profiler pseudo-stack) may emit a callVM, which
240 // forces them to have an osi point associated with them. The
241 // FunctionBoundary for inline function entry is added to the caller's
242 // graph with a PC from the caller's code, but during codegen it modifies
243 // Gecko Profiler instrumentation to add the callee as the current top-most
244 // script. When codegen gets to the OSIPoint, and the callWithABI below is
245 // emitted, the codegen thinks that the current frame is the callee, but
246 // the PC it's using from the OSIPoint refers to the caller. This causes
247 // the profiler instrumentation of the callWithABI below to ASSERT, since
248 // the script and pc are mismatched. To avoid this, we simply omit
249 // instrumentation for these callWithABIs.
250
251 // Any live register captured by a safepoint (other than temp registers)
252 // must remain unchanged between the call and the OsiPoint instruction.
253 masm.bind(&failure);
254 masm.assumeUnreachable("Modified registers between VM call and OsiPoint");
255
256 masm.bind(&done);
257 masm.pop(scratch);
258}
259
260bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) {
261 if (!checkOsiPointRegisters) {
262 return false;
263 }
264
265 if (safepoint->liveRegs().emptyGeneral() &&
266 safepoint->liveRegs().emptyFloat()) {
267 return false; // No registers to check.
268 }
269
270 return true;
271}
272
273void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) {
274 if (!shouldVerifyOsiPointRegs(safepoint)) {
275 return;
276 }
277
278 // Set checkRegs to 0. If we perform a VM call, the instruction
279 // will set it to 1.
280 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
281 Register scratch = allRegs.takeAny();
282 masm.push(scratch);
283 masm.loadJitActivation(scratch);
284 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
285 masm.store32(Imm32(0), checkRegs);
286 masm.pop(scratch);
287}
288
289static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) {
290 // Store a copy of all live registers before performing the call.
291 // When we reach the OsiPoint, we can use this to check nothing
292 // modified them in the meantime.
293
294 // Load pointer to the JitActivation in a scratch register.
295 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
296 Register scratch = allRegs.takeAny();
297 masm.push(scratch);
298 masm.loadJitActivation(scratch);
299
300 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
301 masm.add32(Imm32(1), checkRegs);
302
303 StoreOp op(masm);
304 HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
305
306 masm.pop(scratch);
307}
308#endif // CHECK_OSIPOINT_REGISTERS
309
310// Before doing any call to Cpp, you should ensure that volatile
311// registers are evicted by the register allocator.
312void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) {
313 TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id);
314 const VMFunctionData& fun = GetVMFunction(id);
315
316 // Stack is:
317 // ... frame ...
318 // [args]
319#ifdef DEBUG1
320 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"
, 320); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs"
")"); do { *((volatile int*)__null) = 320; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
321 pushedArgs_ = 0;
322#endif
323
324#ifdef CHECK_OSIPOINT_REGISTERS1
325 if (shouldVerifyOsiPointRegs(ins->safepoint())) {
326 StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
327 }
328#endif
329
330#ifdef DEBUG1
331 if (ins->mirRaw()) {
332 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"
, 332); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()"
")"); do { *((volatile int*)__null) = 332; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
333 MInstruction* mir = ins->mirRaw()->toInstruction();
334 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"
, 334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()"
")"); do { *((volatile int*)__null) = 334; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
335
336 // If this MIR instruction has an overridden AliasSet, set the JitRuntime's
337 // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call
338 // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because
339 // interrupt callbacks can call JS (chrome JS or shell testing functions).
340 bool isWhitelisted = mir->isInterruptCheck() || mir->isCheckOverRecursed();
341 if (!mir->hasDefaultAliasSet() && !isWhitelisted) {
342 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
343 masm.move32(Imm32(1), ReturnReg);
344 masm.store32(ReturnReg, AbsoluteAddress(addr));
345 }
346 }
347#endif
348
349 // Push an exit frame descriptor.
350 masm.PushFrameDescriptor(FrameType::IonJS);
351
352 // Call the wrapper function. The wrapper is in charge to unwind the stack
353 // when returning from the call. Failures are handled with exceptions based
354 // on the return value of the C functions. To guard the outcome of the
355 // returned value, use another LIR instruction.
356 ensureOsiSpace();
357 uint32_t callOffset = masm.callJit(code);
358 markSafepointAt(callOffset, ins);
359
360#ifdef DEBUG1
361 // Reset the disallowArbitraryCode flag after the call.
362 {
363 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
364 masm.push(ReturnReg);
365 masm.move32(Imm32(0), ReturnReg);
366 masm.store32(ReturnReg, AbsoluteAddress(addr));
367 masm.pop(ReturnReg);
368 }
369#endif
370
371 // Pop rest of the exit frame and the arguments left on the stack.
372 int framePop =
373 sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall();
374 masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop);
375
376 // Stack is:
377 // ... frame ...
378}
379
380template <typename Fn, Fn fn>
381void CodeGenerator::callVM(LInstruction* ins) {
382 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
383 callVMInternal(id, ins);
384}
385
386// ArgSeq store arguments for OutOfLineCallVM.
387//
388// OutOfLineCallVM are created with "oolCallVM" function. The third argument of
389// this function is an instance of a class which provides a "generate" in charge
390// of pushing the argument, with "pushArg", for a VMFunction.
391//
392// Such list of arguments can be created by using the "ArgList" function which
393// creates one instance of "ArgSeq", where the type of the arguments are
394// inferred from the type of the arguments.
395//
396// The list of arguments must be written in the same order as if you were
397// calling the function in C++.
398//
399// Example:
400// ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs()))
401
402template <typename... ArgTypes>
403class ArgSeq {
404 std::tuple<std::remove_reference_t<ArgTypes>...> args_;
405
406 template <std::size_t... ISeq>
407 inline void generate(CodeGenerator* codegen,
408 std::index_sequence<ISeq...>) const {
409 // Arguments are pushed in reverse order, from last argument to first
410 // argument.
411 (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...);
412 }
413
414 public:
415 explicit ArgSeq(ArgTypes&&... args)
416 : args_(std::forward<ArgTypes>(args)...) {}
417
418 inline void generate(CodeGenerator* codegen) const {
419 generate(codegen, std::index_sequence_for<ArgTypes...>{});
420 }
421
422#ifdef DEBUG1
423 static constexpr size_t numArgs = sizeof...(ArgTypes);
424#endif
425};
426
427template <typename... ArgTypes>
428inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) {
429 return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...);
430}
431
432// Store wrappers, to generate the right move of data after the VM call.
433
434struct StoreNothing {
435 inline void generate(CodeGenerator* codegen) const {}
436 inline LiveRegisterSet clobbered() const {
437 return LiveRegisterSet(); // No register gets clobbered
438 }
439};
440
441class StoreRegisterTo {
442 private:
443 Register out_;
444
445 public:
446 explicit StoreRegisterTo(Register out) : out_(out) {}
447
448 inline void generate(CodeGenerator* codegen) const {
449 // It's okay to use storePointerResultTo here - the VMFunction wrapper
450 // ensures the upper bytes are zero for bool/int32 return values.
451 codegen->storePointerResultTo(out_);
452 }
453 inline LiveRegisterSet clobbered() const {
454 LiveRegisterSet set;
455 set.add(out_);
456 return set;
457 }
458};
459
460class StoreFloatRegisterTo {
461 private:
462 FloatRegister out_;
463
464 public:
465 explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {}
466
467 inline void generate(CodeGenerator* codegen) const {
468 codegen->storeFloatResultTo(out_);
469 }
470 inline LiveRegisterSet clobbered() const {
471 LiveRegisterSet set;
472 set.add(out_);
473 return set;
474 }
475};
476
477template <typename Output>
478class StoreValueTo_ {
479 private:
480 Output out_;
481
482 public:
483 explicit StoreValueTo_(const Output& out) : out_(out) {}
484
485 inline void generate(CodeGenerator* codegen) const {
486 codegen->storeResultValueTo(out_);
487 }
488 inline LiveRegisterSet clobbered() const {
489 LiveRegisterSet set;
490 set.add(out_);
491 return set;
492 }
493};
494
495template <typename Output>
496StoreValueTo_<Output> StoreValueTo(const Output& out) {
497 return StoreValueTo_<Output>(out);
498}
499
500template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
501class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> {
502 private:
503 LInstruction* lir_;
504 ArgSeq args_;
505 StoreOutputTo out_;
506
507 public:
508 OutOfLineCallVM(LInstruction* lir, const ArgSeq& args,
509 const StoreOutputTo& out)
510 : lir_(lir), args_(args), out_(out) {}
511
512 void accept(CodeGenerator* codegen) override {
513 codegen->visitOutOfLineCallVM(this);
514 }
515
516 LInstruction* lir() const { return lir_; }
517 const ArgSeq& args() const { return args_; }
518 const StoreOutputTo& out() const { return out_; }
519};
520
521template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
522OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args,
523 const StoreOutputTo& out) {
524 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"
, 524); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()"
")"); do { *((volatile int*)__null) = 524; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
525 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"
, 525); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()"
")"); do { *((volatile int*)__null) = 525; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
526
527#ifdef DEBUG1
528 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
529 const VMFunctionData& fun = GetVMFunction(id);
530 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"
, 530); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs"
")"); do { *((volatile int*)__null) = 530; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
531 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"
, 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
532 (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"
, 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
533#endif
534
535 OutOfLineCode* ool = new (alloc())
536 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out);
537 addOutOfLineCode(ool, lir->mirRaw()->toInstruction());
538 return ool;
539}
540
541template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
542void CodeGenerator::visitOutOfLineCallVM(
543 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) {
544 LInstruction* lir = ool->lir();
545
546#ifdef JS_JITSPEW1
547 JitSpewStart(JitSpew_Codegen, " # LIR=%s",
548 lir->opName());
549 if (const char* extra = lir->getExtraName()) {
550 JitSpewCont(JitSpew_Codegen, ":%s", extra);
551 }
552 JitSpewFin(JitSpew_Codegen);
553#endif
554 perfSpewer_.recordInstruction(masm, lir);
555 saveLive(lir);
556 ool->args().generate(this);
557 callVM<Fn, fn>(lir);
558 ool->out().generate(this);
559 restoreLiveIgnore(lir, ool->out().clobbered());
560 masm.jump(ool->rejoin());
561}
562
563class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> {
564 private:
565 LInstruction* lir_;
566 size_t cacheIndex_;
567 size_t cacheInfoIndex_;
568
569 public:
570 OutOfLineICFallback(LInstruction* lir, size_t cacheIndex,
571 size_t cacheInfoIndex)
572 : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {}
573
574 void bind(MacroAssembler* masm) override {
575 // The binding of the initial jump is done in
576 // CodeGenerator::visitOutOfLineICFallback.
577 }
578
579 size_t cacheIndex() const { return cacheIndex_; }
580 size_t cacheInfoIndex() const { return cacheInfoIndex_; }
581 LInstruction* lir() const { return lir_; }
582
583 void accept(CodeGenerator* codegen) override {
584 codegen->visitOutOfLineICFallback(this);
585 }
586};
587
588void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) {
589 if (cacheIndex == SIZE_MAX(18446744073709551615UL)) {
590 masm.setOOM();
591 return;
592 }
593
594 DataPtr<IonIC> cache(this, cacheIndex);
595 MInstruction* mir = lir->mirRaw()->toInstruction();
596 cache->setScriptedLocation(mir->block()->info().script(),
597 mir->resumePoint()->pc());
598
599 Register temp = cache->scratchRegisterForEntryJump();
600 icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp);
601 masm.jump(Address(temp, 0));
602
603 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"
, 603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!icInfo_.empty()"
")"); do { *((volatile int*)__null) = 603; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
604
605 OutOfLineICFallback* ool =
606 new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1);
607 addOutOfLineCode(ool, mir);
608
609 masm.bind(ool->rejoin());
610 cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset()));
611}
612
613void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) {
614 LInstruction* lir = ool->lir();
615 size_t cacheIndex = ool->cacheIndex();
616 size_t cacheInfoIndex = ool->cacheInfoIndex();
617
618 DataPtr<IonIC> ic(this, cacheIndex);
619
620 // Register the location of the OOL path in the IC.
621 ic->setFallbackOffset(CodeOffset(masm.currentOffset()));
622
623 switch (ic->kind()) {
624 case CacheKind::GetProp:
625 case CacheKind::GetElem: {
626 IonGetPropertyIC* getPropIC = ic->asGetPropertyIC();
627
628 saveLive(lir);
629
630 pushArg(getPropIC->id());
631 pushArg(getPropIC->value());
632 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
633 pushArg(ImmGCPtr(gen->outerInfo().script()));
634
635 using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*,
636 HandleValue, HandleValue, MutableHandleValue);
637 callVM<Fn, IonGetPropertyIC::update>(lir);
638
639 StoreValueTo(getPropIC->output()).generate(this);
640 restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
641
642 masm.jump(ool->rejoin());
643 return;
644 }
645 case CacheKind::GetPropSuper:
646 case CacheKind::GetElemSuper: {
647 IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC();
648
649 saveLive(lir);
650
651 pushArg(getPropSuperIC->id());
652 pushArg(getPropSuperIC->receiver());
653 pushArg(getPropSuperIC->object());
654 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
655 pushArg(ImmGCPtr(gen->outerInfo().script()));
656
657 using Fn =
658 bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject,
659 HandleValue, HandleValue, MutableHandleValue);
660 callVM<Fn, IonGetPropSuperIC::update>(lir);
661
662 StoreValueTo(getPropSuperIC->output()).generate(this);
663 restoreLiveIgnore(lir,
664 StoreValueTo(getPropSuperIC->output()).clobbered());
665
666 masm.jump(ool->rejoin());
667 return;
668 }
669 case CacheKind::SetProp:
670 case CacheKind::SetElem: {
671 IonSetPropertyIC* setPropIC = ic->asSetPropertyIC();
672
673 saveLive(lir);
674
675 pushArg(setPropIC->rhs());
676 pushArg(setPropIC->id());
677 pushArg(setPropIC->object());
678 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
679 pushArg(ImmGCPtr(gen->outerInfo().script()));
680
681 using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*,
682 HandleObject, HandleValue, HandleValue);
683 callVM<Fn, IonSetPropertyIC::update>(lir);
684
685 restoreLive(lir);
686
687 masm.jump(ool->rejoin());
688 return;
689 }
690 case CacheKind::GetName: {
691 IonGetNameIC* getNameIC = ic->asGetNameIC();
692
693 saveLive(lir);
694
695 pushArg(getNameIC->environment());
696 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
697 pushArg(ImmGCPtr(gen->outerInfo().script()));
698
699 using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject,
700 MutableHandleValue);
701 callVM<Fn, IonGetNameIC::update>(lir);
702
703 StoreValueTo(getNameIC->output()).generate(this);
704 restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered());
705
706 masm.jump(ool->rejoin());
707 return;
708 }
709 case CacheKind::BindName: {
710 IonBindNameIC* bindNameIC = ic->asBindNameIC();
711
712 saveLive(lir);
713
714 pushArg(bindNameIC->environment());
715 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
716 pushArg(ImmGCPtr(gen->outerInfo().script()));
717
718 using Fn =
719 JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject);
720 callVM<Fn, IonBindNameIC::update>(lir);
721
722 StoreRegisterTo(bindNameIC->output()).generate(this);
723 restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered());
724
725 masm.jump(ool->rejoin());
726 return;
727 }
728 case CacheKind::GetIterator: {
729 IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC();
730
731 saveLive(lir);
732
733 pushArg(getIteratorIC->value());
734 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
735 pushArg(ImmGCPtr(gen->outerInfo().script()));
736
737 using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*,
738 HandleValue);
739 callVM<Fn, IonGetIteratorIC::update>(lir);
740
741 StoreRegisterTo(getIteratorIC->output()).generate(this);
742 restoreLiveIgnore(lir,
743 StoreRegisterTo(getIteratorIC->output()).clobbered());
744
745 masm.jump(ool->rejoin());
746 return;
747 }
748 case CacheKind::OptimizeSpreadCall: {
749 auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC();
750
751 saveLive(lir);
752
753 pushArg(optimizeSpreadCallIC->value());
754 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
755 pushArg(ImmGCPtr(gen->outerInfo().script()));
756
757 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*,
758 HandleValue, MutableHandleValue);
759 callVM<Fn, IonOptimizeSpreadCallIC::update>(lir);
760
761 StoreValueTo(optimizeSpreadCallIC->output()).generate(this);
762 restoreLiveIgnore(
763 lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered());
764
765 masm.jump(ool->rejoin());
766 return;
767 }
768 case CacheKind::In: {
769 IonInIC* inIC = ic->asInIC();
770
771 saveLive(lir);
772
773 pushArg(inIC->object());
774 pushArg(inIC->key());
775 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
776 pushArg(ImmGCPtr(gen->outerInfo().script()));
777
778 using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue,
779 HandleObject, bool*);
780 callVM<Fn, IonInIC::update>(lir);
781
782 StoreRegisterTo(inIC->output()).generate(this);
783 restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered());
784
785 masm.jump(ool->rejoin());
786 return;
787 }
788 case CacheKind::HasOwn: {
789 IonHasOwnIC* hasOwnIC = ic->asHasOwnIC();
790
791 saveLive(lir);
792
793 pushArg(hasOwnIC->id());
794 pushArg(hasOwnIC->value());
795 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
796 pushArg(ImmGCPtr(gen->outerInfo().script()));
797
798 using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue,
799 HandleValue, int32_t*);
800 callVM<Fn, IonHasOwnIC::update>(lir);
801
802 StoreRegisterTo(hasOwnIC->output()).generate(this);
803 restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered());
804
805 masm.jump(ool->rejoin());
806 return;
807 }
808 case CacheKind::CheckPrivateField: {
809 IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC();
810
811 saveLive(lir);
812
813 pushArg(checkPrivateFieldIC->id());
814 pushArg(checkPrivateFieldIC->value());
815
816 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
817 pushArg(ImmGCPtr(gen->outerInfo().script()));
818
819 using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*,
820 HandleValue, HandleValue, bool*);
821 callVM<Fn, IonCheckPrivateFieldIC::update>(lir);
822
823 StoreRegisterTo(checkPrivateFieldIC->output()).generate(this);
824 restoreLiveIgnore(
825 lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered());
826
827 masm.jump(ool->rejoin());
828 return;
829 }
830 case CacheKind::InstanceOf: {
831 IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC();
832
833 saveLive(lir);
834
835 pushArg(hasInstanceOfIC->rhs());
836 pushArg(hasInstanceOfIC->lhs());
837 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
838 pushArg(ImmGCPtr(gen->outerInfo().script()));
839
840 using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*,
841 HandleValue lhs, HandleObject rhs, bool* res);
842 callVM<Fn, IonInstanceOfIC::update>(lir);
843
844 StoreRegisterTo(hasInstanceOfIC->output()).generate(this);
845 restoreLiveIgnore(lir,
846 StoreRegisterTo(hasInstanceOfIC->output()).clobbered());
847
848 masm.jump(ool->rejoin());
849 return;
850 }
851 case CacheKind::UnaryArith: {
852 IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC();
853
854 saveLive(lir);
855
856 pushArg(unaryArithIC->input());
857 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
858 pushArg(ImmGCPtr(gen->outerInfo().script()));
859
860 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
861 IonUnaryArithIC* stub, HandleValue val,
862 MutableHandleValue res);
863 callVM<Fn, IonUnaryArithIC::update>(lir);
864
865 StoreValueTo(unaryArithIC->output()).generate(this);
866 restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered());
867
868 masm.jump(ool->rejoin());
869 return;
870 }
871 case CacheKind::ToPropertyKey: {
872 IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC();
873
874 saveLive(lir);
875
876 pushArg(toPropertyKeyIC->input());
877 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
878 pushArg(ImmGCPtr(gen->outerInfo().script()));
879
880 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
881 IonToPropertyKeyIC* ic, HandleValue val,
882 MutableHandleValue res);
883 callVM<Fn, IonToPropertyKeyIC::update>(lir);
884
885 StoreValueTo(toPropertyKeyIC->output()).generate(this);
886 restoreLiveIgnore(lir,
887 StoreValueTo(toPropertyKeyIC->output()).clobbered());
888
889 masm.jump(ool->rejoin());
890 return;
891 }
892 case CacheKind::BinaryArith: {
893 IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC();
894
895 saveLive(lir);
896
897 pushArg(binaryArithIC->rhs());
898 pushArg(binaryArithIC->lhs());
899 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
900 pushArg(ImmGCPtr(gen->outerInfo().script()));
901
902 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
903 IonBinaryArithIC* stub, HandleValue lhs,
904 HandleValue rhs, MutableHandleValue res);
905 callVM<Fn, IonBinaryArithIC::update>(lir);
906
907 StoreValueTo(binaryArithIC->output()).generate(this);
908 restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered());
909
910 masm.jump(ool->rejoin());
911 return;
912 }
913 case CacheKind::Compare: {
914 IonCompareIC* compareIC = ic->asCompareIC();
915
916 saveLive(lir);
917
918 pushArg(compareIC->rhs());
919 pushArg(compareIC->lhs());
920 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
921 pushArg(ImmGCPtr(gen->outerInfo().script()));
922
923 using Fn =
924 bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
925 HandleValue lhs, HandleValue rhs, bool* res);
926 callVM<Fn, IonCompareIC::update>(lir);
927
928 StoreRegisterTo(compareIC->output()).generate(this);
929 restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered());
930
931 masm.jump(ool->rejoin());
932 return;
933 }
934 case CacheKind::CloseIter: {
935 IonCloseIterIC* closeIterIC = ic->asCloseIterIC();
936
937 saveLive(lir);
938
939 pushArg(closeIterIC->iter());
940 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
941 pushArg(ImmGCPtr(gen->outerInfo().script()));
942
943 using Fn =
944 bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject);
945 callVM<Fn, IonCloseIterIC::update>(lir);
946
947 restoreLive(lir);
948
949 masm.jump(ool->rejoin());
950 return;
951 }
952 case CacheKind::OptimizeGetIterator: {
953 auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC();
954
955 saveLive(lir);
956
957 pushArg(optimizeGetIteratorIC->value());
958 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
959 pushArg(ImmGCPtr(gen->outerInfo().script()));
960
961 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*,
962 HandleValue, bool* res);
963 callVM<Fn, IonOptimizeGetIteratorIC::update>(lir);
964
965 StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this);
966 restoreLiveIgnore(
967 lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered());
968
969 masm.jump(ool->rejoin());
970 return;
971 }
972 case CacheKind::Call:
973 case CacheKind::TypeOf:
974 case CacheKind::TypeOfEq:
975 case CacheKind::ToBool:
976 case CacheKind::LazyConstant:
977 case CacheKind::NewArray:
978 case CacheKind::NewObject:
979 case CacheKind::Lambda:
980 case CacheKind::GetImport:
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
997CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); }
998
999void CodeGenerator::visitValueToNumberInt32(LValueToNumberInt32* lir) {
1000 ValueOperand operand = ToValue(lir, LValueToNumberInt32::InputIndex);
1001 Register output = ToRegister(lir->output());
1002 FloatRegister temp = ToFloatRegister(lir->temp0());
1003
1004 Label fails;
1005 masm.convertValueToInt32(operand, temp, output, &fails,
1006 lir->mir()->needsNegativeZeroCheck(),
1007 lir->mir()->conversion());
1008
1009 bailoutFrom(&fails, lir->snapshot());
1010}
1011
1012void CodeGenerator::visitValueTruncateToInt32(LValueTruncateToInt32* lir) {
1013 ValueOperand operand = ToValue(lir, LValueTruncateToInt32::InputIndex);
1014 Register output = ToRegister(lir->output());
1015 FloatRegister temp = ToFloatRegister(lir->temp0());
1016 Register stringReg = ToRegister(lir->temp1());
1017
1018 auto* oolDouble = oolTruncateDouble(temp, output, lir->mir());
1019
1020 using Fn = bool (*)(JSContext*, JSString*, double*);
1021 auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg),
1022 StoreFloatRegisterTo(temp));
1023 Label* stringEntry = oolString->entry();
1024 Label* stringRejoin = oolString->rejoin();
1025
1026 Label fails;
1027 masm.truncateValueToInt32(operand, stringEntry, stringRejoin,
1028 oolDouble->entry(), stringReg, temp, output,
1029 &fails);
1030 masm.bind(oolDouble->rejoin());
1031
1032 bailoutFrom(&fails, lir->snapshot());
1033}
1034
1035void CodeGenerator::visitValueToDouble(LValueToDouble* lir) {
1036 ValueOperand operand = ToValue(lir, LValueToDouble::InputIndex);
1037 FloatRegister output = ToFloatRegister(lir->output());
1038
1039 Label fail;
1040 masm.convertValueToDouble(operand, output, &fail);
1041 bailoutFrom(&fail, lir->snapshot());
1042}
1043
1044void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) {
1045 ValueOperand operand = ToValue(lir, LValueToFloat32::InputIndex);
1046 FloatRegister output = ToFloatRegister(lir->output());
1047
1048 Label fail;
1049 masm.convertValueToFloat32(operand, output, &fail);
1050 bailoutFrom(&fail, lir->snapshot());
1051}
1052
1053void CodeGenerator::visitValueToFloat16(LValueToFloat16* lir) {
1054 ValueOperand operand = ToValue(lir, LValueToFloat16::InputIndex);
1055 Register temp = ToTempRegisterOrInvalid(lir->temp0());
1056 FloatRegister output = ToFloatRegister(lir->output());
1057
1058 LiveRegisterSet volatileRegs;
1059 if (!MacroAssembler::SupportsFloat64To16()) {
1060 volatileRegs = liveVolatileRegs(lir);
1061 }
1062
1063 Label fail;
1064 masm.convertValueToFloat16(operand, output, temp, volatileRegs, &fail);
1065 bailoutFrom(&fail, lir->snapshot());
1066}
1067
1068void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) {
1069 ValueOperand operand = ToValue(lir, LValueToBigInt::InputIndex);
1070 Register output = ToRegister(lir->output());
1071
1072 using Fn = BigInt* (*)(JSContext*, HandleValue);
1073 auto* ool =
1074 oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output));
1075
1076 Register tag = masm.extractTag(operand, output);
1077
1078 Label notBigInt, done;
1079 masm.branchTestBigInt(Assembler::NotEqual, tag, &notBigInt);
1080 masm.unboxBigInt(operand, output);
1081 masm.jump(&done);
1082 masm.bind(&notBigInt);
1083
1084 masm.branchTestBoolean(Assembler::Equal, tag, ool->entry());
1085 masm.branchTestString(Assembler::Equal, tag, ool->entry());
1086
1087 // ToBigInt(object) can have side-effects; all other types throw a TypeError.
1088 bailout(lir->snapshot());
1089
1090 masm.bind(ool->rejoin());
1091 masm.bind(&done);
1092}
1093
1094void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) {
1095 masm.convertInt32ToDouble(ToRegister(lir->input()),
1096 ToFloatRegister(lir->output()));
1097}
1098
1099void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) {
1100 masm.convertFloat32ToDouble(ToFloatRegister(lir->input()),
1101 ToFloatRegister(lir->output()));
1102}
1103
1104void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) {
1105 masm.convertDoubleToFloat32(ToFloatRegister(lir->input()),
1106 ToFloatRegister(lir->output()));
1107}
1108
1109void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) {
1110 masm.convertInt32ToFloat32(ToRegister(lir->input()),
1111 ToFloatRegister(lir->output()));
1112}
1113
1114void CodeGenerator::visitDoubleToFloat16(LDoubleToFloat16* lir) {
1115 LiveRegisterSet volatileRegs;
1116 if (!MacroAssembler::SupportsFloat64To16()) {
1117 volatileRegs = liveVolatileRegs(lir);
1118 }
1119 masm.convertDoubleToFloat16(
1120 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1121 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1122}
1123
1124void CodeGenerator::visitDoubleToFloat32ToFloat16(
1125 LDoubleToFloat32ToFloat16* lir) {
1126 masm.convertDoubleToFloat16(
1127 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1128 ToRegister(lir->temp0()), ToRegister(lir->temp1()));
1129}
1130
1131void CodeGenerator::visitFloat32ToFloat16(LFloat32ToFloat16* lir) {
1132 LiveRegisterSet volatileRegs;
1133 if (!MacroAssembler::SupportsFloat32To16()) {
1134 volatileRegs = liveVolatileRegs(lir);
1135 }
1136 masm.convertFloat32ToFloat16(
1137 ToFloatRegister(lir->input()), ToFloatRegister(lir->output()),
1138 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1139}
1140
1141void CodeGenerator::visitInt32ToFloat16(LInt32ToFloat16* lir) {
1142 LiveRegisterSet volatileRegs;
1143 if (!MacroAssembler::SupportsFloat32To16()) {
1144 volatileRegs = liveVolatileRegs(lir);
1145 }
1146 masm.convertInt32ToFloat16(
1147 ToRegister(lir->input()), ToFloatRegister(lir->output()),
1148 ToTempRegisterOrInvalid(lir->temp0()), volatileRegs);
1149}
1150
1151void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) {
1152 Label fail;
1153 FloatRegister input = ToFloatRegister(lir->input());
1154 Register output = ToRegister(lir->output());
1155 masm.convertDoubleToInt32(input, output, &fail,
1156 lir->mir()->needsNegativeZeroCheck());
1157 bailoutFrom(&fail, lir->snapshot());
1158}
1159
1160void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) {
1161 Label fail;
1162 FloatRegister input = ToFloatRegister(lir->input());
1163 Register output = ToRegister(lir->output());
1164 masm.convertFloat32ToInt32(input, output, &fail,
1165 lir->mir()->needsNegativeZeroCheck());
1166 bailoutFrom(&fail, lir->snapshot());
1167}
1168
1169void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) {
1170#ifdef JS_64BIT1
1171 // This LIR instruction is only used if the input can be negative.
1172 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"
, 1172); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->canBeNegative()"
")"); do { *((volatile int*)__null) = 1172; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1173
1174 Register output = ToRegister(lir->output());
1175 const LAllocation* input = lir->input();
1176 if (input->isRegister()) {
1177 masm.move32SignExtendToPtr(ToRegister(input), output);
1178 } else {
1179 masm.load32SignExtendToPtr(ToAddress(input), output);
1180 }
1181#else
1182 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"
, 1182); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms"
")"); do { *((volatile int*)__null) = 1182; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1183#endif
1184}
1185
1186void CodeGenerator::visitNonNegativeIntPtrToInt32(
1187 LNonNegativeIntPtrToInt32* lir) {
1188#ifdef JS_64BIT1
1189 Register output = ToRegister(lir->output());
1190 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"
, 1190); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output"
")"); do { *((volatile int*)__null) = 1190; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1191
1192 Label bail;
1193 masm.guardNonNegativeIntPtrToInt32(output, &bail);
1194 bailoutFrom(&bail, lir->snapshot());
1195#else
1196 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"
, 1196); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms"
")"); do { *((volatile int*)__null) = 1196; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1197#endif
1198}
1199
1200void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) {
1201 Register input = ToRegister(lir->input());
1202 FloatRegister output = ToFloatRegister(lir->output());
1203 masm.convertIntPtrToDouble(input, output);
1204}
1205
1206void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) {
1207 Register output = ToRegister(lir->output());
1208 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"
, 1208); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output"
")"); do { *((volatile int*)__null) = 1208; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1209
1210 uint32_t byteSize = lir->mir()->byteSize();
1211
1212#ifdef DEBUG1
1213 Label ok;
1214 masm.branchTestPtr(Assembler::NotSigned, output, output, &ok);
1215 masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength");
1216 masm.bind(&ok);
1217#endif
1218
1219 Label bail;
1220 masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail);
1221 bailoutFrom(&bail, lir->snapshot());
1222}
1223
1224void CodeGenerator::emitOOLTestObject(Register objreg,
1225 Label* ifEmulatesUndefined,
1226 Label* ifDoesntEmulateUndefined,
1227 Register scratch) {
1228 saveVolatile(scratch);
1229#if defined(DEBUG1) || defined(FUZZING)
1230 masm.loadPtr(AbsoluteAddress(
1231 gen->runtime->addressOfHasSeenObjectEmulateUndefinedFuse()),
1232 scratch);
1233 using Fn = bool (*)(JSObject* obj, size_t fuseValue);
1234 masm.setupAlignedABICall();
1235 masm.passABIArg(objreg);
1236 masm.passABIArg(scratch);
1237 masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>();
1238#else
1239 using Fn = bool (*)(JSObject* obj);
1240 masm.setupAlignedABICall();
1241 masm.passABIArg(objreg);
1242 masm.callWithABI<Fn, js::EmulatesUndefined>();
1243#endif
1244 masm.storeCallPointerResult(scratch);
1245 restoreVolatile(scratch);
1246
1247 masm.branchIfTrueBool(scratch, ifEmulatesUndefined);
1248 masm.jump(ifDoesntEmulateUndefined);
1249}
1250
1251// Base out-of-line code generator for all tests of the truthiness of an
1252// object, where the object might not be truthy. (Recall that per spec all
1253// objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class
1254// flag to permit objects to look like |undefined| in certain contexts,
1255// including in object truthiness testing.) We check truthiness inline except
1256// when we're testing it on a proxy, in which case out-of-line code will call
1257// EmulatesUndefined for a conclusive answer.
1258class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> {
1259 Register objreg_;
1260 Register scratch_;
1261
1262 Label* ifEmulatesUndefined_;
1263 Label* ifDoesntEmulateUndefined_;
1264
1265#ifdef DEBUG1
1266 bool initialized() { return ifEmulatesUndefined_ != nullptr; }
1267#endif
1268
1269 public:
1270 OutOfLineTestObject()
1271 : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {}
1272
1273 void accept(CodeGenerator* codegen) final {
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 codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_,
1276 ifDoesntEmulateUndefined_, scratch_);
1277 }
1278
1279 // Specify the register where the object to be tested is found, labels to
1280 // jump to if the object is truthy or falsy, and a scratch register for
1281 // use in the out-of-line path.
1282 void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined,
1283 Label* ifDoesntEmulateUndefined, Register scratch) {
1284 MOZ_ASSERT(!initialized())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!initialized())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!initialized()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!initialized()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1284); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!initialized()"
")"); do { *((volatile int*)__null) = 1284; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1285 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"
, 1285); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ifEmulatesUndefined"
")"); do { *((volatile int*)__null) = 1285; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1286 objreg_ = objreg;
1287 scratch_ = scratch;
1288 ifEmulatesUndefined_ = ifEmulatesUndefined;
1289 ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined;
1290 }
1291};
1292
1293// A subclass of OutOfLineTestObject containing two extra labels, for use when
1294// the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line
1295// code. The user should bind these labels in inline code, and specify them as
1296// targets via setInputAndTargets, as appropriate.
1297class OutOfLineTestObjectWithLabels : public OutOfLineTestObject {
1298 Label label1_;
1299 Label label2_;
1300
1301 public:
1302 OutOfLineTestObjectWithLabels() = default;
1303
1304 Label* label1() { return &label1_; }
1305 Label* label2() { return &label2_; }
1306};
1307
1308void CodeGenerator::testObjectEmulatesUndefinedKernel(
1309 Register objreg, Label* ifEmulatesUndefined,
1310 Label* ifDoesntEmulateUndefined, Register scratch,
1311 OutOfLineTestObject* ool) {
1312 ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
1313 scratch);
1314
1315 // Perform a fast-path check of the object's class flags if the object's
1316 // not a proxy. Let out-of-line code handle the slow cases that require
1317 // saving registers, making a function call, and restoring registers.
1318 masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(),
1319 ifEmulatesUndefined);
1320}
1321
1322void CodeGenerator::branchTestObjectEmulatesUndefined(
1323 Register objreg, Label* ifEmulatesUndefined,
1324 Label* ifDoesntEmulateUndefined, Register scratch,
1325 OutOfLineTestObject* ool) {
1326 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"
, 1327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()"
") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { *((volatile int*)__null) = 1327; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1327 "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"
, 1327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()"
") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { *((volatile int*)__null) = 1327; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1328
1329 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1330 ifDoesntEmulateUndefined, scratch, ool);
1331 masm.bind(ifDoesntEmulateUndefined);
1332}
1333
1334void CodeGenerator::testObjectEmulatesUndefined(Register objreg,
1335 Label* ifEmulatesUndefined,
1336 Label* ifDoesntEmulateUndefined,
1337 Register scratch,
1338 OutOfLineTestObject* ool) {
1339 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1340 ifDoesntEmulateUndefined, scratch, ool);
1341 masm.jump(ifDoesntEmulateUndefined);
1342}
1343
1344void CodeGenerator::testValueTruthyForType(
1345 JSValueType type, ScratchTagScope& tag, const ValueOperand& value,
1346 Register tempToUnbox, Register temp, FloatRegister floatTemp,
1347 Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool,
1348 bool skipTypeTest) {
1349#ifdef DEBUG1
1350 if (skipTypeTest) {
1351 Label expected;
1352 masm.branchTestType(Assembler::Equal, tag, type, &expected);
1353 masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType");
1354 masm.bind(&expected);
1355 }
1356#endif
1357
1358 // Handle irregular types first.
1359 switch (type) {
1360 case JSVAL_TYPE_UNDEFINED:
1361 case JSVAL_TYPE_NULL:
1362 // Undefined and null are falsy.
1363 if (!skipTypeTest) {
1364 masm.branchTestType(Assembler::Equal, tag, type, ifFalsy);
1365 } else {
1366 masm.jump(ifFalsy);
1367 }
1368 return;
1369 case JSVAL_TYPE_SYMBOL:
1370 // Symbols are truthy.
1371 if (!skipTypeTest) {
1372 masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy);
1373 } else {
1374 masm.jump(ifTruthy);
1375 }
1376 return;
1377 case JSVAL_TYPE_OBJECT: {
1378 Label notObject;
1379 if (!skipTypeTest) {
1380 masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
1381 }
1382 ScratchTagScopeRelease _(&tag);
1383 Register objreg = masm.extractObject(value, tempToUnbox);
1384 testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool);
1385 masm.bind(&notObject);
1386 return;
1387 }
1388 default:
1389 break;
1390 }
1391
1392 // Check the type of the value (unless this is the last possible type).
1393 Label differentType;
1394 if (!skipTypeTest) {
1395 masm.branchTestType(Assembler::NotEqual, tag, type, &differentType);
1396 }
1397
1398 // Branch if the value is falsy.
1399 ScratchTagScopeRelease _(&tag);
1400 switch (type) {
1401 case JSVAL_TYPE_BOOLEAN: {
1402 masm.branchTestBooleanTruthy(false, value, ifFalsy);
1403 break;
1404 }
1405 case JSVAL_TYPE_INT32: {
1406 masm.branchTestInt32Truthy(false, value, ifFalsy);
1407 break;
1408 }
1409 case JSVAL_TYPE_STRING: {
1410 masm.branchTestStringTruthy(false, value, ifFalsy);
1411 break;
1412 }
1413 case JSVAL_TYPE_BIGINT: {
1414 masm.branchTestBigIntTruthy(false, value, ifFalsy);
1415 break;
1416 }
1417 case JSVAL_TYPE_DOUBLE: {
1418 masm.unboxDouble(value, floatTemp);
1419 masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy);
1420 break;
1421 }
1422 default:
1423 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"
, 1423); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected value type"
")"); do { *((volatile int*)__null) = 1423; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1424 }
1425
1426 // If we reach this point, the value is truthy. We fall through for
1427 // truthy on the last test; otherwise, branch.
1428 if (!skipTypeTest) {
1429 masm.jump(ifTruthy);
1430 }
1431
1432 masm.bind(&differentType);
1433}
1434
1435void CodeGenerator::testValueTruthy(const ValueOperand& value,
1436 Register tempToUnbox, Register temp,
1437 FloatRegister floatTemp,
1438 const TypeDataList& observedTypes,
1439 Label* ifTruthy, Label* ifFalsy,
1440 OutOfLineTestObject* ool) {
1441 ScratchTagScope tag(masm, value);
1442 masm.splitTagForTest(value, tag);
1443
1444 const std::initializer_list<JSValueType> defaultOrder = {
1445 JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN,
1446 JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING,
1447 JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
1448
1449 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
1450
1451 // Generate tests for previously observed types first.
1452 // The TypeDataList is sorted by descending frequency.
1453 for (auto& observed : observedTypes) {
1454 JSValueType type = observed.type();
1455 remaining -= type;
1456
1457 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1458 ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false);
1459 }
1460
1461 // Generate tests for remaining types.
1462 for (auto type : defaultOrder) {
1463 if (!remaining.contains(type)) {
1464 continue;
1465 }
1466 remaining -= type;
1467
1468 // We don't need a type test for the last possible type.
1469 bool skipTypeTest = remaining.isEmpty();
1470 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1471 ifTruthy, ifFalsy, ool, skipTypeTest);
1472 }
1473 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"
, 1473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()"
")"); do { *((volatile int*)__null) = 1473; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1474
1475 // We fall through if the final test is truthy.
1476}
1477
1478void CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test) {
1479 Register input = ToRegister(test->input());
1480 MBasicBlock* ifTrue = test->ifTrue();
1481 MBasicBlock* ifFalse = test->ifFalse();
1482
1483 if (isNextBlock(ifFalse->lir())) {
1484 masm.branchTest32(Assembler::NonZero, input, input,
1485 getJumpLabelForBranch(ifTrue));
1486 } else {
1487 masm.branchTest32(Assembler::Zero, input, input,
1488 getJumpLabelForBranch(ifFalse));
1489 jumpToBlock(ifTrue);
1490 }
1491}
1492
1493void CodeGenerator::visitTestIPtrAndBranch(LTestIPtrAndBranch* test) {
1494 Register input = ToRegister(test->input());
1495 MBasicBlock* ifTrue = test->ifTrue();
1496 MBasicBlock* ifFalse = test->ifFalse();
1497
1498 if (isNextBlock(ifFalse->lir())) {
1499 masm.branchTestPtr(Assembler::NonZero, input, input,
1500 getJumpLabelForBranch(ifTrue));
1501 } else {
1502 masm.branchTestPtr(Assembler::Zero, input, input,
1503 getJumpLabelForBranch(ifFalse));
1504 jumpToBlock(ifTrue);
1505 }
1506}
1507
1508void CodeGenerator::visitTestI64AndBranch(LTestI64AndBranch* test) {
1509 Register64 input = ToRegister64(test->input());
1510 MBasicBlock* ifTrue = test->ifTrue();
1511 MBasicBlock* ifFalse = test->ifFalse();
1512
1513 if (isNextBlock(ifFalse->lir())) {
1514 masm.branchTest64(Assembler::NonZero, input, input,
1515 getJumpLabelForBranch(ifTrue));
1516 } else if (isNextBlock(ifTrue->lir())) {
1517 masm.branchTest64(Assembler::Zero, input, input,
1518 getJumpLabelForBranch(ifFalse));
1519 } else {
1520 masm.branchTest64(Assembler::NonZero, input, input,
1521 getJumpLabelForBranch(ifTrue),
1522 getJumpLabelForBranch(ifFalse));
1523 }
1524}
1525
1526void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) {
1527 Register input = ToRegister(lir->input());
1528 MBasicBlock* ifTrue = lir->ifTrue();
1529 MBasicBlock* ifFalse = lir->ifFalse();
1530
1531 if (isNextBlock(ifFalse->lir())) {
1532 masm.branchIfBigIntIsNonZero(input, getJumpLabelForBranch(ifTrue));
1533 } else {
1534 masm.branchIfBigIntIsZero(input, getJumpLabelForBranch(ifFalse));
1535 jumpToBlock(ifTrue);
1536 }
1537}
1538
1539static Assembler::Condition ReverseCondition(Assembler::Condition condition) {
1540 switch (condition) {
1541 case Assembler::Equal:
1542 case Assembler::NotEqual:
1543 return condition;
1544 case Assembler::Above:
1545 return Assembler::Below;
1546 case Assembler::AboveOrEqual:
1547 return Assembler::BelowOrEqual;
1548 case Assembler::Below:
1549 return Assembler::Above;
1550 case Assembler::BelowOrEqual:
1551 return Assembler::AboveOrEqual;
1552 case Assembler::GreaterThan:
1553 return Assembler::LessThan;
1554 case Assembler::GreaterThanOrEqual:
1555 return Assembler::LessThanOrEqual;
1556 case Assembler::LessThan:
1557 return Assembler::GreaterThan;
1558 case Assembler::LessThanOrEqual:
1559 return Assembler::GreaterThanOrEqual;
1560 default:
1561 break;
1562 }
1563 MOZ_CRASH("unhandled condition")do { do { } while (false); MOZ_ReportCrash("" "unhandled condition"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1563); AnnotateMozCrashReason("MOZ_CRASH(" "unhandled condition"
")"); do { *((volatile int*)__null) = 1563; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1564}
1565
1566void CodeGenerator::visitCompare(LCompare* comp) {
1567 MCompare::CompareType compareType = comp->mir()->compareType();
1568 Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop());
1569 Register left = ToRegister(comp->left());
1570 const LAllocation* right = comp->right();
1571 Register output = ToRegister(comp->output());
1572
1573 if (compareType == MCompare::Compare_Object ||
1574 compareType == MCompare::Compare_Symbol ||
1575 compareType == MCompare::Compare_IntPtr ||
1576 compareType == MCompare::Compare_UIntPtr ||
1577 compareType == MCompare::Compare_WasmAnyRef) {
1578 if (right->isConstant()) {
1579 MOZ_ASSERT(compareType == MCompare::Compare_IntPtr ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_IntPtr || compareType
== MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr
|| compareType == MCompare::Compare_UIntPtr))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { *((volatile int*)__null) = 1580; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1580 compareType == MCompare::Compare_UIntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_IntPtr || compareType
== MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr
|| compareType == MCompare::Compare_UIntPtr))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { *((volatile int*)__null) = 1580; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1581 masm.cmpPtrSet(cond, left, ImmWord(ToInt32(right)), output);
1582 } else if (right->isRegister()) {
1583 masm.cmpPtrSet(cond, left, ToRegister(right), output);
1584 } else {
1585 masm.cmpPtrSet(ReverseCondition(cond), ToAddress(right), left, output);
1586 }
1587 return;
1588 }
1589
1590 MOZ_ASSERT(compareType == MCompare::Compare_Int32 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int32 || compareType
== MCompare::Compare_UInt32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32
|| compareType == MCompare::Compare_UInt32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1591); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { *((volatile int*)__null) = 1591; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1591 compareType == MCompare::Compare_UInt32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int32 || compareType
== MCompare::Compare_UInt32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32
|| compareType == MCompare::Compare_UInt32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1591); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { *((volatile int*)__null) = 1591; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1592
1593 if (right->isConstant()) {
1594 masm.cmp32Set(cond, left, Imm32(ToInt32(right)), output);
1595 } else if (right->isRegister()) {
1596 masm.cmp32Set(cond, left, ToRegister(right), output);
1597 } else {
1598 masm.cmp32Set(ReverseCondition(cond), ToAddress(right), left, output);
1599 }
1600}
1601
1602void CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp) {
1603 MCompare::CompareType compareType = comp->cmpMir()->compareType();
1604 Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop());
1605 Register left = ToRegister(comp->left());
1606 const LAllocation* right = comp->right();
1607
1608 MBasicBlock* ifTrue = comp->ifTrue();
1609 MBasicBlock* ifFalse = comp->ifFalse();
1610
1611 // If the next block is the true case, invert the condition to fall through.
1612 Label* label;
1613 if (isNextBlock(ifTrue->lir())) {
1614 cond = Assembler::InvertCondition(cond);
1615 label = getJumpLabelForBranch(ifFalse);
1616 } else {
1617 label = getJumpLabelForBranch(ifTrue);
1618 }
1619
1620 if (compareType == MCompare::Compare_Object ||
1621 compareType == MCompare::Compare_Symbol ||
1622 compareType == MCompare::Compare_IntPtr ||
1623 compareType == MCompare::Compare_UIntPtr ||
1624 compareType == MCompare::Compare_WasmAnyRef) {
1625 if (right->isConstant()) {
1626 MOZ_ASSERT(compareType == MCompare::Compare_IntPtr ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_IntPtr || compareType
== MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr
|| compareType == MCompare::Compare_UIntPtr))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { *((volatile int*)__null) = 1627; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1627 compareType == MCompare::Compare_UIntPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_IntPtr || compareType
== MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr
|| compareType == MCompare::Compare_UIntPtr))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr"
")"); do { *((volatile int*)__null) = 1627; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1628 masm.branchPtr(cond, left, ImmWord(ToInt32(right)), label);
1629 } else if (right->isRegister()) {
1630 masm.branchPtr(cond, left, ToRegister(right), label);
1631 } else {
1632 masm.branchPtr(ReverseCondition(cond), ToAddress(right), left, label);
1633 }
1634 } else {
1635 MOZ_ASSERT(compareType == MCompare::Compare_Int32 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int32 || compareType
== MCompare::Compare_UInt32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32
|| compareType == MCompare::Compare_UInt32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { *((volatile int*)__null) = 1636; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1636 compareType == MCompare::Compare_UInt32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int32 || compareType
== MCompare::Compare_UInt32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32
|| compareType == MCompare::Compare_UInt32))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32"
")"); do { *((volatile int*)__null) = 1636; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1637
1638 if (right->isConstant()) {
1639 masm.branch32(cond, left, Imm32(ToInt32(right)), label);
1640 } else if (right->isRegister()) {
1641 masm.branch32(cond, left, ToRegister(right), label);
1642 } else {
1643 masm.branch32(ReverseCondition(cond), ToAddress(right), left, label);
1644 }
1645 }
1646
1647 if (!isNextBlock(ifTrue->lir())) {
1648 jumpToBlock(ifFalse);
1649 }
1650}
1651
1652void CodeGenerator::visitCompareI64(LCompareI64* lir) {
1653 MCompare::CompareType compareType = lir->mir()->compareType();
1654 MOZ_ASSERT(compareType == MCompare::Compare_Int64 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int64 || compareType
== MCompare::Compare_UInt64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64
|| compareType == MCompare::Compare_UInt64))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { *((volatile int*)__null) = 1655; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1655 compareType == MCompare::Compare_UInt64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int64 || compareType
== MCompare::Compare_UInt64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64
|| compareType == MCompare::Compare_UInt64))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { *((volatile int*)__null) = 1655; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1656 bool isSigned = compareType == MCompare::Compare_Int64;
1657 Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
1658 Register64 left = ToRegister64(lir->left());
1659 LInt64Allocation right = lir->right();
1660 Register output = ToRegister(lir->output());
1661
1662 if (IsConstant(right)) {
1663 masm.cmp64Set(cond, left, Imm64(ToInt64(right)), output);
1664 } else if (IsRegister64(right)) {
1665 masm.cmp64Set(cond, left, ToRegister64(right), output);
1666 } else {
1667 masm.cmp64Set(ReverseCondition(cond), ToAddress(right), left, output);
1668 }
1669}
1670
1671void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) {
1672 MCompare::CompareType compareType = lir->cmpMir()->compareType();
1673 MOZ_ASSERT(compareType == MCompare::Compare_Int64 ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int64 || compareType
== MCompare::Compare_UInt64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64
|| compareType == MCompare::Compare_UInt64))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { *((volatile int*)__null) = 1674; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1674 compareType == MCompare::Compare_UInt64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(compareType == MCompare::Compare_Int64 || compareType
== MCompare::Compare_UInt64)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64
|| compareType == MCompare::Compare_UInt64))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64"
")"); do { *((volatile int*)__null) = 1674; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1675 bool isSigned = compareType == MCompare::Compare_Int64;
1676 Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned);
1677 Register64 left = ToRegister64(lir->left());
1678 LInt64Allocation right = lir->right();
1679
1680 MBasicBlock* ifTrue = lir->ifTrue();
1681 MBasicBlock* ifFalse = lir->ifFalse();
1682
1683 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1684 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1685
1686 // If the next block is the true case, invert the condition to fall through.
1687 if (isNextBlock(ifTrue->lir())) {
1688 cond = Assembler::InvertCondition(cond);
1689 trueLabel = falseLabel;
1690 falseLabel = nullptr;
1691 } else if (isNextBlock(ifFalse->lir())) {
1692 falseLabel = nullptr;
1693 }
1694
1695 if (IsConstant(right)) {
1696 masm.branch64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel);
1697 } else if (IsRegister64(right)) {
1698 masm.branch64(cond, left, ToRegister64(right), trueLabel, falseLabel);
1699 } else {
1700 masm.branch64(ReverseCondition(cond), ToAddress(right), left, trueLabel,
1701 falseLabel);
1702 }
1703}
1704
1705void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) {
1706 Assembler::Condition cond = baab->cond();
1707 Register left = ToRegister(baab->left());
1708 const LAllocation* right = baab->right();
1709
1710 MBasicBlock* ifTrue = baab->ifTrue();
1711 MBasicBlock* ifFalse = baab->ifFalse();
1712
1713 // If the next block is the true case, invert the condition to fall through.
1714 Label* label;
1715 if (isNextBlock(ifTrue->lir())) {
1716 cond = Assembler::InvertCondition(cond);
1717 label = getJumpLabelForBranch(ifFalse);
1718 } else {
1719 label = getJumpLabelForBranch(ifTrue);
1720 }
1721
1722 if (right->isConstant()) {
1723 masm.branchTest32(cond, left, Imm32(ToInt32(right)), label);
1724 } else {
1725 masm.branchTest32(cond, left, ToRegister(right), label);
1726 }
1727
1728 if (!isNextBlock(ifTrue->lir())) {
1729 jumpToBlock(ifFalse);
1730 }
1731}
1732
1733void CodeGenerator::visitBitAnd64AndBranch(LBitAnd64AndBranch* baab) {
1734 Assembler::Condition cond = baab->cond();
1735 Register64 left = ToRegister64(baab->left());
1736 LInt64Allocation right = baab->right();
1737
1738 MBasicBlock* ifTrue = baab->ifTrue();
1739 MBasicBlock* ifFalse = baab->ifFalse();
1740
1741 Label* trueLabel = getJumpLabelForBranch(ifTrue);
1742 Label* falseLabel = getJumpLabelForBranch(ifFalse);
1743
1744 // If the next block is the true case, invert the condition to fall through.
1745 if (isNextBlock(ifTrue->lir())) {
1746 cond = Assembler::InvertCondition(cond);
1747 trueLabel = falseLabel;
1748 falseLabel = nullptr;
1749 } else if (isNextBlock(ifFalse->lir())) {
1750 falseLabel = nullptr;
1751 }
1752
1753 if (IsConstant(right)) {
1754 masm.branchTest64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel);
1755 } else {
1756 masm.branchTest64(cond, left, ToRegister64(right), trueLabel, falseLabel);
1757 }
1758}
1759
1760void CodeGenerator::assertObjectDoesNotEmulateUndefined(
1761 Register input, Register temp, const MInstruction* mir) {
1762#if defined(DEBUG1) || defined(FUZZING)
1763 // Validate that the object indeed doesn't have the emulates undefined flag.
1764 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
1765 addOutOfLineCode(ool, mir);
1766
1767 Label* doesNotEmulateUndefined = ool->label1();
1768 Label* emulatesUndefined = ool->label2();
1769
1770 testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined,
1771 temp, ool);
1772 masm.bind(emulatesUndefined);
1773 masm.assumeUnreachable(
1774 "Found an object emulating undefined while the fuse is intact");
1775 masm.bind(doesNotEmulateUndefined);
1776#endif
1777}
1778
1779void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) {
1780 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1781 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1782 Register input = ToRegister(lir->input());
1783
1784 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
1785 if (intact) {
1786 assertObjectDoesNotEmulateUndefined(input, ToRegister(lir->temp()),
1787 lir->mir());
1788 // Bug 1874905: It would be fantastic if this could be optimized out
1789 masm.jump(truthy);
1790 } else {
1791 auto* ool = new (alloc()) OutOfLineTestObject();
1792 addOutOfLineCode(ool, lir->mir());
1793
1794 testObjectEmulatesUndefined(input, falsy, truthy, ToRegister(lir->temp()),
1795 ool);
1796 }
1797}
1798
1799void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) {
1800 auto* ool = new (alloc()) OutOfLineTestObject();
1801 addOutOfLineCode(ool, lir->mir());
1802
1803 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1804 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1805
1806 ValueOperand input = ToValue(lir, LTestVAndBranch::Input);
1807 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
1808 Register temp = ToRegister(lir->temp2());
1809 FloatRegister floatTemp = ToFloatRegister(lir->tempFloat());
1810 const TypeDataList& observedTypes = lir->mir()->observedTypes();
1811
1812 testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy,
1813 falsy, ool);
1814 masm.jump(truthy);
1815}
1816
1817void CodeGenerator::visitBooleanToString(LBooleanToString* lir) {
1818 Register input = ToRegister(lir->input());
1819 Register output = ToRegister(lir->output());
1820 const JSAtomState& names = gen->runtime->names();
1821 Label true_, done;
1822
1823 masm.branchTest32(Assembler::NonZero, input, input, &true_);
1824 masm.movePtr(ImmGCPtr(names.false_), output);
1825 masm.jump(&done);
1826
1827 masm.bind(&true_);
1828 masm.movePtr(ImmGCPtr(names.true_), output);
1829
1830 masm.bind(&done);
1831}
1832
1833void CodeGenerator::visitIntToString(LIntToString* lir) {
1834 Register input = ToRegister(lir->input());
1835 Register output = ToRegister(lir->output());
1836
1837 using Fn = JSLinearString* (*)(JSContext*, int);
1838 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
1839 lir, ArgList(input), StoreRegisterTo(output));
1840
1841 masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(),
1842 ool->entry());
1843
1844 masm.bind(ool->rejoin());
1845}
1846
1847void CodeGenerator::visitDoubleToString(LDoubleToString* lir) {
1848 FloatRegister input = ToFloatRegister(lir->input());
1849 Register temp = ToRegister(lir->temp0());
1850 Register output = ToRegister(lir->output());
1851
1852 using Fn = JSString* (*)(JSContext*, double);
1853 OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>(
1854 lir, ArgList(input), StoreRegisterTo(output));
1855
1856 // Try double to integer conversion and run integer to string code.
1857 masm.convertDoubleToInt32(input, temp, ool->entry(), false);
1858 masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(),
1859 ool->entry());
1860
1861 masm.bind(ool->rejoin());
1862}
1863
1864void CodeGenerator::visitValueToString(LValueToString* lir) {
1865 ValueOperand input = ToValue(lir, LValueToString::InputIndex);
1866 Register output = ToRegister(lir->output());
1867
1868 using Fn = JSString* (*)(JSContext*, HandleValue);
1869 OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>(
1870 lir, ArgList(input), StoreRegisterTo(output));
1871
1872 Label done;
1873 Register tag = masm.extractTag(input, output);
1874 const JSAtomState& names = gen->runtime->names();
1875
1876 // String
1877 {
1878 Label notString;
1879 masm.branchTestString(Assembler::NotEqual, tag, &notString);
1880 masm.unboxString(input, output);
1881 masm.jump(&done);
1882 masm.bind(&notString);
1883 }
1884
1885 // Integer
1886 {
1887 Label notInteger;
1888 masm.branchTestInt32(Assembler::NotEqual, tag, &notInteger);
1889 Register unboxed = ToTempUnboxRegister(lir->temp0());
1890 unboxed = masm.extractInt32(input, unboxed);
1891 masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(),
1892 ool->entry());
1893 masm.jump(&done);
1894 masm.bind(&notInteger);
1895 }
1896
1897 // Double
1898 {
1899 // Note: no fastpath. Need two extra registers and can only convert doubles
1900 // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT.
1901 masm.branchTestDouble(Assembler::Equal, tag, ool->entry());
1902 }
1903
1904 // Undefined
1905 {
1906 Label notUndefined;
1907 masm.branchTestUndefined(Assembler::NotEqual, tag, &notUndefined);
1908 masm.movePtr(ImmGCPtr(names.undefined), output);
1909 masm.jump(&done);
1910 masm.bind(&notUndefined);
1911 }
1912
1913 // Null
1914 {
1915 Label notNull;
1916 masm.branchTestNull(Assembler::NotEqual, tag, &notNull);
1917 masm.movePtr(ImmGCPtr(names.null), output);
1918 masm.jump(&done);
1919 masm.bind(&notNull);
1920 }
1921
1922 // Boolean
1923 {
1924 Label notBoolean, true_;
1925 masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
1926 masm.branchTestBooleanTruthy(true, input, &true_);
1927 masm.movePtr(ImmGCPtr(names.false_), output);
1928 masm.jump(&done);
1929 masm.bind(&true_);
1930 masm.movePtr(ImmGCPtr(names.true_), output);
1931 masm.jump(&done);
1932 masm.bind(&notBoolean);
1933 }
1934
1935 // Objects/symbols are only possible when |mir->mightHaveSideEffects()|.
1936 if (lir->mir()->mightHaveSideEffects()) {
1937 // Object
1938 if (lir->mir()->supportSideEffects()) {
1939 masm.branchTestObject(Assembler::Equal, tag, ool->entry());
1940 } else {
1941 // Bail.
1942 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"
, 1942); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { *((volatile int*)__null) = 1942; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1943 Label bail;
1944 masm.branchTestObject(Assembler::Equal, tag, &bail);
1945 bailoutFrom(&bail, lir->snapshot());
1946 }
1947
1948 // Symbol
1949 if (lir->mir()->supportSideEffects()) {
1950 masm.branchTestSymbol(Assembler::Equal, tag, ool->entry());
1951 } else {
1952 // Bail.
1953 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"
, 1953); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { *((volatile int*)__null) = 1953; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1954 Label bail;
1955 masm.branchTestSymbol(Assembler::Equal, tag, &bail);
1956 bailoutFrom(&bail, lir->snapshot());
1957 }
1958 }
1959
1960 // BigInt
1961 {
1962 // No fastpath currently implemented.
1963 masm.branchTestBigInt(Assembler::Equal, tag, ool->entry());
1964 }
1965
1966 masm.assumeUnreachable("Unexpected type for LValueToString.");
1967
1968 masm.bind(&done);
1969 masm.bind(ool->rejoin());
1970}
1971
1972using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**);
1973
1974static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder,
1975 size_t offset, Register buffer,
1976 LiveGeneralRegisterSet& liveVolatiles,
1977 StoreBufferMutationFn fun) {
1978 Label callVM;
1979 Label exit;
1980
1981 // Call into the VM to barrier the write. The only registers that need to
1982 // be preserved are those in liveVolatiles, so once they are saved on the
1983 // stack all volatile registers are available for use.
1984 masm.bind(&callVM);
1985 masm.PushRegsInMask(liveVolatiles);
1986
1987 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
1988 regs.takeUnchecked(buffer);
1989 regs.takeUnchecked(holder);
1990 Register addrReg = regs.takeAny();
1991
1992 masm.computeEffectiveAddress(Address(holder, offset), addrReg);
1993
1994 bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>();
1995 if (needExtraReg) {
1996 masm.push(holder);
1997 masm.setupUnalignedABICall(holder);
1998 } else {
1999 masm.setupUnalignedABICall(regs.takeAny());
2000 }
2001 masm.passABIArg(buffer);
2002 masm.passABIArg(addrReg);
2003 masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun),
2004 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
2005
2006 if (needExtraReg) {
2007 masm.pop(holder);
2008 }
2009 masm.PopRegsInMask(liveVolatiles);
2010 masm.bind(&exit);
2011}
2012
2013// Warning: this function modifies prev and next.
2014static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder,
2015 size_t offset, Register prev, Register next,
2016 LiveGeneralRegisterSet& liveVolatiles) {
2017 Label exit;
2018 Label checkRemove, putCell;
2019
2020 // if (next && (buffer = next->storeBuffer()))
2021 // but we never pass in nullptr for next.
2022 Register storebuffer = next;
2023 masm.loadStoreBuffer(next, storebuffer);
2024 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove);
2025
2026 // if (prev && prev->storeBuffer())
2027 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell);
2028 masm.loadStoreBuffer(prev, prev);
2029 masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit);
2030
2031 // buffer->putCell(cellp)
2032 masm.bind(&putCell);
2033 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
2034 JSString::addCellAddressToStoreBuffer);
2035 masm.jump(&exit);
2036
2037 // if (prev && (buffer = prev->storeBuffer()))
2038 masm.bind(&checkRemove);
2039 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit);
2040 masm.loadStoreBuffer(prev, storebuffer);
2041 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit);
2042 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
2043 JSString::removeCellAddressFromStoreBuffer);
2044
2045 masm.bind(&exit);
2046}
2047
2048void CodeGenerator::visitRegExp(LRegExp* lir) {
2049 Register output = ToRegister(lir->output());
2050 Register temp = ToRegister(lir->temp0());
2051 JSObject* source = lir->mir()->source();
2052
2053 using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>);
2054 OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>(
2055 lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output));
2056 if (lir->mir()->hasShared()) {
2057 TemplateObject templateObject(source);
2058 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
2059 ool->entry());
2060 } else {
2061 masm.jump(ool->entry());
2062 }
2063 masm.bind(ool->rejoin());
2064}
2065
2066static constexpr int32_t RegExpPairsVectorStartOffset(
2067 int32_t inputOutputDataStartOffset) {
2068 return inputOutputDataStartOffset + int32_t(InputOutputDataSize) +
2069 int32_t(sizeof(MatchPairs));
2070}
2071
2072static Address RegExpPairCountAddress(MacroAssembler& masm,
2073 int32_t inputOutputDataStartOffset) {
2074 return Address(FramePointer, inputOutputDataStartOffset +
2075 int32_t(InputOutputDataSize) +
2076 MatchPairs::offsetOfPairCount());
2077}
2078
2079static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp,
2080 Register input, Register lastIndex,
2081 Register staticsReg, Register temp1,
2082 Register temp2, gc::Heap initialStringHeap,
2083 LiveGeneralRegisterSet& volatileRegs) {
2084 Address pendingInputAddress(staticsReg,
2085 RegExpStatics::offsetOfPendingInput());
2086 Address matchesInputAddress(staticsReg,
2087 RegExpStatics::offsetOfMatchesInput());
2088 Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource());
2089 Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex());
2090
2091 masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String);
2092 masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String);
2093 masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String);
2094
2095 if (initialStringHeap == gc::Heap::Default) {
2096 // Writing into RegExpStatics tenured memory; must post-barrier.
2097 if (staticsReg.volatile_()) {
2098 volatileRegs.add(staticsReg);
2099 }
2100
2101 masm.loadPtr(pendingInputAddress, temp1);
2102 masm.storePtr(input, pendingInputAddress);
2103 masm.movePtr(input, temp2);
2104 EmitPostWriteBarrierS(masm, staticsReg,
2105 RegExpStatics::offsetOfPendingInput(),
2106 temp1 /* prev */, temp2 /* next */, volatileRegs);
2107
2108 masm.loadPtr(matchesInputAddress, temp1);
2109 masm.storePtr(input, matchesInputAddress);
2110 masm.movePtr(input, temp2);
2111 EmitPostWriteBarrierS(masm, staticsReg,
2112 RegExpStatics::offsetOfMatchesInput(),
2113 temp1 /* prev */, temp2 /* next */, volatileRegs);
2114 } else {
2115 masm.debugAssertGCThingIsTenured(input, temp1);
2116 masm.storePtr(input, pendingInputAddress);
2117 masm.storePtr(input, matchesInputAddress);
2118 }
2119
2120 masm.storePtr(lastIndex,
2121 Address(staticsReg, RegExpStatics::offsetOfLazyIndex()));
2122 masm.store32(
2123 Imm32(1),
2124 Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation()));
2125
2126 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset(
2127 RegExpObject::SHARED_SLOT)),
2128 temp1, JSVAL_TYPE_PRIVATE_GCTHING);
2129 masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2);
2130 masm.storePtr(temp2, lazySourceAddress);
2131 static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size");
2132 masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2);
2133 masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags()));
2134}
2135
2136// Prepare an InputOutputData and optional MatchPairs which space has been
2137// allocated for on the stack, and try to execute a RegExp on a string input.
2138// If the RegExp was successfully executed and matched the input, fallthrough.
2139// Otherwise, jump to notFound or failure.
2140//
2141// inputOutputDataStartOffset is the offset relative to the frame pointer
2142// register. This offset is negative for the RegExpExecTest stub.
2143static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp,
2144 Register input, Register lastIndex,
2145 Register temp1, Register temp2,
2146 Register temp3,
2147 int32_t inputOutputDataStartOffset,
2148 gc::Heap initialStringHeap, Label* notFound,
2149 Label* failure) {
2150 JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp");
2151
2152 using irregexp::InputOutputData;
2153
2154 /*
2155 * [SMDOC] Stack layout for PrepareAndExecuteRegExp
2156 *
2157 * Before this function is called, the caller is responsible for
2158 * allocating enough stack space for the following data:
2159 *
2160 * inputOutputDataStartOffset +-----> +---------------+
2161 * |InputOutputData|
2162 * inputStartAddress +----------> inputStart|
2163 * inputEndAddress +----------> inputEnd|
2164 * startIndexAddress +----------> startIndex|
2165 * matchesAddress +----------> matches|-----+
2166 * +---------------+ |
2167 * matchPairs(Address|Offset) +-----> +---------------+ <--+
2168 * | MatchPairs |
2169 * pairCountAddress +----------> count |
2170 * pairsPointerAddress +----------> pairs |-----+
2171 * +---------------+ |
2172 * pairsArray(Address|Offset) +-----> +---------------+ <--+
2173 * | MatchPair |
2174 * firstMatchStartAddress +----------> start | <--+
2175 * | limit | |
2176 * +---------------+ |
2177 * . |
2178 * . Reserved space for
2179 * . RegExpObject::MaxPairCount
2180 * . MatchPair objects
2181 * . |
2182 * +---------------+ |
2183 * | MatchPair | |
2184 * | start | |
2185 * | limit | <--+
2186 * +---------------+
2187 */
2188
2189 int32_t ioOffset = inputOutputDataStartOffset;
2190 int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData));
2191 int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs));
2192
2193 Address inputStartAddress(FramePointer,
2194 ioOffset + InputOutputData::offsetOfInputStart());
2195 Address inputEndAddress(FramePointer,
2196 ioOffset + InputOutputData::offsetOfInputEnd());
2197 Address startIndexAddress(FramePointer,
2198 ioOffset + InputOutputData::offsetOfStartIndex());
2199 Address matchesAddress(FramePointer,
2200 ioOffset + InputOutputData::offsetOfMatches());
2201
2202 Address matchPairsAddress(FramePointer, matchPairsOffset);
2203 Address pairCountAddress(FramePointer,
2204 matchPairsOffset + MatchPairs::offsetOfPairCount());
2205 Address pairsPointerAddress(FramePointer,
2206 matchPairsOffset + MatchPairs::offsetOfPairs());
2207
2208 Address pairsArrayAddress(FramePointer, pairsArrayOffset);
2209 Address firstMatchStartAddress(FramePointer,
2210 pairsArrayOffset + MatchPair::offsetOfStart());
2211
2212 // First, fill in a skeletal MatchPairs instance on the stack. This will be
2213 // passed to the OOL stub in the caller if we aren't able to execute the
2214 // RegExp inline, and that stub needs to be able to determine whether the
2215 // execution finished successfully.
2216
2217 // Initialize MatchPairs::pairCount to 1. The correct value can only
2218 // be determined after loading the RegExpShared. If the RegExpShared
2219 // has Kind::Atom, this is the correct pairCount.
2220 masm.store32(Imm32(1), pairCountAddress);
2221
2222 // Initialize MatchPairs::pairs pointer
2223 masm.computeEffectiveAddress(pairsArrayAddress, temp1);
2224 masm.storePtr(temp1, pairsPointerAddress);
2225
2226 // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch
2227 masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress);
2228
2229 // Determine the set of volatile inputs to save when calling into C++ or
2230 // regexp code.
2231 LiveGeneralRegisterSet volatileRegs;
2232 if (lastIndex.volatile_()) {
2233 volatileRegs.add(lastIndex);
2234 }
2235 if (input.volatile_()) {
2236 volatileRegs.add(input);
2237 }
2238 if (regexp.volatile_()) {
2239 volatileRegs.add(regexp);
2240 }
2241
2242 // Ensure the input string is not a rope.
2243 Label isLinear;
2244 masm.branchIfNotRope(input, &isLinear);
2245 {
2246 masm.PushRegsInMask(volatileRegs);
2247
2248 using Fn = JSLinearString* (*)(JSString*);
2249 masm.setupUnalignedABICall(temp1);
2250 masm.passABIArg(input);
2251 masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>();
2252
2253 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"
, 2253); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)"
")"); do { *((volatile int*)__null) = 2253; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2254 masm.storeCallPointerResult(temp1);
2255 masm.PopRegsInMask(volatileRegs);
2256
2257 masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure);
2258 }
2259 masm.bind(&isLinear);
2260
2261 // Load the RegExpShared.
2262 Register regexpReg = temp1;
2263 Address sharedSlot = Address(
2264 regexp, NativeObject::getFixedSlotOffset(RegExpObject::SHARED_SLOT));
2265 masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure);
2266 masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING);
2267
2268 // Handle Atom matches
2269 Label notAtom, checkSuccess;
2270 masm.branchPtr(Assembler::Equal,
2271 Address(regexpReg, RegExpShared::offsetOfPatternAtom()),
2272 ImmWord(0), &notAtom);
2273 {
2274 masm.computeEffectiveAddress(matchPairsAddress, temp3);
2275
2276 masm.PushRegsInMask(volatileRegs);
2277 using Fn =
2278 RegExpRunStatus (*)(RegExpShared* re, const JSLinearString* input,
2279 size_t start, MatchPairs* matchPairs);
2280 masm.setupUnalignedABICall(temp2);
2281 masm.passABIArg(regexpReg);
2282 masm.passABIArg(input);
2283 masm.passABIArg(lastIndex);
2284 masm.passABIArg(temp3);
2285 masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>();
2286
2287 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"
, 2287); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)"
")"); do { *((volatile int*)__null) = 2287; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2288 masm.storeCallInt32Result(temp1);
2289 masm.PopRegsInMask(volatileRegs);
2290
2291 masm.jump(&checkSuccess);
2292 }
2293 masm.bind(&notAtom);
2294
2295 // Don't handle regexps with too many capture pairs.
2296 masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2);
2297 masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount),
2298 failure);
2299
2300 // Fill in the pair count in the MatchPairs on the stack.
2301 masm.store32(temp2, pairCountAddress);
2302
2303 // Load code pointer and length of input (in bytes).
2304 // Store the input start in the InputOutputData.
2305 Register codePointer = temp1; // Note: temp1 was previously regexpReg.
2306 Register byteLength = temp3;
2307 {
2308 Label isLatin1, done;
2309 masm.loadStringLength(input, byteLength);
2310
2311 masm.branchLatin1String(input, &isLatin1);
2312
2313 // Two-byte input
2314 masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
2315 masm.storePtr(temp2, inputStartAddress);
2316 masm.loadPtr(
2317 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)),
2318 codePointer);
2319 masm.lshiftPtr(Imm32(1), byteLength);
2320 masm.jump(&done);
2321
2322 // Latin1 input
2323 masm.bind(&isLatin1);
2324 masm.loadStringChars(input, temp2, CharEncoding::Latin1);
2325 masm.storePtr(temp2, inputStartAddress);
2326 masm.loadPtr(
2327 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)),
2328 codePointer);
2329
2330 masm.bind(&done);
2331
2332 // Store end pointer
2333 masm.addPtr(byteLength, temp2);
2334 masm.storePtr(temp2, inputEndAddress);
2335 }
2336
2337 // Guard that the RegExpShared has been compiled for this type of input.
2338 // If it has not been compiled, we fall back to the OOL case, which will
2339 // do a VM call into the interpreter.
2340 // TODO: add an interpreter trampoline?
2341 masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure);
2342 masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
2343
2344 // Finish filling in the InputOutputData instance on the stack
2345 masm.computeEffectiveAddress(matchPairsAddress, temp2);
2346 masm.storePtr(temp2, matchesAddress);
2347 masm.storePtr(lastIndex, startIndexAddress);
2348
2349 // Execute the RegExp.
2350 masm.computeEffectiveAddress(
2351 Address(FramePointer, inputOutputDataStartOffset), temp2);
2352 masm.PushRegsInMask(volatileRegs);
2353 masm.setupUnalignedABICall(temp3);
2354 masm.passABIArg(temp2);
2355 masm.callWithABI(codePointer);
2356 masm.storeCallInt32Result(temp1);
2357 masm.PopRegsInMask(volatileRegs);
2358
2359 masm.bind(&checkSuccess);
2360 masm.branch32(Assembler::Equal, temp1,
2361 Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound);
2362 masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)),
2363 failure);
2364
2365 // Lazily update the RegExpStatics.
2366 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2367 RegExpRealm::offsetOfRegExpStatics();
2368 masm.loadGlobalObjectData(temp1);
2369 masm.loadPtr(Address(temp1, offset), temp1);
2370 UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3,
2371 initialStringHeap, volatileRegs);
2372
2373 return true;
2374}
2375
2376static void EmitInitDependentStringBase(MacroAssembler& masm,
2377 Register dependent, Register base,
2378 Register temp1, Register temp2,
2379 bool needsPostBarrier) {
2380 // Determine the base string to use and store it in temp2.
2381 Label notDependent, markedDependedOn;
2382 masm.load32(Address(base, JSString::offsetOfFlags()), temp1);
2383 masm.branchTest32(Assembler::Zero, temp1, Imm32(JSString::DEPENDENT_BIT),
2384 &notDependent);
2385 {
2386 // The base is also a dependent string. Load its base to prevent chains of
2387 // dependent strings in most cases. This must either be an atom or already
2388 // have the DEPENDED_ON_BIT set.
2389 masm.loadDependentStringBase(base, temp2);
2390 masm.jump(&markedDependedOn);
2391 }
2392 masm.bind(&notDependent);
2393 {
2394 // The base is not a dependent string. Set the DEPENDED_ON_BIT if it's not
2395 // an atom.
2396 masm.movePtr(base, temp2);
2397 masm.branchTest32(Assembler::NonZero, temp1, Imm32(JSString::ATOM_BIT),
2398 &markedDependedOn);
2399 masm.or32(Imm32(JSString::DEPENDED_ON_BIT), temp1);
2400 masm.store32(temp1, Address(temp2, JSString::offsetOfFlags()));
2401 }
2402 masm.bind(&markedDependedOn);
2403
2404#ifdef DEBUG1
2405 // Assert the base has the DEPENDED_ON_BIT set or is an atom.
2406 Label isAppropriatelyMarked;
2407 masm.branchTest32(Assembler::NonZero,
2408 Address(temp2, JSString::offsetOfFlags()),
2409 Imm32(JSString::ATOM_BIT | JSString::DEPENDED_ON_BIT),
2410 &isAppropriatelyMarked);
2411 masm.assumeUnreachable("Base string is missing DEPENDED_ON_BIT");
2412 masm.bind(&isAppropriatelyMarked);
2413#endif
2414 masm.storeDependentStringBase(temp2, dependent);
2415
2416 // Post-barrier the base store. The base is still in temp2.
2417 if (needsPostBarrier) {
2418 Label done;
2419 masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done);
2420 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done);
2421
2422 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2423 regsToSave.takeUnchecked(temp1);
2424 regsToSave.takeUnchecked(temp2);
2425
2426 masm.PushRegsInMask(regsToSave);
2427
2428 masm.mov(ImmPtr(masm.runtime()), temp1);
2429
2430 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell);
2431 masm.setupUnalignedABICall(temp2);
2432 masm.passABIArg(temp1);
2433 masm.passABIArg(dependent);
2434 masm.callWithABI<Fn, PostWriteBarrier>();
2435
2436 masm.PopRegsInMask(regsToSave);
2437
2438 masm.bind(&done);
2439 } else {
2440#ifdef DEBUG1
2441 Label done;
2442 masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done);
2443 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done);
2444 masm.assumeUnreachable("Missing post barrier for dependent string base");
2445 masm.bind(&done);
2446#endif
2447 }
2448}
2449
2450static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
2451 Register len, Register byteOpScratch,
2452 CharEncoding encoding,
2453 size_t maximumLength = SIZE_MAX(18446744073709551615UL));
2454
2455class CreateDependentString {
2456 CharEncoding encoding_;
2457 Register string_;
2458 Register temp1_;
2459 Register temp2_;
2460 Label* failure_;
2461
2462 enum class FallbackKind : uint8_t {
2463 InlineString,
2464 FatInlineString,
2465 NotInlineString,
2466 Count
2467 };
2468 mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)>
2469 fallbacks_, joins_;
2470
2471 public:
2472 CreateDependentString(CharEncoding encoding, Register string, Register temp1,
2473 Register temp2, Label* failure)
2474 : encoding_(encoding),
2475 string_(string),
2476 temp1_(temp1),
2477 temp2_(temp2),
2478 failure_(failure) {}
2479
2480 Register string() const { return string_; }
2481 CharEncoding encoding() const { return encoding_; }
2482
2483 // Generate code that creates DependentString.
2484 // Caller should call generateFallback after masm.ret(), to generate
2485 // fallback path.
2486 void generate(MacroAssembler& masm, const JSAtomState& names,
2487 CompileRuntime* runtime, Register base,
2488 BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
2489 gc::Heap initialStringHeap);
2490
2491 // Generate fallback path for creating DependentString.
2492 void generateFallback(MacroAssembler& masm);
2493};
2494
2495void CreateDependentString::generate(MacroAssembler& masm,
2496 const JSAtomState& names,
2497 CompileRuntime* runtime, Register base,
2498 BaseIndex startIndexAddress,
2499 BaseIndex limitIndexAddress,
2500 gc::Heap initialStringHeap) {
2501 JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)",
2502 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2503
2504 auto newGCString = [&](FallbackKind kind) {
2505 uint32_t flags = kind == FallbackKind::InlineString
2506 ? JSString::INIT_THIN_INLINE_FLAGS
2507 : kind == FallbackKind::FatInlineString
2508 ? JSString::INIT_FAT_INLINE_FLAGS
2509 : JSString::INIT_DEPENDENT_FLAGS;
2510 if (encoding_ == CharEncoding::Latin1) {
2511 flags |= JSString::LATIN1_CHARS_BIT;
2512 }
2513
2514 if (kind != FallbackKind::FatInlineString) {
2515 masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]);
2516 } else {
2517 masm.newGCFatInlineString(string_, temp2_, initialStringHeap,
2518 &fallbacks_[kind]);
2519 }
2520 masm.bind(&joins_[kind]);
2521 masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags()));
2522 };
2523
2524 // Compute the string length.
2525 masm.load32(startIndexAddress, temp2_);
2526 masm.load32(limitIndexAddress, temp1_);
2527 masm.sub32(temp2_, temp1_);
2528
2529 Label done, nonEmpty;
2530
2531 // Zero length matches use the empty string.
2532 masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty);
2533 masm.movePtr(ImmGCPtr(names.empty_), string_);
2534 masm.jump(&done);
2535
2536 masm.bind(&nonEmpty);
2537
2538 // Complete matches use the base string.
2539 Label nonBaseStringMatch;
2540 masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch);
2541 masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()),
2542 temp1_, &nonBaseStringMatch);
2543 masm.movePtr(base, string_);
2544 masm.jump(&done);
2545
2546 masm.bind(&nonBaseStringMatch);
2547
2548 Label notInline;
2549
2550 int32_t maxInlineLength = encoding_ == CharEncoding::Latin1
2551 ? JSFatInlineString::MAX_LENGTH_LATIN1
2552 : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
2553 masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), &notInline);
2554 {
2555 // Make a thin or fat inline string.
2556 Label stringAllocated, fatInline;
2557
2558 int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1
2559 ? JSThinInlineString::MAX_LENGTH_LATIN1
2560 : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
2561 masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength),
2562 &fatInline);
2563 if (encoding_ == CharEncoding::Latin1) {
2564 // One character Latin-1 strings can be loaded directly from the
2565 // static strings table.
2566 Label thinInline;
2567 masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline);
2568 {
2569 static_assert(
2570 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
2571 "Latin-1 strings can be loaded from static strings");
2572
2573 masm.loadStringChars(base, temp1_, encoding_);
2574 masm.loadChar(temp1_, temp2_, temp1_, encoding_);
2575
2576 masm.lookupStaticString(temp1_, string_, runtime->staticStrings());
2577
2578 masm.jump(&done);
2579 }
2580 masm.bind(&thinInline);
2581 }
2582 {
2583 newGCString(FallbackKind::InlineString);
2584 masm.jump(&stringAllocated);
2585 }
2586 masm.bind(&fatInline);
2587 {
2588 newGCString(FallbackKind::FatInlineString);
2589 }
2590 masm.bind(&stringAllocated);
2591
2592 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2593
2594 masm.push(string_);
2595 masm.push(base);
2596
2597 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"
, 2598); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer"
") (" "startIndexAddress is still valid after stack pushes" ")"
); do { *((volatile int*)__null) = 2598; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2598 "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"
, 2598); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer"
") (" "startIndexAddress is still valid after stack pushes" ")"
); do { *((volatile int*)__null) = 2598; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2599
2600 // Load chars pointer for the new string.
2601 masm.loadInlineStringCharsForStore(string_, string_);
2602
2603 // Load the source characters pointer.
2604 masm.loadStringChars(base, temp2_, encoding_);
2605 masm.load32(startIndexAddress, base);
2606 masm.addToCharPtr(temp2_, base, encoding_);
2607
2608 CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_);
2609
2610 masm.pop(base);
2611 masm.pop(string_);
2612
2613 masm.jump(&done);
2614 }
2615
2616 masm.bind(&notInline);
2617
2618 {
2619 // Make a dependent string.
2620 // Warning: string may be tenured (if the fallback case is hit), so
2621 // stores into it must be post barriered.
2622 newGCString(FallbackKind::NotInlineString);
2623
2624 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2625
2626 masm.loadNonInlineStringChars(base, temp1_, encoding_);
2627 masm.load32(startIndexAddress, temp2_);
2628 masm.addToCharPtr(temp1_, temp2_, encoding_);
2629 masm.storeNonInlineStringChars(temp1_, string_);
2630
2631 EmitInitDependentStringBase(masm, string_, base, temp1_, temp2_,
2632 /* needsPostBarrier = */ true);
2633 }
2634
2635 masm.bind(&done);
2636}
2637
2638void CreateDependentString::generateFallback(MacroAssembler& masm) {
2639 JitSpew(JitSpew_Codegen,
2640 "# Emitting CreateDependentString fallback (encoding=%s)",
2641 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2642
2643 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2644 regsToSave.takeUnchecked(string_);
2645 regsToSave.takeUnchecked(temp2_);
2646
2647 for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) {
2648 masm.bind(&fallbacks_[kind]);
2649
2650 masm.PushRegsInMask(regsToSave);
2651
2652 using Fn = void* (*)(JSContext* cx);
2653 masm.setupUnalignedABICall(string_);
2654 masm.loadJSContext(string_);
2655 masm.passABIArg(string_);
2656 if (kind == FallbackKind::FatInlineString) {
2657 masm.callWithABI<Fn, AllocateFatInlineString>();
2658 } else {
2659 masm.callWithABI<Fn, AllocateDependentString>();
2660 }
2661 masm.storeCallPointerResult(string_);
2662
2663 masm.PopRegsInMask(regsToSave);
2664
2665 masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_);
2666
2667 masm.jump(&joins_[kind]);
2668 }
2669}
2670
2671// Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar,
2672// but RegExpExecMatch also has to load and update .lastIndex for global/sticky
2673// regular expressions.
2674static JitCode* GenerateRegExpMatchStubShared(JSContext* cx,
2675 gc::Heap initialStringHeap,
2676 bool isExecMatch) {
2677 if (isExecMatch) {
2678 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub");
2679 } else {
2680 JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub");
2681 }
2682
2683 // |initialStringHeap| could be stale after a GC.
2684 JS::AutoCheckCannotGC nogc(cx);
2685
2686 Register regexp = RegExpMatcherRegExpReg;
2687 Register input = RegExpMatcherStringReg;
2688 Register lastIndex = RegExpMatcherLastIndexReg;
2689 ValueOperand result = JSReturnOperand;
2690
2691 // We are free to clobber all registers, as LRegExpMatcher is a call
2692 // instruction.
2693 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2694 regs.take(input);
2695 regs.take(regexp);
2696 regs.take(lastIndex);
2697
2698 Register temp1 = regs.takeAny();
2699 Register temp2 = regs.takeAny();
2700 Register temp3 = regs.takeAny();
2701 Register maybeTemp4 = InvalidReg;
2702 if (!regs.empty()) {
2703 // There are not enough registers on x86.
2704 maybeTemp4 = regs.takeAny();
2705 }
2706 Register maybeTemp5 = InvalidReg;
2707 if (!regs.empty()) {
2708 // There are not enough registers on x86.
2709 maybeTemp5 = regs.takeAny();
2710 }
2711
2712 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
2713 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
2714
2715 TempAllocator temp(&cx->tempLifoAlloc());
2716 JitContext jcx(cx);
2717 StackMacroAssembler masm(cx, temp);
2718 AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared");
2719
2720#ifdef JS_USE_LINK_REGISTER
2721 masm.pushReturnAddress();
2722#endif
2723 masm.push(FramePointer);
2724 masm.moveStackPtrTo(FramePointer);
2725
2726 Label notFoundZeroLastIndex;
2727 if (isExecMatch) {
2728 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
2729 }
2730
2731 // The InputOutputData is placed above the frame pointer and return address on
2732 // the stack.
2733 int32_t inputOutputDataStartOffset = 2 * sizeof(void*);
2734
2735 Label notFound, oolEntry;
2736 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
2737 temp3, inputOutputDataStartOffset,
2738 initialStringHeap, &notFound, &oolEntry)) {
2739 return nullptr;
2740 }
2741
2742 // If a regexp has named captures, fall back to the OOL stub, which
2743 // will end up calling CreateRegExpMatchResults.
2744 Register shared = temp2;
2745 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset(
2746 RegExpObject::SHARED_SLOT)),
2747 shared, JSVAL_TYPE_PRIVATE_GCTHING);
2748 masm.branchPtr(Assembler::NotEqual,
2749 Address(shared, RegExpShared::offsetOfGroupsTemplate()),
2750 ImmWord(0), &oolEntry);
2751
2752 // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub.
2753 masm.branchTest32(Assembler::NonZero,
2754 Address(shared, RegExpShared::offsetOfFlags()),
2755 Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry);
2756
2757 Address pairCountAddress =
2758 RegExpPairCountAddress(masm, inputOutputDataStartOffset);
2759
2760 // Construct the result.
2761 Register object = temp1;
2762 {
2763 // In most cases, the array will have just 1-2 elements, so we optimize for
2764 // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots
2765 // because two slots are used for the elements header).
2766
2767 // Load the array length in temp2 and the shape in temp3.
2768 Label allocated;
2769 masm.load32(pairCountAddress, temp2);
2770 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2771 RegExpRealm::offsetOfNormalMatchResultShape();
2772 masm.loadGlobalObjectData(temp3);
2773 masm.loadPtr(Address(temp3, offset), temp3);
2774
2775 auto emitAllocObject = [&](size_t elementCapacity) {
2776 gc::AllocKind kind = GuessArrayGCKind(elementCapacity);
2777 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"
, 2777); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)"
")"); do { *((volatile int*)__null) = 2777; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2778 kind = ForegroundToBackgroundAllocKind(kind);
2779
2780#ifdef DEBUG1
2781 // Assert all of the available slots are used for |elementCapacity|
2782 // elements.
2783 size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity;
2784 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"
, 2784); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)"
")"); do { *((volatile int*)__null) = 2784; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2785#endif
2786
2787 constexpr size_t numUsedDynamicSlots =
2788 RegExpRealm::MatchResultObjectSlotSpan;
2789 constexpr size_t numDynamicSlots =
2790 RegExpRealm::MatchResultObjectNumDynamicSlots;
2791 constexpr size_t arrayLength = 1;
2792 masm.createArrayWithFixedElements(object, temp3, temp2, temp3,
2793 arrayLength, elementCapacity,
2794 numUsedDynamicSlots, numDynamicSlots,
2795 kind, gc::Heap::Default, &oolEntry);
2796 };
2797
2798 Label moreThan2;
2799 masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2);
2800 emitAllocObject(2);
2801 masm.jump(&allocated);
2802
2803 Label moreThan6;
2804 masm.bind(&moreThan2);
2805 masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6);
2806 emitAllocObject(6);
2807 masm.jump(&allocated);
2808
2809 masm.bind(&moreThan6);
2810 static_assert(RegExpObject::MaxPairCount == 14);
2811 emitAllocObject(RegExpObject::MaxPairCount);
2812
2813 masm.bind(&allocated);
2814 }
2815
2816 // clang-format off
2817 /*
2818 * [SMDOC] Stack layout for the RegExpMatcher stub
2819 *
2820 * +---------------+
2821 * FramePointer +-----> |Caller-FramePtr|
2822 * +---------------+
2823 * |Return-Address |
2824 * +---------------+
2825 * inputOutputDataStartOffset +-----> +---------------+
2826 * |InputOutputData|
2827 * +---------------+
2828 * +---------------+
2829 * | MatchPairs |
2830 * pairsCountAddress +-----------> count |
2831 * | pairs |
2832 * | |
2833 * +---------------+
2834 * pairsVectorStartOffset +-----> +---------------+
2835 * | MatchPair |
2836 * matchPairStart +------------> start | <-------+
2837 * matchPairLimit +------------> limit | | Reserved space for
2838 * +---------------+ | `RegExpObject::MaxPairCount`
2839 * . | MatchPair objects.
2840 * . |
2841 * . | `count` objects will be
2842 * +---------------+ | initialized and can be
2843 * | MatchPair | | accessed below.
2844 * | start | <-------+
2845 * | limit |
2846 * +---------------+
2847 */
2848 // clang-format on
2849
2850 static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t),
2851 "MatchPair consists of two int32 values representing the start"
2852 "and the end offset of the match");
2853
2854 int32_t pairsVectorStartOffset =
2855 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
2856
2857 // Incremented by one below for each match pair.
2858 Register matchIndex = temp2;
2859 masm.move32(Imm32(0), matchIndex);
2860
2861 // The element in which to store the result of the current match.
2862 size_t elementsOffset = NativeObject::offsetOfFixedElements();
2863 BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset);
2864
2865 // The current match pair's "start" and "limit" member.
2866 BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight,
2867 pairsVectorStartOffset + MatchPair::offsetOfStart());
2868 BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight,
2869 pairsVectorStartOffset + MatchPair::offsetOfLimit());
2870
2871 Label* depStrFailure = &oolEntry;
2872 Label restoreRegExpAndLastIndex;
2873
2874 Register temp4;
2875 if (maybeTemp4 == InvalidReg) {
2876 depStrFailure = &restoreRegExpAndLastIndex;
2877
2878 // We don't have enough registers for a fourth temporary. Reuse |regexp|
2879 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
2880 masm.push(regexp);
2881 temp4 = regexp;
2882 } else {
2883 temp4 = maybeTemp4;
2884 }
2885
2886 Register temp5;
2887 if (maybeTemp5 == InvalidReg) {
2888 depStrFailure = &restoreRegExpAndLastIndex;
2889
2890 // We don't have enough registers for a fifth temporary. Reuse |lastIndex|
2891 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
2892 masm.push(lastIndex);
2893 temp5 = lastIndex;
2894 } else {
2895 temp5 = maybeTemp5;
2896 }
2897
2898 auto maybeRestoreRegExpAndLastIndex = [&]() {
2899 if (maybeTemp5 == InvalidReg) {
2900 masm.pop(lastIndex);
2901 }
2902 if (maybeTemp4 == InvalidReg) {
2903 masm.pop(regexp);
2904 }
2905 };
2906
2907 // Loop to construct the match strings. There are two different loops,
2908 // depending on whether the input is a Two-Byte or a Latin-1 string.
2909 CreateDependentString depStrs[]{
2910 {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure},
2911 {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure},
2912 };
2913
2914 {
2915 Label isLatin1, done;
2916 masm.branchLatin1String(input, &isLatin1);
2917
2918 for (auto& depStr : depStrs) {
2919 if (depStr.encoding() == CharEncoding::Latin1) {
2920 masm.bind(&isLatin1);
2921 }
2922
2923 Label matchLoop;
2924 masm.bind(&matchLoop);
2925
2926 static_assert(MatchPair::NoMatch == -1,
2927 "MatchPair::start is negative if no match was found");
2928
2929 Label isUndefined, storeDone;
2930 masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0),
2931 &isUndefined);
2932 {
2933 depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()),
2934 input, matchPairStart, matchPairLimit,
2935 initialStringHeap);
2936
2937 // Storing into nursery-allocated results object's elements; no post
2938 // barrier.
2939 masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement);
2940 masm.jump(&storeDone);
2941 }
2942 masm.bind(&isUndefined);
2943 {
2944 masm.storeValue(UndefinedValue(), objectMatchElement);
2945 }
2946 masm.bind(&storeDone);
2947
2948 masm.add32(Imm32(1), matchIndex);
2949 masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex,
2950 &done);
2951 masm.jump(&matchLoop);
2952 }
2953
2954#ifdef DEBUG1
2955 masm.assumeUnreachable("The match string loop doesn't fall through.");
2956#endif
2957
2958 masm.bind(&done);
2959 }
2960
2961 maybeRestoreRegExpAndLastIndex();
2962
2963 // Fill in the rest of the output object.
2964 masm.store32(
2965 matchIndex,
2966 Address(object,
2967 elementsOffset + ObjectElements::offsetOfInitializedLength()));
2968 masm.store32(
2969 matchIndex,
2970 Address(object, elementsOffset + ObjectElements::offsetOfLength()));
2971
2972 Address firstMatchPairStartAddress(
2973 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart());
2974 Address firstMatchPairLimitAddress(
2975 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit());
2976
2977 static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0,
2978 "First slot holds the 'index' property");
2979 static_assert(RegExpRealm::MatchResultObjectInputSlot == 1,
2980 "Second slot holds the 'input' property");
2981
2982 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
2983
2984 masm.load32(firstMatchPairStartAddress, temp3);
2985 masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
2986
2987 // No post barrier needed (address is within nursery object.)
2988 masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
2989
2990 // For the ExecMatch stub, if the regular expression is global or sticky, we
2991 // have to update its .lastIndex slot.
2992 if (isExecMatch) {
2993 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"
, 2993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "object != lastIndex"
")"); do { *((volatile int*)__null) = 2993; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2994 Label notGlobalOrSticky;
2995 masm.branchTest32(Assembler::Zero, flagsSlot,
2996 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
2997 &notGlobalOrSticky);
2998 masm.load32(firstMatchPairLimitAddress, lastIndex);
2999 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
3000 masm.bind(&notGlobalOrSticky);
3001 }
3002
3003 // All done!
3004 masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
3005 masm.pop(FramePointer);
3006 masm.ret();
3007
3008 masm.bind(&notFound);
3009 if (isExecMatch) {
3010 Label notGlobalOrSticky;
3011 masm.branchTest32(Assembler::Zero, flagsSlot,
3012 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3013 &notGlobalOrSticky);
3014 masm.bind(&notFoundZeroLastIndex);
3015 masm.storeValue(Int32Value(0), lastIndexSlot);
3016 masm.bind(&notGlobalOrSticky);
3017 }
3018 masm.moveValue(NullValue(), result);
3019 masm.pop(FramePointer);
3020 masm.ret();
3021
3022 // Fallback paths for CreateDependentString.
3023 for (auto& depStr : depStrs) {
3024 depStr.generateFallback(masm);
3025 }
3026
3027 // Fall-through to the ool entry after restoring the registers.
3028 masm.bind(&restoreRegExpAndLastIndex);
3029 maybeRestoreRegExpAndLastIndex();
3030
3031 // Use an undefined value to signal to the caller that the OOL stub needs to
3032 // be called.
3033 masm.bind(&oolEntry);
3034 masm.moveValue(UndefinedValue(), 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 const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub";
3045 CollectPerfSpewerJitCodeProfile(code, name);
3046#ifdef MOZ_VTUNE1
3047 vtune::MarkStub(code, name);
3048#endif
3049
3050 return code;
3051}
3052
3053JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) {
3054 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
3055 /* isExecMatch = */ false);
3056}
3057
3058JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) {
3059 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
3060 /* isExecMatch = */ true);
3061}
3062
3063class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator> {
3064 LRegExpMatcher* lir_;
3065
3066 public:
3067 explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir) : lir_(lir) {}
3068
3069 void accept(CodeGenerator* codegen) override {
3070 codegen->visitOutOfLineRegExpMatcher(this);
3071 }
3072
3073 LRegExpMatcher* lir() const { return lir_; }
3074};
3075
3076void CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool) {
3077 LRegExpMatcher* lir = ool->lir();
3078 Register lastIndex = ToRegister(lir->lastIndex());
3079 Register input = ToRegister(lir->string());
3080 Register regexp = ToRegister(lir->regexp());
3081
3082 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3083 regs.take(lastIndex);
3084 regs.take(input);
3085 regs.take(regexp);
3086 Register temp = regs.takeAny();
3087
3088 masm.computeEffectiveAddress(
3089 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3090
3091 pushArg(temp);
3092 pushArg(lastIndex);
3093 pushArg(input);
3094 pushArg(regexp);
3095
3096 // We are not using oolCallVM because we are in a Call, and that live
3097 // registers are already saved by the the register allocator.
3098 using Fn =
3099 bool (*)(JSContext*, HandleObject regexp, HandleString input,
3100 int32_t lastIndex, MatchPairs* pairs, MutableHandleValue output);
3101 callVM<Fn, RegExpMatcherRaw>(lir);
3102
3103 masm.jump(ool->rejoin());
3104}
3105
3106void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) {
3107 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"
, 3107); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { *((volatile int*)__null) = 3107; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3108 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"
, 3108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { *((volatile int*)__null) = 3108; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3109 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"
, 3109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg"
")"); do { *((volatile int*)__null) = 3109; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3110 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"
, 3110); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { *((volatile int*)__null) = 3110; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3111
3112#if defined(JS_NUNBOX32)
3113 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
3114 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
3115 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
3116 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
3117 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type);
3118 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data);
3119#elif defined(JS_PUNBOX641)
3120 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
3121 static_assert(RegExpMatcherStringReg != JSReturnReg);
3122 static_assert(RegExpMatcherLastIndexReg != JSReturnReg);
3123#endif
3124
3125 masm.reserveStack(RegExpReservedStack);
3126
3127 OutOfLineRegExpMatcher* ool = new (alloc()) OutOfLineRegExpMatcher(lir);
3128 addOutOfLineCode(ool, lir->mir());
3129
3130 JitCode* regExpMatcherStub =
3131 snapshot_->getZoneStub(JitZone::StubKind::RegExpMatcher);
3132 masm.call(regExpMatcherStub);
3133 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
3134 masm.bind(ool->rejoin());
3135
3136 masm.freeStack(RegExpReservedStack);
3137}
3138
3139class OutOfLineRegExpExecMatch : public OutOfLineCodeBase<CodeGenerator> {
3140 LRegExpExecMatch* lir_;
3141
3142 public:
3143 explicit OutOfLineRegExpExecMatch(LRegExpExecMatch* lir) : lir_(lir) {}
3144
3145 void accept(CodeGenerator* codegen) override {
3146 codegen->visitOutOfLineRegExpExecMatch(this);
3147 }
3148
3149 LRegExpExecMatch* lir() const { return lir_; }
3150};
3151
3152void CodeGenerator::visitOutOfLineRegExpExecMatch(
3153 OutOfLineRegExpExecMatch* ool) {
3154 LRegExpExecMatch* lir = ool->lir();
3155 Register input = ToRegister(lir->string());
3156 Register regexp = ToRegister(lir->regexp());
3157
3158 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3159 regs.take(input);
3160 regs.take(regexp);
3161 Register temp = regs.takeAny();
3162
3163 masm.computeEffectiveAddress(
3164 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3165
3166 pushArg(temp);
3167 pushArg(input);
3168 pushArg(regexp);
3169
3170 // We are not using oolCallVM because we are in a Call and live registers have
3171 // already been saved by the register allocator.
3172 using Fn =
3173 bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input,
3174 MatchPairs* pairs, MutableHandleValue output);
3175 callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir);
3176 masm.jump(ool->rejoin());
3177}
3178
3179void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) {
3180 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"
, 3180); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { *((volatile int*)__null) = 3180; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3181 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"
, 3181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { *((volatile int*)__null) = 3181; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3182 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"
, 3182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { *((volatile int*)__null) = 3182; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3183
3184#if defined(JS_NUNBOX32)
3185 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
3186 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
3187 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
3188 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
3189#elif defined(JS_PUNBOX641)
3190 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
3191 static_assert(RegExpMatcherStringReg != JSReturnReg);
3192#endif
3193
3194 masm.reserveStack(RegExpReservedStack);
3195
3196 auto* ool = new (alloc()) OutOfLineRegExpExecMatch(lir);
3197 addOutOfLineCode(ool, lir->mir());
3198
3199 JitCode* regExpExecMatchStub =
3200 snapshot_->getZoneStub(JitZone::StubKind::RegExpExecMatch);
3201 masm.call(regExpExecMatchStub);
3202 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
3203
3204 masm.bind(ool->rejoin());
3205 masm.freeStack(RegExpReservedStack);
3206}
3207
3208JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) {
3209 JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub");
3210
3211 Register regexp = RegExpSearcherRegExpReg;
3212 Register input = RegExpSearcherStringReg;
3213 Register lastIndex = RegExpSearcherLastIndexReg;
3214 Register result = ReturnReg;
3215
3216 // We are free to clobber all registers, as LRegExpSearcher is a call
3217 // instruction.
3218 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3219 regs.take(input);
3220 regs.take(regexp);
3221 regs.take(lastIndex);
3222
3223 Register temp1 = regs.takeAny();
3224 Register temp2 = regs.takeAny();
3225 Register temp3 = regs.takeAny();
3226
3227 TempAllocator temp(&cx->tempLifoAlloc());
3228 JitContext jcx(cx);
3229 StackMacroAssembler masm(cx, temp);
3230 AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub");
3231
3232#ifdef JS_USE_LINK_REGISTER
3233 masm.pushReturnAddress();
3234#endif
3235 masm.push(FramePointer);
3236 masm.moveStackPtrTo(FramePointer);
3237
3238#ifdef DEBUG1
3239 // Store sentinel value to cx->regExpSearcherLastLimit.
3240 // See comment in RegExpSearcherImpl.
3241 masm.loadJSContext(temp1);
3242 masm.store32(Imm32(RegExpSearcherLastLimitSentinel),
3243 Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit()));
3244#endif
3245
3246 // The InputOutputData is placed above the frame pointer and return address on
3247 // the stack.
3248 int32_t inputOutputDataStartOffset = 2 * sizeof(void*);
3249
3250 Label notFound, oolEntry;
3251 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3252 temp3, inputOutputDataStartOffset,
3253 initialStringHeap, &notFound, &oolEntry)) {
3254 return nullptr;
3255 }
3256
3257 // clang-format off
3258 /*
3259 * [SMDOC] Stack layout for the RegExpSearcher stub
3260 *
3261 * +---------------+
3262 * FramePointer +-----> |Caller-FramePtr|
3263 * +---------------+
3264 * |Return-Address |
3265 * +---------------+
3266 * inputOutputDataStartOffset +-----> +---------------+
3267 * |InputOutputData|
3268 * +---------------+
3269 * +---------------+
3270 * | MatchPairs |
3271 * | count |
3272 * | pairs |
3273 * | |
3274 * +---------------+
3275 * pairsVectorStartOffset +-----> +---------------+
3276 * | MatchPair |
3277 * matchPairStart +------------> start | <-------+
3278 * matchPairLimit +------------> limit | | Reserved space for
3279 * +---------------+ | `RegExpObject::MaxPairCount`
3280 * . | MatchPair objects.
3281 * . |
3282 * . | Only a single object will
3283 * +---------------+ | be initialized and can be
3284 * | MatchPair | | accessed below.
3285 * | start | <-------+
3286 * | limit |
3287 * +---------------+
3288 */
3289 // clang-format on
3290
3291 int32_t pairsVectorStartOffset =
3292 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
3293 Address matchPairStart(FramePointer,
3294 pairsVectorStartOffset + MatchPair::offsetOfStart());
3295 Address matchPairLimit(FramePointer,
3296 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3297
3298 // Store match limit to cx->regExpSearcherLastLimit and return the index.
3299 masm.load32(matchPairLimit, result);
3300 masm.loadJSContext(input);
3301 masm.store32(result,
3302 Address(input, JSContext::offsetOfRegExpSearcherLastLimit()));
3303 masm.load32(matchPairStart, result);
3304 masm.pop(FramePointer);
3305 masm.ret();
3306
3307 masm.bind(&notFound);
3308 masm.move32(Imm32(RegExpSearcherResultNotFound), result);
3309 masm.pop(FramePointer);
3310 masm.ret();
3311
3312 masm.bind(&oolEntry);
3313 masm.move32(Imm32(RegExpSearcherResultFailed), result);
3314 masm.pop(FramePointer);
3315 masm.ret();
3316
3317 Linker linker(masm);
3318 JitCode* code = linker.newCode(cx, CodeKind::Other);
3319 if (!code) {
3320 return nullptr;
3321 }
3322
3323 CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub");
3324#ifdef MOZ_VTUNE1
3325 vtune::MarkStub(code, "RegExpSearcherStub");
3326#endif
3327
3328 return code;
3329}
3330
3331class OutOfLineRegExpSearcher : public OutOfLineCodeBase<CodeGenerator> {
3332 LRegExpSearcher* lir_;
3333
3334 public:
3335 explicit OutOfLineRegExpSearcher(LRegExpSearcher* lir) : lir_(lir) {}
3336
3337 void accept(CodeGenerator* codegen) override {
3338 codegen->visitOutOfLineRegExpSearcher(this);
3339 }
3340
3341 LRegExpSearcher* lir() const { return lir_; }
3342};
3343
3344void CodeGenerator::visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool) {
3345 LRegExpSearcher* lir = ool->lir();
3346 Register lastIndex = ToRegister(lir->lastIndex());
3347 Register input = ToRegister(lir->string());
3348 Register regexp = ToRegister(lir->regexp());
3349
3350 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3351 regs.take(lastIndex);
3352 regs.take(input);
3353 regs.take(regexp);
3354 Register temp = regs.takeAny();
3355
3356 masm.computeEffectiveAddress(
3357 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3358
3359 pushArg(temp);
3360 pushArg(lastIndex);
3361 pushArg(input);
3362 pushArg(regexp);
3363
3364 // We are not using oolCallVM because we are in a Call, and that live
3365 // registers are already saved by the the register allocator.
3366 using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input,
3367 int32_t lastIndex, MatchPairs* pairs, int32_t* result);
3368 callVM<Fn, RegExpSearcherRaw>(lir);
3369
3370 masm.jump(ool->rejoin());
3371}
3372
3373void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) {
3374 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"
, 3374); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg"
")"); do { *((volatile int*)__null) = 3374; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3375 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"
, 3375); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg"
")"); do { *((volatile int*)__null) = 3375; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3376 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"
, 3376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg"
")"); do { *((volatile int*)__null) = 3376; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3377 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"
, 3377); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { *((volatile int*)__null) = 3377; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3378
3379 static_assert(RegExpSearcherRegExpReg != ReturnReg);
3380 static_assert(RegExpSearcherStringReg != ReturnReg);
3381 static_assert(RegExpSearcherLastIndexReg != ReturnReg);
3382
3383 masm.reserveStack(RegExpReservedStack);
3384
3385 OutOfLineRegExpSearcher* ool = new (alloc()) OutOfLineRegExpSearcher(lir);
3386 addOutOfLineCode(ool, lir->mir());
3387
3388 JitCode* regExpSearcherStub =
3389 snapshot_->getZoneStub(JitZone::StubKind::RegExpSearcher);
3390 masm.call(regExpSearcherStub);
3391 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed),
3392 ool->entry());
3393 masm.bind(ool->rejoin());
3394
3395 masm.freeStack(RegExpReservedStack);
3396}
3397
3398void CodeGenerator::visitRegExpSearcherLastLimit(
3399 LRegExpSearcherLastLimit* lir) {
3400 Register result = ToRegister(lir->output());
3401 Register scratch = ToRegister(lir->temp0());
3402
3403 masm.loadAndClearRegExpSearcherLastLimit(result, scratch);
3404}
3405
3406JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) {
3407 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub");
3408
3409 Register regexp = RegExpExecTestRegExpReg;
3410 Register input = RegExpExecTestStringReg;
3411 Register result = ReturnReg;
3412
3413 TempAllocator temp(&cx->tempLifoAlloc());
3414 JitContext jcx(cx);
3415 StackMacroAssembler masm(cx, temp);
3416 AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub");
3417
3418#ifdef JS_USE_LINK_REGISTER
3419 masm.pushReturnAddress();
3420#endif
3421 masm.push(FramePointer);
3422 masm.moveStackPtrTo(FramePointer);
3423
3424 // We are free to clobber all registers, as LRegExpExecTest is a call
3425 // instruction.
3426 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3427 regs.take(input);
3428 regs.take(regexp);
3429
3430 // Ensure lastIndex != result.
3431 regs.take(result);
3432 Register lastIndex = regs.takeAny();
3433 regs.add(result);
3434 Register temp1 = regs.takeAny();
3435 Register temp2 = regs.takeAny();
3436 Register temp3 = regs.takeAny();
3437
3438 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
3439 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
3440
3441 masm.reserveStack(RegExpReservedStack);
3442
3443 // Load lastIndex and skip RegExp execution if needed.
3444 Label notFoundZeroLastIndex;
3445 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
3446
3447 // In visitRegExpMatcher and visitRegExpSearcher, we reserve stack space
3448 // before calling the stub. For RegExpExecTest we call the stub before
3449 // reserving stack space, so the offset of the InputOutputData relative to the
3450 // frame pointer is negative.
3451 constexpr int32_t inputOutputDataStartOffset = -int32_t(RegExpReservedStack);
3452
3453 // On ARM64, load/store instructions can encode an immediate offset in the
3454 // range [-256, 4095]. If we ever fail this assertion, it would be more
3455 // efficient to store the data above the frame pointer similar to
3456 // RegExpMatcher and RegExpSearcher.
3457 static_assert(inputOutputDataStartOffset >= -256);
3458
3459 Label notFound, oolEntry;
3460 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3461 temp3, inputOutputDataStartOffset,
3462 initialStringHeap, &notFound, &oolEntry)) {
3463 return nullptr;
3464 }
3465
3466 // Set `result` to true/false to indicate found/not-found, or to
3467 // RegExpExecTestResultFailed if we have to retry in C++. If the regular
3468 // expression is global or sticky, we also have to update its .lastIndex slot.
3469
3470 Label done;
3471 int32_t pairsVectorStartOffset =
3472 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
3473 Address matchPairLimit(FramePointer,
3474 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3475
3476 masm.move32(Imm32(1), result);
3477 masm.branchTest32(Assembler::Zero, flagsSlot,
3478 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3479 &done);
3480 masm.load32(matchPairLimit, lastIndex);
3481 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
3482 masm.jump(&done);
3483
3484 masm.bind(&notFound);
3485 masm.move32(Imm32(0), result);
3486 masm.branchTest32(Assembler::Zero, flagsSlot,
3487 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3488 &done);
3489 masm.storeValue(Int32Value(0), lastIndexSlot);
3490 masm.jump(&done);
3491
3492 masm.bind(&notFoundZeroLastIndex);
3493 masm.move32(Imm32(0), result);
3494 masm.storeValue(Int32Value(0), lastIndexSlot);
3495 masm.jump(&done);
3496
3497 masm.bind(&oolEntry);
3498 masm.move32(Imm32(RegExpExecTestResultFailed), result);
3499
3500 masm.bind(&done);
3501 masm.freeStack(RegExpReservedStack);
3502 masm.pop(FramePointer);
3503 masm.ret();
3504
3505 Linker linker(masm);
3506 JitCode* code = linker.newCode(cx, CodeKind::Other);
3507 if (!code) {
3508 return nullptr;
3509 }
3510
3511 CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub");
3512#ifdef MOZ_VTUNE1
3513 vtune::MarkStub(code, "RegExpExecTestStub");
3514#endif
3515
3516 return code;
3517}
3518
3519class OutOfLineRegExpExecTest : public OutOfLineCodeBase<CodeGenerator> {
3520 LRegExpExecTest* lir_;
3521
3522 public:
3523 explicit OutOfLineRegExpExecTest(LRegExpExecTest* lir) : lir_(lir) {}
3524
3525 void accept(CodeGenerator* codegen) override {
3526 codegen->visitOutOfLineRegExpExecTest(this);
3527 }
3528
3529 LRegExpExecTest* lir() const { return lir_; }
3530};
3531
3532void CodeGenerator::visitOutOfLineRegExpExecTest(OutOfLineRegExpExecTest* ool) {
3533 LRegExpExecTest* lir = ool->lir();
3534 Register input = ToRegister(lir->string());
3535 Register regexp = ToRegister(lir->regexp());
3536
3537 pushArg(input);
3538 pushArg(regexp);
3539
3540 // We are not using oolCallVM because we are in a Call and live registers have
3541 // already been saved by the register allocator.
3542 using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp,
3543 HandleString input, bool* result);
3544 callVM<Fn, RegExpBuiltinExecTestFromJit>(lir);
3545
3546 masm.jump(ool->rejoin());
3547}
3548
3549void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) {
3550 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"
, 3550); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg"
")"); do { *((volatile int*)__null) = 3550; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3551 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"
, 3551); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg"
")"); do { *((volatile int*)__null) = 3551; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3552 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"
, 3552); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { *((volatile int*)__null) = 3552; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3553
3554 static_assert(RegExpExecTestRegExpReg != ReturnReg);
3555 static_assert(RegExpExecTestStringReg != ReturnReg);
3556
3557 auto* ool = new (alloc()) OutOfLineRegExpExecTest(lir);
3558 addOutOfLineCode(ool, lir->mir());
3559
3560 JitCode* regExpExecTestStub =
3561 snapshot_->getZoneStub(JitZone::StubKind::RegExpExecTest);
3562 masm.call(regExpExecTestStub);
3563
3564 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed),
3565 ool->entry());
3566
3567 masm.bind(ool->rejoin());
3568}
3569
3570void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) {
3571 Register regexp = ToRegister(ins->regexp());
3572 Register input = ToRegister(ins->input());
3573 Register output = ToRegister(ins->output());
3574
3575 using Fn =
3576 bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*);
3577 auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>(
3578 ins, ArgList(regexp, input), StoreRegisterTo(output));
3579
3580 // Load RegExpShared in |output|.
3581 Label vmCall;
3582 masm.loadParsedRegExpShared(regexp, output, ool->entry());
3583
3584 // Return true iff pairCount > 1.
3585 Label returnTrue;
3586 masm.branch32(Assembler::Above,
3587 Address(output, RegExpShared::offsetOfPairCount()), Imm32(1),
3588 &returnTrue);
3589 masm.move32(Imm32(0), output);
3590 masm.jump(ool->rejoin());
3591
3592 masm.bind(&returnTrue);
3593 masm.move32(Imm32(1), output);
3594
3595 masm.bind(ool->rejoin());
3596}
3597
3598class OutOfLineRegExpPrototypeOptimizable
3599 : public OutOfLineCodeBase<CodeGenerator> {
3600 LRegExpPrototypeOptimizable* ins_;
3601
3602 public:
3603 explicit OutOfLineRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
3604 : ins_(ins) {}
3605
3606 void accept(CodeGenerator* codegen) override {
3607 codegen->visitOutOfLineRegExpPrototypeOptimizable(this);
3608 }
3609 LRegExpPrototypeOptimizable* ins() const { return ins_; }
3610};
3611
3612void CodeGenerator::visitRegExpPrototypeOptimizable(
3613 LRegExpPrototypeOptimizable* ins) {
3614 Register object = ToRegister(ins->object());
3615 Register output = ToRegister(ins->output());
3616 Register temp = ToRegister(ins->temp0());
3617
3618 OutOfLineRegExpPrototypeOptimizable* ool =
3619 new (alloc()) OutOfLineRegExpPrototypeOptimizable(ins);
3620 addOutOfLineCode(ool, ins->mir());
3621
3622 const GlobalObject* global = gen->realm->maybeGlobal();
3623 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"
, 3623); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")"
); do { *((volatile int*)__null) = 3623; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3624 masm.branchIfNotRegExpPrototypeOptimizable(object, temp, global,
3625 ool->entry());
3626 masm.move32(Imm32(0x1), output);
3627
3628 masm.bind(ool->rejoin());
3629}
3630
3631void CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable(
3632 OutOfLineRegExpPrototypeOptimizable* ool) {
3633 LRegExpPrototypeOptimizable* ins = ool->ins();
3634 Register object = ToRegister(ins->object());
3635 Register output = ToRegister(ins->output());
3636
3637 saveVolatile(output);
3638
3639 using Fn = bool (*)(JSContext* cx, JSObject* proto);
3640 masm.setupAlignedABICall();
3641 masm.loadJSContext(output);
3642 masm.passABIArg(output);
3643 masm.passABIArg(object);
3644 masm.callWithABI<Fn, RegExpPrototypeOptimizableRaw>();
3645 masm.storeCallBoolResult(output);
3646
3647 restoreVolatile(output);
3648
3649 masm.jump(ool->rejoin());
3650}
3651
3652class OutOfLineRegExpInstanceOptimizable
3653 : public OutOfLineCodeBase<CodeGenerator> {
3654 LRegExpInstanceOptimizable* ins_;
3655
3656 public:
3657 explicit OutOfLineRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
3658 : ins_(ins) {}
3659
3660 void accept(CodeGenerator* codegen) override {
3661 codegen->visitOutOfLineRegExpInstanceOptimizable(this);
3662 }
3663 LRegExpInstanceOptimizable* ins() const { return ins_; }
3664};
3665
3666void CodeGenerator::visitRegExpInstanceOptimizable(
3667 LRegExpInstanceOptimizable* ins) {
3668 Register object = ToRegister(ins->object());
3669 Register output = ToRegister(ins->output());
3670 Register temp = ToRegister(ins->temp0());
3671
3672 OutOfLineRegExpInstanceOptimizable* ool =
3673 new (alloc()) OutOfLineRegExpInstanceOptimizable(ins);
3674 addOutOfLineCode(ool, ins->mir());
3675
3676 const GlobalObject* global = gen->realm->maybeGlobal();
3677 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"
, 3677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")"
); do { *((volatile int*)__null) = 3677; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3678 masm.branchIfNotRegExpInstanceOptimizable(object, temp, global, ool->entry());
3679 masm.move32(Imm32(0x1), output);
3680
3681 masm.bind(ool->rejoin());
3682}
3683
3684void CodeGenerator::visitOutOfLineRegExpInstanceOptimizable(
3685 OutOfLineRegExpInstanceOptimizable* ool) {
3686 LRegExpInstanceOptimizable* ins = ool->ins();
3687 Register object = ToRegister(ins->object());
3688 Register proto = ToRegister(ins->proto());
3689 Register output = ToRegister(ins->output());
3690
3691 saveVolatile(output);
3692
3693 using Fn = bool (*)(JSContext* cx, JSObject* obj, JSObject* proto);
3694 masm.setupAlignedABICall();
3695 masm.loadJSContext(output);
3696 masm.passABIArg(output);
3697 masm.passABIArg(object);
3698 masm.passABIArg(proto);
3699 masm.callWithABI<Fn, RegExpInstanceOptimizableRaw>();
3700 masm.storeCallBoolResult(output);
3701
3702 restoreVolatile(output);
3703
3704 masm.jump(ool->rejoin());
3705}
3706
3707static void FindFirstDollarIndex(MacroAssembler& masm, Register str,
3708 Register len, Register temp0, Register temp1,
3709 Register output, CharEncoding encoding) {
3710#ifdef DEBUG1
3711 Label ok;
3712 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
3713 masm.assumeUnreachable("Length should be greater than 0.");
3714 masm.bind(&ok);
3715#endif
3716
3717 Register chars = temp0;
3718 masm.loadStringChars(str, chars, encoding);
3719
3720 masm.move32(Imm32(0), output);
3721
3722 Label start, done;
3723 masm.bind(&start);
3724
3725 Register currentChar = temp1;
3726 masm.loadChar(chars, output, currentChar, encoding);
3727 masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done);
3728
3729 masm.add32(Imm32(1), output);
3730 masm.branch32(Assembler::NotEqual, output, len, &start);
3731
3732 masm.move32(Imm32(-1), output);
3733
3734 masm.bind(&done);
3735}
3736
3737void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) {
3738 Register str = ToRegister(ins->str());
3739 Register output = ToRegister(ins->output());
3740 Register temp0 = ToRegister(ins->temp0());
3741 Register temp1 = ToRegister(ins->temp1());
3742 Register len = ToRegister(ins->temp2());
3743
3744 using Fn = bool (*)(JSContext*, JSString*, int32_t*);
3745 OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>(
3746 ins, ArgList(str), StoreRegisterTo(output));
3747
3748 masm.branchIfRope(str, ool->entry());
3749 masm.loadStringLength(str, len);
3750
3751 Label isLatin1, done;
3752 masm.branchLatin1String(str, &isLatin1);
3753 {
3754 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3755 CharEncoding::TwoByte);
3756 masm.jump(&done);
3757 }
3758 masm.bind(&isLatin1);
3759 {
3760 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3761 CharEncoding::Latin1);
3762 }
3763 masm.bind(&done);
3764 masm.bind(ool->rejoin());
3765}
3766
3767void CodeGenerator::visitStringReplace(LStringReplace* lir) {
3768 if (lir->replacement()->isConstant()) {
3769 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
3770 } else {
3771 pushArg(ToRegister(lir->replacement()));
3772 }
3773
3774 if (lir->pattern()->isConstant()) {
3775 pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString()));
3776 } else {
3777 pushArg(ToRegister(lir->pattern()));
3778 }
3779
3780 if (lir->string()->isConstant()) {
3781 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
3782 } else {
3783 pushArg(ToRegister(lir->string()));
3784 }
3785
3786 using Fn =
3787 JSString* (*)(JSContext*, HandleString, HandleString, HandleString);
3788 if (lir->mir()->isFlatReplacement()) {
3789 callVM<Fn, StringFlatReplaceString>(lir);
3790 } else {
3791 callVM<Fn, StringReplace>(lir);
3792 }
3793}
3794
3795void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) {
3796 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3797 TypedOrValueRegister lhs =
3798 TypedOrValueRegister(ToValue(lir, LBinaryValueCache::LhsIndex));
3799 TypedOrValueRegister rhs =
3800 TypedOrValueRegister(ToValue(lir, LBinaryValueCache::RhsIndex));
3801 ValueOperand output = ToOutValue(lir);
3802
3803 JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
3804
3805 switch (jsop) {
3806 case JSOp::Add:
3807 case JSOp::Sub:
3808 case JSOp::Mul:
3809 case JSOp::Div:
3810 case JSOp::Mod:
3811 case JSOp::Pow:
3812 case JSOp::BitAnd:
3813 case JSOp::BitOr:
3814 case JSOp::BitXor:
3815 case JSOp::Lsh:
3816 case JSOp::Rsh:
3817 case JSOp::Ursh: {
3818 IonBinaryArithIC ic(liveRegs, lhs, rhs, output);
3819 addIC(lir, allocateIC(ic));
3820 return;
3821 }
3822 default:
3823 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"
, 3823); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache"
")"); do { *((volatile int*)__null) = 3823; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3824 }
3825}
3826
3827void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) {
3828 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3829 TypedOrValueRegister lhs =
3830 TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::LhsIndex));
3831 TypedOrValueRegister rhs =
3832 TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::RhsIndex));
3833 Register output = ToRegister(lir->output());
3834
3835 JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
3836
3837 switch (jsop) {
3838 case JSOp::Lt:
3839 case JSOp::Le:
3840 case JSOp::Gt:
3841 case JSOp::Ge:
3842 case JSOp::Eq:
3843 case JSOp::Ne:
3844 case JSOp::StrictEq:
3845 case JSOp::StrictNe: {
3846 IonCompareIC ic(liveRegs, lhs, rhs, output);
3847 addIC(lir, allocateIC(ic));
3848 return;
3849 }
3850 default:
3851 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"
, 3851); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache"
")"); do { *((volatile int*)__null) = 3851; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3852 }
3853}
3854
3855void CodeGenerator::visitUnaryCache(LUnaryCache* lir) {
3856 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3857 TypedOrValueRegister input =
3858 TypedOrValueRegister(ToValue(lir, LUnaryCache::InputIndex));
3859 ValueOperand output = ToOutValue(lir);
3860
3861 IonUnaryArithIC ic(liveRegs, input, output);
3862 addIC(lir, allocateIC(ic));
3863}
3864
3865void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) {
3866 pushArg(ImmPtr(lir->mir()->module()));
3867
3868 using Fn = JSObject* (*)(JSContext*, HandleObject);
3869 callVM<Fn, js::GetOrCreateModuleMetaObject>(lir);
3870}
3871
3872void CodeGenerator::visitDynamicImport(LDynamicImport* lir) {
3873 pushArg(ToValue(lir, LDynamicImport::OptionsIndex));
3874 pushArg(ToValue(lir, LDynamicImport::SpecifierIndex));
3875 pushArg(ImmGCPtr(current->mir()->info().script()));
3876
3877 using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue);
3878 callVM<Fn, js::StartDynamicModuleImport>(lir);
3879}
3880
3881void CodeGenerator::visitLambda(LLambda* lir) {
3882 Register envChain = ToRegister(lir->environmentChain());
3883 Register output = ToRegister(lir->output());
3884 Register tempReg = ToRegister(lir->temp0());
3885
3886 JSFunction* fun = lir->mir()->templateFunction();
3887
3888 using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject);
3889 OutOfLineCode* ool = oolCallVM<Fn, js::Lambda>(
3890 lir, ArgList(ImmGCPtr(fun), envChain), StoreRegisterTo(output));
3891
3892 TemplateObject templateObject(fun);
3893 masm.createGCObject(output, tempReg, templateObject, gc::Heap::Default,
3894 ool->entry());
3895
3896 masm.storeValue(JSVAL_TYPE_OBJECT, envChain,
3897 Address(output, JSFunction::offsetOfEnvironment()));
3898 // No post barrier needed because output is guaranteed to be allocated in
3899 // the nursery.
3900
3901 masm.bind(ool->rejoin());
3902}
3903
3904void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) {
3905 Register envChain = ToRegister(lir->envChain());
3906 Register prototype = ToRegister(lir->prototype());
3907
3908 pushArg(prototype);
3909 pushArg(envChain);
3910 pushArg(ImmGCPtr(lir->mir()->function()));
3911
3912 using Fn =
3913 JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject);
3914 callVM<Fn, js::FunWithProtoOperation>(lir);
3915}
3916
3917void CodeGenerator::visitSetFunName(LSetFunName* lir) {
3918 pushArg(Imm32(lir->mir()->prefixKind()));
3919 pushArg(ToValue(lir, LSetFunName::NameIndex));
3920 pushArg(ToRegister(lir->fun()));
3921
3922 using Fn =
3923 bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
3924 callVM<Fn, js::SetFunctionName>(lir);
3925}
3926
3927void CodeGenerator::visitOsiPoint(LOsiPoint* lir) {
3928 // Note: markOsiPoint ensures enough space exists between the last
3929 // LOsiPoint and this one to patch adjacent call instructions.
3930
3931 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"
, 3931); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 3931; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3932
3933 uint32_t osiCallPointOffset = markOsiPoint(lir);
3934
3935 LSafepoint* safepoint = lir->associatedSafepoint();
3936 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"
, 3936); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()"
")"); do { *((volatile int*)__null) = 3936; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3937 safepoint->setOsiCallPointOffset(osiCallPointOffset);
3938
3939#ifdef DEBUG1
3940 // There should be no movegroups or other instructions between
3941 // an instruction and its OsiPoint. This is necessary because
3942 // we use the OsiPoint's snapshot from within VM calls.
3943 for (LInstructionReverseIterator iter(current->rbegin(lir));
3944 iter != current->rend(); iter++) {
3945 if (*iter == lir) {
3946 continue;
3947 }
3948 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"
, 3948); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!iter->isMoveGroup()"
")"); do { *((volatile int*)__null) = 3948; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3949 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"
, 3949); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint"
")"); do { *((volatile int*)__null) = 3949; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3950 break;
3951 }
3952#endif
3953
3954#ifdef CHECK_OSIPOINT_REGISTERS1
3955 if (shouldVerifyOsiPointRegs(safepoint)) {
3956 verifyOsiPointRegs(safepoint);
3957 }
3958#endif
3959}
3960
3961void CodeGenerator::visitPhi(LPhi* lir) {
3962 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"
, 3962); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator"
")"); do { *((volatile int*)__null) = 3962; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3963}
3964
3965void CodeGenerator::visitGoto(LGoto* lir) { jumpToBlock(lir->target()); }
3966
3967void CodeGenerator::visitTableSwitch(LTableSwitch* ins) {
3968 MTableSwitch* mir = ins->mir();
3969 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
3970 const LAllocation* temp;
3971
3972 if (mir->getOperand(0)->type() != MIRType::Int32) {
3973 temp = ins->tempInt()->output();
3974
3975 // The input is a double, so try and convert it to an integer.
3976 // If it does not fit in an integer, take the default case.
3977 masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp),
3978 defaultcase, false);
3979 } else {
3980 temp = ins->index();
3981 }
3982
3983 emitTableSwitchDispatch(mir, ToRegister(temp),
3984 ToRegisterOrInvalid(ins->tempPointer()));
3985}
3986
3987void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) {
3988 MTableSwitch* mir = ins->mir();
3989 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
3990
3991 Register index = ToRegister(ins->tempInt());
3992 ValueOperand value = ToValue(ins, LTableSwitchV::InputValue);
3993 Register tag = masm.extractTag(value, index);
3994 masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase);
3995
3996 Label unboxInt, isInt;
3997 masm.branchTestInt32(Assembler::Equal, tag, &unboxInt);
3998 {
3999 FloatRegister floatIndex = ToFloatRegister(ins->tempFloat());
4000 masm.unboxDouble(value, floatIndex);
4001 masm.convertDoubleToInt32(floatIndex, index, defaultcase, false);
4002 masm.jump(&isInt);
4003 }
4004
4005 masm.bind(&unboxInt);
4006 masm.unboxInt32(value, index);
4007
4008 masm.bind(&isInt);
4009
4010 emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
4011}
4012
4013void CodeGenerator::visitParameter(LParameter* lir) {}
4014
4015void CodeGenerator::visitCallee(LCallee* lir) {
4016 Register callee = ToRegister(lir->output());
4017 Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken());
4018
4019 masm.loadFunctionFromCalleeToken(ptr, callee);
4020}
4021
4022void CodeGenerator::visitIsConstructing(LIsConstructing* lir) {
4023 Register output = ToRegister(lir->output());
4024 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
4025 masm.loadPtr(calleeToken, output);
4026
4027 // We must be inside a function.
4028 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"
, 4028); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()"
")"); do { *((volatile int*)__null) = 4028; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4029
4030 // The low bit indicates whether this call is constructing, just clear the
4031 // other bits.
4032 static_assert(CalleeToken_Function == 0x0,
4033 "CalleeTokenTag value should match");
4034 static_assert(CalleeToken_FunctionConstructing == 0x1,
4035 "CalleeTokenTag value should match");
4036 masm.andPtr(Imm32(0x1), output);
4037}
4038
4039void CodeGenerator::visitReturn(LReturn* lir) {
4040#if defined(JS_NUNBOX32)
4041 DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX);
4042 DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX);
4043 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"
, 4043); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type"
")"); do { *((volatile int*)__null) = 4043; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4044 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"
, 4044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data"
")"); do { *((volatile int*)__null) = 4044; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4045#elif defined(JS_PUNBOX641)
4046 DebugOnly<LAllocation*> result = lir->getOperand(0);
4047 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"
, 4047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg"
")"); do { *((volatile int*)__null) = 4047; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4048#endif
4049 // Don't emit a jump to the return label if this is the last block, as
4050 // it'll fall through to the epilogue.
4051 //
4052 // This is -not- true however for a Generator-return, which may appear in the
4053 // middle of the last block, so we should always emit the jump there.
4054 if (current->mir() != *gen->graph().poBegin() || lir->isGenerator()) {
4055 masm.jump(&returnLabel_);
4056 }
4057}
4058
4059void CodeGenerator::visitOsrEntry(LOsrEntry* lir) {
4060 Register temp = ToRegister(lir->temp());
4061
4062 // Remember the OSR entry offset into the code buffer.
4063 masm.flushBuffer();
4064 setOsrEntryOffset(masm.size());
4065
4066 // Allocate the full frame for this function
4067 // Note we have a new entry here. So we reset MacroAssembler::framePushed()
4068 // to 0, before reserving the stack.
4069 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"
, 4069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 4069; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4070 masm.setFramePushed(0);
4071
4072 // The Baseline code ensured both the frame pointer and stack pointer point to
4073 // the JitFrameLayout on the stack.
4074
4075 // If profiling, save the current frame pointer to a per-thread global field.
4076 if (isProfilerInstrumentationEnabled()) {
4077 masm.profilerEnterFrame(FramePointer, temp);
4078 }
4079
4080 masm.reserveStack(frameSize());
4081 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"
, 4081); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 4081; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4082
4083 // Ensure that the Ion frames is properly aligned.
4084 masm.assertStackAlignment(JitStackAlignment, 0);
4085}
4086
4087void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) {
4088 const LAllocation* frame = lir->getOperand(0);
4089 const LDefinition* object = lir->getDef(0);
4090
4091 const ptrdiff_t frameOffset =
4092 BaselineFrame::reverseOffsetOfEnvironmentChain();
4093
4094 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
4095}
4096
4097void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) {
4098 const LAllocation* frame = lir->getOperand(0);
4099 const LDefinition* object = lir->getDef(0);
4100
4101 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj();
4102
4103 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
4104}
4105
4106void CodeGenerator::visitOsrValue(LOsrValue* value) {
4107 const LAllocation* frame = value->getOperand(0);
4108 const ValueOperand out = ToOutValue(value);
4109
4110 const ptrdiff_t frameOffset = value->mir()->frameOffset();
4111
4112 masm.loadValue(Address(ToRegister(frame), frameOffset), out);
4113}
4114
4115void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) {
4116 const LAllocation* frame = lir->getOperand(0);
4117 const ValueOperand out = ToOutValue(lir);
4118
4119 Address flags =
4120 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags());
4121 Address retval =
4122 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue());
4123
4124 masm.moveValue(UndefinedValue(), out);
4125
4126 Label done;
4127 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL),
4128 &done);
4129 masm.loadValue(retval, out);
4130 masm.bind(&done);
4131}
4132
4133void CodeGenerator::visitStackArgT(LStackArgT* lir) {
4134 const LAllocation* arg = lir->arg();
4135 MIRType argType = lir->type();
4136 uint32_t argslot = lir->argslot();
4137 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"
, 4137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { *((volatile int*)__null) = 4137; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4138
4139 Address dest = AddressOfPassedArg(argslot);
4140
4141 if (arg->isFloatReg()) {
4142 masm.boxDouble(ToFloatRegister(arg), dest);
4143 } else if (arg->isRegister()) {
4144 masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest);
4145 } else {
4146 masm.storeValue(arg->toConstant()->toJSValue(), dest);
4147 }
4148}
4149
4150void CodeGenerator::visitStackArgV(LStackArgV* lir) {
4151 ValueOperand val = ToValue(lir, 0);
4152 uint32_t argslot = lir->argslot();
4153 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"
, 4153); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { *((volatile int*)__null) = 4153; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4154
4155 masm.storeValue(val, AddressOfPassedArg(argslot));
4156}
4157
4158void CodeGenerator::visitMoveGroup(LMoveGroup* group) {
4159 if (!group->numMoves()) {
4160 return;
4161 }
4162
4163 MoveResolver& resolver = masm.moveResolver();
4164
4165 for (size_t i = 0; i < group->numMoves(); i++) {
4166 const LMove& move = group->getMove(i);
4167
4168 LAllocation from = move.from();
4169 LAllocation to = move.to();
4170 LDefinition::Type type = move.type();
4171
4172 // No bogus moves.
4173 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"
, 4173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to"
")"); do { *((volatile int*)__null) = 4173; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4174 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"
, 4174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!from.isConstant()"
")"); do { *((volatile int*)__null) = 4174; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4175 MoveOp::Type moveType;
4176 switch (type) {
4177 case LDefinition::OBJECT:
4178 case LDefinition::SLOTS:
4179 case LDefinition::WASM_ANYREF:
4180#ifdef JS_NUNBOX32
4181 case LDefinition::TYPE:
4182 case LDefinition::PAYLOAD:
4183#else
4184 case LDefinition::BOX:
4185#endif
4186 case LDefinition::GENERAL:
4187 case LDefinition::STACKRESULTS:
4188 moveType = MoveOp::GENERAL;
4189 break;
4190 case LDefinition::INT32:
4191 moveType = MoveOp::INT32;
4192 break;
4193 case LDefinition::FLOAT32:
4194 moveType = MoveOp::FLOAT32;
4195 break;
4196 case LDefinition::DOUBLE:
4197 moveType = MoveOp::DOUBLE;
4198 break;
4199 case LDefinition::SIMD128:
4200 moveType = MoveOp::SIMD128;
4201 break;
4202 default:
4203 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"
, 4203); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected move type"
")"); do { *((volatile int*)__null) = 4203; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
4204 }
4205
4206 masm.propagateOOM(
4207 resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType));
4208 }
4209
4210 masm.propagateOOM(resolver.resolve());
4211 if (masm.oom()) {
4212 return;
4213 }
4214
4215 MoveEmitter emitter(masm);
4216
4217#ifdef JS_CODEGEN_X86
4218 if (group->maybeScratchRegister().isGeneralReg()) {
4219 emitter.setScratchRegister(
4220 group->maybeScratchRegister().toGeneralReg()->reg());
4221 } else {
4222 resolver.sortMemoryToMemoryMoves();
4223 }
4224#endif
4225
4226 emitter.emit(resolver);
4227 emitter.finish();
4228}
4229
4230void CodeGenerator::visitInteger(LInteger* lir) {
4231 masm.move32(Imm32(lir->i32()), ToRegister(lir->output()));
4232}
4233
4234void CodeGenerator::visitInteger64(LInteger64* lir) {
4235 masm.move64(Imm64(lir->i64()), ToOutRegister64(lir));
4236}
4237
4238void CodeGenerator::visitPointer(LPointer* lir) {
4239 masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output()));
4240}
4241
4242void CodeGenerator::visitDouble(LDouble* ins) {
4243 masm.loadConstantDouble(ins->value(), ToFloatRegister(ins->output()));
4244}
4245
4246void CodeGenerator::visitFloat32(LFloat32* ins) {
4247 masm.loadConstantFloat32(ins->value(), ToFloatRegister(ins->output()));
4248}
4249
4250void CodeGenerator::visitValue(LValue* value) {
4251 ValueOperand result = ToOutValue(value);
4252 masm.moveValue(value->value(), result);
4253}
4254
4255void CodeGenerator::visitNurseryObject(LNurseryObject* lir) {
4256 Register output = ToRegister(lir->output());
4257 uint32_t nurseryIndex = lir->mir()->nurseryIndex();
4258
4259 // Load a pointer to the entry in IonScript's nursery objects list.
4260 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output);
4261 masm.propagateOOM(ionNurseryObjectLabels_.emplaceBack(label, nurseryIndex));
4262
4263 // Load the JSObject*.
4264 masm.loadPtr(Address(output, 0), output);
4265}
4266
4267void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) {
4268 // No-op.
4269}
4270
4271void CodeGenerator::visitDebugEnterGCUnsafeRegion(
4272 LDebugEnterGCUnsafeRegion* lir) {
4273 Register temp = ToRegister(lir->temp0());
4274
4275 masm.loadJSContext(temp);
4276
4277 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4278 masm.add32(Imm32(1), inUnsafeRegion);
4279
4280 Label ok;
4281 masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok);
4282 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4283 masm.bind(&ok);
4284}
4285
4286void CodeGenerator::visitDebugLeaveGCUnsafeRegion(
4287 LDebugLeaveGCUnsafeRegion* lir) {
4288 Register temp = ToRegister(lir->temp0());
4289
4290 masm.loadJSContext(temp);
4291
4292 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4293 masm.add32(Imm32(-1), inUnsafeRegion);
4294
4295 Label ok;
4296 masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok);
4297 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4298 masm.bind(&ok);
4299}
4300
4301void CodeGenerator::visitSlots(LSlots* lir) {
4302 Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots());
4303 masm.loadPtr(slots, ToRegister(lir->output()));
4304}
4305
4306void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) {
4307 ValueOperand dest = ToOutValue(lir);
4308 Register base = ToRegister(lir->input());
4309 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4310
4311 masm.loadValue(Address(base, offset), dest);
4312}
4313
4314static ConstantOrRegister ToConstantOrRegister(const LAllocation* value,
4315 MIRType valueType) {
4316 if (value->isConstant()) {
4317 return ConstantOrRegister(value->toConstant()->toJSValue());
4318 }
4319 return TypedOrValueRegister(valueType, ToAnyRegister(value));
4320}
4321
4322void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) {
4323 Register base = ToRegister(lir->slots());
4324 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4325 Address dest(base, offset);
4326
4327 if (lir->mir()->needsBarrier()) {
4328 emitPreBarrier(dest);
4329 }
4330
4331 MIRType valueType = lir->mir()->value()->type();
4332 ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType);
4333 masm.storeUnboxedValue(value, valueType, dest);
4334}
4335
4336void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) {
4337 Register base = ToRegister(lir->slots());
4338 int32_t offset = lir->mir()->slot() * sizeof(Value);
4339
4340 const ValueOperand value = ToValue(lir, LStoreDynamicSlotV::ValueIndex);
4341
4342 if (lir->mir()->needsBarrier()) {
4343 emitPreBarrier(Address(base, offset));
4344 }
4345
4346 masm.storeValue(value, Address(base, offset));
4347}
4348
4349void CodeGenerator::visitElements(LElements* lir) {
4350 Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
4351 masm.loadPtr(elements, ToRegister(lir->output()));
4352}
4353
4354void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) {
4355 Address environment(ToRegister(lir->function()),
4356 JSFunction::offsetOfEnvironment());
4357 masm.unboxObject(environment, ToRegister(lir->output()));
4358}
4359
4360void CodeGenerator::visitHomeObject(LHomeObject* lir) {
4361 Register func = ToRegister(lir->function());
4362 Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
4363
4364 masm.assertFunctionIsExtended(func);
4365#ifdef DEBUG1
4366 Label isObject;
4367 masm.branchTestObject(Assembler::Equal, homeObject, &isObject);
4368 masm.assumeUnreachable("[[HomeObject]] must be Object");
4369 masm.bind(&isObject);
4370#endif
4371
4372 masm.unboxObject(homeObject, ToRegister(lir->output()));
4373}
4374
4375void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) {
4376 Register homeObject = ToRegister(lir->homeObject());
4377 ValueOperand output = ToOutValue(lir);
4378 Register temp = output.scratchReg();
4379
4380 masm.loadObjProto(homeObject, temp);
4381
4382#ifdef DEBUG1
4383 // We won't encounter a lazy proto, because the prototype is guaranteed to
4384 // either be a JSFunction or a PlainObject, and only proxy objects can have a
4385 // lazy proto.
4386 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"
, 4386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 4386; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4387
4388 Label proxyCheckDone;
4389 masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone);
4390 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase");
4391 masm.bind(&proxyCheckDone);
4392#endif
4393
4394 Label nullProto, done;
4395 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
4396
4397 // Box prototype and return
4398 masm.tagValue(JSVAL_TYPE_OBJECT, temp, output);
4399 masm.jump(&done);
4400
4401 masm.bind(&nullProto);
4402 masm.moveValue(NullValue(), output);
4403
4404 masm.bind(&done);
4405}
4406
4407template <class T>
4408static T* ToConstantObject(MDefinition* def) {
4409 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"
, 4409); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->isConstant()"
")"); do { *((volatile int*)__null) = 4409; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4410 return &def->toConstant()->toObject().as<T>();
4411}
4412
4413void CodeGenerator::visitNewLexicalEnvironmentObject(
4414 LNewLexicalEnvironmentObject* lir) {
4415 Register output = ToRegister(lir->output());
4416 Register temp = ToRegister(lir->temp0());
4417
4418 auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>(
4419 lir->mir()->templateObj());
4420 auto* scope = &templateObj->scope();
4421 gc::Heap initialHeap = gc::Heap::Default;
4422
4423 using Fn =
4424 BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>);
4425 auto* ool =
4426 oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>(
4427 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4428
4429 TemplateObject templateObject(templateObj);
4430 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4431
4432 masm.bind(ool->rejoin());
4433}
4434
4435void CodeGenerator::visitNewClassBodyEnvironmentObject(
4436 LNewClassBodyEnvironmentObject* lir) {
4437 Register output = ToRegister(lir->output());
4438 Register temp = ToRegister(lir->temp0());
4439
4440 auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>(
4441 lir->mir()->templateObj());
4442 auto* scope = &templateObj->scope();
4443 gc::Heap initialHeap = gc::Heap::Default;
4444
4445 using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*,
4446 Handle<ClassBodyScope*>);
4447 auto* ool =
4448 oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>(
4449 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4450
4451 TemplateObject templateObject(templateObj);
4452 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4453
4454 masm.bind(ool->rejoin());
4455}
4456
4457void CodeGenerator::visitNewVarEnvironmentObject(
4458 LNewVarEnvironmentObject* lir) {
4459 Register output = ToRegister(lir->output());
4460 Register temp = ToRegister(lir->temp0());
4461
4462 auto* templateObj =
4463 ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj());
4464 auto* scope = &templateObj->scope().as<VarScope>();
4465 gc::Heap initialHeap = gc::Heap::Default;
4466
4467 using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>);
4468 auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>(
4469 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4470
4471 TemplateObject templateObject(templateObj);
4472 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4473
4474 masm.bind(ool->rejoin());
4475}
4476
4477void CodeGenerator::visitGuardShape(LGuardShape* guard) {
4478 Register obj = ToRegister(guard->input());
4479 Register temp = ToTempRegisterOrInvalid(guard->temp0());
4480 Label bail;
4481 masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp,
4482 obj, &bail);
4483 bailoutFrom(&bail, guard->snapshot());
4484}
4485
4486void CodeGenerator::visitGuardFuse(LGuardFuse* guard) {
4487 auto fuseIndex = guard->mir()->fuseIndex();
4488
4489 Register temp = ToRegister(guard->temp0());
4490 Label bail;
4491
4492 // Bake specific fuse address for Ion code, because we won't share this code
4493 // across realms.
4494 GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex);
4495 masm.loadPtr(AbsoluteAddress(fuse->fuseRef()), temp);
4496 masm.branchPtr(Assembler::NotEqual, temp, ImmPtr(nullptr), &bail);
4497
4498 bailoutFrom(&bail, guard->snapshot());
4499}
4500
4501void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) {
4502 Register obj = ToRegister(guard->object());
4503 Register shapeList = ToRegister(guard->shapeList());
4504 Register temp = ToRegister(guard->temp0());
4505 Register temp2 = ToRegister(guard->temp1());
4506 Register temp3 = ToRegister(guard->temp2());
4507 Register spectre = ToTempRegisterOrInvalid(guard->temp3());
4508
4509 Label bail;
4510 masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp);
4511 masm.branchTestObjShapeList(Assembler::NotEqual, obj, temp, temp2, temp3,
4512 spectre, &bail);
4513 bailoutFrom(&bail, guard->snapshot());
4514}
4515
4516void CodeGenerator::visitGuardProto(LGuardProto* guard) {
4517 Register obj = ToRegister(guard->object());
4518 Register expected = ToRegister(guard->expected());
4519 Register temp = ToRegister(guard->temp0());
4520
4521 masm.loadObjProto(obj, temp);
4522
4523 Label bail;
4524 masm.branchPtr(Assembler::NotEqual, temp, expected, &bail);
4525 bailoutFrom(&bail, guard->snapshot());
4526}
4527
4528void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) {
4529 Register obj = ToRegister(guard->input());
4530 Register temp = ToRegister(guard->temp0());
4531
4532 masm.loadObjProto(obj, temp);
4533
4534 Label bail;
4535 masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail);
4536 bailoutFrom(&bail, guard->snapshot());
4537}
4538
4539void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) {
4540 Register obj = ToRegister(guard->input());
4541 Register temp = ToRegister(guard->temp0());
4542
4543 Label bail;
4544 masm.branchIfNonNativeObj(obj, temp, &bail);
4545 bailoutFrom(&bail, guard->snapshot());
4546}
4547
4548void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) {
4549 Register temp = ToRegister(guard->temp0());
4550 Label bail;
4551
4552 masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp);
4553 masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()),
4554 &bail);
4555 bailoutFrom(&bail, guard->snapshot());
4556}
4557
4558void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) {
4559 Register obj = ToRegister(guard->input());
4560 Register temp = ToRegister(guard->temp0());
4561
4562 Label bail;
4563 masm.branchTestObjectIsProxy(false, obj, temp, &bail);
4564 bailoutFrom(&bail, guard->snapshot());
4565}
4566
4567void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) {
4568 Register obj = ToRegister(guard->input());
4569 Register temp = ToRegister(guard->temp0());
4570
4571 Label bail;
4572 masm.branchTestObjectIsProxy(true, obj, temp, &bail);
4573 bailoutFrom(&bail, guard->snapshot());
4574}
4575
4576void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) {
4577 Register proxy = ToRegister(guard->proxy());
4578 Register temp = ToRegister(guard->temp0());
4579
4580 Label bail;
4581 masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp,
4582 GetDOMProxyHandlerFamily(), &bail);
4583 bailoutFrom(&bail, guard->snapshot());
4584}
4585
4586void CodeGenerator::visitProxyGet(LProxyGet* lir) {
4587 Register proxy = ToRegister(lir->proxy());
4588 Register temp = ToRegister(lir->temp0());
4589
4590 pushArg(lir->mir()->id(), temp);
4591 pushArg(proxy);
4592
4593 using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue);
4594 callVM<Fn, ProxyGetProperty>(lir);
4595}
4596
4597void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) {
4598 Register proxy = ToRegister(lir->proxy());
4599 ValueOperand idVal = ToValue(lir, LProxyGetByValue::IdIndex);
4600
4601 pushArg(idVal);
4602 pushArg(proxy);
4603
4604 using Fn =
4605 bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
4606 callVM<Fn, ProxyGetPropertyByValue>(lir);
4607}
4608
4609void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) {
4610 Register proxy = ToRegister(lir->proxy());
4611 ValueOperand idVal = ToValue(lir, LProxyHasProp::IdIndex);
4612
4613 pushArg(idVal);
4614 pushArg(proxy);
4615
4616 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*);
4617 if (lir->mir()->hasOwn()) {
4618 callVM<Fn, ProxyHasOwn>(lir);
4619 } else {
4620 callVM<Fn, ProxyHas>(lir);
4621 }
4622}
4623
4624void CodeGenerator::visitProxySet(LProxySet* lir) {
4625 Register proxy = ToRegister(lir->proxy());
4626 ValueOperand rhs = ToValue(lir, LProxySet::RhsIndex);
4627 Register temp = ToRegister(lir->temp0());
4628
4629 pushArg(Imm32(lir->mir()->strict()));
4630 pushArg(rhs);
4631 pushArg(lir->mir()->id(), temp);
4632 pushArg(proxy);
4633
4634 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
4635 callVM<Fn, ProxySetProperty>(lir);
4636}
4637
4638void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) {
4639 Register proxy = ToRegister(lir->proxy());
4640 ValueOperand idVal = ToValue(lir, LProxySetByValue::IdIndex);
4641 ValueOperand rhs = ToValue(lir, LProxySetByValue::RhsIndex);
4642
4643 pushArg(Imm32(lir->mir()->strict()));
4644 pushArg(rhs);
4645 pushArg(idVal);
4646 pushArg(proxy);
4647
4648 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
4649 callVM<Fn, ProxySetPropertyByValue>(lir);
4650}
4651
4652void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) {
4653 Register obj = ToRegister(lir->obj());
4654 ValueOperand rhs = ToValue(lir, LCallSetArrayLength::RhsIndex);
4655
4656 pushArg(Imm32(lir->mir()->strict()));
4657 pushArg(rhs);
4658 pushArg(obj);
4659
4660 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool);
4661 callVM<Fn, jit::SetArrayLength>(lir);
4662}
4663
4664void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) {
4665 Register obj = ToRegister(lir->object());
4666 Register temp0 = ToRegister(lir->temp0());
4667 Register temp1 = ToRegister(lir->temp1());
4668 Register temp2 = ToRegister(lir->temp2());
4669 Register temp3 = ToRegister(lir->temp3());
4670 ValueOperand output = ToOutValue(lir);
4671
4672 Label cacheHit;
4673 masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2,
4674 output, &cacheHit);
4675
4676 Label bail;
4677 masm.branchIfNonNativeObj(obj, temp0, &bail);
4678
4679 masm.Push(UndefinedValue());
4680 masm.moveStackPtrTo(temp3);
4681
4682 using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id,
4683 MegamorphicCache::Entry* cacheEntry, Value* vp);
4684 masm.setupAlignedABICall();
4685 masm.loadJSContext(temp0);
4686 masm.passABIArg(temp0);
4687 masm.passABIArg(obj);
4688 masm.movePropertyKey(lir->mir()->name(), temp1);
4689 masm.passABIArg(temp1);
4690 masm.passABIArg(temp2);
4691 masm.passABIArg(temp3);
4692
4693 masm.callWithABI<Fn, GetNativeDataPropertyPure>();
4694
4695 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"
, 4695); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)"
")"); do { *((volatile int*)__null) = 4695; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4696 masm.Pop(output);
4697
4698 masm.branchIfFalseBool(ReturnReg, &bail);
4699 masm.bind(&cacheHit);
4700
4701 bailoutFrom(&bail, lir->snapshot());
4702}
4703
4704void CodeGenerator::visitMegamorphicLoadSlotPermissive(
4705 LMegamorphicLoadSlotPermissive* lir) {
4706 Register obj = ToRegister(lir->object());
4707 Register temp0 = ToRegister(lir->temp0());
4708 Register temp1 = ToRegister(lir->temp1());
4709 Register temp2 = ToRegister(lir->temp2());
4710 ValueOperand output = ToOutValue(lir);
4711
4712 Label cacheHit;
4713 masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2,
4714 output, &cacheHit);
4715
4716 masm.movePropertyKey(lir->mir()->name(), temp1);
4717 pushArg(temp2);
4718 pushArg(temp1);
4719 pushArg(obj);
4720
4721 using Fn = bool (*)(JSContext*, HandleObject, HandleId,
4722 MegamorphicCacheEntry*, MutableHandleValue);
4723 callVM<Fn, GetPropMaybeCached>(lir);
4724
4725 masm.bind(&cacheHit);
4726}
4727
4728void CodeGenerator::visitMegamorphicLoadSlotByValue(
4729 LMegamorphicLoadSlotByValue* lir) {
4730 Register obj = ToRegister(lir->object());
4731 ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex);
4732 Register temp0 = ToRegister(lir->temp0());
4733 Register temp1 = ToRegister(lir->temp1());
4734 Register temp2 = ToRegister(lir->temp2());
4735 ValueOperand output = ToOutValue(lir);
4736
4737 Label cacheHit, bail;
4738 masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2,
4739 output, &cacheHit);
4740
4741 masm.branchIfNonNativeObj(obj, temp0, &bail);
4742
4743 // idVal will be in vp[0], result will be stored in vp[1].
4744 masm.reserveStack(sizeof(Value));
4745 masm.Push(idVal);
4746 masm.moveStackPtrTo(temp0);
4747
4748 using Fn = bool (*)(JSContext* cx, JSObject* obj,
4749 MegamorphicCache::Entry* cacheEntry, Value* vp);
4750 masm.setupAlignedABICall();
4751 masm.loadJSContext(temp1);
4752 masm.passABIArg(temp1);
4753 masm.passABIArg(obj);
4754 masm.passABIArg(temp2);
4755 masm.passABIArg(temp0);
4756 masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>();
4757
4758 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"
, 4758); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)"
")"); do { *((volatile int*)__null) = 4758; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4759 masm.storeCallPointerResult(temp0);
4760 masm.Pop(idVal);
4761
4762 uint32_t framePushed = masm.framePushed();
4763 Label ok;
4764 masm.branchIfTrueBool(temp0, &ok);
4765 masm.freeStack(sizeof(Value)); // Discard result Value.
4766 masm.jump(&bail);
4767
4768 masm.bind(&ok);
4769 masm.setFramePushed(framePushed);
4770 masm.Pop(output);
4771
4772 masm.bind(&cacheHit);
4773
4774 bailoutFrom(&bail, lir->snapshot());
4775}
4776
4777void CodeGenerator::visitMegamorphicLoadSlotByValuePermissive(
4778 LMegamorphicLoadSlotByValuePermissive* lir) {
4779 Register obj = ToRegister(lir->object());
4780 ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex);
4781 Register temp0 = ToRegister(lir->temp0());
4782 Register temp1 = ToRegister(lir->temp1());
4783 Register temp2 = ToRegister(lir->temp2());
4784 ValueOperand output = ToOutValue(lir);
4785
4786 Label cacheHit;
4787 masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2,
4788 output, &cacheHit);
4789
4790 pushArg(temp2);
4791 pushArg(idVal);
4792 pushArg(obj);
4793
4794 using Fn = bool (*)(JSContext*, HandleObject, HandleValue,
4795 MegamorphicCacheEntry*, MutableHandleValue);
4796 callVM<Fn, GetElemMaybeCached>(lir);
4797
4798 masm.bind(&cacheHit);
4799}
4800
4801void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) {
4802 Register obj = ToRegister(lir->object());
4803 ValueOperand value = ToValue(lir, LMegamorphicStoreSlot::RhsIndex);
4804
4805 Register temp0 = ToRegister(lir->temp0());
4806#ifndef JS_CODEGEN_X86
4807 Register temp1 = ToRegister(lir->temp1());
4808 Register temp2 = ToRegister(lir->temp2());
4809#endif
4810
4811 // The instruction is marked as call-instruction so only these registers are
4812 // live.
4813 LiveRegisterSet liveRegs;
4814 liveRegs.addUnchecked(obj);
4815 liveRegs.addUnchecked(value);
4816 liveRegs.addUnchecked(temp0);
4817#ifndef JS_CODEGEN_X86
4818 liveRegs.addUnchecked(temp1);
4819 liveRegs.addUnchecked(temp2);
4820#endif
4821
4822 Label cacheHit, done;
4823#ifdef JS_CODEGEN_X86
4824 masm.emitMegamorphicCachedSetSlot(
4825 lir->mir()->name(), obj, temp0, value, liveRegs, &cacheHit,
4826 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
4827 EmitPreBarrier(masm, addr, mirType);
4828 });
4829#else
4830 masm.emitMegamorphicCachedSetSlot(
4831 lir->mir()->name(), obj, temp0, temp1, temp2, value, liveRegs, &cacheHit,
4832 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
4833 EmitPreBarrier(masm, addr, mirType);
4834 });
4835#endif
4836
4837 pushArg(Imm32(lir->mir()->strict()));
4838 pushArg(value);
4839 pushArg(lir->mir()->name(), temp0);
4840 pushArg(obj);
4841
4842 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
4843 callVM<Fn, SetPropertyMegamorphic<true>>(lir);
4844
4845 masm.jump(&done);
4846 masm.bind(&cacheHit);
4847
4848 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
4849 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
4850
4851 // Note: because this is a call-instruction, no registers need to be saved.
4852 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"
, 4852); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->isCall()"
")"); do { *((volatile int*)__null) = 4852; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4853 emitPostWriteBarrier(obj);
4854
4855 masm.bind(&done);
4856}
4857
4858void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) {
4859 Register obj = ToRegister(lir->object());
4860 ValueOperand idVal = ToValue(lir, LMegamorphicHasProp::IdIndex);
4861 Register temp0 = ToRegister(lir->temp0());
4862 Register temp1 = ToRegister(lir->temp1());
4863 Register temp2 = ToRegister(lir->temp2());
4864 Register output = ToRegister(lir->output());
4865
4866 Label bail, cacheHit;
4867 masm.emitMegamorphicCacheLookupExists(idVal, obj, temp0, temp1, temp2, output,
4868 &cacheHit, lir->mir()->hasOwn());
4869
4870 masm.branchIfNonNativeObj(obj, temp0, &bail);
4871
4872 // idVal will be in vp[0], result will be stored in vp[1].
4873 masm.reserveStack(sizeof(Value));
4874 masm.Push(idVal);
4875 masm.moveStackPtrTo(temp0);
4876
4877 using Fn = bool (*)(JSContext* cx, JSObject* obj,
4878 MegamorphicCache::Entry* cacheEntry, Value* vp);
4879 masm.setupAlignedABICall();
4880 masm.loadJSContext(temp1);
4881 masm.passABIArg(temp1);
4882 masm.passABIArg(obj);
4883 masm.passABIArg(temp2);
4884 masm.passABIArg(temp0);
4885 if (lir->mir()->hasOwn()) {
4886 masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>();
4887 } else {
4888 masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>();
4889 }
4890
4891 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"
, 4891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)"
")"); do { *((volatile int*)__null) = 4891; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4892 masm.storeCallPointerResult(temp0);
4893 masm.Pop(idVal);
4894
4895 uint32_t framePushed = masm.framePushed();
4896 Label ok;
4897 masm.branchIfTrueBool(temp0, &ok);
4898 masm.freeStack(sizeof(Value)); // Discard result Value.
4899 masm.jump(&bail);
4900
4901 masm.bind(&ok);
4902 masm.setFramePushed(framePushed);
4903 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
4904 masm.freeStack(sizeof(Value));
4905 masm.bind(&cacheHit);
4906
4907 bailoutFrom(&bail, lir->snapshot());
4908}
4909
4910void CodeGenerator::visitSmallObjectVariableKeyHasProp(
4911 LSmallObjectVariableKeyHasProp* lir) {
4912 Register id = ToRegister(lir->id());
4913 Register output = ToRegister(lir->output());
4914
4915#ifdef DEBUG1
4916 Label isAtom;
4917 masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()),
4918 Imm32(JSString::ATOM_BIT), &isAtom);
4919 masm.assumeUnreachable("Expected atom input");
4920 masm.bind(&isAtom);
4921#endif
4922
4923 SharedShape* shape = &lir->mir()->shape()->asShared();
4924
4925 Label done, success;
4926 for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) {
4927 masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()),
4928 &success);
4929 }
4930 masm.move32(Imm32(0), output);
4931 masm.jump(&done);
4932 masm.bind(&success);
4933 masm.move32(Imm32(1), output);
4934 masm.bind(&done);
4935}
4936
4937void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared(
4938 LGuardIsNotArrayBufferMaybeShared* guard) {
4939 Register obj = ToRegister(guard->input());
4940 Register temp = ToRegister(guard->temp0());
4941
4942 Label bail;
4943 masm.loadObjClassUnsafe(obj, temp);
4944 masm.branchPtr(Assembler::Equal, temp,
4945 ImmPtr(&FixedLengthArrayBufferObject::class_), &bail);
4946 masm.branchPtr(Assembler::Equal, temp,
4947 ImmPtr(&FixedLengthSharedArrayBufferObject::class_), &bail);
4948 masm.branchPtr(Assembler::Equal, temp,
4949 ImmPtr(&ResizableArrayBufferObject::class_), &bail);
4950 masm.branchPtr(Assembler::Equal, temp,
4951 ImmPtr(&GrowableSharedArrayBufferObject::class_), &bail);
4952 bailoutFrom(&bail, guard->snapshot());
4953}
4954
4955void CodeGenerator::visitGuardIsTypedArray(LGuardIsTypedArray* guard) {
4956 Register obj = ToRegister(guard->input());
4957 Register temp = ToRegister(guard->temp0());
4958
4959 Label bail;
4960 masm.loadObjClassUnsafe(obj, temp);
4961 masm.branchIfClassIsNotTypedArray(temp, &bail);
4962 bailoutFrom(&bail, guard->snapshot());
4963}
4964
4965void CodeGenerator::visitGuardIsFixedLengthTypedArray(
4966 LGuardIsFixedLengthTypedArray* guard) {
4967 Register obj = ToRegister(guard->input());
4968 Register temp = ToRegister(guard->temp0());
4969
4970 Label bail;
4971 masm.loadObjClassUnsafe(obj, temp);
4972 masm.branchIfClassIsNotFixedLengthTypedArray(temp, &bail);
4973 bailoutFrom(&bail, guard->snapshot());
4974}
4975
4976void CodeGenerator::visitGuardIsResizableTypedArray(
4977 LGuardIsResizableTypedArray* guard) {
4978 Register obj = ToRegister(guard->input());
4979 Register temp = ToRegister(guard->temp0());
4980
4981 Label bail;
4982 masm.loadObjClassUnsafe(obj, temp);
4983 masm.branchIfClassIsNotResizableTypedArray(temp, &bail);
4984 bailoutFrom(&bail, guard->snapshot());
4985}
4986
4987void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) {
4988 Register obj = ToRegister(guard->input());
4989
4990 Label bail;
4991
4992 Address handlerAddr(obj, ProxyObject::offsetOfHandler());
4993 masm.branchPtr(Assembler::NotEqual, handlerAddr,
4994 ImmPtr(guard->mir()->handler()), &bail);
4995
4996 bailoutFrom(&bail, guard->snapshot());
4997}
4998
4999void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) {
5000 Register input = ToRegister(guard->input());
5001 Register expected = ToRegister(guard->expected());
5002
5003 Assembler::Condition cond =
5004 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
5005 bailoutCmpPtr(cond, input, expected, guard->snapshot());
5006}
5007
5008void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) {
5009 Register input = ToRegister(guard->input());
5010 Register expected = ToRegister(guard->expected());
5011
5012 bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot());
5013}
5014
5015void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) {
5016 Register str = ToRegister(guard->str());
5017 Register scratch = ToRegister(guard->temp0());
5018
5019 LiveRegisterSet volatileRegs = liveVolatileRegs(guard);
5020 volatileRegs.takeUnchecked(scratch);
5021
5022 Label bail;
5023 masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs,
5024 &bail);
5025 bailoutFrom(&bail, guard->snapshot());
5026}
5027
5028void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) {
5029 Register symbol = ToRegister(guard->symbol());
5030
5031 bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()),
5032 guard->snapshot());
5033}
5034
5035void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) {
5036 Register num = ToRegister(guard->num());
5037
5038 bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()),
5039 guard->snapshot());
5040}
5041
5042void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) {
5043 Register str = ToRegister(lir->string());
5044 Register output = ToRegister(lir->output());
5045
5046 Label vmCall, done;
5047 masm.loadStringIndexValue(str, output, &vmCall);
5048 masm.jump(&done);
5049
5050 {
5051 masm.bind(&vmCall);
5052
5053 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5054 volatileRegs.takeUnchecked(output);
5055 masm.PushRegsInMask(volatileRegs);
5056
5057 using Fn = int32_t (*)(JSString* str);
5058 masm.setupAlignedABICall();
5059 masm.passABIArg(str);
5060 masm.callWithABI<Fn, GetIndexFromString>();
5061 masm.storeCallInt32Result(output);
5062
5063 masm.PopRegsInMask(volatileRegs);
5064
5065 // GetIndexFromString returns a negative value on failure.
5066 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
5067 }
5068
5069 masm.bind(&done);
5070}
5071
5072void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) {
5073 Register str = ToRegister(lir->string());
5074 Register output = ToRegister(lir->output());
5075 Register temp = ToRegister(lir->temp0());
5076
5077 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5078
5079 Label bail;
5080 masm.guardStringToInt32(str, output, temp, volatileRegs, &bail);
5081 bailoutFrom(&bail, lir->snapshot());
5082}
5083
5084void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) {
5085 Register str = ToRegister(lir->string());
5086 FloatRegister output = ToFloatRegister(lir->output());
5087 Register temp0 = ToRegister(lir->temp0());
5088 Register temp1 = ToRegister(lir->temp1());
5089
5090 Label vmCall, done;
5091 // Use indexed value as fast path if possible.
5092 masm.loadStringIndexValue(str, temp0, &vmCall);
5093 masm.convertInt32ToDouble(temp0, output);
5094 masm.jump(&done);
5095 {
5096 masm.bind(&vmCall);
5097
5098 // Reserve stack for holding the result value of the call.
5099 masm.reserveStack(sizeof(double));
5100 masm.moveStackPtrTo(temp0);
5101
5102 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
5103 volatileRegs.takeUnchecked(temp0);
5104 volatileRegs.takeUnchecked(temp1);
5105 masm.PushRegsInMask(volatileRegs);
5106
5107 using Fn = bool (*)(JSContext* cx, JSString* str, double* result);
5108 masm.setupAlignedABICall();
5109 masm.loadJSContext(temp1);
5110 masm.passABIArg(temp1);
5111 masm.passABIArg(str);
5112 masm.passABIArg(temp0);
5113 masm.callWithABI<Fn, StringToNumberPure>();
5114 masm.storeCallPointerResult(temp0);
5115
5116 masm.PopRegsInMask(volatileRegs);
5117
5118 Label ok;
5119 masm.branchIfTrueBool(temp0, &ok);
5120 {
5121 // OOM path, recovered by StringToNumberPure.
5122 //
5123 // Use addToStackPtr instead of freeStack as freeStack tracks stack height
5124 // flow-insensitively, and using it here would confuse the stack height
5125 // tracking.
5126 masm.addToStackPtr(Imm32(sizeof(double)));
5127 bailout(lir->snapshot());
5128 }
5129 masm.bind(&ok);
5130 masm.Pop(output);
5131 }
5132 masm.bind(&done);
5133}
5134
5135void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) {
5136 Register obj = ToRegister(guard->input());
5137 Register temp = ToRegister(guard->temp0());
5138
5139 // Load obj->elements.
5140 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp);
5141
5142 // Make sure there are no dense elements.
5143 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
5144 bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot());
5145}
5146
5147void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) {
5148 Register input = ToRegister(lir->input());
5149 Register64 output = ToOutRegister64(lir);
5150
5151 masm.move32To64ZeroExtend(input, output);
5152}
5153
5154void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input,
5155 Register64 output) {
5156 Register temp = output.scratchReg();
5157
5158 saveLive(lir);
5159
5160 masm.reserveStack(sizeof(uint64_t));
5161 masm.moveStackPtrTo(temp);
5162 pushArg(temp);
5163 pushArg(input);
5164
5165 using Fn = bool (*)(JSContext*, HandleString, uint64_t*);
5166 callVM<Fn, DoStringToInt64>(lir);
5167
5168 masm.load64(Address(masm.getStackPointer(), 0), output);
5169 masm.freeStack(sizeof(uint64_t));
5170
5171 restoreLiveIgnore(lir, StoreValueTo(output).clobbered());
5172}
5173
5174void CodeGenerator::visitStringToInt64(LStringToInt64* lir) {
5175 Register input = ToRegister(lir->input());
5176 Register64 output = ToOutRegister64(lir);
5177
5178 emitStringToInt64(lir, input, output);
5179}
5180
5181void CodeGenerator::visitValueToInt64(LValueToInt64* lir) {
5182 ValueOperand input = ToValue(lir, LValueToInt64::InputIndex);
5183 Register temp = ToRegister(lir->temp0());
5184 Register64 output = ToOutRegister64(lir);
5185
5186 int checks = 3;
5187
5188 Label fail, done;
5189 // Jump to fail if this is the last check and we fail it,
5190 // otherwise to the next test.
5191 auto emitTestAndUnbox = [&](auto testAndUnbox) {
5192 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"
, 5192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks > 0"
")"); do { *((volatile int*)__null) = 5192; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5193
5194 checks--;
5195 Label notType;
5196 Label* target = checks ? &notType : &fail;
5197
5198 testAndUnbox(target);
5199
5200 if (checks) {
5201 masm.jump(&done);
5202 masm.bind(&notType);
5203 }
5204 };
5205
5206 Register tag = masm.extractTag(input, temp);
5207
5208 // BigInt.
5209 emitTestAndUnbox([&](Label* target) {
5210 masm.branchTestBigInt(Assembler::NotEqual, tag, target);
5211 masm.unboxBigInt(input, temp);
5212 masm.loadBigInt64(temp, output);
5213 });
5214
5215 // Boolean
5216 emitTestAndUnbox([&](Label* target) {
5217 masm.branchTestBoolean(Assembler::NotEqual, tag, target);
5218 masm.unboxBoolean(input, temp);
5219 masm.move32To64ZeroExtend(temp, output);
5220 });
5221
5222 // String
5223 emitTestAndUnbox([&](Label* target) {
5224 masm.branchTestString(Assembler::NotEqual, tag, target);
5225 masm.unboxString(input, temp);
5226 emitStringToInt64(lir, temp, output);
5227 });
5228
5229 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"
, 5229); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0"
")"); do { *((volatile int*)__null) = 5229; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5230
5231 bailoutFrom(&fail, lir->snapshot());
5232 masm.bind(&done);
5233}
5234
5235void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) {
5236 Register operand = ToRegister(lir->input());
5237 Register64 output = ToOutRegister64(lir);
5238
5239 masm.loadBigInt64(operand, output);
5240}
5241
5242OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir,
5243 Scalar::Type type,
5244 Register64 input,
5245 Register output) {
5246#if JS_BITS_PER_WORD64 == 32
5247 using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t);
5248 auto args = ArgList(input.low, input.high);
5249#else
5250 using Fn = BigInt* (*)(JSContext*, uint64_t);
5251 auto args = ArgList(input);
5252#endif
5253
5254 if (type == Scalar::BigInt64) {
5255 return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args,
5256 StoreRegisterTo(output));
5257 }
5258 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"
, 5258); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == Scalar::BigUint64"
")"); do { *((volatile int*)__null) = 5258; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5259 return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args,
5260 StoreRegisterTo(output));
5261}
5262
5263void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type,
5264 Register64 input, Register output,
5265 Register maybeTemp,
5266 Register64 maybeTemp64) {
5267 OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output);
5268
5269 if (maybeTemp != InvalidReg) {
5270 masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry());
5271 } else {
5272 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
5273 regs.take(input);
5274 regs.take(output);
5275
5276 Register temp = regs.takeAny();
5277
5278 masm.push(temp);
5279
5280 Label fail, ok;
5281 masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail);
5282 masm.pop(temp);
5283 masm.jump(&ok);
5284 masm.bind(&fail);
5285 masm.pop(temp);
5286 masm.jump(ool->entry());
5287 masm.bind(&ok);
5288 }
5289 masm.initializeBigInt64(type, output, input, maybeTemp64);
5290 masm.bind(ool->rejoin());
5291}
5292
5293void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) {
5294 Register64 input = ToRegister64(lir->input());
5295 Register64 temp = ToRegister64(lir->temp());
5296 Register output = ToRegister(lir->output());
5297
5298 emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp.scratchReg(),
5299 temp);
5300}
5301
5302void CodeGenerator::visitUint64ToBigInt(LUint64ToBigInt* lir) {
5303 Register64 input = ToRegister64(lir->input());
5304 Register temp = ToRegister(lir->temp0());
5305 Register output = ToRegister(lir->output());
5306
5307 emitCreateBigInt(lir, Scalar::BigUint64, input, output, temp);
5308}
5309
5310void CodeGenerator::visitInt64ToIntPtr(LInt64ToIntPtr* lir) {
5311 Register64 input = ToRegister64(lir->input());
5312#ifdef JS_64BIT1
5313 MOZ_ASSERT(input.reg == ToRegister(lir->output()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(input.reg == ToRegister(lir->output()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(input.reg == ToRegister(lir->output())))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("input.reg == ToRegister(lir->output())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input.reg == ToRegister(lir->output())"
")"); do { *((volatile int*)__null) = 5313; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5314#else
5315 Register output = ToRegister(lir->output());
5316#endif
5317
5318 Label bail;
5319 if (lir->mir()->isSigned()) {
5320 masm.branchInt64NotInPtrRange(input, &bail);
5321 } else {
5322 masm.branchUInt64NotInPtrRange(input, &bail);
5323 }
5324 bailoutFrom(&bail, lir->snapshot());
5325
5326#ifndef JS_64BIT1
5327 masm.move64To32(input, output);
5328#endif
5329}
5330
5331void CodeGenerator::visitIntPtrToInt64(LIntPtrToInt64* lir) {
5332#ifdef JS_64BIT1
5333 MOZ_CRASH("Not used on 64-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 64-bit platforms"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 5333); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 64-bit platforms"
")"); do { *((volatile int*)__null) = 5333; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
5334#else
5335 Register input = ToRegister(lir->input());
5336 Register64 output = ToOutRegister64(lir);
5337
5338 masm.move32To64SignExtend(input, output);
5339#endif
5340}
5341
5342void CodeGenerator::visitGuardValue(LGuardValue* lir) {
5343 ValueOperand input = ToValue(lir, LGuardValue::InputIndex);
5344 Value expected = lir->mir()->expected();
5345 Label bail;
5346 masm.branchTestValue(Assembler::NotEqual, input, expected, &bail);
5347 bailoutFrom(&bail, lir->snapshot());
5348}
5349
5350void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) {
5351 ValueOperand input = ToValue(lir, LGuardNullOrUndefined::InputIndex);
5352
5353 ScratchTagScope tag(masm, input);
5354 masm.splitTagForTest(input, tag);
5355
5356 Label done;
5357 masm.branchTestNull(Assembler::Equal, tag, &done);
5358
5359 Label bail;
5360 masm.branchTestUndefined(Assembler::NotEqual, tag, &bail);
5361 bailoutFrom(&bail, lir->snapshot());
5362
5363 masm.bind(&done);
5364}
5365
5366void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) {
5367 ValueOperand input = ToValue(lir, LGuardIsNotObject::InputIndex);
5368
5369 Label bail;
5370 masm.branchTestObject(Assembler::Equal, input, &bail);
5371 bailoutFrom(&bail, lir->snapshot());
5372}
5373
5374void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) {
5375 Register function = ToRegister(lir->function());
5376
5377 Label bail;
5378 if (uint16_t flags = lir->mir()->expectedFlags()) {
5379 masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail);
5380 }
5381 if (uint16_t flags = lir->mir()->unexpectedFlags()) {
5382 masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail);
5383 }
5384 bailoutFrom(&bail, lir->snapshot());
5385}
5386
5387void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor(
5388 LGuardFunctionIsNonBuiltinCtor* lir) {
5389 Register function = ToRegister(lir->function());
5390 Register temp = ToRegister(lir->temp0());
5391
5392 Label bail;
5393 masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail);
5394 bailoutFrom(&bail, lir->snapshot());
5395}
5396
5397void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) {
5398 Register function = ToRegister(lir->function());
5399 Register temp = ToRegister(lir->temp0());
5400
5401 Assembler::Condition cond =
5402 lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
5403
5404 Label bail;
5405 masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail);
5406 bailoutFrom(&bail, lir->snapshot());
5407}
5408
5409void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) {
5410 Register function = ToRegister(lir->function());
5411
5412 Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript());
5413 bailoutCmpPtr(Assembler::NotEqual, scriptAddr,
5414 ImmGCPtr(lir->mir()->expected()), lir->snapshot());
5415}
5416
5417// Out-of-line path to update the store buffer.
5418class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> {
5419 LInstruction* lir_;
5420 const LAllocation* object_;
5421
5422 public:
5423 OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object)
5424 : lir_(lir), object_(object) {}
5425
5426 void accept(CodeGenerator* codegen) override {
5427 codegen->visitOutOfLineCallPostWriteBarrier(this);
5428 }
5429
5430 LInstruction* lir() const { return lir_; }
5431 const LAllocation* object() const { return object_; }
5432};
5433
5434static void EmitStoreBufferCheckForConstant(MacroAssembler& masm,
5435 const gc::TenuredCell* cell,
5436 AllocatableGeneralRegisterSet& regs,
5437 Label* exit, Label* callVM) {
5438 Register temp = regs.takeAny();
5439
5440 gc::Arena* arena = cell->arena();
5441
5442 Register cells = temp;
5443 masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells);
5444
5445 size_t index = gc::ArenaCellSet::getCellIndex(cell);
5446 size_t word;
5447 uint32_t mask;
5448 gc::ArenaCellSet::getWordIndexAndMask(index, &word, &mask);
5449 size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t);
5450
5451 masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask),
5452 exit);
5453
5454 // Check whether this is the sentinel set and if so call the VM to allocate
5455 // one for this arena.
5456 masm.branchPtr(Assembler::Equal,
5457 Address(cells, gc::ArenaCellSet::offsetOfArena()),
5458 ImmPtr(nullptr), callVM);
5459
5460 // Add the cell to the set.
5461 masm.or32(Imm32(mask), Address(cells, offset));
5462 masm.jump(exit);
5463
5464 regs.add(temp);
5465}
5466
5467static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime,
5468 Register objreg, JSObject* maybeConstant,
5469 bool isGlobal,
5470 AllocatableGeneralRegisterSet& regs) {
5471 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"
, 5471); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeConstant"
")"); do { *((volatile int*)__null) = 5471; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5472
5473 Label callVM;
5474 Label exit;
5475
5476 Register temp = regs.takeAny();
5477
5478 // We already have a fast path to check whether a global is in the store
5479 // buffer.
5480 if (!isGlobal) {
5481 if (maybeConstant) {
5482 // Check store buffer bitmap directly for known object.
5483 EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs,
5484 &exit, &callVM);
5485 } else {
5486 // Check one element cache to avoid VM call.
5487 masm.branchPtr(Assembler::Equal,
5488 AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()),
5489 objreg, &exit);
5490 }
5491 }
5492
5493 // Call into the VM to barrier the write.
5494 masm.bind(&callVM);
5495
5496 Register runtimereg = temp;
5497 masm.mov(ImmPtr(runtime), runtimereg);
5498
5499 masm.setupAlignedABICall();
5500 masm.passABIArg(runtimereg);
5501 masm.passABIArg(objreg);
5502 if (isGlobal) {
5503 using Fn = void (*)(JSRuntime* rt, GlobalObject* obj);
5504 masm.callWithABI<Fn, PostGlobalWriteBarrier>();
5505 } else {
5506 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj);
5507 masm.callWithABI<Fn, PostWriteBarrier>();
5508 }
5509
5510 masm.bind(&exit);
5511}
5512
5513void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) {
5514 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5515
5516 Register objreg;
5517 JSObject* object = nullptr;
5518 bool isGlobal = false;
5519 if (obj->isConstant()) {
5520 object = &obj->toConstant()->toObject();
5521 isGlobal = isGlobalObject(object);
5522 objreg = regs.takeAny();
5523 masm.movePtr(ImmGCPtr(object), objreg);
5524 } else {
5525 objreg = ToRegister(obj);
5526 regs.takeUnchecked(objreg);
5527 }
5528
5529 EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs);
5530}
5531
5532// Returns true if `def` might be allocated in the nursery.
5533static bool ValueNeedsPostBarrier(MDefinition* def) {
5534 if (def->isBox()) {
5535 def = def->toBox()->input();
5536 }
5537 if (def->type() == MIRType::Value) {
5538 return true;
5539 }
5540 return NeedsPostBarrier(def->type());
5541}
5542
5543class OutOfLineElementPostWriteBarrier
5544 : public OutOfLineCodeBase<CodeGenerator> {
5545 LiveRegisterSet liveVolatileRegs_;
5546 const LAllocation* index_;
5547 int32_t indexDiff_;
5548 Register obj_;
5549 Register scratch_;
5550
5551 public:
5552 OutOfLineElementPostWriteBarrier(const LiveRegisterSet& liveVolatileRegs,
5553 Register obj, const LAllocation* index,
5554 Register scratch, int32_t indexDiff)
5555 : liveVolatileRegs_(liveVolatileRegs),
5556 index_(index),
5557 indexDiff_(indexDiff),
5558 obj_(obj),
5559 scratch_(scratch) {}
5560
5561 void accept(CodeGenerator* codegen) override {
5562 codegen->visitOutOfLineElementPostWriteBarrier(this);
5563 }
5564
5565 const LiveRegisterSet& liveVolatileRegs() const { return liveVolatileRegs_; }
5566 const LAllocation* index() const { return index_; }
5567 int32_t indexDiff() const { return indexDiff_; }
5568
5569 Register object() const { return obj_; }
5570 Register scratch() const { return scratch_; }
5571};
5572
5573void CodeGenerator::emitElementPostWriteBarrier(
5574 MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj,
5575 const LAllocation* index, Register scratch, const ConstantOrRegister& val,
5576 int32_t indexDiff) {
5577 if (val.constant()) {
5578 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"
, 5579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { *((volatile int*)__null) = 5579; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5579 !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"
, 5579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { *((volatile int*)__null) = 5579; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5580 return;
5581 }
5582
5583 TypedOrValueRegister reg = val.reg();
5584 if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) {
5585 return;
5586 }
5587
5588 auto* ool = new (alloc()) OutOfLineElementPostWriteBarrier(
5589 liveVolatileRegs, obj, index, scratch, indexDiff);
5590 addOutOfLineCode(ool, mir);
5591
5592 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, ool->rejoin());
5593
5594 if (reg.hasValue()) {
5595 masm.branchValueIsNurseryCell(Assembler::Equal, reg.valueReg(), scratch,
5596 ool->entry());
5597 } else {
5598 masm.branchPtrInNurseryChunk(Assembler::Equal, reg.typedReg().gpr(),
5599 scratch, ool->entry());
5600 }
5601
5602 masm.bind(ool->rejoin());
5603}
5604
5605void CodeGenerator::visitOutOfLineElementPostWriteBarrier(
5606 OutOfLineElementPostWriteBarrier* ool) {
5607 Register obj = ool->object();
5608 Register scratch = ool->scratch();
5609 const LAllocation* index = ool->index();
5610 int32_t indexDiff = ool->indexDiff();
5611
5612 masm.PushRegsInMask(ool->liveVolatileRegs());
5613
5614 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5615 regs.takeUnchecked(obj);
5616 regs.takeUnchecked(scratch);
5617
5618 Register indexReg;
5619 if (index->isConstant()) {
5620 indexReg = regs.takeAny();
5621 masm.move32(Imm32(ToInt32(index) + indexDiff), indexReg);
5622 } else {
5623 indexReg = ToRegister(index);
5624 regs.takeUnchecked(indexReg);
5625 if (indexDiff != 0) {
5626 masm.add32(Imm32(indexDiff), indexReg);
5627 }
5628 }
5629
5630 masm.setupUnalignedABICall(scratch);
5631 masm.movePtr(ImmPtr(gen->runtime), scratch);
5632 masm.passABIArg(scratch);
5633 masm.passABIArg(obj);
5634 masm.passABIArg(indexReg);
5635 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
5636 masm.callWithABI<Fn, PostWriteElementBarrier>();
5637
5638 // We don't need a sub32 here because indexReg must be in liveVolatileRegs
5639 // if indexDiff is not zero, so it will be restored below.
5640 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"
, 5640); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ool->liveVolatileRegs().has(indexReg)"
")"); do { *((volatile int*)__null) = 5640; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5641
5642 masm.PopRegsInMask(ool->liveVolatileRegs());
5643
5644 masm.jump(ool->rejoin());
5645}
5646
5647void CodeGenerator::emitPostWriteBarrier(Register objreg) {
5648 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5649 regs.takeUnchecked(objreg);
5650 EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs);
5651}
5652
5653void CodeGenerator::visitOutOfLineCallPostWriteBarrier(
5654 OutOfLineCallPostWriteBarrier* ool) {
5655 saveLiveVolatile(ool->lir());
5656 const LAllocation* obj = ool->object();
5657 emitPostWriteBarrier(obj);
5658 restoreLiveVolatile(ool->lir());
5659
5660 masm.jump(ool->rejoin());
5661}
5662
5663void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal,
5664 OutOfLineCode* ool) {
5665 // Check whether an object is a global that we have already barriered before
5666 // calling into the VM.
5667 //
5668 // We only check for the script's global, not other globals within the same
5669 // compartment, because we bake in a pointer to realm->globalWriteBarriered
5670 // and doing that would be invalid for other realms because they could be
5671 // collected before the Ion code is discarded.
5672
5673 if (!maybeGlobal->isConstant()) {
5674 return;
5675 }
5676
5677 JSObject* obj = &maybeGlobal->toConstant()->toObject();
5678 if (gen->realm->maybeGlobal() != obj) {
5679 return;
5680 }
5681
5682 const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered();
5683 masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0),
5684 ool->rejoin());
5685}
5686
5687template <class LPostBarrierType, MIRType nurseryType>
5688void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir,
5689 OutOfLineCode* ool) {
5690 static_assert(NeedsPostBarrier(nurseryType));
5691
5692 addOutOfLineCode(ool, lir->mir());
5693
5694 Register temp = ToTempRegisterOrInvalid(lir->temp0());
5695
5696 if (lir->object()->isConstant()) {
5697 // Constant nursery objects cannot appear here, see
5698 // LIRGenerator::visitPostWriteElementBarrier.
5699 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"
, 5699); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { *((volatile int*)__null) = 5699; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5700 } else {
5701 masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()),
5702 temp, ool->rejoin());
5703 }
5704
5705 maybeEmitGlobalBarrierCheck(lir->object(), ool);
5706
5707 Register value = ToRegister(lir->value());
5708 if constexpr (nurseryType == MIRType::Object) {
5709 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"
, 5709); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object"
")"); do { *((volatile int*)__null) = 5709; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5710 } else if constexpr (nurseryType == MIRType::String) {
5711 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"
, 5711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String"
")"); do { *((volatile int*)__null) = 5711; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5712 } else {
5713 static_assert(nurseryType == MIRType::BigInt);
5714 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"
, 5714); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt"
")"); do { *((volatile int*)__null) = 5714; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5715 }
5716 masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry());
5717
5718 masm.bind(ool->rejoin());
5719}
5720
5721template <class LPostBarrierType>
5722void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir,
5723 OutOfLineCode* ool) {
5724 addOutOfLineCode(ool, lir->mir());
5725
5726 Register temp = ToTempRegisterOrInvalid(lir->temp0());
5727
5728 if (lir->object()->isConstant()) {
5729 // Constant nursery objects cannot appear here, see
5730 // LIRGenerator::visitPostWriteElementBarrier.
5731 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"
, 5731); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { *((volatile int*)__null) = 5731; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5732 } else {
5733 masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()),
5734 temp, ool->rejoin());
5735 }
5736
5737 maybeEmitGlobalBarrierCheck(lir->object(), ool);
5738
5739 ValueOperand value = ToValue(lir, LPostBarrierType::ValueIndex);
5740 masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry());
5741
5742 masm.bind(ool->rejoin());
5743}
5744
5745void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) {
5746 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5747 visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool);
5748}
5749
5750void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) {
5751 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5752 visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool);
5753}
5754
5755void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) {
5756 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5757 visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool);
5758}
5759
5760void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) {
5761 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5762 visitPostWriteBarrierCommonV(lir, ool);
5763}
5764
5765// Out-of-line path to update the store buffer.
5766class OutOfLineCallPostWriteElementBarrier
5767 : public OutOfLineCodeBase<CodeGenerator> {
5768 LInstruction* lir_;
5769 const LAllocation* object_;
5770 const LAllocation* index_;
5771
5772 public:
5773 OutOfLineCallPostWriteElementBarrier(LInstruction* lir,
5774 const LAllocation* object,
5775 const LAllocation* index)
5776 : lir_(lir), object_(object), index_(index) {}
5777
5778 void accept(CodeGenerator* codegen) override {
5779 codegen->visitOutOfLineCallPostWriteElementBarrier(this);
5780 }
5781
5782 LInstruction* lir() const { return lir_; }
5783
5784 const LAllocation* object() const { return object_; }
5785
5786 const LAllocation* index() const { return index_; }
5787};
5788
5789void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier(
5790 OutOfLineCallPostWriteElementBarrier* ool) {
5791 saveLiveVolatile(ool->lir());
5792
5793 const LAllocation* obj = ool->object();
5794 const LAllocation* index = ool->index();
5795
5796 Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj);
5797 Register indexreg = ToRegister(index);
5798
5799 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5800 regs.takeUnchecked(indexreg);
5801
5802 if (obj->isConstant()) {
5803 objreg = regs.takeAny();
5804 masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg);
5805 } else {
5806 regs.takeUnchecked(objreg);
5807 }
5808
5809 Register runtimereg = regs.takeAny();
5810 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
5811 masm.setupAlignedABICall();
5812 masm.mov(ImmPtr(gen->runtime), runtimereg);
5813 masm.passABIArg(runtimereg);
5814 masm.passABIArg(objreg);
5815 masm.passABIArg(indexreg);
5816 masm.callWithABI<Fn, PostWriteElementBarrier>();
5817
5818 restoreLiveVolatile(ool->lir());
5819
5820 masm.jump(ool->rejoin());
5821}
5822
5823void CodeGenerator::visitPostWriteElementBarrierO(
5824 LPostWriteElementBarrierO* lir) {
5825 auto ool = new (alloc())
5826 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5827 visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir,
5828 ool);
5829}
5830
5831void CodeGenerator::visitPostWriteElementBarrierS(
5832 LPostWriteElementBarrierS* lir) {
5833 auto ool = new (alloc())
5834 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5835 visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir,
5836 ool);
5837}
5838
5839void CodeGenerator::visitPostWriteElementBarrierBI(
5840 LPostWriteElementBarrierBI* lir) {
5841 auto ool = new (alloc())
5842 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5843 visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir,
5844 ool);
5845}
5846
5847void CodeGenerator::visitPostWriteElementBarrierV(
5848 LPostWriteElementBarrierV* lir) {
5849 auto ool = new (alloc())
5850 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5851 visitPostWriteBarrierCommonV(lir, ool);
5852}
5853
5854void CodeGenerator::visitAssertCanElidePostWriteBarrier(
5855 LAssertCanElidePostWriteBarrier* lir) {
5856 Register object = ToRegister(lir->object());
5857 ValueOperand value =
5858 ToValue(lir, LAssertCanElidePostWriteBarrier::ValueIndex);
5859 Register temp = ToRegister(lir->temp0());
5860
5861 Label ok;
5862 masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok);
5863 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok);
5864
5865 masm.assumeUnreachable("Unexpected missing post write barrier");
5866
5867 masm.bind(&ok);
5868}
5869
5870template <typename LCallIns>
5871void CodeGenerator::emitCallNative(LCallIns* call, JSNative native,
5872 Register argContextReg, Register argUintNReg,
5873 Register argVpReg, Register tempReg,
5874 uint32_t unusedStack) {
5875 masm.checkStackAlignment();
5876
5877 // Native functions have the signature:
5878 // bool (*)(JSContext*, unsigned, Value* vp)
5879 // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
5880 // are the function arguments.
5881
5882 // Allocate space for the outparam, moving the StackPointer to what will be
5883 // &vp[1].
5884 masm.adjustStack(unusedStack);
5885
5886 // Push a Value containing the callee object: natives are allowed to access
5887 // their callee before setting the return value. The StackPointer is moved
5888 // to &vp[0].
5889 //
5890 // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|.
5891 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
5892 Register calleeReg = ToRegister(call->getCallee());
5893 masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg)));
5894
5895 // Enter the callee realm.
5896 if (call->mir()->maybeCrossRealm()) {
5897 masm.switchToObjectRealm(calleeReg, tempReg);
5898 }
5899 } else {
5900 WrappedFunction* target = call->mir()->getSingleTarget();
5901 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
5902
5903 // Enter the callee realm.
5904 if (call->mir()->maybeCrossRealm()) {
5905 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg);
5906 masm.switchToObjectRealm(tempReg, tempReg);
5907 }
5908 }
5909
5910 // Preload arguments into registers.
5911 masm.loadJSContext(argContextReg);
5912 masm.moveStackPtrTo(argVpReg);
5913
5914 // Initialize |NativeExitFrameLayout::argc_|.
5915 masm.Push(argUintNReg);
5916
5917 // Construct native exit frame.
5918 //
5919 // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and
5920 // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|.
5921 //
5922 // The NativeExitFrameLayout is now fully initialized.
5923 uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg);
5924 masm.enterFakeExitFrameForNative(argContextReg, tempReg,
5925 call->mir()->isConstructing());
5926
5927 markSafepointAt(safepointOffset, call);
5928
5929 // Construct and execute call.
5930 masm.setupAlignedABICall();
5931 masm.passABIArg(argContextReg);
5932 masm.passABIArg(argUintNReg);
5933 masm.passABIArg(argVpReg);
5934
5935 ensureOsiSpace();
5936 // If we're using a simulator build, `native` will already point to the
5937 // simulator's call-redirection code for LCallClassHook. Load the address in
5938 // a register first so that we don't try to redirect it a second time.
5939 bool emittedCall = false;
5940#ifdef JS_SIMULATOR
5941 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
5942 masm.movePtr(ImmPtr(native), tempReg);
5943 masm.callWithABI(tempReg);
5944 emittedCall = true;
5945 }
5946#endif
5947 if (!emittedCall) {
5948 masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General,
5949 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
5950 }
5951
5952 // Test for failure.
5953 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
5954
5955 // Exit the callee realm.
5956 if (call->mir()->maybeCrossRealm()) {
5957 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5958 }
5959
5960 // Load the outparam vp[0] into output register(s).
5961 masm.loadValue(
5962 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
5963 JSReturnOperand);
5964
5965 // Until C++ code is instrumented against Spectre, prevent speculative
5966 // execution from returning any private data.
5967 if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() &&
5968 call->mir()->hasLiveDefUses()) {
5969 masm.speculationBarrier();
5970 }
5971
5972#ifdef DEBUG1
5973 // Native constructors are guaranteed to return an Object value.
5974 if (call->mir()->isConstructing()) {
5975 Label notPrimitive;
5976 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
5977 &notPrimitive);
5978 masm.assumeUnreachable("native constructors don't return primitives");
5979 masm.bind(&notPrimitive);
5980 }
5981#endif
5982}
5983
5984template <typename LCallIns>
5985void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) {
5986 uint32_t unusedStack =
5987 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
5988
5989 // Registers used for callWithABI() argument-passing.
5990 const Register argContextReg = ToRegister(call->getArgContextReg());
5991 const Register argUintNReg = ToRegister(call->getArgUintNReg());
5992 const Register argVpReg = ToRegister(call->getArgVpReg());
5993
5994 // Misc. temporary registers.
5995 const Register tempReg = ToRegister(call->getTempReg());
5996
5997 DebugOnly<uint32_t> initialStack = masm.framePushed();
5998
5999 // Initialize the argc register.
6000 masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg);
6001
6002 // Create the exit frame and call the native.
6003 emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg,
6004 unusedStack);
6005
6006 // The next instruction is removing the footer of the exit frame, so there
6007 // is no need for leaveFakeExitFrame.
6008
6009 // Move the StackPointer back to its original location, unwinding the native
6010 // exit frame.
6011 masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack);
6012 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"
, 6012); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 6012; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6013}
6014
6015void CodeGenerator::visitCallNative(LCallNative* call) {
6016 WrappedFunction* target = call->getSingleTarget();
6017 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"
, 6017); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { *((volatile int*)__null) = 6017; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6018 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"
, 6018); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 6018; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6019
6020 JSNative native = target->native();
6021 if (call->ignoresReturnValue() && target->hasJitInfo()) {
6022 const JSJitInfo* jitInfo = target->jitInfo();
6023 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
6024 native = jitInfo->ignoresReturnValueMethod;
6025 }
6026 }
6027 emitCallNative(call, native);
6028}
6029
6030void CodeGenerator::visitCallClassHook(LCallClassHook* call) {
6031 emitCallNative(call, call->mir()->target());
6032}
6033
6034static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv,
6035 DOMObjectKind kind) {
6036 // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This
6037 // will be in the first slot but may be fixed or non-fixed.
6038 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"
, 6038); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv"
")"); do { *((volatile int*)__null) = 6038; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6039
6040 switch (kind) {
6041 case DOMObjectKind::Native:
6042 // If it's a native object, the value must be in a fixed slot.
6043 // See CanAttachDOMCall in CacheIR.cpp.
6044 masm.debugAssertObjHasFixedSlots(obj, priv);
6045 masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv);
6046 break;
6047 case DOMObjectKind::Proxy: {
6048#ifdef DEBUG1
6049 // Sanity check: it must be a DOM proxy.
6050 Label isDOMProxy;
6051 masm.branchTestProxyHandlerFamily(
6052 Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy);
6053 masm.assumeUnreachable("Expected a DOM proxy");
6054 masm.bind(&isDOMProxy);
6055#endif
6056 masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), priv);
6057 masm.loadPrivate(
6058 Address(priv, js::detail::ProxyReservedSlots::offsetOfSlot(0)), priv);
6059 break;
6060 }
6061 }
6062}
6063
6064void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) {
6065 WrappedFunction* target = call->getSingleTarget();
6066 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"
, 6066); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { *((volatile int*)__null) = 6066; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6067 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"
, 6067); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 6067; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6068 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"
, 6068); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitInfo()"
")"); do { *((volatile int*)__null) = 6068; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6069 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"
, 6069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()"
")"); do { *((volatile int*)__null) = 6069; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6070
6071 int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6072
6073 // Registers used for callWithABI() argument-passing.
6074 const Register argJSContext = ToRegister(call->getArgJSContext());
6075 const Register argObj = ToRegister(call->getArgObj());
6076 const Register argPrivate = ToRegister(call->getArgPrivate());
6077 const Register argArgs = ToRegister(call->getArgArgs());
6078
6079 DebugOnly<uint32_t> initialStack = masm.framePushed();
6080
6081 masm.checkStackAlignment();
6082
6083 // DOM methods have the signature:
6084 // bool (*)(JSContext*, HandleObject, void* private, const
6085 // JSJitMethodCallArgs& args)
6086 // Where args is initialized from an argc and a vp, vp[0] is space for an
6087 // outparam and the callee, vp[1] is |this|, and vp[2] onward are the
6088 // function arguments. Note that args stores the argv, not the vp, and
6089 // argv == vp + 2.
6090
6091 // Nestle the stack up against the pushed arguments, leaving StackPointer at
6092 // &vp[1]
6093 masm.adjustStack(unusedStack);
6094 // argObj is filled with the extracted object, then returned.
6095 Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj);
6096 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"
, 6096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj == argObj"
")"); do { *((volatile int*)__null) = 6096; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6097
6098 // Push a Value containing the callee object: natives are allowed to access
6099 // their callee before setting the return value. After this the StackPointer
6100 // points to &vp[0].
6101 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
6102
6103 // Now compute the argv value. Since StackPointer is pointing to &vp[0] and
6104 // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
6105 // StackPointer.
6106 static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
6107 static_assert(JSJitMethodCallArgsTraits::offsetOfArgc ==
6108 IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
6109 masm.computeEffectiveAddress(
6110 Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs);
6111
6112 LoadDOMPrivate(masm, obj, argPrivate,
6113 static_cast<MCallDOMNative*>(call->mir())->objectKind());
6114
6115 // Push argc from the call instruction into what will become the IonExitFrame
6116 masm.Push(Imm32(call->numActualArgs()));
6117
6118 // Push our argv onto the stack
6119 masm.Push(argArgs);
6120 // And store our JSJitMethodCallArgs* in argArgs.
6121 masm.moveStackPtrTo(argArgs);
6122
6123 // Push |this| object for passing HandleObject. We push after argc to
6124 // maintain the same sp-relative location of the object pointer with other
6125 // DOMExitFrames.
6126 masm.Push(argObj);
6127 masm.moveStackPtrTo(argObj);
6128
6129 if (call->mir()->maybeCrossRealm()) {
6130 // We use argJSContext as scratch register here.
6131 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext);
6132 masm.switchToObjectRealm(argJSContext, argJSContext);
6133 }
6134
6135 bool preTenureWrapperAllocation =
6136 call->mir()->to<MCallDOMNative>()->initialHeap() == gc::Heap::Tenured;
6137 if (preTenureWrapperAllocation) {
6138 auto ptr = ImmPtr(mirGen().realm->zone()->tenuringAllocSite());
6139 masm.storeLocalAllocSite(ptr, argJSContext);
6140 }
6141
6142 // Construct native exit frame.
6143 uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext);
6144
6145 masm.loadJSContext(argJSContext);
6146 masm.enterFakeExitFrame(argJSContext, argJSContext,
6147 ExitFrameType::IonDOMMethod);
6148
6149 markSafepointAt(safepointOffset, call);
6150
6151 // Construct and execute call.
6152 masm.setupAlignedABICall();
6153 masm.loadJSContext(argJSContext);
6154 masm.passABIArg(argJSContext);
6155 masm.passABIArg(argObj);
6156 masm.passABIArg(argPrivate);
6157 masm.passABIArg(argArgs);
6158 ensureOsiSpace();
6159 masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method),
6160 ABIType::General,
6161 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
6162
6163 if (target->jitInfo()->isInfallible) {
6164 masm.loadValue(Address(masm.getStackPointer(),
6165 IonDOMMethodExitFrameLayout::offsetOfResult()),
6166 JSReturnOperand);
6167 } else {
6168 // Test for failure.
6169 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
6170
6171 // Load the outparam vp[0] into output register(s).
6172 masm.loadValue(Address(masm.getStackPointer(),
6173 IonDOMMethodExitFrameLayout::offsetOfResult()),
6174 JSReturnOperand);
6175 }
6176
6177 static_assert(!JSReturnOperand.aliases(ReturnReg),
6178 "Clobbering ReturnReg should not affect the return value");
6179
6180 // Switch back to the current realm if needed. Note: if the DOM method threw
6181 // an exception, the exception handler will do this.
6182 if (call->mir()->maybeCrossRealm()) {
6183 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6184 }
6185
6186 // Wipe out the preTenuring bit from the local alloc site
6187 // On exception we handle this in C++
6188 if (preTenureWrapperAllocation) {
6189 masm.storeLocalAllocSite(ImmPtr(nullptr), ReturnReg);
6190 }
6191
6192 // Until C++ code is instrumented against Spectre, prevent speculative
6193 // execution from returning any private data.
6194 if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) {
6195 masm.speculationBarrier();
6196 }
6197
6198 // The next instruction is removing the footer of the exit frame, so there
6199 // is no need for leaveFakeExitFrame.
6200
6201 // Move the StackPointer back to its original location, unwinding the native
6202 // exit frame.
6203 masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
6204 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"
, 6204); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 6204; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6205}
6206
6207void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) {
6208 pushArg(ImmGCPtr(lir->mir()->name()));
6209
6210 using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue);
6211 callVM<Fn, GetIntrinsicValue>(lir);
6212}
6213
6214void CodeGenerator::emitCallInvokeFunction(
6215 LInstruction* call, Register calleereg, bool constructing,
6216 bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) {
6217 // Nestle %esp up to the argument vector.
6218 // Each path must account for framePushed_ separately, for callVM to be valid.
6219 masm.freeStack(unusedStack);
6220
6221 pushArg(masm.getStackPointer()); // argv.
6222 pushArg(Imm32(argc)); // argc.
6223 pushArg(Imm32(ignoresReturnValue));
6224 pushArg(Imm32(constructing)); // constructing.
6225 pushArg(calleereg); // JSFunction*.
6226
6227 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6228 MutableHandleValue);
6229 callVM<Fn, jit::InvokeFunction>(call);
6230
6231 // Un-nestle %esp from the argument vector. No prefix was pushed.
6232 masm.reserveStack(unusedStack);
6233}
6234
6235void CodeGenerator::visitCallGeneric(LCallGeneric* call) {
6236 // The callee is passed straight through to the trampoline.
6237 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"
, 6237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg"
")"); do { *((volatile int*)__null) = 6237; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6238
6239 Register argcReg = ToRegister(call->getArgc());
6240 uint32_t unusedStack =
6241 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6242
6243 // Known-target case is handled by LCallKnown.
6244 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"
, 6244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->hasSingleTarget()"
")"); do { *((volatile int*)__null) = 6244; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6245
6246 masm.checkStackAlignment();
6247
6248 masm.move32(Imm32(call->numActualArgs()), argcReg);
6249
6250 // Nestle the StackPointer up to the argument vector.
6251 masm.freeStack(unusedStack);
6252 ensureOsiSpace();
6253
6254 auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct
6255 : IonGenericCallKind::Call;
6256
6257 TrampolinePtr genericCallStub =
6258 gen->jitRuntime()->getIonGenericCallStub(kind);
6259 uint32_t callOffset = masm.callJit(genericCallStub);
6260 markSafepointAt(callOffset, call);
6261
6262 if (call->mir()->maybeCrossRealm()) {
6263 static_assert(!JSReturnOperand.aliases(ReturnReg),
6264 "ReturnReg available as scratch after scripted calls");
6265 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6266 }
6267
6268 // Restore stack pointer.
6269 masm.setFramePushed(frameSize());
6270 emitRestoreStackPointerFromFP();
6271
6272 // If the return value of the constructing function is Primitive,
6273 // replace the return value with the Object from CreateThis.
6274 if (call->mir()->isConstructing()) {
6275 Label notPrimitive;
6276 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6277 &notPrimitive);
6278 masm.loadValue(Address(masm.getStackPointer(), unusedStack),
6279 JSReturnOperand);
6280#ifdef DEBUG1
6281 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6282 &notPrimitive);
6283 masm.assumeUnreachable("CreateThis creates an object");
6284#endif
6285 masm.bind(&notPrimitive);
6286 }
6287}
6288
6289void JitRuntime::generateIonGenericCallArgumentsShift(
6290 MacroAssembler& masm, Register argc, Register curr, Register end,
6291 Register scratch, Label* done) {
6292 static_assert(sizeof(Value) == 8);
6293 // There are |argc| Values on the stack. Shift them all down by 8 bytes,
6294 // overwriting the first value.
6295
6296 // Initialize `curr` to the destination of the first copy, and `end` to the
6297 // final value of curr.
6298 masm.moveStackPtrTo(curr);
6299 masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end);
6300
6301 Label loop;
6302 masm.bind(&loop);
6303 masm.branchPtr(Assembler::Equal, curr, end, done);
6304 masm.loadPtr(Address(curr, 8), scratch);
6305 masm.storePtr(scratch, Address(curr, 0));
6306 masm.addPtr(Imm32(sizeof(uintptr_t)), curr);
6307 masm.jump(&loop);
6308}
6309
6310void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm,
6311 IonGenericCallKind kind) {
6312 AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub");
6313 ionGenericCallStubOffset_[kind] = startTrampolineCode(masm);
6314
6315 // This code is tightly coupled with visitCallGeneric.
6316 //
6317 // Upon entry:
6318 // IonGenericCallCalleeReg contains a pointer to the callee object.
6319 // IonGenericCallArgcReg contains the number of actual args.
6320 // The arguments have been pushed onto the stack:
6321 // [newTarget] (iff isConstructing)
6322 // [argN]
6323 // ...
6324 // [arg1]
6325 // [arg0]
6326 // [this]
6327 // <return address> (if not JS_USE_LINK_REGISTER)
6328 //
6329 // This trampoline is responsible for entering the callee's realm,
6330 // massaging the stack into the right shape, and then performing a
6331 // tail call. We will return directly to the Ion code from the
6332 // callee.
6333 //
6334 // To do a tail call, we keep the return address in a register, even
6335 // on platforms that don't normally use a link register, and push it
6336 // just before jumping to the callee, after we are done setting up
6337 // the stack.
6338 //
6339 // The caller is responsible for switching back to the caller's
6340 // realm and cleaning up the stack.
6341
6342 Register calleeReg = IonGenericCallCalleeReg;
6343 Register argcReg = IonGenericCallArgcReg;
6344 Register scratch = IonGenericCallScratch;
6345 Register scratch2 = IonGenericCallScratch2;
6346
6347#ifndef JS_USE_LINK_REGISTER
6348 Register returnAddrReg = IonGenericCallReturnAddrReg;
6349 masm.pop(returnAddrReg);
6350#endif
6351
6352#ifdef JS_CODEGEN_ARM
6353 // The default second scratch register on arm is lr, which we need
6354 // preserved for tail calls.
6355 AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg);
6356#endif
6357
6358 bool isConstructing = kind == IonGenericCallKind::Construct;
6359
6360 Label entry, notFunction, noJitEntry, vmCall;
6361 masm.bind(&entry);
6362
6363 // Guard that the callee is actually a function.
6364 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
6365 calleeReg, &notFunction);
6366
6367 // Guard that the callee supports the [[Call]] or [[Construct]] operation.
6368 // If these tests fail, we will call into the VM to throw an exception.
6369 if (isConstructing) {
6370 masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR,
6371 Assembler::Zero, &vmCall);
6372 } else {
6373 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
6374 calleeReg, scratch, &vmCall);
6375 }
6376
6377 if (isConstructing) {
6378 // Use the slow path if CreateThis was unable to create the |this| object.
6379 Address thisAddr(masm.getStackPointer(), 0);
6380 masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall);
6381 }
6382
6383 masm.switchToObjectRealm(calleeReg, scratch);
6384
6385 // Load jitCodeRaw for callee if it exists.
6386 masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry);
6387
6388 // ****************************
6389 // * Functions with jit entry *
6390 // ****************************
6391 masm.loadJitCodeRaw(calleeReg, scratch2);
6392
6393 // Construct the JitFrameLayout.
6394 masm.PushCalleeToken(calleeReg, isConstructing);
6395 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch);
6396#ifndef JS_USE_LINK_REGISTER
6397 masm.push(returnAddrReg);
6398#endif
6399
6400 // Check whether we need a rectifier frame.
6401 Label noRectifier;
6402 masm.loadFunctionArgCount(calleeReg, scratch);
6403 masm.branch32(Assembler::BelowOrEqual, scratch, argcReg, &noRectifier);
6404 {
6405 // Tail-call the arguments rectifier.
6406 // Because all trampolines are created at the same time,
6407 // we can't create a TrampolinePtr for the arguments rectifier,
6408 // because it hasn't been linked yet. We can, however, directly
6409 // encode its offset.
6410 Label rectifier;
6411 bindLabelToOffset(&rectifier, argumentsRectifierOffset_);
6412
6413 masm.jump(&rectifier);
6414 }
6415
6416 // Tail call the jit entry.
6417 masm.bind(&noRectifier);
6418 masm.jump(scratch2);
6419
6420 // ********************
6421 // * Native functions *
6422 // ********************
6423 masm.bind(&noJitEntry);
6424 if (!isConstructing) {
6425 generateIonGenericCallFunCall(masm, &entry, &vmCall);
6426 }
6427 generateIonGenericCallNativeFunction(masm, isConstructing);
6428
6429 // *******************
6430 // * Bound functions *
6431 // *******************
6432 // TODO: support class hooks?
6433 masm.bind(&notFunction);
6434 if (!isConstructing) {
6435 // TODO: support generic bound constructors?
6436 generateIonGenericCallBoundFunction(masm, &entry, &vmCall);
6437 }
6438
6439 // ********************
6440 // * Fallback VM call *
6441 // ********************
6442 masm.bind(&vmCall);
6443
6444 masm.push(masm.getStackPointer()); // argv
6445 masm.push(argcReg); // argc
6446 masm.push(Imm32(false)); // ignores return value
6447 masm.push(Imm32(isConstructing)); // constructing
6448 masm.push(calleeReg); // callee
6449
6450 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6451 MutableHandleValue);
6452 VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id;
6453 uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)];
6454 Label invokeFunctionVMEntry;
6455 bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset);
6456
6457 masm.pushFrameDescriptor(FrameType::IonJS);
6458#ifndef JS_USE_LINK_REGISTER
6459 masm.push(returnAddrReg);
6460#endif
6461 masm.jump(&invokeFunctionVMEntry);
6462}
6463
6464void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm,
6465 bool isConstructing) {
6466 Register calleeReg = IonGenericCallCalleeReg;
6467 Register argcReg = IonGenericCallArgcReg;
6468 Register scratch = IonGenericCallScratch;
6469 Register scratch2 = IonGenericCallScratch2;
6470 Register contextReg = IonGenericCallScratch3;
6471#ifndef JS_USE_LINK_REGISTER
6472 Register returnAddrReg = IonGenericCallReturnAddrReg;
6473#endif
6474
6475 // Push a value containing the callee, which will become argv[0].
6476 masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg);
6477
6478 // Load the callee address into calleeReg.
6479#ifdef JS_SIMULATOR
6480 masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg);
6481#else
6482 masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
6483 calleeReg);
6484#endif
6485
6486 // Load argv into scratch2.
6487 masm.moveStackPtrTo(scratch2);
6488
6489 // Push argc.
6490 masm.push(argcReg);
6491
6492 masm.loadJSContext(contextReg);
6493
6494 // Construct native exit frame. Note that unlike other cases in this
6495 // trampoline, this code does not use a tail call.
6496 masm.pushFrameDescriptor(FrameType::IonJS);
6497#ifdef JS_USE_LINK_REGISTER
6498 masm.pushReturnAddress();
6499#else
6500 masm.push(returnAddrReg);
6501#endif
6502
6503 masm.push(FramePointer);
6504 masm.moveStackPtrTo(FramePointer);
6505 masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing);
6506
6507 masm.setupUnalignedABICall(scratch);
6508 masm.passABIArg(contextReg); // cx
6509 masm.passABIArg(argcReg); // argc
6510 masm.passABIArg(scratch2); // argv
6511
6512 masm.callWithABI(calleeReg);
6513
6514 // Test for failure.
6515 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
6516
6517 masm.loadValue(
6518 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
6519 JSReturnOperand);
6520
6521 // Leave the exit frame.
6522 masm.moveToStackPtr(FramePointer);
6523 masm.pop(FramePointer);
6524
6525 // Return.
6526 masm.ret();
6527}
6528
6529void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm,
6530 Label* entry, Label* vmCall) {
6531 Register calleeReg = IonGenericCallCalleeReg;
6532 Register argcReg = IonGenericCallArgcReg;
6533 Register scratch = IonGenericCallScratch;
6534 Register scratch2 = IonGenericCallScratch2;
6535 Register scratch3 = IonGenericCallScratch3;
6536
6537 Label notFunCall;
6538 masm.branchPtr(Assembler::NotEqual,
6539 Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
6540 ImmPtr(js::fun_call), &notFunCall);
6541
6542 // In general, we can implement fun_call by replacing calleeReg with
6543 // |this|, sliding all the other arguments down, and decrementing argc.
6544 //
6545 // *BEFORE* *AFTER*
6546 // [argN] argc = N+1 <padding>
6547 // ... [argN] argc = N
6548 // [arg1] ...
6549 // [arg0] [arg1] <- now arg0
6550 // [this] <- top of stack (aligned) [arg0] <- now this
6551 //
6552 // The only exception is when argc is already 0, in which case instead
6553 // of shifting arguments down we replace [this] with UndefinedValue():
6554 //
6555 // *BEFORE* *AFTER*
6556 // [this] argc = 0 [undef] argc = 0
6557 //
6558 // After making this transformation, we can jump back to the beginning
6559 // of this trampoline to handle the inner call.
6560
6561 // Guard that |this| is an object. If it is, replace calleeReg.
6562 masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall);
6563 masm.movePtr(scratch, calleeReg);
6564
6565 Label hasArgs;
6566 masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs);
6567
6568 // No arguments. Replace |this| with |undefined| and start from the top.
6569 masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0));
6570 masm.jump(entry);
6571
6572 masm.bind(&hasArgs);
6573
6574 Label doneSliding;
6575 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
6576 scratch3, &doneSliding);
6577 masm.bind(&doneSliding);
6578 masm.sub32(Imm32(1), argcReg);
6579
6580 masm.jump(entry);
6581
6582 masm.bind(&notFunCall);
6583}
6584
6585void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm,
6586 Label* entry,
6587 Label* vmCall) {
6588 Register calleeReg = IonGenericCallCalleeReg;
6589 Register argcReg = IonGenericCallArgcReg;
6590 Register scratch = IonGenericCallScratch;
6591 Register scratch2 = IonGenericCallScratch2;
6592 Register scratch3 = IonGenericCallScratch3;
6593
6594 masm.branchTestObjClass(Assembler::NotEqual, calleeReg,
6595 &BoundFunctionObject::class_, scratch, calleeReg,
6596 vmCall);
6597
6598 Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
6599 Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot());
6600 Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot());
6601 Address firstInlineArgSlot(
6602 calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg());
6603
6604 // Check that we won't be pushing too many arguments.
6605 masm.load32(flagsSlot, scratch);
6606 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
6607 masm.add32(argcReg, scratch);
6608 masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall);
6609
6610 // The stack is currently correctly aligned for a jit call. We will
6611 // be updating the `this` value and potentially adding additional
6612 // arguments. On platforms with 16-byte alignment, if the number of
6613 // bound arguments is odd, we have to move the arguments that are
6614 // currently on the stack. For example, with one bound argument:
6615 //
6616 // *BEFORE* *AFTER*
6617 // [argN] <padding>
6618 // ... [argN] |
6619 // [arg1] ... | These arguments have been
6620 // [arg0] [arg1] | shifted down 8 bytes.
6621 // [this] <- top of stack (aligned) [arg0] v
6622 // [bound0] <- one bound argument (odd)
6623 // [boundThis] <- top of stack (aligned)
6624 //
6625 Label poppedThis;
6626 if (JitStackValueAlignment > 1) {
6627 Label alreadyAligned;
6628 masm.branchTest32(Assembler::Zero, flagsSlot,
6629 Imm32(1 << BoundFunctionObject::NumBoundArgsShift),
6630 &alreadyAligned);
6631
6632 // We have an odd number of bound arguments. Shift the existing arguments
6633 // down by 8 bytes.
6634 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
6635 scratch3, &poppedThis);
6636 masm.bind(&alreadyAligned);
6637 }
6638
6639 // Pop the current `this`. It will be replaced with the bound `this`.
6640 masm.freeStack(sizeof(Value));
6641 masm.bind(&poppedThis);
6642
6643 // Load the number of bound arguments in scratch
6644 masm.load32(flagsSlot, scratch);
6645 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
6646
6647 Label donePushingBoundArguments;
6648 masm.branch32(Assembler::Equal, scratch, Imm32(0),
6649 &donePushingBoundArguments);
6650
6651 // Update argc to include bound arguments.
6652 masm.add32(scratch, argcReg);
6653
6654 // Load &boundArgs[0] in scratch2.
6655 Label outOfLineBoundArguments, haveBoundArguments;
6656 masm.branch32(Assembler::Above, scratch,
6657 Imm32(BoundFunctionObject::MaxInlineBoundArgs),
6658 &outOfLineBoundArguments);
6659 masm.computeEffectiveAddress(firstInlineArgSlot, scratch2);
6660 masm.jump(&haveBoundArguments);
6661
6662 masm.bind(&outOfLineBoundArguments);
6663 masm.unboxObject(firstInlineArgSlot, scratch2);
6664 masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2);
6665
6666 masm.bind(&haveBoundArguments);
6667
6668 // Load &boundArgs[numBoundArgs] in scratch.
6669 BaseObjectElementIndex lastBoundArg(scratch2, scratch);
6670 masm.computeEffectiveAddress(lastBoundArg, scratch);
6671
6672 // Push the bound arguments, starting with the last one.
6673 // Copying pre-decrements scratch until scratch2 is reached.
6674 Label boundArgumentsLoop;
6675 masm.bind(&boundArgumentsLoop);
6676 masm.subPtr(Imm32(sizeof(Value)), scratch);
6677 masm.pushValue(Address(scratch, 0));
6678 masm.branchPtr(Assembler::Above, scratch, scratch2, &boundArgumentsLoop);
6679 masm.bind(&donePushingBoundArguments);
6680
6681 // Push the bound `this`.
6682 masm.pushValue(thisSlot);
6683
6684 // Load the target in calleeReg.
6685 masm.unboxObject(targetSlot, calleeReg);
6686
6687 // At this point, all preconditions for entering the trampoline are met:
6688 // - calleeReg contains a pointer to the callee object
6689 // - argcReg contains the number of actual args (now including bound args)
6690 // - the arguments are on the stack with the correct alignment.
6691 // Instead of generating more code, we can jump back to the entry point
6692 // of the trampoline to call the bound target.
6693 masm.jump(entry);
6694}
6695
6696void CodeGenerator::visitCallKnown(LCallKnown* call) {
6697 Register calleereg = ToRegister(call->getFunction());
6698 Register objreg = ToRegister(call->getTempObject());
6699 uint32_t unusedStack =
6700 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
6701 WrappedFunction* target = call->getSingleTarget();
6702
6703 // Native single targets (except Wasm and TrampolineNative functions) are
6704 // handled by LCallNative.
6705 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"
, 6705); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitEntry()"
")"); do { *((volatile int*)__null) = 6705; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6706
6707 // Missing arguments must have been explicitly appended by WarpBuilder.
6708 DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing();
6709 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"
, 6710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
")"); do { *((volatile int*)__null) = 6710; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6710 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"
, 6710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack"
")"); do { *((volatile int*)__null) = 6710; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6711
6712 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"
, 6712); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isConstructor()"
")"); do { *((volatile int*)__null) = 6712; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6713
6714 masm.checkStackAlignment();
6715
6716 if (target->isClassConstructor() && !call->isConstructing()) {
6717 emitCallInvokeFunction(call, calleereg, call->isConstructing(),
6718 call->ignoresReturnValue(), call->numActualArgs(),
6719 unusedStack);
6720 return;
6721 }
6722
6723 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"
, 6723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->isConstructing()"
")"); do { *((volatile int*)__null) = 6723; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6724
6725 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"
, 6725); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->mir()->needsThisCheck()"
")"); do { *((volatile int*)__null) = 6725; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6726
6727 if (call->mir()->maybeCrossRealm()) {
6728 masm.switchToObjectRealm(calleereg, objreg);
6729 }
6730
6731 masm.loadJitCodeRaw(calleereg, objreg);
6732
6733 // Nestle the StackPointer up to the argument vector.
6734 masm.freeStack(unusedStack);
6735
6736 // Construct the JitFrameLayout.
6737 masm.PushCalleeToken(calleereg, call->mir()->isConstructing());
6738 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, call->numActualArgs());
6739
6740 // Finally call the function in objreg.
6741 ensureOsiSpace();
6742 uint32_t callOffset = masm.callJit(objreg);
6743 markSafepointAt(callOffset, call);
6744
6745 if (call->mir()->maybeCrossRealm()) {
6746 static_assert(!JSReturnOperand.aliases(ReturnReg),
6747 "ReturnReg available as scratch after scripted calls");
6748 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
6749 }
6750
6751 // Restore stack pointer: pop JitFrameLayout fields still left on the stack
6752 // and undo the earlier |freeStack(unusedStack)|.
6753 int prefixGarbage =
6754 sizeof(JitFrameLayout) - JitFrameLayout::bytesPoppedAfterCall();
6755 masm.adjustStack(prefixGarbage - unusedStack);
6756
6757 // If the return value of the constructing function is Primitive,
6758 // replace the return value with the Object from CreateThis.
6759 if (call->mir()->isConstructing()) {
6760 Label notPrimitive;
6761 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6762 &notPrimitive);
6763 masm.loadValue(Address(masm.getStackPointer(), unusedStack),
6764 JSReturnOperand);
6765#ifdef DEBUG1
6766 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
6767 &notPrimitive);
6768 masm.assumeUnreachable("CreateThis creates an object");
6769#endif
6770 masm.bind(&notPrimitive);
6771 }
6772}
6773
6774template <typename T>
6775void CodeGenerator::emitCallInvokeFunction(T* apply) {
6776 pushArg(masm.getStackPointer()); // argv.
6777 pushArg(ToRegister(apply->getArgc())); // argc.
6778 pushArg(Imm32(apply->mir()->ignoresReturnValue())); // ignoresReturnValue.
6779 pushArg(Imm32(apply->mir()->isConstructing())); // isConstructing.
6780 pushArg(ToRegister(apply->getFunction())); // JSFunction*.
6781
6782 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6783 MutableHandleValue);
6784 callVM<Fn, jit::InvokeFunction>(apply);
6785}
6786
6787// Do not bailout after the execution of this function since the stack no longer
6788// correspond to what is expected by the snapshots.
6789void CodeGenerator::emitAllocateSpaceForApply(Register argcreg,
6790 Register scratch) {
6791 // Use scratch register to calculate stack space (including padding).
6792 masm.movePtr(argcreg, scratch);
6793
6794 // Align the JitFrameLayout on the JitStackAlignment.
6795 if (JitStackValueAlignment > 1) {
6796 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"
, 6797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6797; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6797 "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"
, 6797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6797; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6798 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"
, 6798); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
")"); do { *((volatile int*)__null) = 6798; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6799 Label noPaddingNeeded;
6800 // If the number of arguments is odd, then we do not need any padding.
6801 //
6802 // Note: The |JitStackValueAlignment == 2| condition requires that the
6803 // overall number of values on the stack is even. When we have an odd number
6804 // of arguments, we don't need any padding, because the |thisValue| is
6805 // pushed after the arguments, so the overall number of values on the stack
6806 // is even.
6807 masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded);
6808 masm.addPtr(Imm32(1), scratch);
6809 masm.bind(&noPaddingNeeded);
6810 }
6811
6812 // Reserve space for copying the arguments.
6813 NativeObject::elementsSizeMustNotOverflow();
6814 masm.lshiftPtr(Imm32(ValueShift), scratch);
6815 masm.subFromStackPtr(scratch);
6816
6817#ifdef DEBUG1
6818 // Put a magic value in the space reserved for padding. Note, this code cannot
6819 // be merged with the previous test, as not all architectures can write below
6820 // their stack pointers.
6821 if (JitStackValueAlignment > 1) {
6822 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"
, 6822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
")"); do { *((volatile int*)__null) = 6822; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6823 Label noPaddingNeeded;
6824 // If the number of arguments is odd, then we do not need any padding.
6825 masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded);
6826 BaseValueIndex dstPtr(masm.getStackPointer(), argcreg);
6827 masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr);
6828 masm.bind(&noPaddingNeeded);
6829 }
6830#endif
6831}
6832
6833// Do not bailout after the execution of this function since the stack no longer
6834// correspond to what is expected by the snapshots.
6835void CodeGenerator::emitAllocateSpaceForConstructAndPushNewTarget(
6836 Register argcreg, Register newTargetAndScratch) {
6837 // Align the JitFrameLayout on the JitStackAlignment. Contrary to
6838 // |emitAllocateSpaceForApply()|, we're always pushing a magic value, because
6839 // we can't write to |newTargetAndScratch| before |new.target| has been pushed
6840 // onto the stack.
6841 if (JitStackValueAlignment > 1) {
6842 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"
, 6843); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6843; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6843 "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"
, 6843); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 6843; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6844 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"
, 6844); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
")"); do { *((volatile int*)__null) = 6844; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6845
6846 Label noPaddingNeeded;
6847 // If the number of arguments is even, then we do not need any padding.
6848 //
6849 // Note: The |JitStackValueAlignment == 2| condition requires that the
6850 // overall number of values on the stack is even. When we have an even
6851 // number of arguments, we don't need any padding, because |new.target| is
6852 // is pushed before the arguments and |thisValue| is pushed after all
6853 // arguments, so the overall number of values on the stack is even.
6854 masm.branchTestPtr(Assembler::Zero, argcreg, Imm32(1), &noPaddingNeeded);
6855 masm.pushValue(MagicValue(JS_ARG_POISON));
6856 masm.bind(&noPaddingNeeded);
6857 }
6858
6859 // Push |new.target| after the padding value, but before any arguments.
6860 masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch);
6861
6862 // Use newTargetAndScratch to calculate stack space (including padding).
6863 masm.movePtr(argcreg, newTargetAndScratch);
6864
6865 // Reserve space for copying the arguments.
6866 NativeObject::elementsSizeMustNotOverflow();
6867 masm.lshiftPtr(Imm32(ValueShift), newTargetAndScratch);
6868 masm.subFromStackPtr(newTargetAndScratch);
6869}
6870
6871// Destroys argvIndex and copyreg.
6872void CodeGenerator::emitCopyValuesForApply(Register argvSrcBase,
6873 Register argvIndex, Register copyreg,
6874 size_t argvSrcOffset,
6875 size_t argvDstOffset) {
6876 Label loop;
6877 masm.bind(&loop);
6878
6879 // As argvIndex is off by 1, and we use the decBranchPtr instruction to loop
6880 // back, we have to substract the size of the word which are copied.
6881 BaseValueIndex srcPtr(argvSrcBase, argvIndex,
6882 int32_t(argvSrcOffset) - sizeof(void*));
6883 BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex,
6884 int32_t(argvDstOffset) - sizeof(void*));
6885 masm.loadPtr(srcPtr, copyreg);
6886 masm.storePtr(copyreg, dstPtr);
6887
6888 // Handle 32 bits architectures.
6889 if (sizeof(Value) == 2 * sizeof(void*)) {
6890 BaseValueIndex srcPtrLow(argvSrcBase, argvIndex,
6891 int32_t(argvSrcOffset) - 2 * sizeof(void*));
6892 BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex,
6893 int32_t(argvDstOffset) - 2 * sizeof(void*));
6894 masm.loadPtr(srcPtrLow, copyreg);
6895 masm.storePtr(copyreg, dstPtrLow);
6896 }
6897
6898 masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop);
6899}
6900
6901void CodeGenerator::emitRestoreStackPointerFromFP() {
6902 // This is used to restore the stack pointer after a call with a dynamic
6903 // number of arguments.
6904
6905 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"
, 6905); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 6905; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6906
6907 int32_t offset = -int32_t(frameSize());
6908 masm.computeEffectiveAddress(Address(FramePointer, offset),
6909 masm.getStackPointer());
6910#if JS_CODEGEN_ARM64
6911 masm.syncStackPtr();
6912#endif
6913}
6914
6915void CodeGenerator::emitPushArguments(Register argcreg, Register scratch,
6916 Register copyreg, uint32_t extraFormals) {
6917 Label end;
6918
6919 // Skip the copy of arguments if there are none.
6920 masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end);
6921
6922 // clang-format off
6923 //
6924 // We are making a copy of the arguments which are above the JitFrameLayout
6925 // of the current Ion frame.
6926 //
6927 // [arg1] [arg0] <- src [this] [JitFrameLayout] [.. frameSize ..] [pad] [arg1] [arg0] <- dst
6928 //
6929 // clang-format on
6930
6931 // Compute the source and destination offsets into the stack.
6932 //
6933 // The |extraFormals| parameter is used when copying rest-parameters and
6934 // allows to skip the initial parameters before the actual rest-parameters.
6935 Register argvSrcBase = FramePointer;
6936 size_t argvSrcOffset =
6937 JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value);
6938 size_t argvDstOffset = 0;
6939
6940 Register argvIndex = scratch;
6941 masm.move32(argcreg, argvIndex);
6942
6943 // Copy arguments.
6944 emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset,
6945 argvDstOffset);
6946
6947 // Join with all arguments copied.
6948 masm.bind(&end);
6949}
6950
6951void CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply) {
6952 // Holds the function nargs.
6953 Register argcreg = ToRegister(apply->getArgc());
6954 Register copyreg = ToRegister(apply->getTempObject());
6955 Register scratch = ToRegister(apply->getTempForArgCopy());
6956 uint32_t extraFormals = apply->numExtraFormals();
6957
6958 // Allocate space on the stack for arguments.
6959 emitAllocateSpaceForApply(argcreg, scratch);
6960
6961 emitPushArguments(argcreg, scratch, copyreg, extraFormals);
6962
6963 // Push |this|.
6964 masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex));
6965}
6966
6967void CodeGenerator::emitPushArguments(LApplyArgsObj* apply) {
6968 Register argsObj = ToRegister(apply->getArgsObj());
6969 Register tmpArgc = ToRegister(apply->getTempObject());
6970 Register scratch = ToRegister(apply->getTempForArgCopy());
6971
6972 // argc and argsObj are mapped to the same calltemp register.
6973 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"
, 6973); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argsObj == ToRegister(apply->getArgc())"
")"); do { *((volatile int*)__null) = 6973; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6974
6975 // Load argc into tmpArgc.
6976 masm.loadArgumentsObjectLength(argsObj, tmpArgc);
6977
6978 // Allocate space on the stack for arguments.
6979 emitAllocateSpaceForApply(tmpArgc, scratch);
6980
6981 // Load arguments data.
6982 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
6983 argsObj);
6984 size_t argsSrcOffset = ArgumentsData::offsetOfArgs();
6985
6986 // This is the end of the lifetime of argsObj.
6987 // After this call, the argsObj register holds the argument count instead.
6988 emitPushArrayAsArguments(tmpArgc, argsObj, scratch, argsSrcOffset);
6989
6990 // Push |this|.
6991 masm.pushValue(ToValue(apply, LApplyArgsObj::ThisIndex));
6992}
6993
6994void CodeGenerator::emitPushArrayAsArguments(Register tmpArgc,
6995 Register srcBaseAndArgc,
6996 Register scratch,
6997 size_t argvSrcOffset) {
6998 // Preconditions:
6999 // 1. |tmpArgc| * sizeof(Value) bytes have been allocated at the top of
7000 // the stack to hold arguments.
7001 // 2. |srcBaseAndArgc| + |srcOffset| points to an array of |tmpArgc| values.
7002 //
7003 // Postconditions:
7004 // 1. The arguments at |srcBaseAndArgc| + |srcOffset| have been copied into
7005 // the allocated space.
7006 // 2. |srcBaseAndArgc| now contains the original value of |tmpArgc|.
7007 //
7008 // |scratch| is used as a temp register within this function and clobbered.
7009
7010 Label noCopy, epilogue;
7011
7012 // Skip the copy of arguments if there are none.
7013 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
7014 {
7015 // Copy the values. This code is skipped entirely if there are no values.
7016 size_t argvDstOffset = 0;
7017
7018 Register argvSrcBase = srcBaseAndArgc;
7019
7020 // Stash away |tmpArgc| and adjust argvDstOffset accordingly.
7021 masm.push(tmpArgc);
7022 Register argvIndex = tmpArgc;
7023 argvDstOffset += sizeof(void*);
7024
7025 // Copy
7026 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
7027 argvDstOffset);
7028
7029 // Restore.
7030 masm.pop(srcBaseAndArgc); // srcBaseAndArgc now contains argc.
7031 masm.jump(&epilogue);
7032 }
7033 masm.bind(&noCopy);
7034 {
7035 // Clear argc if we skipped the copy step.
7036 masm.movePtr(ImmWord(0), srcBaseAndArgc);
7037 }
7038
7039 // Join with all arguments copied.
7040 // Note, "srcBase" has become "argc".
7041 masm.bind(&epilogue);
7042}
7043
7044void CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply) {
7045 Register elements = ToRegister(apply->getElements());
7046 Register tmpArgc = ToRegister(apply->getTempObject());
7047 Register scratch = ToRegister(apply->getTempForArgCopy());
7048
7049 // argc and elements are mapped to the same calltemp register.
7050 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"
, 7050); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(apply->getArgc())"
")"); do { *((volatile int*)__null) = 7050; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7051
7052 // Invariants guarded in the caller:
7053 // - the array is not too long
7054 // - the array length equals its initialized length
7055
7056 // The array length is our argc for the purposes of allocating space.
7057 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
7058
7059 // Allocate space for the values.
7060 emitAllocateSpaceForApply(tmpArgc, scratch);
7061
7062 // After this call "elements" has become "argc".
7063 size_t elementsOffset = 0;
7064 emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset);
7065
7066 // Push |this|.
7067 masm.pushValue(ToValue(apply, LApplyArrayGeneric::ThisIndex));
7068}
7069
7070void CodeGenerator::emitPushArguments(LConstructArgsGeneric* construct) {
7071 // Holds the function nargs.
7072 Register argcreg = ToRegister(construct->getArgc());
7073 Register copyreg = ToRegister(construct->getTempObject());
7074 Register scratch = ToRegister(construct->getTempForArgCopy());
7075 uint32_t extraFormals = construct->numExtraFormals();
7076
7077 // newTarget and scratch are mapped to the same calltemp register.
7078 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"
, 7078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())"
")"); do { *((volatile int*)__null) = 7078; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7079
7080 // Allocate space for the values.
7081 // After this call "newTarget" has become "scratch".
7082 emitAllocateSpaceForConstructAndPushNewTarget(argcreg, scratch);
7083
7084 emitPushArguments(argcreg, scratch, copyreg, extraFormals);
7085
7086 // Push |this|.
7087 masm.pushValue(ToValue(construct, LConstructArgsGeneric::ThisIndex));
7088}
7089
7090void CodeGenerator::emitPushArguments(LConstructArrayGeneric* construct) {
7091 Register elements = ToRegister(construct->getElements());
7092 Register tmpArgc = ToRegister(construct->getTempObject());
7093 Register scratch = ToRegister(construct->getTempForArgCopy());
7094
7095 // argc and elements are mapped to the same calltemp register.
7096 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"
, 7096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(construct->getArgc())"
")"); do { *((volatile int*)__null) = 7096; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7097
7098 // newTarget and scratch are mapped to the same calltemp register.
7099 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"
, 7099); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())"
")"); do { *((volatile int*)__null) = 7099; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7100
7101 // Invariants guarded in the caller:
7102 // - the array is not too long
7103 // - the array length equals its initialized length
7104
7105 // The array length is our argc for the purposes of allocating space.
7106 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
7107
7108 // Allocate space for the values.
7109 // After this call "newTarget" has become "scratch".
7110 emitAllocateSpaceForConstructAndPushNewTarget(tmpArgc, scratch);
7111
7112 // After this call "elements" has become "argc".
7113 size_t elementsOffset = 0;
7114 emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset);
7115
7116 // Push |this|.
7117 masm.pushValue(ToValue(construct, LConstructArrayGeneric::ThisIndex));
7118}
7119
7120template <typename T>
7121void CodeGenerator::emitApplyGeneric(T* apply) {
7122 // Holds the function object.
7123 Register calleereg = ToRegister(apply->getFunction());
7124
7125 // Temporary register for modifying the function object.
7126 Register objreg = ToRegister(apply->getTempObject());
7127 Register scratch = ToRegister(apply->getTempForArgCopy());
7128
7129 // Holds the function nargs, computed in the invoker or (for ApplyArray,
7130 // ConstructArray, or ApplyArgsObj) in the argument pusher.
7131 Register argcreg = ToRegister(apply->getArgc());
7132
7133 // Copy the arguments of the current function.
7134 //
7135 // In the case of ApplyArray, ConstructArray, or ApplyArgsObj, also compute
7136 // argc. The argc register and the elements/argsObj register are the same;
7137 // argc must not be referenced before the call to emitPushArguments() and
7138 // elements/argsObj must not be referenced after it returns.
7139 //
7140 // In the case of ConstructArray or ConstructArgs, also overwrite newTarget;
7141 // newTarget must not be referenced after this point.
7142 //
7143 // objreg is dead across this call.
7144 emitPushArguments(apply);
7145
7146 masm.checkStackAlignment();
7147
7148 bool constructing = apply->mir()->isConstructing();
7149
7150 // If the function is native, the call is compiled through emitApplyNative.
7151 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"
, 7152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 7152; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
7152 !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"
, 7152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 7152; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
7153
7154 Label end, invoke;
7155
7156 // Unless already known, guard that calleereg is actually a function object.
7157 if (!apply->hasSingleTarget()) {
7158 masm.branchTestObjIsFunction(Assembler::NotEqual, calleereg, objreg,
7159 calleereg, &invoke);
7160 }
7161
7162 // Guard that calleereg is an interpreted function with a JSScript.
7163 masm.branchIfFunctionHasNoJitEntry(calleereg, &invoke);
7164
7165 // Guard that callee allows the [[Call]] or [[Construct]] operation required.
7166 if (constructing) {
7167 masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR,
7168 Assembler::Zero, &invoke);
7169 } else {
7170 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
7171 calleereg, objreg, &invoke);
7172 }
7173
7174 // Use the slow path if CreateThis was unable to create the |this| object.
7175 if (constructing) {
7176 Address thisAddr(masm.getStackPointer(), 0);
7177 masm.branchTestNull(Assembler::Equal, thisAddr, &invoke);
7178 }
7179
7180 // Call with an Ion frame or a rectifier frame.
7181 {
7182 if (apply->mir()->maybeCrossRealm()) {
7183 masm.switchToObjectRealm(calleereg, objreg);
7184 }
7185
7186 // Knowing that calleereg is a non-native function, load jitcode.
7187 masm.loadJitCodeRaw(calleereg, objreg);
7188
7189 masm.PushCalleeToken(calleereg, constructing);
7190 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcreg, scratch);
7191
7192 Label underflow, rejoin;
7193
7194 // Check whether the provided arguments satisfy target argc.
7195 if (!apply->hasSingleTarget()) {
7196 Register nformals = scratch;
7197 masm.loadFunctionArgCount(calleereg, nformals);
7198 masm.branch32(Assembler::Below, argcreg, nformals, &underflow);
7199 } else {
7200 masm.branch32(Assembler::Below, argcreg,
7201 Imm32(apply->getSingleTarget()->nargs()), &underflow);
7202 }
7203
7204 // Skip the construction of the rectifier frame because we have no
7205 // underflow.
7206 masm.jump(&rejoin);
7207
7208 // Argument fixup needed. Get ready to call the argumentsRectifier.
7209 {
7210 masm.bind(&underflow);
7211
7212 // Hardcode the address of the argumentsRectifier code.
7213 TrampolinePtr argumentsRectifier =
7214 gen->jitRuntime()->getArgumentsRectifier();
7215 masm.movePtr(argumentsRectifier, objreg);
7216 }
7217
7218 masm.bind(&rejoin);
7219
7220 // Finally call the function in objreg, as assigned by one of the paths
7221 // above.
7222 ensureOsiSpace();
7223 uint32_t callOffset = masm.callJit(objreg);
7224 markSafepointAt(callOffset, apply);
7225
7226 if (apply->mir()->maybeCrossRealm()) {
7227 static_assert(!JSReturnOperand.aliases(ReturnReg),
7228 "ReturnReg available as scratch after scripted calls");
7229 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
7230 }
7231
7232 // Discard JitFrameLayout fields still left on the stack.
7233 masm.freeStack(sizeof(JitFrameLayout) -
7234 JitFrameLayout::bytesPoppedAfterCall());
7235 masm.jump(&end);
7236 }
7237
7238 // Handle uncompiled or native functions.
7239 {
7240 masm.bind(&invoke);
7241 emitCallInvokeFunction(apply);
7242 }
7243
7244 masm.bind(&end);
7245
7246 // If the return value of the constructing function is Primitive, replace the
7247 // return value with the Object from CreateThis.
7248 if (constructing) {
7249 Label notPrimitive;
7250 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7251 &notPrimitive);
7252 masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand);
7253
7254#ifdef DEBUG1
7255 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
7256 &notPrimitive);
7257 masm.assumeUnreachable("CreateThis creates an object");
7258#endif
7259
7260 masm.bind(&notPrimitive);
7261 }
7262
7263 // Pop arguments and continue.
7264 emitRestoreStackPointerFromFP();
7265}
7266
7267template <typename T>
7268void CodeGenerator::emitAlignStackForApplyNative(T* apply, Register argc) {
7269 static_assert(JitStackAlignment % ABIStackAlignment == 0,
7270 "aligning on JIT stack subsumes ABI alignment");
7271
7272 // Align the arguments on the JitStackAlignment.
7273 if (JitStackValueAlignment > 1) {
7274 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"
, 7275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
") (" "Stack padding adds exactly one Value" ")"); do { *((volatile
int*)__null) = 7275; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
7275 "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"
, 7275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2"
") (" "Stack padding adds exactly one Value" ")"); do { *((volatile
int*)__null) = 7275; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
7276 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"
, 7277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 7277; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7277 "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"
, 7277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0"
") (" "Stack padding assumes that the frameSize is correct" ")"
); do { *((volatile int*)__null) = 7277; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7278
7279 Assembler::Condition cond;
7280 if constexpr (T::isConstructing()) {
7281 // If the number of arguments is even, then we do not need any padding.
7282 //
7283 // Also see emitAllocateSpaceForApply().
7284 cond = Assembler::Zero;
7285 } else {
7286 // If the number of arguments is odd, then we do not need any padding.
7287 //
7288 // Also see emitAllocateSpaceForConstructAndPushNewTarget().
7289 cond = Assembler::NonZero;
7290 }
7291
7292 Label noPaddingNeeded;
7293 masm.branchTestPtr(cond, argc, Imm32(1), &noPaddingNeeded);
7294 masm.pushValue(MagicValue(JS_ARG_POISON));
7295 masm.bind(&noPaddingNeeded);
7296 }
7297}
7298
7299template <typename T>
7300void CodeGenerator::emitPushNativeArguments(T* apply) {
7301 Register argc = ToRegister(apply->getArgc());
7302 Register tmpArgc = ToRegister(apply->getTempObject());
7303 Register scratch = ToRegister(apply->getTempForArgCopy());
7304 uint32_t extraFormals = apply->numExtraFormals();
7305
7306 // Align stack.
7307 emitAlignStackForApplyNative(apply, argc);
7308
7309 // Push newTarget.
7310 if constexpr (T::isConstructing()) {
7311 masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget()));
7312 }
7313
7314 // Push arguments.
7315 Label noCopy;
7316 masm.branchTestPtr(Assembler::Zero, argc, argc, &noCopy);
7317 {
7318 // Use scratch register to calculate stack space.
7319 masm.movePtr(argc, scratch);
7320
7321 // Reserve space for copying the arguments.
7322 NativeObject::elementsSizeMustNotOverflow();
7323 masm.lshiftPtr(Imm32(ValueShift), scratch);
7324 masm.subFromStackPtr(scratch);
7325
7326 // Compute the source and destination offsets into the stack.
7327 Register argvSrcBase = FramePointer;
7328 size_t argvSrcOffset =
7329 JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value);
7330 size_t argvDstOffset = 0;
7331
7332 Register argvIndex = tmpArgc;
7333 masm.move32(argc, argvIndex);
7334
7335 // Copy arguments.
7336 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
7337 argvDstOffset);
7338 }
7339 masm.bind(&noCopy);
7340
7341 // Push |this|.
7342 if constexpr (T::isConstructing()) {
7343 masm.pushValue(MagicValue(JS_IS_CONSTRUCTING));
7344 } else {
7345 masm.pushValue(ToValue(apply, T::ThisIndex));
7346 }
7347}
7348
7349template <typename T>
7350void CodeGenerator::emitPushArrayAsNativeArguments(T* apply) {
7351 Register argc = ToRegister(apply->getArgc());
7352 Register elements = ToRegister(apply->getElements());
7353 Register tmpArgc = ToRegister(apply->getTempObject());
7354 Register scratch = ToRegister(apply->getTempForArgCopy());
7355
7356 // NB: argc and elements are mapped to the same register.
7357 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"
, 7357); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == elements"
")"); do { *((volatile int*)__null) = 7357; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7358
7359 // Invariants guarded in the caller:
7360 // - the array is not too long
7361 // - the array length equals its initialized length
7362
7363 // The array length is our argc.
7364 masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc);
7365
7366 // Align stack.
7367 emitAlignStackForApplyNative(apply, tmpArgc);
7368
7369 // Push newTarget.
7370 if constexpr (T::isConstructing()) {
7371 masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget()));
7372 }
7373
7374 // Skip the copy of arguments if there are none.
7375 Label noCopy;
7376 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
7377 {
7378 // |tmpArgc| is off-by-one, so adjust the offset accordingly.
7379 BaseObjectElementIndex srcPtr(elements, tmpArgc,
7380 -int32_t(sizeof(JS::Value)));
7381
7382 Label loop;
7383 masm.bind(&loop);
7384 masm.pushValue(srcPtr, scratch);
7385 masm.decBranchPtr(Assembler::NonZero, tmpArgc, Imm32(1), &loop);
7386 }
7387 masm.bind(&noCopy);
7388
7389 // Set argc in preparation for calling the native function.
7390 masm.load32(Address(elements, ObjectElements::offsetOfLength()), argc);
7391
7392 // Push |this|.
7393 if constexpr (T::isConstructing()) {
7394 masm.pushValue(MagicValue(JS_IS_CONSTRUCTING));
7395 } else {
7396 masm.pushValue(ToValue(apply, T::ThisIndex));
7397 }
7398}
7399
7400void CodeGenerator::emitPushArguments(LApplyArgsNative* apply) {
7401 emitPushNativeArguments(apply);
7402}
7403
7404void CodeGenerator::emitPushArguments(LApplyArrayNative* apply) {
7405 emitPushArrayAsNativeArguments(apply);
7406}
7407
7408void CodeGenerator::emitPushArguments(LConstructArgsNative* construct) {
7409 emitPushNativeArguments(construct);
7410}
7411
7412void CodeGenerator::emitPushArguments(LConstructArrayNative* construct) {
7413 emitPushArrayAsNativeArguments(construct);
7414}
7415
7416void CodeGenerator::emitPushArguments(LApplyArgsObjNative* apply) {
7417 Register argc = ToRegister(apply->getArgc());
7418 Register argsObj = ToRegister(apply->getArgsObj());
7419 Register tmpArgc = ToRegister(apply->getTempObject());
7420 Register scratch = ToRegister(apply->getTempForArgCopy());
7421 Register scratch2 = ToRegister(apply->getTempExtra());
7422
7423 // NB: argc and argsObj are mapped to the same register.
7424 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"
, 7424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == argsObj"
")"); do { *((volatile int*)__null) = 7424; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7425
7426 // Load argc into tmpArgc.
7427 masm.loadArgumentsObjectLength(argsObj, tmpArgc);
7428
7429 // Align stack.
7430 emitAlignStackForApplyNative(apply, tmpArgc);
7431
7432 // Push arguments.
7433 Label noCopy, epilogue;
7434 masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy);
7435 {
7436 // Use scratch register to calculate stack space.
7437 masm.movePtr(tmpArgc, scratch);
7438
7439 // Reserve space for copying the arguments.
7440 NativeObject::elementsSizeMustNotOverflow();
7441 masm.lshiftPtr(Imm32(ValueShift), scratch);
7442 masm.subFromStackPtr(scratch);
7443
7444 // Load arguments data.
7445 Register argvSrcBase = argsObj;
7446 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
7447 argvSrcBase);
7448 size_t argvSrcOffset = ArgumentsData::offsetOfArgs();
7449 size_t argvDstOffset = 0;
7450
7451 Register argvIndex = scratch2;
7452 masm.move32(tmpArgc, argvIndex);
7453
7454 // Copy the values.
7455 emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset,
7456 argvDstOffset);
7457 }
7458 masm.bind(&noCopy);
7459
7460 // Set argc in preparation for calling the native function.
7461 masm.movePtr(tmpArgc, argc);
7462
7463 // Push |this|.
7464 masm.pushValue(ToValue(apply, LApplyArgsObjNative::ThisIndex));
7465}
7466
7467template <typename T>
7468void CodeGenerator::emitApplyNative(T* apply) {
7469 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"
, 7470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()"
") (" "isConstructing condition must be consistent" ")"); do
{ *((volatile int*)__null) = 7470; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
7470 "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"
, 7470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()"
") (" "isConstructing condition must be consistent" ")"); do
{ *((volatile int*)__null) = 7470; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7471
7472 WrappedFunction* target = apply->mir()->getSingleTarget();
7473 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"
, 7473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 7473; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7474
7475 JSNative native = target->native();
7476 if (apply->mir()->ignoresReturnValue() && target->hasJitInfo()) {
7477 const JSJitInfo* jitInfo = target->jitInfo();
7478 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
7479 native = jitInfo->ignoresReturnValueMethod;
7480 }
7481 }
7482
7483 // Push arguments, including newTarget and |this|.
7484 emitPushArguments(apply);
7485
7486 // Registers used for callWithABI() argument-passing.
7487 Register argContextReg = ToRegister(apply->getTempObject());
7488 Register argUintNReg = ToRegister(apply->getArgc());
7489 Register argVpReg = ToRegister(apply->getTempForArgCopy());
7490 Register tempReg = ToRegister(apply->getTempExtra());
7491
7492 // No unused stack for variadic calls.
7493 uint32_t unusedStack = 0;
7494
7495 // Pushed arguments don't change the pushed frames amount.
7496 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"
, 7496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 7496; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7497
7498 // Create the exit frame and call the native.
7499 emitCallNative(apply, native, argContextReg, argUintNReg, argVpReg, tempReg,
7500 unusedStack);
7501
7502 // The exit frame is still on the stack.
7503 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"
, 7503); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()"
")"); do { *((volatile int*)__null) = 7503; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7504
7505 // The next instruction is removing the exit frame, so there is no need for
7506 // leaveFakeExitFrame.
7507
7508 // Pop arguments and continue.
7509 masm.setFramePushed(frameSize());
7510 emitRestoreStackPointerFromFP();
7511}
7512
7513template <typename T>
7514void CodeGenerator::emitApplyArgsGuard(T* apply) {
7515 LSnapshot* snapshot = apply->snapshot();
7516 Register argcreg = ToRegister(apply->getArgc());
7517
7518 // Ensure that we have a reasonable number of arguments.
7519 bailoutCmp32(Assembler::Above, argcreg, Imm32(JIT_ARGS_LENGTH_MAX), snapshot);
7520}
7521
7522template <typename T>
7523void CodeGenerator::emitApplyArgsObjGuard(T* apply) {
7524 Register argsObj = ToRegister(apply->getArgsObj());
7525 Register temp = ToRegister(apply->getTempObject());
7526
7527 Label bail;
7528 masm.loadArgumentsObjectLength(argsObj, temp, &bail);
7529 masm.branch32(Assembler::Above, temp, Imm32(JIT_ARGS_LENGTH_MAX), &bail);
7530 bailoutFrom(&bail, apply->snapshot());
7531}
7532
7533template <typename T>
7534void CodeGenerator::emitApplyArrayGuard(T* apply) {
7535 LSnapshot* snapshot = apply->snapshot();
7536 Register elements = ToRegister(apply->getElements());
7537 Register tmp = ToRegister(apply->getTempObject());
7538
7539 Address length(elements, ObjectElements::offsetOfLength());
7540 masm.load32(length, tmp);
7541
7542 // Ensure that we have a reasonable number of arguments.
7543 bailoutCmp32(Assembler::Above, tmp, Imm32(JIT_ARGS_LENGTH_MAX), snapshot);
7544
7545 // Ensure that the array does not contain an uninitialized tail.
7546
7547 Address initializedLength(elements,
7548 ObjectElements::offsetOfInitializedLength());
7549 masm.sub32(initializedLength, tmp);
7550 bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot);
7551}
7552
7553void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) {
7554 emitApplyArgsGuard(apply);
7555 emitApplyGeneric(apply);
7556}
7557
7558void CodeGenerator::visitApplyArgsObj(LApplyArgsObj* apply) {
7559 emitApplyArgsObjGuard(apply);
7560 emitApplyGeneric(apply);
7561}
7562
7563void CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) {
7564 emitApplyArrayGuard(apply);
7565 emitApplyGeneric(apply);
7566}
7567
7568void CodeGenerator::visitConstructArgsGeneric(LConstructArgsGeneric* lir) {
7569 emitApplyArgsGuard(lir);
7570 emitApplyGeneric(lir);
7571}
7572
7573void CodeGenerator::visitConstructArrayGeneric(LConstructArrayGeneric* lir) {
7574 emitApplyArrayGuard(lir);
7575 emitApplyGeneric(lir);
7576}
7577
7578void CodeGenerator::visitApplyArgsNative(LApplyArgsNative* lir) {
7579 emitApplyArgsGuard(lir);
7580 emitApplyNative(lir);
7581}
7582
7583void CodeGenerator::visitApplyArgsObjNative(LApplyArgsObjNative* lir) {
7584 emitApplyArgsObjGuard(lir);
7585 emitApplyNative(lir);
7586}
7587
7588void CodeGenerator::visitApplyArrayNative(LApplyArrayNative* lir) {
7589 emitApplyArrayGuard(lir);
7590 emitApplyNative(lir);
7591}
7592
7593void CodeGenerator::visitConstructArgsNative(LConstructArgsNative* lir) {
7594 emitApplyArgsGuard(lir);
7595 emitApplyNative(lir);
7596}
7597
7598void CodeGenerator::visitConstructArrayNative(LConstructArrayNative* lir) {
7599 emitApplyArrayGuard(lir);
7600 emitApplyNative(lir);
7601}
7602
7603void CodeGenerator::visitBail(LBail* lir) { bailout(lir->snapshot()); }
7604
7605void CodeGenerator::visitUnreachable(LUnreachable* lir) {
7606 masm.assumeUnreachable("end-of-block assumed unreachable");
7607}
7608
7609void CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot* lir) {
7610 encode(lir->snapshot());
7611}
7612
7613void CodeGenerator::visitUnreachableResultV(LUnreachableResultV* lir) {
7614 masm.assumeUnreachable("must be unreachable");
7615}
7616
7617void CodeGenerator::visitUnreachableResultT(LUnreachableResultT* lir) {
7618 masm.assumeUnreachable("must be unreachable");
7619}
7620
7621// Out-of-line path to report over-recursed error and fail.
7622class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator> {
7623 LInstruction* lir_;
7624
7625 public:
7626 explicit CheckOverRecursedFailure(LInstruction* lir) : lir_(lir) {}
7627
7628 void accept(CodeGenerator* codegen) override {
7629 codegen->visitCheckOverRecursedFailure(this);
7630 }
7631
7632 LInstruction* lir() const { return lir_; }
7633};
7634
7635void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) {
7636 // If we don't push anything on the stack, skip the check.
7637 if (omitOverRecursedCheck()) {
7638 return;
7639 }
7640
7641 // Ensure that this frame will not cross the stack limit.
7642 // This is a weak check, justified by Ion using the C stack: we must always
7643 // be some distance away from the actual limit, since if the limit is
7644 // crossed, an error must be thrown, which requires more frames.
7645 //
7646 // It must always be possible to trespass past the stack limit.
7647 // Ion may legally place frames very close to the limit. Calling additional
7648 // C functions may then violate the limit without any checking.
7649 //
7650 // Since Ion frames exist on the C stack, the stack limit may be
7651 // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota().
7652
7653 CheckOverRecursedFailure* ool = new (alloc()) CheckOverRecursedFailure(lir);
7654 addOutOfLineCode(ool, lir->mir());
7655
7656 // Conditional forward (unlikely) branch to failure.
7657 const void* limitAddr = gen->runtime->addressOfJitStackLimit();
7658 masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr),
7659 ool->entry());
7660 masm.bind(ool->rejoin());
7661}
7662
7663void CodeGenerator::visitCheckOverRecursedFailure(
7664 CheckOverRecursedFailure* ool) {
7665 // The OOL path is hit if the recursion depth has been exceeded.
7666 // Throw an InternalError for over-recursion.
7667
7668 // LFunctionEnvironment can appear before LCheckOverRecursed, so we have
7669 // to save all live registers to avoid crashes if CheckOverRecursed triggers
7670 // a GC.
7671 saveLive(ool->lir());
7672
7673 using Fn = bool (*)(JSContext*);
7674 callVM<Fn, CheckOverRecursed>(ool->lir());
7675
7676 restoreLive(ool->lir());
7677 masm.jump(ool->rejoin());
7678}
7679
7680IonScriptCounts* CodeGenerator::maybeCreateScriptCounts() {
7681 // If scripts are being profiled, create a new IonScriptCounts for the
7682 // profiling data, which will be attached to the associated JSScript or
7683 // wasm module after code generation finishes.
7684 if (!gen->hasProfilingScripts()) {
7685 return nullptr;
7686 }
7687
7688 // This test inhibits IonScriptCount creation for wasm code which is
7689 // currently incompatible with wasm codegen for two reasons: (1) wasm code
7690 // must be serializable and script count codegen bakes in absolute
7691 // addresses, (2) wasm code does not have a JSScript with which to associate
7692 // code coverage data.
7693 JSScript* script = gen->outerInfo().script();
7694 if (!script) {
7695 return nullptr;
7696 }
7697
7698 auto counts = MakeUnique<IonScriptCounts>();
7699 if (!counts || !counts->init(graph.numBlocks())) {
7700 return nullptr;
7701 }
7702
7703 for (size_t i = 0; i < graph.numBlocks(); i++) {
7704 MBasicBlock* block = graph.getBlock(i)->mir();
7705
7706 uint32_t offset = 0;
7707 char* description = nullptr;
7708 if (MResumePoint* resume = block->entryResumePoint()) {
7709 // Find a PC offset in the outermost script to use. If this
7710 // block is from an inlined script, find a location in the
7711 // outer script to associate information about the inlining
7712 // with.
7713 while (resume->caller()) {
7714 resume = resume->caller();
7715 }
7716 offset = script->pcToOffset(resume->pc());
7717
7718 if (block->entryResumePoint()->caller()) {
7719 // Get the filename and line number of the inner script.
7720 JSScript* innerScript = block->info().script();
7721 description = js_pod_calloc<char>(200);
7722 if (description) {
7723 snprintf(description, 200, "%s:%u", innerScript->filename(),
7724 innerScript->lineno());
7725 }
7726 }
7727 }
7728
7729 if (!counts->block(i).init(block->id(), offset, description,
7730 block->numSuccessors())) {
7731 return nullptr;
7732 }
7733
7734 for (size_t j = 0; j < block->numSuccessors(); j++) {
7735 counts->block(i).setSuccessor(
7736 j, skipTrivialBlocks(block->getSuccessor(j))->id());
7737 }
7738 }
7739
7740 scriptCounts_ = counts.release();
7741 return scriptCounts_;
7742}
7743
7744// Structure for managing the state tracked for a block by script counters.
7745struct ScriptCountBlockState {
7746 IonBlockCounts& block;
7747 MacroAssembler& masm;
7748
7749 Sprinter printer;
7750
7751 public:
7752 ScriptCountBlockState(IonBlockCounts* block, MacroAssembler* masm)
7753 : block(*block), masm(*masm), printer(GetJitContext()->cx, false) {}
7754
7755 bool init() {
7756 if (!printer.init()) {
7757 return false;
7758 }
7759
7760 // Bump the hit count for the block at the start. This code is not
7761 // included in either the text for the block or the instruction byte
7762 // counts.
7763 masm.inc64(AbsoluteAddress(block.addressOfHitCount()));
7764
7765 // Collect human readable assembly for the code generated in the block.
7766 masm.setPrinter(&printer);
7767
7768 return true;
7769 }
7770
7771 void visitInstruction(LInstruction* ins) {
7772#ifdef JS_JITSPEW1
7773 // Prefix stream of assembly instructions with their LIR instruction
7774 // name and any associated high level info.
7775 if (const char* extra = ins->getExtraName()) {
7776 printer.printf("[%s:%s]\n", ins->opName(), extra);
7777 } else {
7778 printer.printf("[%s]\n", ins->opName());
7779 }
7780#endif
7781 }
7782
7783 ~ScriptCountBlockState() {
7784 masm.setPrinter(nullptr);
7785
7786 if (JS::UniqueChars str = printer.release()) {
7787 block.setCode(str.get());
7788 }
7789 }
7790};
7791
7792void CodeGenerator::branchIfInvalidated(Register temp, Label* invalidated) {
7793 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp);
7794 masm.propagateOOM(ionScriptLabels_.append(label));
7795
7796 // If IonScript::invalidationCount_ != 0, the script has been invalidated.
7797 masm.branch32(Assembler::NotEqual,
7798 Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0),
7799 invalidated);
7800}
7801
7802#ifdef DEBUG1
7803void CodeGenerator::emitAssertGCThingResult(Register input,
7804 const MDefinition* mir) {
7805 MIRType type = mir->type();
7806 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"
, 7807); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
")"); do { *((volatile int*)__null) = 7807; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7807 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"
, 7807); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt"
")"); do { *((volatile int*)__null) = 7807; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7808
7809 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
7810 regs.take(input);
7811
7812 Register temp = regs.takeAny();
7813 masm.push(temp);
7814
7815 // Don't check if the script has been invalidated. In that case invalid
7816 // types are expected (until we reach the OsiPoint and bailout).
7817 Label done;
7818 branchIfInvalidated(temp, &done);
7819
7820# ifndef JS_SIMULATOR
7821 // Check that we have a valid GC pointer.
7822 // Disable for wasm because we don't have a context on wasm compilation
7823 // threads and this needs a context.
7824 // Also disable for simulator builds because the C++ call is a lot slower
7825 // there than on actual hardware.
7826 if (JitOptions.fullDebugChecks && !IsCompilingWasm()) {
7827 saveVolatile();
7828 masm.setupUnalignedABICall(temp);
7829 masm.loadJSContext(temp);
7830 masm.passABIArg(temp);
7831 masm.passABIArg(input);
7832
7833 switch (type) {
7834 case MIRType::Object: {
7835 using Fn = void (*)(JSContext* cx, JSObject* obj);
7836 masm.callWithABI<Fn, AssertValidObjectPtr>();
7837 break;
7838 }
7839 case MIRType::String: {
7840 using Fn = void (*)(JSContext* cx, JSString* str);
7841 masm.callWithABI<Fn, AssertValidStringPtr>();
7842 break;
7843 }
7844 case MIRType::Symbol: {
7845 using Fn = void (*)(JSContext* cx, JS::Symbol* sym);
7846 masm.callWithABI<Fn, AssertValidSymbolPtr>();
7847 break;
7848 }
7849 case MIRType::BigInt: {
7850 using Fn = void (*)(JSContext* cx, JS::BigInt* bi);
7851 masm.callWithABI<Fn, AssertValidBigIntPtr>();
7852 break;
7853 }
7854 default:
7855 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 7855); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 7855; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
7856 }
7857
7858 restoreVolatile();
7859 }
7860# endif
7861
7862 masm.bind(&done);
7863 masm.pop(temp);
7864}
7865
7866void CodeGenerator::emitAssertResultV(const ValueOperand input,
7867 const MDefinition* mir) {
7868 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
7869 regs.take(input);
7870
7871 Register temp1 = regs.takeAny();
7872 Register temp2 = regs.takeAny();
7873 masm.push(temp1);
7874 masm.push(temp2);
7875
7876 // Don't check if the script has been invalidated. In that case invalid
7877 // types are expected (until we reach the OsiPoint and bailout).
7878 Label done;
7879 branchIfInvalidated(temp1, &done);
7880
7881 // Check that we have a valid GC pointer.
7882 if (JitOptions.fullDebugChecks) {
7883 saveVolatile();
7884
7885 masm.pushValue(input);
7886 masm.moveStackPtrTo(temp1);
7887
7888 using Fn = void (*)(JSContext* cx, Value* v);
7889 masm.setupUnalignedABICall(temp2);
7890 masm.loadJSContext(temp2);
7891 masm.passABIArg(temp2);
7892 masm.passABIArg(temp1);
7893 masm.callWithABI<Fn, AssertValidValue>();
7894 masm.popValue(input);
7895 restoreVolatile();
7896 }
7897
7898 masm.bind(&done);
7899 masm.pop(temp2);
7900 masm.pop(temp1);
7901}
7902
7903void CodeGenerator::emitGCThingResultChecks(LInstruction* lir,
7904 MDefinition* mir) {
7905 if (lir->numDefs() == 0) {
7906 return;
7907 }
7908
7909 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"
, 7909); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1"
")"); do { *((volatile int*)__null) = 7909; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7910 if (lir->getDef(0)->isBogusTemp()) {
7911 return;
7912 }
7913
7914 Register output = ToRegister(lir->getDef(0));
7915 emitAssertGCThingResult(output, mir);
7916}
7917
7918void CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir) {
7919 if (lir->numDefs() == 0) {
7920 return;
7921 }
7922
7923 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"
, 7923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1"
")"); do { *((volatile int*)__null) = 7923; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7924 if (!lir->getDef(0)->output()->isRegister()) {
7925 return;
7926 }
7927
7928 ValueOperand output = ToOutValue(lir);
7929
7930 emitAssertResultV(output, mir);
7931}
7932
7933void CodeGenerator::emitDebugResultChecks(LInstruction* ins) {
7934 // In debug builds, check that LIR instructions return valid values.
7935
7936 MDefinition* mir = ins->mirRaw();
7937 if (!mir) {
7938 return;
7939 }
7940
7941 switch (mir->type()) {
7942 case MIRType::Object:
7943 case MIRType::String:
7944 case MIRType::Symbol:
7945 case MIRType::BigInt:
7946 emitGCThingResultChecks(ins, mir);
7947 break;
7948 case MIRType::Value:
7949 emitValueResultChecks(ins, mir);
7950 break;
7951 default:
7952 break;
7953 }
7954}
7955
7956void CodeGenerator::emitDebugForceBailing(LInstruction* lir) {
7957 if (MOZ_LIKELY(!gen->options.ionBailAfterEnabled())(__builtin_expect(!!(!gen->options.ionBailAfterEnabled()),
1))
) {
7958 return;
7959 }
7960 if (!lir->snapshot()) {
7961 return;
7962 }
7963 if (lir->isOsiPoint()) {
7964 return;
7965 }
7966
7967 masm.comment("emitDebugForceBailing");
7968 const void* bailAfterCounterAddr =
7969 gen->runtime->addressOfIonBailAfterCounter();
7970
7971 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
7972
7973 Label done, notBail;
7974 masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterCounterAddr),
7975 Imm32(0), &done);
7976 {
7977 Register temp = regs.takeAny();
7978
7979 masm.push(temp);
7980 masm.load32(AbsoluteAddress(bailAfterCounterAddr), temp);
7981 masm.sub32(Imm32(1), temp);
7982 masm.store32(temp, AbsoluteAddress(bailAfterCounterAddr));
7983
7984 masm.branch32(Assembler::NotEqual, temp, Imm32(0), &notBail);
7985 {
7986 masm.pop(temp);
7987 bailout(lir->snapshot());
7988 }
7989 masm.bind(&notBail);
7990 masm.pop(temp);
7991 }
7992 masm.bind(&done);
7993}
7994#endif
7995
7996bool CodeGenerator::generateBody() {
7997 JitSpewCont(JitSpew_Codegen, "\n");
7998 AutoCreatedBy acb(masm, "CodeGenerator::generateBody");
7999
8000 JitSpew(JitSpew_Codegen, "==== BEGIN CodeGenerator::generateBody ====");
8001 IonScriptCounts* counts = maybeCreateScriptCounts();
8002
8003 const bool compilingWasm = gen->compilingWasm();
8004
8005 for (size_t i = 0; i < graph.numBlocks(); i++) {
8006 current = graph.getBlock(i);
8007
8008 // Don't emit any code for trivial blocks, containing just a goto. Such
8009 // blocks are created to split critical edges, and if we didn't end up
8010 // putting any instructions in them, we can skip them.
8011 if (current->isTrivial()) {
8012 continue;
8013 }
8014
8015#ifdef JS_JITSPEW1
8016 const char* filename = nullptr;
8017 size_t lineNumber = 0;
8018 JS::LimitedColumnNumberOneOrigin columnNumber;
8019 if (current->mir()->info().script()) {
8020 filename = current->mir()->info().script()->filename();
8021 if (current->mir()->pc()) {
8022 lineNumber = PCToLineNumber(current->mir()->info().script(),
8023 current->mir()->pc(), &columnNumber);
8024 }
8025 }
8026 JitSpew(JitSpew_Codegen, "--------------------------------");
8027 JitSpew(JitSpew_Codegen, "# block%zu %s:%zu:%u%s:", i,
8028 filename ? filename : "?", lineNumber,
8029 columnNumber.oneOriginValue(),
8030 current->mir()->isLoopHeader() ? " (loop header)" : "");
8031#endif
8032
8033 if (current->mir()->isLoopHeader() && compilingWasm) {
8034 masm.nopAlign(CodeAlignment);
8035 }
8036
8037 masm.bind(current->label());
8038
8039 mozilla::Maybe<ScriptCountBlockState> blockCounts;
8040 if (counts) {
8041 blockCounts.emplace(&counts->block(i), &masm);
8042 if (!blockCounts->init()) {
8043 return false;
8044 }
8045 }
8046
8047 for (LInstructionIterator iter = current->begin(); iter != current->end();
8048 iter++) {
8049 if (!alloc().ensureBallast()) {
8050 return false;
8051 }
8052
8053 perfSpewer_.recordInstruction(masm, *iter);
8054#ifdef JS_JITSPEW1
8055 JitSpewStart(JitSpew_Codegen, " # LIR=%s",
8056 iter->opName());
8057 if (const char* extra = iter->getExtraName()) {
8058 JitSpewCont(JitSpew_Codegen, ":%s", extra);
8059 }
8060 JitSpewFin(JitSpew_Codegen);
8061#endif
8062
8063 if (counts) {
8064 blockCounts->visitInstruction(*iter);
8065 }
8066
8067#ifdef CHECK_OSIPOINT_REGISTERS1
8068 if (iter->safepoint() && !compilingWasm) {
8069 resetOsiPointRegs(iter->safepoint());
8070 }
8071#endif
8072
8073 if (!compilingWasm) {
8074 if (MDefinition* mir = iter->mirRaw()) {
8075 if (!addNativeToBytecodeEntry(mir->trackedSite())) {
8076 return false;
8077 }
8078 }
8079 }
8080
8081 setElement(*iter); // needed to encode correct snapshot location.
8082
8083#ifdef DEBUG1
8084 emitDebugForceBailing(*iter);
8085#endif
8086
8087 switch (iter->op()) {
8088#ifndef JS_CODEGEN_NONE
8089# define LIROP(op) \
8090 case LNode::Opcode::op: \
8091 visit##op(iter->to##op()); \
8092 break;
8093 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(NewMapObject)LIROP(NewSetObject)LIROP
(NewMapObjectFromIterable)LIROP(NewSetObjectFromIterable)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(ReinterpretCast)LIROP(ReinterpretCastFromI64
)LIROP(ReinterpretCastToI64)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(TestIPtrAndBranch)LIROP(TestI64AndBranch
)LIROP(TestDAndBranch)LIROP(TestFAndBranch)LIROP(TestBIAndBranch
)LIROP(TestOAndBranch)LIROP(TestVAndBranch)LIROP(Compare)LIROP
(CompareI64)LIROP(CompareI64AndBranch)LIROP(CompareAndBranch)
LIROP(CompareD)LIROP(CompareF)LIROP(CompareDAndBranch)LIROP(CompareFAndBranch
)LIROP(CompareS)LIROP(CompareSInline)LIROP(CompareSSingle)LIROP
(CompareBigInt)LIROP(CompareBigIntInt32)LIROP(CompareBigIntDouble
)LIROP(CompareBigIntString)LIROP(CompareBigIntInt32AndBranch)
LIROP(BitAndAndBranch)LIROP(BitAnd64AndBranch)LIROP(IsNullOrLikeUndefinedV
)LIROP(IsNullOrLikeUndefinedT)LIROP(IsNull)LIROP(IsUndefined)
LIROP(IsNullOrLikeUndefinedAndBranchV)LIROP(IsNullOrLikeUndefinedAndBranchT
)LIROP(IsNullAndBranch)LIROP(IsUndefinedAndBranch)LIROP(SameValueDouble
)LIROP(SameValue)LIROP(NotI)LIROP(NotIPtr)LIROP(NotI64)LIROP(
NotD)LIROP(NotF)LIROP(NotBI)LIROP(NotO)LIROP(NotV)LIROP(BitNotI
)LIROP(BitNotI64)LIROP(BitOpI)LIROP(BitOpI64)LIROP(ShiftI)LIROP
(ShiftI64)LIROP(SignExtendInt32)LIROP(SignExtendIntPtr)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(BigIntToIntPtr)LIROP(IntPtrToBigInt)LIROP(BigIntPtrAdd)
LIROP(BigIntPtrSub)LIROP(BigIntPtrMul)LIROP(BigIntPtrDiv)LIROP
(BigIntPtrDivPowTwo)LIROP(BigIntPtrMod)LIROP(BigIntPtrModPowTwo
)LIROP(BigIntPtrPow)LIROP(BigIntPtrBitAnd)LIROP(BigIntPtrBitOr
)LIROP(BigIntPtrBitXor)LIROP(BigIntPtrLsh)LIROP(BigIntPtrRsh)
LIROP(BigIntPtrBitNot)LIROP(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(DoubleToFloat32ToFloat16)LIROP(Float32ToFloat16)LIROP(
Int32ToFloat16)LIROP(ValueToDouble)LIROP(ValueToFloat32)LIROP
(ValueToFloat16)LIROP(ValueToNumberInt32)LIROP(ValueTruncateToInt32
)LIROP(ValueToBigInt)LIROP(DoubleToInt32)LIROP(Float32ToInt32
)LIROP(TruncateDToInt32)LIROP(WasmBuiltinTruncateDToInt32)LIROP
(TruncateFToInt32)LIROP(WasmBuiltinTruncateFToInt32)LIROP(WasmTruncateToInt32
)LIROP(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(LoadUnboxedInt64)LIROP(LoadDataViewElement)LIROP(LoadDataViewElement64
)LIROP(LoadTypedArrayElementHole)LIROP(LoadTypedArrayElementHoleBigInt
)LIROP(StoreUnboxedScalar)LIROP(StoreUnboxedInt64)LIROP(StoreDataViewElement
)LIROP(StoreDataViewElement64)LIROP(StoreTypedArrayElementHole
)LIROP(StoreTypedArrayElementHoleInt64)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(Uint64ToBigInt)LIROP(Int64ToIntPtr)LIROP(IntPtrToInt64
)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(WasmClampTable64Address)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(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(MapObjectGetValueVMCall
)LIROP(MapObjectSize)LIROP(BigIntAsUintN)LIROP(IonToWasmCall)
LIROP(IonToWasmCallV)LIROP(IonToWasmCallI64)LIROP(WasmAnyRefFromJSValue
)LIROP(WasmAnyRefFromJSObject)LIROP(WasmAnyRefFromJSString)LIROP
(WasmAnyRefIsJSString)LIROP(WasmTrapIfAnyRefIsNotJSString)LIROP
(WasmAnyRefJSStringLength)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
)LIROP(AddDisposableResource)LIROP(TakeDisposeCapability)LIROP
(CreateSuppressedError)LIROP(AtomicPause)LIROP(SetObjectDelete
)LIROP(SetObjectAdd)LIROP(MapObjectGetValue)LIROP(MapObjectDelete
)LIROP(MapObjectSet)LIROP(DateFillLocalTimeSlots)LIROP(DateHoursFromSecondsIntoYear
)LIROP(DateMinutesFromSecondsIntoYear)LIROP(DateSecondsFromSecondsIntoYear
)
8094# undef LIROP
8095#endif
8096 case LNode::Opcode::Invalid:
8097 default:
8098 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"
, 8098); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid LIR op"
")"); do { *((volatile int*)__null) = 8098; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
8099 }
8100
8101#ifdef DEBUG1
8102 if (!counts) {
8103 emitDebugResultChecks(*iter);
8104 }
8105#endif
8106 }
8107 if (masm.oom()) {
8108 return false;
8109 }
8110 }
8111
8112 JitSpew(JitSpew_Codegen, "==== END CodeGenerator::generateBody ====\n");
8113 return true;
8114}
8115
8116// Out-of-line object allocation for LNewArray.
8117class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator> {
8118 LNewArray* lir_;
8119
8120 public:
8121 explicit OutOfLineNewArray(LNewArray* lir) : lir_(lir) {}
8122
8123 void accept(CodeGenerator* codegen) override {
8124 codegen->visitOutOfLineNewArray(this);
8125 }
8126
8127 LNewArray* lir() const { return lir_; }
8128};
8129
8130void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) {
8131 Register objReg = ToRegister(lir->output());
8132
8133 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"
, 8133); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()"
")"); do { *((volatile int*)__null) = 8133; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8134 saveLive(lir);
8135
8136 JSObject* templateObject = lir->mir()->templateObject();
8137
8138 if (templateObject) {
8139 pushArg(ImmGCPtr(templateObject->shape()));
8140 pushArg(Imm32(lir->mir()->length()));
8141
8142 using Fn = ArrayObject* (*)(JSContext*, uint32_t, Handle<Shape*>);
8143 callVM<Fn, NewArrayWithShape>(lir);
8144 } else {
8145 pushArg(Imm32(GenericObject));
8146 pushArg(Imm32(lir->mir()->length()));
8147
8148 using Fn = ArrayObject* (*)(JSContext*, uint32_t, NewObjectKind);
8149 callVM<Fn, NewArrayOperation>(lir);
8150 }
8151
8152 masm.storeCallPointerResult(objReg);
8153
8154 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"
, 8154); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)"
")"); do { *((volatile int*)__null) = 8154; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8155 restoreLive(lir);
8156}
8157
8158void CodeGenerator::visitAtan2D(LAtan2D* lir) {
8159 FloatRegister y = ToFloatRegister(lir->y());
8160 FloatRegister x = ToFloatRegister(lir->x());
8161
8162 using Fn = double (*)(double x, double y);
8163 masm.setupAlignedABICall();
8164 masm.passABIArg(y, ABIType::Float64);
8165 masm.passABIArg(x, ABIType::Float64);
8166 masm.callWithABI<Fn, ecmaAtan2>(ABIType::Float64);
8167
8168 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"
, 8168); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 8168; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8169}
8170
8171void CodeGenerator::visitHypot(LHypot* lir) {
8172 uint32_t numArgs = lir->numArgs();
8173 masm.setupAlignedABICall();
8174
8175 for (uint32_t i = 0; i < numArgs; ++i) {
8176 masm.passABIArg(ToFloatRegister(lir->getOperand(i)), ABIType::Float64);
8177 }
8178
8179 switch (numArgs) {
8180 case 2: {
8181 using Fn = double (*)(double x, double y);
8182 masm.callWithABI<Fn, ecmaHypot>(ABIType::Float64);
8183 break;
8184 }
8185 case 3: {
8186 using Fn = double (*)(double x, double y, double z);
8187 masm.callWithABI<Fn, hypot3>(ABIType::Float64);
8188 break;
8189 }
8190 case 4: {
8191 using Fn = double (*)(double x, double y, double z, double w);
8192 masm.callWithABI<Fn, hypot4>(ABIType::Float64);
8193 break;
8194 }
8195 default:
8196 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"
, 8196); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected number of arguments to hypot function."
")"); do { *((volatile int*)__null) = 8196; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
8197 }
8198 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"
, 8198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 8198; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8199}
8200
8201void CodeGenerator::visitNewArray(LNewArray* lir) {
8202 Register objReg = ToRegister(lir->output());
8203 Register tempReg = ToRegister(lir->temp());
8204 DebugOnly<uint32_t> length = lir->mir()->length();
8205
8206 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"
, 8206); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT"
")"); do { *((volatile int*)__null) = 8206; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8207
8208 if (lir->mir()->isVMCall()) {
8209 visitNewArrayCallVM(lir);
8210 return;
8211 }
8212
8213 OutOfLineNewArray* ool = new (alloc()) OutOfLineNewArray(lir);
8214 addOutOfLineCode(ool, lir->mir());
8215 TemplateObject templateObject(lir->mir()->templateObject());
8216#ifdef DEBUG1
8217 size_t numInlineElements = gc::GetGCKindSlots(templateObject.getAllocKind()) -
8218 ObjectElements::VALUES_PER_HEADER;
8219 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"
, 8220); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements"
") (" "Inline allocation only supports inline elements" ")")
; do { *((volatile int*)__null) = 8220; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
8220 "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"
, 8220); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements"
") (" "Inline allocation only supports inline elements" ")")
; do { *((volatile int*)__null) = 8220; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8221#endif
8222 masm.createGCObject(objReg, tempReg, templateObject,
8223 lir->mir()->initialHeap(), ool->entry());
8224
8225 masm.bind(ool->rejoin());
8226}
8227
8228void CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray* ool) {
8229 visitNewArrayCallVM(ool->lir());
8230 masm.jump(ool->rejoin());
8231}
8232
8233void CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) {
8234 Register lengthReg = ToRegister(lir->length());
8235 Register objReg = ToRegister(lir->output());
8236 Register tempReg = ToRegister(lir->temp0());
8237
8238 JSObject* templateObject = lir->mir()->templateObject();
8239 gc::Heap initialHeap = lir->mir()->initialHeap();
8240
8241 using Fn = ArrayObject* (*)(JSContext*, Handle<ArrayObject*>, int32_t length,
8242 gc::AllocSite*);
8243 OutOfLineCode* ool = oolCallVM<Fn, ArrayConstructorOneArg>(
8244 lir, ArgList(ImmGCPtr(templateObject), lengthReg, ImmPtr(nullptr)),
8245 StoreRegisterTo(objReg));
8246
8247 bool canInline = true;
8248 size_t inlineLength = 0;
8249 if (templateObject->as<ArrayObject>().hasFixedElements()) {
8250 size_t numSlots =
8251 gc::GetGCKindSlots(templateObject->asTenured().getAllocKind());
8252 inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER;
8253 } else {
8254 canInline = false;
8255 }
8256
8257 if (canInline) {
8258 // Try to do the allocation inline if the template object is big enough
8259 // for the length in lengthReg. If the length is bigger we could still
8260 // use the template object and not allocate the elements, but it's more
8261 // efficient to do a single big allocation than (repeatedly) reallocating
8262 // the array later on when filling it.
8263 masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength),
8264 ool->entry());
8265
8266 TemplateObject templateObj(templateObject);
8267 masm.createGCObject(objReg, tempReg, templateObj, initialHeap,
8268 ool->entry());
8269
8270 size_t lengthOffset = NativeObject::offsetOfFixedElements() +
8271 ObjectElements::offsetOfLength();
8272 masm.store32(lengthReg, Address(objReg, lengthOffset));
8273 } else {
8274 masm.jump(ool->entry());
8275 }
8276
8277 masm.bind(ool->rejoin());
8278}
8279
8280void CodeGenerator::visitNewIterator(LNewIterator* lir) {
8281 Register objReg = ToRegister(lir->output());
8282 Register tempReg = ToRegister(lir->temp0());
8283
8284 OutOfLineCode* ool;
8285 switch (lir->mir()->type()) {
8286 case MNewIterator::ArrayIterator: {
8287 using Fn = ArrayIteratorObject* (*)(JSContext*);
8288 ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(),
8289 StoreRegisterTo(objReg));
8290 break;
8291 }
8292 case MNewIterator::StringIterator: {
8293 using Fn = StringIteratorObject* (*)(JSContext*);
8294 ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(),
8295 StoreRegisterTo(objReg));
8296 break;
8297 }
8298 case MNewIterator::RegExpStringIterator: {
8299 using Fn = RegExpStringIteratorObject* (*)(JSContext*);
8300 ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(),
8301 StoreRegisterTo(objReg));
8302 break;
8303 }
8304 default:
8305 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"
, 8305); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected iterator type"
")"); do { *((volatile int*)__null) = 8305; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
8306 }
8307
8308 TemplateObject templateObject(lir->mir()->templateObject());
8309 masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default,
8310 ool->entry());
8311
8312 masm.bind(ool->rejoin());
8313}
8314
8315void CodeGenerator::visitNewTypedArray(LNewTypedArray* lir) {
8316 Register objReg = ToRegister(lir->output());
8317 Register tempReg = ToRegister(lir->temp0());
8318 Register lengthReg = ToRegister(lir->temp1());
8319 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
8320
8321 JSObject* templateObject = lir->mir()->templateObject();
8322 gc::Heap initialHeap = lir->mir()->initialHeap();
8323
8324 auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>();
8325
8326 size_t n = ttemplate->length();
8327 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"
, 8328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)"
") (" "Template objects are only created for int32 lengths" ")"
); do { *((volatile int*)__null) = 8328; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
8328 "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"
, 8328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)"
") (" "Template objects are only created for int32 lengths" ")"
); do { *((volatile int*)__null) = 8328; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8329
8330 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length);
8331 OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
8332 lir, ArgList(ImmGCPtr(templateObject), Imm32(n)),
8333 StoreRegisterTo(objReg));
8334
8335 TemplateObject templateObj(templateObject);
8336 masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry());
8337
8338 masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(),
8339 ttemplate, MacroAssembler::TypedArrayLength::Fixed);
8340
8341 masm.bind(ool->rejoin());
8342}
8343
8344void CodeGenerator::visitNewTypedArrayDynamicLength(
8345 LNewTypedArrayDynamicLength* lir) {
8346 Register lengthReg = ToRegister(lir->length());
8347 Register objReg = ToRegister(lir->output());
8348 Register tempReg = ToRegister(lir->temp0());
8349 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
8350
8351 JSObject* templateObject = lir->mir()->templateObject();
8352 gc::Heap initialHeap = lir->mir()->initialHeap();
8353
8354 auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>();
8355
8356 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length);
8357 OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>(
8358 lir, ArgList(ImmGCPtr(templateObject), lengthReg),
8359 StoreRegisterTo(objReg));
8360
8361 // Volatile |lengthReg| is saved across the ABI call in |initTypedArraySlots|.
8362 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"
, 8362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "liveRegs.has(lengthReg)"
")"); do { *((volatile int*)__null) = 8362; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
8363
8364 TemplateObject templateObj(templateObject);
8365 masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry());
8366
8367 masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(),
8368 ttemplate,
8369 MacroAssembler::TypedArrayLength::Dynamic);
8370
8371 masm.bind(ool->rejoin());
8372}
8373
8374void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) {
8375 pushArg(ToRegister(lir->array()));
8376 pushArg(ImmGCPtr(lir->mir()->templateObject()));
8377
8378 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject);
8379 callVM<Fn, js::NewTypedArrayWithTemplateAndArray>(lir);
8380}
8381
8382void CodeGenerator::visitNewTypedArrayFromArrayBuffer(
8383 LNewTypedArrayFromArrayBuffer* lir) {
8384 pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::LengthIndex));
8385 pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::ByteOffsetIndex));
8386 pushArg(ToRegister(lir->arrayBuffer()));
8387 pushArg(ImmGCPtr(lir->mir()->templateObject()));
8388
8389 using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject,
8390 HandleValue, HandleValue);
8391 callVM<Fn, js::NewTypedArrayWithTemplateAndBuffer>(lir);
8392}
8393
8394void CodeGenerator::visitBindFunction(LBindFunction* lir) {
8395 Register target = ToRegister(lir->target());
8396 Register temp1 = ToRegister(lir->temp0());
8397 Register temp2 = ToRegister(lir->temp1());
8398
8399 // Try to allocate a new BoundFunctionObject we can pass to the VM function.
8400 // If this fails, we set temp1 to nullptr so we do the allocation in C++.
8401 TemplateObject templateObject(lir->mir()->templateObject());
8402 Label allocOk, allocFailed;
8403 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
8404 &allocFailed);
8405 masm.jump(&allocOk);
8406
8407 masm.bind(&allocFailed);
8408 masm.movePtr(ImmWord(0), temp1);
8409
8410 masm.bind(&allocOk);
8411
8412 // Set temp2 to the address of the first argument on the stack.
8413 // Note that the Value slots used for arguments are currently aligned for a
8414 // JIT call, even though that's not strictly necessary for calling into C++.
8415 uint32_t argc = lir->mir()->numStackArgs();
8416 if (JitStackValueAlignment > 1) {
8417 argc = AlignBytes(argc, JitStackValueAlignment);
8418 }
8419 uint32_t unusedStack = UnusedStackBytesForCall(argc);
8420 masm.computeEffectiveAddress(Address(masm.getStackPointer(), unusedStack),
8421 temp2);
8422
8423 pushArg(temp1);
8424 pushArg(Imm32(lir->mir()->numStackArgs()));
8425 pushArg(temp2);
8426 pushArg(target);
8427
8428 using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*,
8429 uint32_t, Handle<BoundFunctionObject*>);
8430 callVM<Fn, js::BoundFunctionObject::functionBindImpl>(lir);
8431}
8432
8433void CodeGenerator::visitNewBoundFunction(LNewBoundFunction* lir) {
8434 Register output = ToRegister(lir->output());
8435 Register temp = ToRegister(lir->temp0());
8436
8437 JSObject* templateObj = lir->mir()->templateObj();
8438
8439 using Fn = BoundFunctionObject* (*)(JSContext*, Handle<BoundFunctionObject*>);
8440 OutOfLineCode* ool = oolCallVM<Fn, BoundFunctionObject::createWithTemplate>(
8441 lir, ArgList(ImmGCPtr(templateObj)), StoreRegisterTo(output));
8442
8443 TemplateObject templateObject(templateObj);
8444 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
8445 ool->entry());
8446
8447 masm.bind(ool->rejoin());
8448}
8449
8450// Out-of-line object allocation for JSOp::NewObject.
8451class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator> {
8452 LNewObject* lir_;
8453
8454 public:
8455 explicit OutOfLineNewObject(LNewObject* lir) : lir_(lir) {}
8456
8457 void accept(CodeGenerator* codegen) override {
8458 codegen->visitOutOfLineNewObject(this);
8459 }
8460
8461 LNewObject* lir() const { return lir_; }
8462};
8463
8464void CodeGenerator::visitNewObjectVMCall(LNewObject* lir) {
8465 Register objReg = ToRegister(lir->output());
8466
8467 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"
, 8467); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()"
")"); do { *((volatile int*)__null) = 8467; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8468 saveLive(lir);
8469
8470 JSObject* templateObject = lir->mir()->templateObject();
8471
8472 // If we're making a new object with a class prototype (that is, an object
8473 // that derives its class from its prototype instead of being
8474 // PlainObject::class_'d) from self-hosted code, we need a different init
8475 // function.
8476 switch (lir->mir()->mode()) {
8477 case MNewObject::ObjectLiteral: {
8478 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"
, 8478); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateObject"
")"); do { *((volatile int*)__null) = 8478; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8479 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
8480 pushArg(ImmGCPtr(lir->mir()->block()->info().script()));
8481
8482 using Fn = JSObject* (*)(JSContext*, HandleScript, const jsbytecode* pc);
8483 callVM<Fn, NewObjectOperation>(lir);
8484 break;
8485 }
8486 case MNewObject::ObjectCreate: {
8487 pushArg(ImmGCPtr(templateObject));
8488
8489 using Fn = PlainObject* (*)(JSContext*, Handle<PlainObject*>);
8490 callVM<Fn, ObjectCreateWithTemplate>(lir);
8491 break;
8492 }
8493 }
8494
8495 masm.storeCallPointerResult(objReg);
8496
8497 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"
, 8497); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)"
")"); do { *((volatile int*)__null) = 8497; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8498 restoreLive(lir);
8499}
8500
8501static bool ShouldInitFixedSlots(MIRGenerator* gen, LNewPlainObject* lir,
8502 const Shape* shape, uint32_t nfixed) {
8503 // Look for StoreFixedSlot instructions following an object allocation
8504 // that write to this object before a GC is triggered or this object is
8505 // passed to a VM call. If all fixed slots will be initialized, the
8506 // allocation code doesn't need to set the slots to |undefined|.
8507
8508 if (nfixed == 0) {
8509 return false;
8510 }
8511
8512#ifdef DEBUG1
8513 // The bailAfter testing function can trigger a bailout between allocating the
8514 // object and initializing the slots.
8515 if (gen->options.ionBailAfterEnabled()) {
8516 return true;
8517 }
8518#endif
8519
8520 // Keep track of the fixed slots that are initialized. initializedSlots is
8521 // a bit mask with a bit for each slot.
8522 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"
, 8522); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nfixed <= NativeObject::MAX_FIXED_SLOTS"
")"); do { *((volatile int*)__null) = 8522; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8523 static_assert(NativeObject::MAX_FIXED_SLOTS <= 32,
8524 "Slot bits must fit in 32 bits");
8525 uint32_t initializedSlots = 0;
8526 uint32_t numInitialized = 0;
8527
8528 MInstruction* allocMir = lir->mir();
8529 MBasicBlock* block = allocMir->block();
8530
8531 // Skip the allocation instruction.
8532 MInstructionIterator iter = block->begin(allocMir);
8533 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"
, 8533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "*iter == allocMir"
")"); do { *((volatile int*)__null) = 8533; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8534 iter++;
8535
8536 // Handle the leading shape guard, if present.
8537 for (; iter != block->end(); iter++) {
8538 if (iter->isConstant()) {
8539 // This instruction won't trigger a GC or read object slots.
8540 continue;
8541 }
8542 if (iter->isGuardShape()) {
8543 auto* guard = iter->toGuardShape();
8544 if (guard->object() != allocMir || guard->shape() != shape) {
8545 return true;
8546 }
8547 allocMir = guard;
8548 iter++;
8549 }
8550 break;
8551 }
8552
8553 for (; iter != block->end(); iter++) {
8554 if (iter->isConstant() || iter->isPostWriteBarrier()) {
8555 // These instructions won't trigger a GC or read object slots.
8556 continue;
8557 }
8558
8559 if (iter->isStoreFixedSlot()) {
8560 MStoreFixedSlot* store = iter->toStoreFixedSlot();
8561 if (store->object() != allocMir) {
8562 return true;
8563 }
8564
8565 // We may not initialize this object slot on allocation, so the
8566 // pre-barrier could read uninitialized memory. Simply disable
8567 // the barrier for this store: the object was just initialized
8568 // so the barrier is not necessary.
8569 store->setNeedsBarrier(false);
8570
8571 uint32_t slot = store->slot();
8572 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"
, 8572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot < nfixed"
")"); do { *((volatile int*)__null) = 8572; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8573 if ((initializedSlots & (1 << slot)) == 0) {
8574 numInitialized++;
8575 initializedSlots |= (1 << slot);
8576
8577 if (numInitialized == nfixed) {
8578 // All fixed slots will be initialized.
8579 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"
, 8579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::CountPopulation32(initializedSlots) == nfixed"
")"); do { *((volatile int*)__null) = 8579; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8580 return false;
8581 }
8582 }
8583 continue;
8584 }
8585
8586 // Unhandled instruction, assume it bails or reads object slots.
8587 return true;
8588 }
8589
8590 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"
, 8590); AnnotateMozCrashReason("MOZ_CRASH(" "Shouldn't get here"
")"); do { *((volatile int*)__null) = 8590; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
8591}
8592
8593void CodeGenerator::visitNewObject(LNewObject* lir) {
8594 Register objReg = ToRegister(lir->output());
8595 Register tempReg = ToRegister(lir->temp());
8596
8597 if (lir->mir()->isVMCall()) {
8598 visitNewObjectVMCall(lir);
8599 return;
8600 }
8601
8602 OutOfLineNewObject* ool = new (alloc()) OutOfLineNewObject(lir);
8603 addOutOfLineCode(ool, lir->mir());
8604
8605 TemplateObject templateObject(lir->mir()->templateObject());
8606
8607 masm.createGCObject(objReg, tempReg, templateObject,
8608 lir->mir()->initialHeap(), ool->entry());
8609
8610 masm.bind(ool->rejoin());
8611}
8612
8613void CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject* ool) {
8614 visitNewObjectVMCall(ool->lir());
8615 masm.jump(ool->rejoin());
8616}
8617
8618void CodeGenerator::visitNewPlainObject(LNewPlainObject* lir) {
8619 Register objReg = ToRegister(lir->output());
8620 Register temp0Reg = ToRegister(lir->temp0());
8621 Register temp1Reg = ToRegister(lir->temp1());
8622 Register shapeReg = ToRegister(lir->temp2());
8623
8624 auto* mir = lir->mir();
8625 const Shape* shape = mir->shape();
8626 gc::Heap initialHeap = mir->initialHeap();
8627 gc::AllocKind allocKind = mir->allocKind();
8628
8629 using Fn =
8630 JSObject* (*)(JSContext*, Handle<SharedShape*>, gc::AllocKind, gc::Heap);
8631 OutOfLineCode* ool = oolCallVM<Fn, NewPlainObjectOptimizedFallback>(
8632 lir,
8633 ArgList(ImmGCPtr(shape), Imm32(int32_t(allocKind)),
8634 Imm32(int32_t(initialHeap))),
8635 StoreRegisterTo(objReg));
8636
8637 bool initContents =
8638 ShouldInitFixedSlots(gen, lir, shape, mir->numFixedSlots());
8639
8640 masm.movePtr(ImmGCPtr(shape), shapeReg);
8641 masm.createPlainGCObject(
8642 objReg, shapeReg, temp0Reg, temp1Reg, mir->numFixedSlots(),
8643 mir->numDynamicSlots(), allocKind, initialHeap, ool->entry(),
8644 AllocSiteInput(gc::CatchAllAllocSite::Optimized), initContents);
8645
8646#ifdef DEBUG1
8647 // ShouldInitFixedSlots expects that the leading GuardShape will never fail,
8648 // so ensure the newly created object has the correct shape. Should the guard
8649 // ever fail, we may end up with uninitialized fixed slots, which can confuse
8650 // the GC.
8651 Label ok;
8652 masm.branchTestObjShape(Assembler::Equal, objReg, shape, temp0Reg, objReg,
8653 &ok);
8654 masm.assumeUnreachable("Newly created object has the correct shape");
8655 masm.bind(&ok);
8656#endif
8657
8658 masm.bind(ool->rejoin());
8659}
8660
8661void CodeGenerator::visitNewArrayObject(LNewArrayObject* lir) {
8662 Register objReg = ToRegister(lir->output());
8663 Register temp0Reg = ToRegister(lir->temp0());
8664 Register shapeReg = ToRegister(lir->temp1());
8665
8666 auto* mir = lir->mir();
8667 uint32_t arrayLength = mir->length();
8668
8669 gc::AllocKind allocKind = GuessArrayGCKind(arrayLength);
8670 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"
, 8670); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)"
")"); do { *((volatile int*)__null) = 8670; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8671 allocKind = ForegroundToBackgroundAllocKind(allocKind);
8672
8673 uint32_t slotCount = GetGCKindSlots(allocKind);
8674 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"
, 8674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotCount >= ObjectElements::VALUES_PER_HEADER"
")"); do { *((volatile int*)__null) = 8674; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8675 uint32_t arrayCapacity = slotCount - ObjectElements::VALUES_PER_HEADER;
8676
8677 const Shape* shape = mir->shape();
8678
8679 NewObjectKind objectKind =
8680 mir->initialHeap() == gc::Heap::Tenured ? TenuredObject : GenericObject;
8681
8682 using Fn =
8683 ArrayObject* (*)(JSContext*, uint32_t, gc::AllocKind, NewObjectKind);
8684 OutOfLineCode* ool = oolCallVM<Fn, NewArrayObjectOptimizedFallback>(
8685 lir,
8686 ArgList(Imm32(arrayLength), Imm32(int32_t(allocKind)), Imm32(objectKind)),
8687 StoreRegisterTo(objReg));
8688
8689 masm.movePtr(ImmPtr(shape), shapeReg);
8690 masm.createArrayWithFixedElements(
8691 objReg, shapeReg, temp0Reg, InvalidReg, arrayLength, arrayCapacity, 0, 0,
8692 allocKind, mir->initialHeap(), ool->entry(),
8693 AllocSiteInput(gc::CatchAllAllocSite::Optimized));
8694 masm.bind(ool->rejoin());
8695}
8696
8697void CodeGenerator::visitNewNamedLambdaObject(LNewNamedLambdaObject* lir) {
8698 Register objReg = ToRegister(lir->output());
8699 Register tempReg = ToRegister(lir->temp0());
8700 const CompileInfo& info = lir->mir()->block()->info();
8701
8702 using Fn = js::NamedLambdaObject* (*)(JSContext*, HandleFunction);
8703 OutOfLineCode* ool = oolCallVM<Fn, NamedLambdaObject::createWithoutEnclosing>(
8704 lir, ArgList(info.funMaybeLazy()), StoreRegisterTo(objReg));
8705
8706 TemplateObject templateObject(lir->mir()->templateObj());
8707
8708 masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default,
8709 ool->entry());
8710
8711 masm.bind(ool->rejoin());
8712}
8713
8714void CodeGenerator::visitNewCallObject(LNewCallObject* lir) {
8715 Register objReg = ToRegister(lir->output());
8716 Register tempReg = ToRegister(lir->temp0());
8717
8718 CallObject* templateObj = lir->mir()->templateObject();
8719
8720 using Fn = CallObject* (*)(JSContext*, Handle<SharedShape*>);
8721 OutOfLineCode* ool = oolCallVM<Fn, CallObject::createWithShape>(
8722 lir, ArgList(ImmGCPtr(templateObj->sharedShape())),
8723 StoreRegisterTo(objReg));
8724
8725 // Inline call object creation, using the OOL path only for tricky cases.
8726 TemplateObject templateObject(templateObj);
8727 masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default,
8728 ool->entry());
8729
8730 masm.bind(ool->rejoin());
8731}
8732
8733void CodeGenerator::visitNewMapObject(LNewMapObject* lir) {
8734 Register output = ToRegister(lir->output());
8735 Register temp = ToRegister(lir->temp0());
8736
8737 // Note: pass nullptr for |proto| to use |Map.prototype|.
8738 using Fn = MapObject* (*)(JSContext*, HandleObject);
8739 auto* ool = oolCallVM<Fn, MapObject::create>(lir, ArgList(ImmPtr(nullptr)),
8740 StoreRegisterTo(output));
8741
8742 TemplateObject templateObject(lir->mir()->templateObject());
8743 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
8744 ool->entry());
8745 masm.bind(ool->rejoin());
8746}
8747
8748void CodeGenerator::visitNewSetObject(LNewSetObject* lir) {
8749 Register output = ToRegister(lir->output());
8750 Register temp = ToRegister(lir->temp0());
8751
8752 // Note: pass nullptr for |proto| to use |Set.prototype|.
8753 using Fn = SetObject* (*)(JSContext*, HandleObject);
8754 auto* ool = oolCallVM<Fn, SetObject::create>(lir, ArgList(ImmPtr(nullptr)),
8755 StoreRegisterTo(output));
8756
8757 TemplateObject templateObject(lir->mir()->templateObject());
8758 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
8759 ool->entry());
8760 masm.bind(ool->rejoin());
8761}
8762
8763void CodeGenerator::visitNewMapObjectFromIterable(
8764 LNewMapObjectFromIterable* lir) {
8765 ValueOperand iterable =
8766 ToValue(lir, LNewMapObjectFromIterable::IterableIndex);
8767 Register output = ToRegister(lir->output());
8768 Register temp1 = ToRegister(lir->temp0());
8769 Register temp2 = ToRegister(lir->temp1());
8770
8771 // Allocate a new MapObject. If this fails we pass nullptr for
8772 // allocatedFromJit.
8773 Label failedAlloc, vmCall, done;
8774 TemplateObject templateObject(lir->mir()->templateObject());
8775 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
8776 &failedAlloc);
8777
8778 // We're done if |iterable| is null or undefined.
8779 masm.branchIfNotNullOrUndefined(iterable, &vmCall);
8780 masm.movePtr(temp1, output);
8781 masm.jump(&done);
8782
8783 masm.bind(&failedAlloc);
8784 masm.movePtr(ImmPtr(nullptr), temp1);
8785
8786 masm.bind(&vmCall);
8787
8788 pushArg(temp1); // allocatedFromJit
8789 pushArg(iterable);
8790 pushArg(ImmPtr(nullptr)); // proto
8791
8792 using Fn = MapObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>,
8793 Handle<MapObject*>);
8794 callVM<Fn, MapObject::createFromIterable>(lir);
8795
8796 masm.bind(&done);
8797}
8798
8799void CodeGenerator::visitNewSetObjectFromIterable(
8800 LNewSetObjectFromIterable* lir) {
8801 ValueOperand iterable =
8802 ToValue(lir, LNewSetObjectFromIterable::IterableIndex);
8803 Register output = ToRegister(lir->output());
8804 Register temp1 = ToRegister(lir->temp0());
8805 Register temp2 = ToRegister(lir->temp1());
8806
8807 // Allocate a new SetObject. If this fails we pass nullptr for
8808 // allocatedFromJit.
8809 Label failedAlloc, vmCall, done;
8810 TemplateObject templateObject(lir->mir()->templateObject());
8811 masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default,
8812 &failedAlloc);
8813
8814 // We're done if |iterable| is null or undefined.
8815 masm.branchIfNotNullOrUndefined(iterable, &vmCall);
8816 masm.movePtr(temp1, output);
8817 masm.jump(&done);
8818
8819 masm.bind(&failedAlloc);
8820 masm.movePtr(ImmPtr(nullptr), temp1);
8821
8822 masm.bind(&vmCall);
8823
8824 pushArg(temp1); // allocatedFromJit
8825 pushArg(iterable);
8826 pushArg(ImmPtr(nullptr)); // proto
8827
8828 using Fn = SetObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>,
8829 Handle<SetObject*>);
8830 callVM<Fn, SetObject::createFromIterable>(lir);
8831
8832 masm.bind(&done);
8833}
8834
8835void CodeGenerator::visitNewStringObject(LNewStringObject* lir) {
8836 Register input = ToRegister(lir->input());
8837 Register output = ToRegister(lir->output());
8838 Register temp = ToRegister(lir->temp0());
8839
8840 StringObject* templateObj = lir->mir()->templateObj();
8841
8842 using Fn = JSObject* (*)(JSContext*, HandleString);
8843 OutOfLineCode* ool = oolCallVM<Fn, NewStringObject>(lir, ArgList(input),
8844 StoreRegisterTo(output));
8845
8846 TemplateObject templateObject(templateObj);
8847 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
8848 ool->entry());
8849
8850 masm.loadStringLength(input, temp);
8851
8852 masm.storeValue(JSVAL_TYPE_STRING, input,
8853 Address(output, StringObject::offsetOfPrimitiveValue()));
8854 masm.storeValue(JSVAL_TYPE_INT32, temp,
8855 Address(output, StringObject::offsetOfLength()));
8856
8857 masm.bind(ool->rejoin());
8858}
8859
8860void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) {
8861 Register obj = ToRegister(lir->object());
8862 Register value = ToRegister(lir->value());
8863
8864 pushArg(value);
8865 pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex));
8866 pushArg(obj);
8867 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
8868
8869 using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue,
8870 HandleObject);
8871 callVM<Fn, InitElemGetterSetterOperation>(lir);
8872}
8873
8874void CodeGenerator::visitMutateProto(LMutateProto* lir) {
8875 Register objReg = ToRegister(lir->object());
8876
8877 pushArg(ToValue(lir, LMutateProto::ValueIndex));
8878 pushArg(objReg);
8879
8880 using Fn =
8881 bool (*)(JSContext* cx, Handle<PlainObject*> obj, HandleValue value);
8882 callVM<Fn, MutatePrototype>(lir);
8883}
8884
8885void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) {
8886 Register obj = ToRegister(lir->object());
8887 Register value = ToRegister(lir->value());
8888
8889 pushArg(value);
8890 pushArg(ImmGCPtr(lir->mir()->name()));
8891 pushArg(obj);
8892 pushArg(ImmPtr(lir->mir()->resumePoint()->pc()));
8893
8894 using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject,
8895 Handle<PropertyName*>, HandleObject);
8896 callVM<Fn, InitPropGetterSetterOperation>(lir);
8897}
8898
8899void CodeGenerator::visitCreateThis(LCreateThis* lir) {
8900 const LAllocation* callee = lir->callee();
8901 const LAllocation* newTarget = lir->newTarget();
8902
8903 if (newTarget->isConstant()) {
8904 pushArg(ImmGCPtr(&newTarget->toConstant()->toObject()));
8905 } else {
8906 pushArg(ToRegister(newTarget));
8907 }
8908
8909 if (callee->isConstant()) {
8910 pushArg(ImmGCPtr(&callee->toConstant()->toObject()));
8911 } else {
8912 pushArg(ToRegister(callee));
8913 }
8914
8915 using Fn = bool (*)(JSContext* cx, HandleObject callee,
8916 HandleObject newTarget, MutableHandleValue rval);
8917 callVM<Fn, jit::CreateThisFromIon>(lir);
8918}
8919
8920void CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) {
8921 // This should be getting constructed in the first block only, and not any OSR
8922 // entry blocks.
8923 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"
, 8923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->block()->id() == 0"
")"); do { *((volatile int*)__null) = 8923; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8924
8925 Register callObj = ToRegister(lir->callObject());
8926 Register temp0 = ToRegister(lir->temp0());
8927 Label done;
8928
8929 if (ArgumentsObject* templateObj = lir->mir()->templateObject()) {
8930 Register objTemp = ToRegister(lir->temp1());
8931 Register cxTemp = ToRegister(lir->temp2());
8932
8933 masm.Push(callObj);
8934
8935 // Try to allocate an arguments object. This will leave the reserved
8936 // slots uninitialized, so it's important we don't GC until we
8937 // initialize these slots in ArgumentsObject::finishForIonPure.
8938 Label failure;
8939 TemplateObject templateObject(templateObj);
8940 masm.createGCObject(objTemp, temp0, templateObject, gc::Heap::Default,
8941 &failure,
8942 /* initContents = */ false);
8943
8944 masm.moveStackPtrTo(temp0);
8945 masm.addPtr(Imm32(masm.framePushed()), temp0);
8946
8947 using Fn = ArgumentsObject* (*)(JSContext* cx, jit::JitFrameLayout* frame,
8948 JSObject* scopeChain, ArgumentsObject* obj);
8949 masm.setupAlignedABICall();
8950 masm.loadJSContext(cxTemp);
8951 masm.passABIArg(cxTemp);
8952 masm.passABIArg(temp0);
8953 masm.passABIArg(callObj);
8954 masm.passABIArg(objTemp);
8955
8956 masm.callWithABI<Fn, ArgumentsObject::finishForIonPure>();
8957 masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
8958
8959 // Discard saved callObj on the stack.
8960 masm.addToStackPtr(Imm32(sizeof(uintptr_t)));
8961 masm.jump(&done);
8962
8963 masm.bind(&failure);
8964 masm.Pop(callObj);
8965 }
8966
8967 masm.moveStackPtrTo(temp0);
8968 masm.addPtr(Imm32(frameSize()), temp0);
8969
8970 pushArg(callObj);
8971 pushArg(temp0);
8972
8973 using Fn = ArgumentsObject* (*)(JSContext*, JitFrameLayout*, HandleObject);
8974 callVM<Fn, ArgumentsObject::createForIon>(lir);
8975
8976 masm.bind(&done);
8977}
8978
8979void CodeGenerator::visitCreateInlinedArgumentsObject(
8980 LCreateInlinedArgumentsObject* lir) {
8981 Register callObj = ToRegister(lir->getCallObject());
8982 Register callee = ToRegister(lir->getCallee());
8983 Register argsAddress = ToRegister(lir->temp1());
8984 Register argsObj = ToRegister(lir->temp2());
8985
8986 // TODO: Do we have to worry about alignment here?
8987
8988 // Create a contiguous array of values for ArgumentsObject::create
8989 // by pushing the arguments onto the stack in reverse order.
8990 uint32_t argc = lir->mir()->numActuals();
8991 for (uint32_t i = 0; i < argc; i++) {
8992 uint32_t argNum = argc - i - 1;
8993 uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(argNum);
8994 ConstantOrRegister arg =
8995 toConstantOrRegister(lir, index, lir->mir()->getArg(argNum)->type());
8996 masm.Push(arg);
8997 }
8998 masm.moveStackPtrTo(argsAddress);
8999
9000 Label done;
9001 if (ArgumentsObject* templateObj = lir->mir()->templateObject()) {
9002 LiveRegisterSet liveRegs;
9003 liveRegs.add(callObj);
9004 liveRegs.add(callee);
9005
9006 masm.PushRegsInMask(liveRegs);
9007
9008 // We are free to clobber all registers, as LCreateInlinedArgumentsObject is
9009 // a call instruction.
9010 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
9011 allRegs.take(callObj);
9012 allRegs.take(callee);
9013 allRegs.take(argsObj);
9014 allRegs.take(argsAddress);
9015
9016 Register temp3 = allRegs.takeAny();
9017 Register temp4 = allRegs.takeAny();
9018
9019 // Try to allocate an arguments object. This will leave the reserved slots
9020 // uninitialized, so it's important we don't GC until we initialize these
9021 // slots in ArgumentsObject::finishForIonPure.
9022 Label failure;
9023 TemplateObject templateObject(templateObj);
9024 masm.createGCObject(argsObj, temp3, templateObject, gc::Heap::Default,
9025 &failure,
9026 /* initContents = */ false);
9027
9028 Register numActuals = temp3;
9029 masm.move32(Imm32(argc), numActuals);
9030
9031 using Fn = ArgumentsObject* (*)(JSContext*, JSObject*, JSFunction*, Value*,
9032 uint32_t, ArgumentsObject*);
9033 masm.setupAlignedABICall();
9034 masm.loadJSContext(temp4);
9035 masm.passABIArg(temp4);
9036 masm.passABIArg(callObj);
9037 masm.passABIArg(callee);
9038 masm.passABIArg(argsAddress);
9039 masm.passABIArg(numActuals);
9040 masm.passABIArg(argsObj);
9041
9042 masm.callWithABI<Fn, ArgumentsObject::finishInlineForIonPure>();
9043 masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure);
9044
9045 // Discard saved callObj, callee, and values array on the stack.
9046 masm.addToStackPtr(
9047 Imm32(MacroAssembler::PushRegsInMaskSizeInBytes(liveRegs) +
9048 argc * sizeof(Value)));
9049 masm.jump(&done);
9050
9051 masm.bind(&failure);
9052 masm.PopRegsInMask(liveRegs);
9053
9054 // Reload argsAddress because it may have been overridden.
9055 masm.moveStackPtrTo(argsAddress);
9056 }
9057
9058 pushArg(Imm32(argc));
9059 pushArg(callObj);
9060 pushArg(callee);
9061 pushArg(argsAddress);
9062
9063 using Fn = ArgumentsObject* (*)(JSContext*, Value*, HandleFunction,
9064 HandleObject, uint32_t);
9065 callVM<Fn, ArgumentsObject::createForInlinedIon>(lir);
9066
9067 // Discard the array of values.
9068 masm.freeStack(argc * sizeof(Value));
9069
9070 masm.bind(&done);
9071}
9072
9073template <class GetInlinedArgument>
9074void CodeGenerator::emitGetInlinedArgument(GetInlinedArgument* lir,
9075 Register index,
9076 ValueOperand output) {
9077 uint32_t numActuals = lir->mir()->numActuals();
9078 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"
, 9078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numActuals <= ArgumentsObject::MaxInlinedArgs"
")"); do { *((volatile int*)__null) = 9078; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9079
9080 // The index has already been bounds-checked, so the code we
9081 // generate here should be unreachable. We can end up in this
9082 // situation in self-hosted code using GetArgument(), or in a
9083 // monomorphically inlined function if we've inlined some CacheIR
9084 // that was created for a different caller.
9085 if (numActuals == 0) {
9086 masm.assumeUnreachable("LGetInlinedArgument: invalid index");
9087 return;
9088 }
9089
9090 // Check the first n-1 possible indices.
9091 Label done;
9092 for (uint32_t i = 0; i < numActuals - 1; i++) {
9093 Label skip;
9094 ConstantOrRegister arg = toConstantOrRegister(
9095 lir, GetInlinedArgument::ArgIndex(i), lir->mir()->getArg(i)->type());
9096 masm.branch32(Assembler::NotEqual, index, Imm32(i), &skip);
9097 masm.moveValue(arg, output);
9098
9099 masm.jump(&done);
9100 masm.bind(&skip);
9101 }
9102
9103#ifdef DEBUG1
9104 Label skip;
9105 masm.branch32(Assembler::Equal, index, Imm32(numActuals - 1), &skip);
9106 masm.assumeUnreachable("LGetInlinedArgument: invalid index");
9107 masm.bind(&skip);
9108#endif
9109
9110 // The index has already been bounds-checked, so load the last argument.
9111 uint32_t lastIdx = numActuals - 1;
9112 ConstantOrRegister arg =
9113 toConstantOrRegister(lir, GetInlinedArgument::ArgIndex(lastIdx),
9114 lir->mir()->getArg(lastIdx)->type());
9115 masm.moveValue(arg, output);
9116 masm.bind(&done);
9117}
9118
9119void CodeGenerator::visitGetInlinedArgument(LGetInlinedArgument* lir) {
9120 Register index = ToRegister(lir->getIndex());
9121 ValueOperand output = ToOutValue(lir);
9122
9123 emitGetInlinedArgument(lir, index, output);
9124}
9125
9126void CodeGenerator::visitGetInlinedArgumentHole(LGetInlinedArgumentHole* lir) {
9127 Register index = ToRegister(lir->getIndex());
9128 ValueOperand output = ToOutValue(lir);
9129
9130 uint32_t numActuals = lir->mir()->numActuals();
9131
9132 if (numActuals == 0) {
9133 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
9134 masm.moveValue(UndefinedValue(), output);
9135 return;
9136 }
9137
9138 Label outOfBounds, done;
9139 masm.branch32(Assembler::AboveOrEqual, index, Imm32(numActuals),
9140 &outOfBounds);
9141
9142 emitGetInlinedArgument(lir, index, output);
9143 masm.jump(&done);
9144
9145 masm.bind(&outOfBounds);
9146 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
9147 masm.moveValue(UndefinedValue(), output);
9148
9149 masm.bind(&done);
9150}
9151
9152void CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir) {
9153 Register temp = ToRegister(lir->temp0());
9154 Register argsObj = ToRegister(lir->argsObject());
9155 ValueOperand out = ToOutValue(lir);
9156
9157 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
9158 temp);
9159 Address argAddr(temp, ArgumentsData::offsetOfArgs() +
9160 lir->mir()->argno() * sizeof(Value));
9161 masm.loadValue(argAddr, out);
9162#ifdef DEBUG1
9163 Label success;
9164 masm.branchTestMagic(Assembler::NotEqual, out, &success);
9165 masm.assumeUnreachable(
9166 "Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
9167 masm.bind(&success);
9168#endif
9169}
9170
9171void CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir) {
9172 Register temp = ToRegister(lir->getTemp(0));
9173 Register argsObj = ToRegister(lir->argsObject());
9174 ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex);
9175
9176 masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()),
9177 temp);
9178 Address argAddr(temp, ArgumentsData::offsetOfArgs() +
9179 lir->mir()->argno() * sizeof(Value));
9180 emitPreBarrier(argAddr);
9181#ifdef DEBUG1
9182 Label success;
9183 masm.branchTestMagic(Assembler::NotEqual, argAddr, &success);
9184 masm.assumeUnreachable(
9185 "Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC.");
9186 masm.bind(&success);
9187#endif
9188 masm.storeValue(value, argAddr);
9189}
9190
9191void CodeGenerator::visitLoadArgumentsObjectArg(LLoadArgumentsObjectArg* lir) {
9192 Register temp = ToRegister(lir->temp0());
9193 Register argsObj = ToRegister(lir->argsObject());
9194 Register index = ToRegister(lir->index());
9195 ValueOperand out = ToOutValue(lir);
9196
9197 Label bail;
9198 masm.loadArgumentsObjectElement(argsObj, index, out, temp, &bail);
9199 bailoutFrom(&bail, lir->snapshot());
9200}
9201
9202void CodeGenerator::visitLoadArgumentsObjectArgHole(
9203 LLoadArgumentsObjectArgHole* lir) {
9204 Register temp = ToRegister(lir->temp0());
9205 Register argsObj = ToRegister(lir->argsObject());
9206 Register index = ToRegister(lir->index());
9207 ValueOperand out = ToOutValue(lir);
9208
9209 Label bail;
9210 masm.loadArgumentsObjectElementHole(argsObj, index, out, temp, &bail);
9211 bailoutFrom(&bail, lir->snapshot());
9212}
9213
9214void CodeGenerator::visitInArgumentsObjectArg(LInArgumentsObjectArg* lir) {
9215 Register temp = ToRegister(lir->temp0());
9216 Register argsObj = ToRegister(lir->argsObject());
9217 Register index = ToRegister(lir->index());
9218 Register out = ToRegister(lir->output());
9219
9220 Label bail;
9221 masm.loadArgumentsObjectElementExists(argsObj, index, out, temp, &bail);
9222 bailoutFrom(&bail, lir->snapshot());
9223}
9224
9225void CodeGenerator::visitArgumentsObjectLength(LArgumentsObjectLength* lir) {
9226 Register argsObj = ToRegister(lir->argsObject());
9227 Register out = ToRegister(lir->output());
9228
9229 Label bail;
9230 masm.loadArgumentsObjectLength(argsObj, out, &bail);
9231 bailoutFrom(&bail, lir->snapshot());
9232}
9233
9234void CodeGenerator::visitArrayFromArgumentsObject(
9235 LArrayFromArgumentsObject* lir) {
9236 pushArg(ToRegister(lir->argsObject()));
9237
9238 using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>);
9239 callVM<Fn, js::ArrayFromArgumentsObject>(lir);
9240}
9241
9242void CodeGenerator::visitGuardArgumentsObjectFlags(
9243 LGuardArgumentsObjectFlags* lir) {
9244 Register argsObj = ToRegister(lir->argsObject());
9245 Register temp = ToRegister(lir->temp0());
9246
9247 Label bail;
9248 masm.branchTestArgumentsObjectFlags(argsObj, temp, lir->mir()->flags(),
9249 Assembler::NonZero, &bail);
9250 bailoutFrom(&bail, lir->snapshot());
9251}
9252
9253void CodeGenerator::visitBoundFunctionNumArgs(LBoundFunctionNumArgs* lir) {
9254 Register obj = ToRegister(lir->object());
9255 Register output = ToRegister(lir->output());
9256
9257 masm.unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()),
9258 output);
9259 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), output);
9260}
9261
9262void CodeGenerator::visitGuardBoundFunctionIsConstructor(
9263 LGuardBoundFunctionIsConstructor* lir) {
9264 Register obj = ToRegister(lir->object());
9265
9266 Label bail;
9267 Address flagsSlot(obj, BoundFunctionObject::offsetOfFlagsSlot());
9268 masm.branchTest32(Assembler::Zero, flagsSlot,
9269 Imm32(BoundFunctionObject::IsConstructorFlag), &bail);
9270 bailoutFrom(&bail, lir->snapshot());
9271}
9272
9273void CodeGenerator::visitReturnFromCtor(LReturnFromCtor* lir) {
9274 ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex);
9275 Register obj = ToRegister(lir->object());
9276 Register output = ToRegister(lir->output());
9277
9278 Label valueIsObject, end;
9279
9280 masm.branchTestObject(Assembler::Equal, value, &valueIsObject);
9281
9282 // Value is not an object. Return that other object.
9283 masm.movePtr(obj, output);
9284 masm.jump(&end);
9285
9286 // Value is an object. Return unbox(Value).
9287 masm.bind(&valueIsObject);
9288 Register payload = masm.extractObject(value, output);
9289 if (payload != output) {
9290 masm.movePtr(payload, output);
9291 }
9292
9293 masm.bind(&end);
9294}
9295
9296class OutOfLineBoxNonStrictThis : public OutOfLineCodeBase<CodeGenerator> {
9297 LBoxNonStrictThis* ins_;
9298
9299 public:
9300 explicit OutOfLineBoxNonStrictThis(LBoxNonStrictThis* ins) : ins_(ins) {}
9301 void accept(CodeGenerator* codegen) override {
9302 codegen->visitOutOfLineBoxNonStrictThis(this);
9303 }
9304 LBoxNonStrictThis* ins() const { return ins_; }
9305};
9306
9307void CodeGenerator::visitBoxNonStrictThis(LBoxNonStrictThis* lir) {
9308 ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex);
9309 Register output = ToRegister(lir->output());
9310
9311 auto* ool = new (alloc()) OutOfLineBoxNonStrictThis(lir);
9312 addOutOfLineCode(ool, lir->mir());
9313
9314 masm.fallibleUnboxObject(value, output, ool->entry());
9315 masm.bind(ool->rejoin());
9316}
9317
9318void CodeGenerator::visitOutOfLineBoxNonStrictThis(
9319 OutOfLineBoxNonStrictThis* ool) {
9320 LBoxNonStrictThis* lir = ool->ins();
9321
9322 ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex);
9323 Register output = ToRegister(lir->output());
9324
9325 Label notNullOrUndefined;
9326 {
9327 Label isNullOrUndefined;
9328 ScratchTagScope tag(masm, value);
9329 masm.splitTagForTest(value, tag);
9330 masm.branchTestUndefined(Assembler::Equal, tag, &isNullOrUndefined);
9331 masm.branchTestNull(Assembler::NotEqual, tag, &notNullOrUndefined);
9332 masm.bind(&isNullOrUndefined);
9333 masm.movePtr(ImmGCPtr(lir->mir()->globalThis()), output);
9334 masm.jump(ool->rejoin());
9335 }
9336
9337 masm.bind(&notNullOrUndefined);
9338
9339 saveLive(lir);
9340
9341 pushArg(value);
9342 using Fn = JSObject* (*)(JSContext*, HandleValue);
9343 callVM<Fn, BoxNonStrictThis>(lir);
9344
9345 StoreRegisterTo(output).generate(this);
9346 restoreLiveIgnore(lir, StoreRegisterTo(output).clobbered());
9347
9348 masm.jump(ool->rejoin());
9349}
9350
9351void CodeGenerator::visitImplicitThis(LImplicitThis* lir) {
9352 Register env = ToRegister(lir->env());
9353 ValueOperand output = ToOutValue(lir);
9354
9355 using Fn = void (*)(JSContext*, HandleObject, MutableHandleValue);
9356 auto* ool = oolCallVM<Fn, ImplicitThisOperation>(lir, ArgList(env),
9357 StoreValueTo(output));
9358
9359 masm.computeImplicitThis(env, output, ool->entry());
9360 masm.bind(ool->rejoin());
9361}
9362
9363void CodeGenerator::visitArrayLength(LArrayLength* lir) {
9364 Register elements = ToRegister(lir->elements());
9365 Register output = ToRegister(lir->output());
9366
9367 Address length(elements, ObjectElements::offsetOfLength());
9368 masm.load32(length, output);
9369
9370 // Bail out if the length doesn't fit in int32.
9371 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
9372}
9373
9374static void SetLengthFromIndex(MacroAssembler& masm, const LAllocation* index,
9375 const Address& length) {
9376 if (index->isConstant()) {
9377 masm.store32(Imm32(ToInt32(index) + 1), length);
9378 } else {
9379 Register newLength = ToRegister(index);
9380 masm.add32(Imm32(1), newLength);
9381 masm.store32(newLength, length);
9382 masm.sub32(Imm32(1), newLength);
9383 }
9384}
9385
9386void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) {
9387 Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength());
9388 SetLengthFromIndex(masm, lir->index(), length);
9389}
9390
9391void CodeGenerator::visitFunctionLength(LFunctionLength* lir) {
9392 Register function = ToRegister(lir->function());
9393 Register output = ToRegister(lir->output());
9394
9395 Label bail;
9396
9397 // Get the JSFunction flags.
9398 masm.load32(Address(function, JSFunction::offsetOfFlagsAndArgCount()),
9399 output);
9400
9401 // Functions with a SelfHostedLazyScript must be compiled with the slow-path
9402 // before the function length is known. If the length was previously resolved,
9403 // the length property may be shadowed.
9404 masm.branchTest32(
9405 Assembler::NonZero, output,
9406 Imm32(FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH),
9407 &bail);
9408
9409 masm.loadFunctionLength(function, output, output, &bail);
9410
9411 bailoutFrom(&bail, lir->snapshot());
9412}
9413
9414void CodeGenerator::visitFunctionName(LFunctionName* lir) {
9415 Register function = ToRegister(lir->function());
9416 Register output = ToRegister(lir->output());
9417
9418 Label bail;
9419
9420 const JSAtomState& names = gen->runtime->names();
9421 masm.loadFunctionName(function, output, ImmGCPtr(names.empty_), &bail);
9422
9423 bailoutFrom(&bail, lir->snapshot());
9424}
9425
9426template <class TableObject>
9427static void TableIteratorLoadEntry(MacroAssembler&, Register, Register,
9428 Register);
9429
9430template <>
9431void TableIteratorLoadEntry<MapObject>(MacroAssembler& masm, Register iter,
9432 Register i, Register front) {
9433 masm.unboxObject(Address(iter, MapIteratorObject::offsetOfTarget()), front);
9434 masm.loadPrivate(Address(front, MapObject::offsetOfData()), front);
9435
9436 static_assert(MapObject::Table::offsetOfImplDataElement() == 0,
9437 "offsetof(Data, element) is 0");
9438 static_assert(MapObject::Table::sizeofImplData() == 24, "sizeof(Data) is 24");
9439 masm.mulBy3(i, i);
9440 masm.lshiftPtr(Imm32(3), i);
9441 masm.addPtr(i, front);
9442}
9443
9444template <>
9445void TableIteratorLoadEntry<SetObject>(MacroAssembler& masm, Register iter,
9446 Register i, Register front) {
9447 masm.unboxObject(Address(iter, SetIteratorObject::offsetOfTarget()), front);
9448 masm.loadPrivate(Address(front, SetObject::offsetOfData()), front);
9449
9450 static_assert(SetObject::Table::offsetOfImplDataElement() == 0,
9451 "offsetof(Data, element) is 0");
9452 static_assert(SetObject::Table::sizeofImplData() == 16, "sizeof(Data) is 16");
9453 masm.lshiftPtr(Imm32(4), i);
9454 masm.addPtr(i, front);
9455}
9456
9457template <class TableObject>
9458static void TableIteratorAdvance(MacroAssembler& masm, Register iter,
9459 Register front, Register dataLength,
9460 Register temp) {
9461 Register i = temp;
9462
9463 // Note: |count| and |index| are stored as PrivateUint32Value. We use add32
9464 // and store32 to change the payload.
9465 masm.add32(Imm32(1), Address(iter, TableIteratorObject::offsetOfCount()));
9466
9467 masm.unboxInt32(Address(iter, TableIteratorObject::offsetOfIndex()), i);
9468
9469 Label done, seek;
9470 masm.bind(&seek);
9471 masm.add32(Imm32(1), i);
9472 masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done);
9473
9474 // We can add sizeof(Data) to |front| to select the next element, because
9475 // |front| and |mapOrSetObject.data[i]| point to the same location.
9476 static_assert(TableObject::Table::offsetOfImplDataElement() == 0,
9477 "offsetof(Data, element) is 0");
9478 masm.addPtr(Imm32(TableObject::Table::sizeofImplData()), front);
9479
9480 masm.branchTestMagic(Assembler::Equal,
9481 Address(front, TableObject::Table::offsetOfEntryKey()),
9482 JS_HASH_KEY_EMPTY, &seek);
9483
9484 masm.bind(&done);
9485 masm.store32(i, Address(iter, TableIteratorObject::offsetOfIndex()));
9486}
9487
9488// Corresponds to TableIteratorObject::finish.
9489static void TableIteratorFinish(MacroAssembler& masm, Register iter,
9490 Register temp0, Register temp1) {
9491 Register next = temp0;
9492 Register prevp = temp1;
9493 masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfNext()), next);
9494 masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfPrevPtr()),
9495 prevp);
9496 masm.storePtr(next, Address(prevp, 0));
9497
9498 Label hasNoNext;
9499 masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext);
9500 masm.storePrivateValue(prevp,
9501 Address(next, TableIteratorObject::offsetOfPrevPtr()));
9502 masm.bind(&hasNoNext);
9503
9504 // Mark iterator inactive.
9505 Address targetAddr(iter, TableIteratorObject::offsetOfTarget());
9506 masm.guardedCallPreBarrier(targetAddr, MIRType::Value);
9507 masm.storeValue(UndefinedValue(), targetAddr);
9508}
9509
9510template <>
9511void CodeGenerator::emitLoadIteratorValues<MapObject>(Register result,
9512 Register temp,
9513 Register front) {
9514 size_t elementsOffset = NativeObject::offsetOfFixedElements();
9515
9516 Address keyAddress(front, MapObject::Table::Entry::offsetOfKey());
9517 Address valueAddress(front, MapObject::Table::Entry::offsetOfValue());
9518 Address keyElemAddress(result, elementsOffset);
9519 Address valueElemAddress(result, elementsOffset + sizeof(Value));
9520 masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value);
9521 masm.guardedCallPreBarrier(valueElemAddress, MIRType::Value);
9522 masm.storeValue(keyAddress, keyElemAddress, temp);
9523 masm.storeValue(valueAddress, valueElemAddress, temp);
9524
9525 Label emitBarrier, skipBarrier;
9526 masm.branchValueIsNurseryCell(Assembler::Equal, keyAddress, temp,
9527 &emitBarrier);
9528 masm.branchValueIsNurseryCell(Assembler::NotEqual, valueAddress, temp,
9529 &skipBarrier);
9530 {
9531 masm.bind(&emitBarrier);
9532 saveVolatile(temp);
9533 emitPostWriteBarrier(result);
9534 restoreVolatile(temp);
9535 }
9536 masm.bind(&skipBarrier);
9537}
9538
9539template <>
9540void CodeGenerator::emitLoadIteratorValues<SetObject>(Register result,
9541 Register temp,
9542 Register front) {
9543 size_t elementsOffset = NativeObject::offsetOfFixedElements();
9544
9545 Address keyAddress(front, SetObject::Table::offsetOfEntryKey());
9546 Address keyElemAddress(result, elementsOffset);
9547 masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value);
9548 masm.storeValue(keyAddress, keyElemAddress, temp);
9549
9550 Label skipBarrier;
9551 masm.branchValueIsNurseryCell(Assembler::NotEqual, keyAddress, temp,
9552 &skipBarrier);
9553 {
9554 saveVolatile(temp);
9555 emitPostWriteBarrier(result);
9556 restoreVolatile(temp);
9557 }
9558 masm.bind(&skipBarrier);
9559}
9560
9561template <class IteratorObject, class TableObject>
9562void CodeGenerator::emitGetNextEntryForIterator(LGetNextEntryForIterator* lir) {
9563 Register iter = ToRegister(lir->iter());
9564 Register result = ToRegister(lir->result());
9565 Register temp = ToRegister(lir->temp0());
9566 Register dataLength = ToRegister(lir->temp1());
9567 Register front = ToRegister(lir->temp2());
9568 Register output = ToRegister(lir->output());
9569
9570#ifdef DEBUG1
9571 // Self-hosted code is responsible for ensuring GetNextEntryForIterator is
9572 // only called with the correct iterator class. Assert here all self-
9573 // hosted callers of GetNextEntryForIterator perform this class check.
9574 // No Spectre mitigations are needed because this is DEBUG-only code.
9575 Label success;
9576 masm.branchTestObjClassNoSpectreMitigations(
9577 Assembler::Equal, iter, &IteratorObject::class_, temp, &success);
9578 masm.assumeUnreachable("Iterator object should have the correct class.");
9579 masm.bind(&success);
9580#endif
9581
9582 // If the iterator has no target, it's already done.
9583 // See TableIteratorObject::isActive.
9584 Label iterAlreadyDone, iterDone, done;
9585 masm.branchTestUndefined(Assembler::Equal,
9586 Address(iter, IteratorObject::offsetOfTarget()),
9587 &iterAlreadyDone);
9588
9589 // Load |iter->index| in |temp| and |iter->target->dataLength| in
9590 // |dataLength|. Both values are stored as PrivateUint32Value.
9591 masm.unboxInt32(Address(iter, IteratorObject::offsetOfIndex()), temp);
9592 masm.unboxObject(Address(iter, IteratorObject::offsetOfTarget()), dataLength);
9593 masm.unboxInt32(Address(dataLength, TableObject::offsetOfDataLength()),
9594 dataLength);
9595 masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone);
9596 {
9597 TableIteratorLoadEntry<TableObject>(masm, iter, temp, front);
9598
9599 emitLoadIteratorValues<TableObject>(result, temp, front);
9600
9601 TableIteratorAdvance<TableObject>(masm, iter, front, dataLength, temp);
9602
9603 masm.move32(Imm32(0), output);
9604 masm.jump(&done);
9605 }
9606 {
9607 masm.bind(&iterDone);
9608 TableIteratorFinish(masm, iter, temp, dataLength);
9609
9610 masm.bind(&iterAlreadyDone);
9611 masm.move32(Imm32(1), output);
9612 }
9613 masm.bind(&done);
9614}
9615
9616void CodeGenerator::visitGetNextEntryForIterator(
9617 LGetNextEntryForIterator* lir) {
9618 if (lir->mir()->mode() == MGetNextEntryForIterator::Map) {
9619 emitGetNextEntryForIterator<MapIteratorObject, MapObject>(lir);
9620 } else {
9621 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"
, 9621); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MGetNextEntryForIterator::Set"
")"); do { *((volatile int*)__null) = 9621; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9622 emitGetNextEntryForIterator<SetIteratorObject, SetObject>(lir);
9623 }
9624}
9625
9626// The point of these is to inform Ion of where these values already are; they
9627// don't normally generate (much) code.
9628void CodeGenerator::visitWasmRegisterPairResult(LWasmRegisterPairResult* lir) {}
9629void CodeGenerator::visitWasmStackResult(LWasmStackResult* lir) {}
9630void CodeGenerator::visitWasmStackResult64(LWasmStackResult64* lir) {}
9631
9632void CodeGenerator::visitWasmStackResultArea(LWasmStackResultArea* lir) {
9633 LAllocation* output = lir->getDef(0)->output();
9634 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"
, 9634); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output->isStackArea()"
")"); do { *((volatile int*)__null) = 9634; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9635 bool tempInit = false;
9636 for (auto iter = output->toStackArea()->results(); iter; iter.next()) {
9637 // Zero out ref stack results.
9638 if (iter.isWasmAnyRef()) {
9639 Register temp = ToRegister(lir->temp0());
9640 if (!tempInit) {
9641 masm.xorPtr(temp, temp);
9642 tempInit = true;
9643 }
9644 masm.storePtr(temp, ToAddress(iter.alloc()));
9645 }
9646 }
9647}
9648
9649void CodeGenerator::visitWasmRegisterResult(LWasmRegisterResult* lir) {
9650#ifdef JS_64BIT1
9651 if (MWasmRegisterResult* mir = lir->mir()) {
9652 if (mir->type() == MIRType::Int32) {
9653 masm.widenInt32(ToRegister(lir->output()));
9654 }
9655 }
9656#endif
9657}
9658
9659void CodeGenerator::visitWasmCall(LWasmCall* lir) {
9660 const MWasmCallBase* callBase = lir->callBase();
9661 bool isReturnCall = lir->isReturnCall();
9662
9663 // If this call is in Wasm try code block, initialise a wasm::TryNote for this
9664 // call.
9665 bool inTry = callBase->inTry();
9666 if (inTry) {
9667 size_t tryNoteIndex = callBase->tryNoteIndex();
9668 wasm::TryNoteVector& tryNotes = masm.tryNotes();
9669 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
9670 tryNote.setTryBodyBegin(masm.currentOffset());
9671 }
9672
9673 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"
, 9674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
")"); do { *((volatile int*)__null) = 9674; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9674 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"
, 9674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0"
")"); do { *((volatile int*)__null) = 9674; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9675 static_assert(
9676 WasmStackAlignment >= ABIStackAlignment &&
9677 WasmStackAlignment % ABIStackAlignment == 0,
9678 "The wasm stack alignment should subsume the ABI-required alignment");
9679
9680#ifdef DEBUG1
9681 Label ok;
9682 masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok);
9683 masm.breakpoint();
9684 masm.bind(&ok);
9685#endif
9686
9687 // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the
9688 // instance and pinned regs. The only case where where we don't have to
9689 // reload the instance and pinned regs is when the callee preserves them.
9690 bool reloadRegs = true;
9691 bool switchRealm = true;
9692
9693 const wasm::CallSiteDesc& desc = callBase->desc();
9694 const wasm::CalleeDesc& callee = callBase->callee();
9695 CodeOffset retOffset;
9696 CodeOffset secondRetOffset;
9697 switch (callee.which()) {
9698 case wasm::CalleeDesc::Func:
9699 if (isReturnCall) {
9700 ReturnCallAdjustmentInfo retCallInfo(
9701 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
9702 masm.wasmReturnCall(desc, callee.funcIndex(), retCallInfo);
9703 // The rest of the method is unnecessary for a return call.
9704 return;
9705 }
9706 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"
, 9706); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9706; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9707 retOffset = masm.call(desc, callee.funcIndex());
9708 reloadRegs = false;
9709 switchRealm = false;
9710 break;
9711 case wasm::CalleeDesc::Import:
9712 if (isReturnCall) {
9713 ReturnCallAdjustmentInfo retCallInfo(
9714 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
9715 masm.wasmReturnCallImport(desc, callee, retCallInfo);
9716 // The rest of the method is unnecessary for a return call.
9717 return;
9718 }
9719 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"
, 9719); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9719; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9720 retOffset = masm.wasmCallImport(desc, callee);
9721 break;
9722 case wasm::CalleeDesc::AsmJSTable:
9723 retOffset = masm.asmCallIndirect(desc, callee);
9724 break;
9725 case wasm::CalleeDesc::WasmTable: {
9726 Label* boundsCheckFailed = nullptr;
9727 if (lir->needsBoundsCheck()) {
9728 OutOfLineAbortingWasmTrap* ool =
9729 new (alloc()) OutOfLineAbortingWasmTrap(desc.toTrapSiteDesc(),
9730 wasm::Trap::OutOfBounds);
9731 if (lir->isCatchable()) {
9732 addOutOfLineCode(ool, lir->mirCatchable());
9733 } else if (isReturnCall) {
9734 addOutOfLineCode(ool, lir->mirReturnCall());
9735 } else {
9736 addOutOfLineCode(ool, lir->mirUncatchable());
9737 }
9738 boundsCheckFailed = ool->entry();
9739 }
9740 Label* nullCheckFailed = nullptr;
9741#ifndef WASM_HAS_HEAPREG1
9742 {
9743 OutOfLineAbortingWasmTrap* ool =
9744 new (alloc()) OutOfLineAbortingWasmTrap(
9745 desc.toTrapSiteDesc(), wasm::Trap::IndirectCallToNull);
9746 if (lir->isCatchable()) {
9747 addOutOfLineCode(ool, lir->mirCatchable());
9748 } else if (isReturnCall) {
9749 addOutOfLineCode(ool, lir->mirReturnCall());
9750 } else {
9751 addOutOfLineCode(ool, lir->mirUncatchable());
9752 }
9753 nullCheckFailed = ool->entry();
9754 }
9755#endif
9756 if (isReturnCall) {
9757 ReturnCallAdjustmentInfo retCallInfo(
9758 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
9759 masm.wasmReturnCallIndirect(desc, callee, boundsCheckFailed,
9760 nullCheckFailed, mozilla::Nothing(),
9761 retCallInfo);
9762 // The rest of the method is unnecessary for a return call.
9763 return;
9764 }
9765 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"
, 9765); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9765; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9766 masm.wasmCallIndirect(desc, callee, boundsCheckFailed, nullCheckFailed,
9767 lir->tableSize(), &retOffset, &secondRetOffset);
9768 // Register reloading and realm switching are handled dynamically inside
9769 // wasmCallIndirect. There are two return offsets, one for each call
9770 // instruction (fast path and slow path).
9771 reloadRegs = false;
9772 switchRealm = false;
9773 break;
9774 }
9775 case wasm::CalleeDesc::Builtin:
9776 retOffset = masm.call(desc, callee.builtin());
9777 reloadRegs = false;
9778 switchRealm = false;
9779 break;
9780 case wasm::CalleeDesc::BuiltinInstanceMethod:
9781 retOffset = masm.wasmCallBuiltinInstanceMethod(
9782 desc, callBase->instanceArg(), callee.builtin(),
9783 callBase->builtinMethodFailureMode());
9784 switchRealm = false;
9785 break;
9786 case wasm::CalleeDesc::FuncRef:
9787 if (isReturnCall) {
9788 ReturnCallAdjustmentInfo retCallInfo(
9789 callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_);
9790 masm.wasmReturnCallRef(desc, callee, retCallInfo);
9791 // The rest of the method is unnecessary for a return call.
9792 return;
9793 }
9794 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"
, 9794); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9794; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9795 // Register reloading and realm switching are handled dynamically inside
9796 // wasmCallRef. There are two return offsets, one for each call
9797 // instruction (fast path and slow path).
9798 masm.wasmCallRef(desc, callee, &retOffset, &secondRetOffset);
9799 reloadRegs = false;
9800 switchRealm = false;
9801 break;
9802 }
9803
9804 // Note the assembler offset for the associated LSafePoint.
9805 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"
, 9805); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall"
")"); do { *((volatile int*)__null) = 9805; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9806 markSafepointAt(retOffset.offset(), lir);
9807
9808 // Now that all the outbound in-memory args are on the stack, note the
9809 // required lower boundary point of the associated StackMap.
9810 uint32_t framePushedAtStackMapBase =
9811 masm.framePushed() -
9812 wasm::AlignStackArgAreaSize(callBase->stackArgAreaSizeUnaligned());
9813 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAtStackMapBase);
9814 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"
, 9815); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
")"); do { *((volatile int*)__null) = 9815; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9815 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"
, 9815); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall"
")"); do { *((volatile int*)__null) = 9815; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9816
9817 // Note the assembler offset and framePushed for use by the adjunct
9818 // LSafePoint, see visitor for LWasmCallIndirectAdjunctSafepoint below.
9819 if (callee.which() == wasm::CalleeDesc::WasmTable ||
9820 callee.which() == wasm::CalleeDesc::FuncRef) {
9821 lir->adjunctSafepoint()->recordSafepointInfo(secondRetOffset,
9822 framePushedAtStackMapBase);
9823 }
9824
9825 if (reloadRegs) {
9826 masm.loadPtr(
9827 Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall),
9828 InstanceReg);
9829 masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing());
9830 if (switchRealm) {
9831 masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1);
9832 }
9833 } else {
9834 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"
, 9834); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!switchRealm"
")"); do { *((volatile int*)__null) = 9834; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9835 }
9836
9837 switch (callee.which()) {
9838 case wasm::CalleeDesc::Func:
9839 case wasm::CalleeDesc::Import:
9840 case wasm::CalleeDesc::WasmTable:
9841 case wasm::CalleeDesc::FuncRef:
9842 // Stack allocation could change during Wasm (return) calls,
9843 // recover pre-call state.
9844 masm.freeStackTo(masm.framePushed());
9845 break;
9846 default:
9847 break;
9848 }
9849
9850 if (inTry) {
9851 // Set the end of the try note range
9852 size_t tryNoteIndex = callBase->tryNoteIndex();
9853 wasm::TryNoteVector& tryNotes = masm.tryNotes();
9854 wasm::TryNote& tryNote = tryNotes[tryNoteIndex];
9855
9856 // Don't set the end of the try note if we've OOM'ed, as the above
9857 // instructions may not have been emitted, which will trigger an assert
9858 // about zero-length try-notes. This is okay as this compilation will be
9859 // thrown away.
9860 if (!masm.oom()) {
9861 tryNote.setTryBodyEnd(masm.currentOffset());
9862 }
9863
9864 // This instruction or the adjunct safepoint must be the last instruction
9865 // in the block. No other instructions may be inserted.
9866 LBlock* block = lir->block();
9867 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"
, 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9868 (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"
, 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
9869 *(++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"
, 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)"
")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9870
9871 // Jump to the fallthrough block
9872 jumpToBlock(lir->mirCatchable()->getSuccessor(
9873 MWasmCallCatchable::FallthroughBranchIndex));
9874 }
9875}
9876
9877#ifdef ENABLE_WASM_JSPI1
9878void CodeGenerator::callWasmUpdateSuspenderState(
9879 wasm::UpdateSuspenderStateAction kind, Register suspender, Register temp) {
9880 masm.Push(InstanceReg);
9881 int32_t framePushedAfterInstance = masm.framePushed();
9882
9883 masm.move32(Imm32(uint32_t(kind)), temp);
9884
9885 masm.setupWasmABICall();
9886 masm.passABIArg(InstanceReg);
9887 masm.passABIArg(suspender);
9888 masm.passABIArg(temp);
9889 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
9890 masm.callWithABI(wasm::BytecodeOffset(0),
9891 wasm::SymbolicAddress::UpdateSuspenderState,
9892 mozilla::Some(instanceOffset));
9893
9894 masm.Pop(InstanceReg);
9895}
9896
9897void CodeGenerator::prepareWasmStackSwitchTrampolineCall(Register suspender,
9898 Register data) {
9899 // Reserve stack space for the wasm call.
9900 unsigned argDecrement;
9901 {
9902 WasmABIArgGenerator abi;
9903 ABIArg arg;
9904 arg = abi.next(MIRType::Pointer);
9905 arg = abi.next(MIRType::Pointer);
9906 argDecrement = StackDecrementForCall(WasmStackAlignment, 0,
9907 abi.stackBytesConsumedSoFar());
9908 }
9909 masm.reserveStack(argDecrement);
9910
9911 // Pass the suspender and data params through the wasm function ABI registers.
9912 WasmABIArgGenerator abi;
9913 ABIArg arg;
9914 arg = abi.next(MIRType::Pointer);
9915 if (arg.kind() == ABIArg::GPR) {
9916 masm.movePtr(suspender, arg.gpr());
9917 } else {
9918 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"
, 9918); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack"
")"); do { *((volatile int*)__null) = 9918; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9919 masm.storePtr(suspender,
9920 Address(masm.getStackPointer(), arg.offsetFromArgBase()));
9921 }
9922 arg = abi.next(MIRType::Pointer);
9923 if (arg.kind() == ABIArg::GPR) {
9924 masm.movePtr(data, arg.gpr());
9925 } else {
9926 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"
, 9926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack"
")"); do { *((volatile int*)__null) = 9926; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9927 masm.storePtr(data,
9928 Address(masm.getStackPointer(), arg.offsetFromArgBase()));
9929 }
9930
9931 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
9932 WasmCallerInstanceOffsetBeforeCall));
9933}
9934#endif // ENABLE_WASM_JSPI
9935
9936void CodeGenerator::visitWasmStackSwitchToSuspendable(
9937 LWasmStackSwitchToSuspendable* lir) {
9938#ifdef ENABLE_WASM_JSPI1
9939 const Register SuspenderReg = lir->suspender()->toRegister().gpr();
9940 const Register FnReg = lir->fn()->toRegister().gpr();
9941 const Register DataReg = lir->data()->toRegister().gpr();
9942 const Register SuspenderDataReg = ABINonArgReg3;
9943
9944# ifdef JS_CODEGEN_ARM64
9945 vixl::UseScratchRegisterScope temps(&masm);
9946 const Register ScratchReg1 = temps.AcquireX().asUnsized();
9947# elif defined(JS_CODEGEN_X86)
9948 const Register ScratchReg1 = ABINonArgReg3;
9949# elif defined(JS_CODEGEN_X641)
9950 const Register ScratchReg1 = ScratchReg;
9951# elif defined(JS_CODEGEN_ARM)
9952 const Register ScratchReg1 = ABINonArgReturnVolatileReg;
9953# elif defined(JS_CODEGEN_LOONG64)
9954 SecondScratchRegisterScope scratch2(masm);
9955 const Register ScratchReg1 = scratch2;
9956# else
9957# error "NYI: scratch register"
9958# endif
9959
9960 masm.Push(SuspenderReg);
9961 masm.Push(FnReg);
9962 masm.Push(DataReg);
9963
9964 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Enter,
9965 SuspenderReg, ScratchReg1);
9966 masm.Pop(DataReg);
9967 masm.Pop(FnReg);
9968 masm.Pop(SuspenderReg);
9969
9970 masm.Push(SuspenderReg);
9971 int32_t framePushedAtSuspender = masm.framePushed();
9972 masm.Push(InstanceReg);
9973
9974 wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch);
9975 CodeLabel returnCallsite;
9976
9977 // Aligning stack before trampoline call.
9978 uint32_t reserve = ComputeByteAlignment(
9979 masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment);
9980 masm.reserveStack(reserve);
9981
9982 masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset(
9983 wasm::SuspenderObjectDataSlot)),
9984 SuspenderDataReg);
9985
9986 // Switch stacks to suspendable, keep original FP to maintain
9987 // frames chain between main and suspendable stack segments.
9988 masm.storeStackPtr(
9989 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP()));
9990 masm.storePtr(
9991 FramePointer,
9992 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()));
9993
9994 masm.loadStackPtr(Address(
9995 SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP()));
9996
9997 masm.assertStackAlignment(WasmStackAlignment);
9998
9999 // The FramePointer is not changed for SwitchToSuspendable.
10000 uint32_t framePushed = masm.framePushed();
10001
10002 // On different stack, reset framePushed. FramePointer is not valid here.
10003 masm.setFramePushed(0);
10004
10005 prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg);
10006
10007 // Get wasm instance pointer for callee.
10008 size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot(
10009 FunctionExtended::WASM_INSTANCE_SLOT);
10010 masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg);
10011
10012 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
10013 WasmCalleeInstanceOffsetBeforeCall));
10014 masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing());
10015
10016 masm.assertStackAlignment(WasmStackAlignment);
10017
10018 const Register ReturnAddressReg = ScratchReg1;
10019
10020 // DataReg is not needed anymore, using it as a scratch register.
10021 const Register ScratchReg2 = DataReg;
10022
10023 // Save future of suspendable stack exit frame pointer.
10024 masm.computeEffectiveAddress(
10025 Address(masm.getStackPointer(), -int32_t(sizeof(wasm::Frame))),
10026 ScratchReg2);
10027 masm.storePtr(
10028 ScratchReg2,
10029 Address(SuspenderDataReg,
10030 wasm::SuspenderObjectData::offsetOfSuspendableExitFP()));
10031
10032 masm.mov(&returnCallsite, ReturnAddressReg);
10033
10034 // Call wasm function fast.
10035# ifdef JS_USE_LINK_REGISTER
10036# if defined(JS_CODEGEN_LOONG64)
10037 masm.mov(ReturnAddressReg, ra);
10038# else
10039 masm.mov(ReturnAddressReg, lr);
10040# endif
10041# else
10042 masm.Push(ReturnAddressReg);
10043# endif
10044 // Get funcUncheckedCallEntry() from the function's
10045 // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot.
10046 size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot(
10047 FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT);
10048 masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2);
10049 masm.jump(ScratchReg2);
10050
10051 // About to use valid FramePointer -- restore framePushed.
10052 masm.setFramePushed(framePushed);
10053
10054 // For IsPlausibleStackMapKey check for the following callsite.
10055 masm.wasmTrapInstruction();
10056
10057 // Callsite for return from main stack.
10058 masm.bind(&returnCallsite);
10059 masm.append(desc, *returnCallsite.target());
10060 masm.addCodeLabel(returnCallsite);
10061
10062 masm.assertStackAlignment(WasmStackAlignment);
10063
10064 markSafepointAt(returnCallsite.target()->offset(), lir);
10065 lir->safepoint()->setFramePushedAtStackMapBase(framePushed);
10066 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
10067 // Rooting SuspenderReg.
10068 masm.propagateOOM(
10069 lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender));
10070
10071 masm.freeStackTo(framePushed);
10072
10073 masm.freeStack(reserve);
10074 masm.Pop(InstanceReg);
10075 masm.Pop(SuspenderReg);
10076
10077 masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2);
10078
10079 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave,
10080 SuspenderReg, ScratchReg1);
10081#else
10082 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10082); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do {
*((volatile int*)__null) = 10082; __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
10083#endif // ENABLE_WASM_JSPI
10084}
10085
10086void CodeGenerator::visitWasmStackSwitchToMain(LWasmStackSwitchToMain* lir) {
10087#ifdef ENABLE_WASM_JSPI1
10088 const Register SuspenderReg = lir->suspender()->toRegister().gpr();
10089 const Register FnReg = lir->fn()->toRegister().gpr();
10090 const Register DataReg = lir->data()->toRegister().gpr();
10091 const Register SuspenderDataReg = ABINonArgReg3;
10092
10093# ifdef JS_CODEGEN_ARM64
10094 vixl::UseScratchRegisterScope temps(&masm);
10095 const Register ScratchReg1 = temps.AcquireX().asUnsized();
10096# elif defined(JS_CODEGEN_X86)
10097 const Register ScratchReg1 = ABINonArgReg3;
10098# elif defined(JS_CODEGEN_X641)
10099 const Register ScratchReg1 = ScratchReg;
10100# elif defined(JS_CODEGEN_ARM)
10101 const Register ScratchReg1 = ABINonArgReturnVolatileReg;
10102# elif defined(JS_CODEGEN_LOONG64)
10103 SecondScratchRegisterScope scratch2(masm);
10104 const Register ScratchReg1 = scratch2;
10105# else
10106# error "NYI: scratch register"
10107# endif
10108
10109 masm.Push(SuspenderReg);
10110 masm.Push(FnReg);
10111 masm.Push(DataReg);
10112
10113 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Suspend,
10114 SuspenderReg, ScratchReg1);
10115
10116 masm.Pop(DataReg);
10117 masm.Pop(FnReg);
10118 masm.Pop(SuspenderReg);
10119
10120 masm.Push(SuspenderReg);
10121 int32_t framePushedAtSuspender = masm.framePushed();
10122 masm.Push(InstanceReg);
10123
10124 wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch);
10125 CodeLabel returnCallsite;
10126
10127 // Aligning stack before trampoline call.
10128 uint32_t reserve = ComputeByteAlignment(
10129 masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment);
10130 masm.reserveStack(reserve);
10131
10132 masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset(
10133 wasm::SuspenderObjectDataSlot)),
10134 SuspenderDataReg);
10135
10136 // Switch stacks to main.
10137 masm.storeStackPtr(Address(
10138 SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP()));
10139 masm.storePtr(FramePointer,
10140 Address(SuspenderDataReg,
10141 wasm::SuspenderObjectData::offsetOfSuspendableFP()));
10142
10143 masm.loadStackPtr(
10144 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP()));
10145 masm.loadPtr(
10146 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()),
10147 FramePointer);
10148
10149 // Set main_ra field to returnCallsite.
10150# ifdef JS_CODEGEN_X86
10151 // SuspenderDataReg is also ScratchReg1, use DataReg as a scratch register.
10152 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"
, 10152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 == SuspenderDataReg"
")"); do { *((volatile int*)__null) = 10152; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10153 masm.push(DataReg);
10154 masm.mov(&returnCallsite, DataReg);
10155 masm.storePtr(
10156 DataReg,
10157 Address(SuspenderDataReg,
10158 wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()));
10159 masm.pop(DataReg);
10160# else
10161 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"
, 10161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 != SuspenderDataReg"
")"); do { *((volatile int*)__null) = 10161; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10162 masm.mov(&returnCallsite, ScratchReg1);
10163 masm.storePtr(
10164 ScratchReg1,
10165 Address(SuspenderDataReg,
10166 wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()));
10167# endif
10168
10169 masm.assertStackAlignment(WasmStackAlignment);
10170
10171 // The FramePointer is pointing to the same
10172 // place as before switch happened.
10173 uint32_t framePushed = masm.framePushed();
10174
10175 // On different stack, reset framePushed. FramePointer is not valid here.
10176 masm.setFramePushed(0);
10177
10178 prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg);
10179
10180 // Get wasm instance pointer for callee.
10181 size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot(
10182 FunctionExtended::WASM_INSTANCE_SLOT);
10183 masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg);
10184
10185 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
10186 WasmCalleeInstanceOffsetBeforeCall));
10187 masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing());
10188
10189 masm.assertStackAlignment(WasmStackAlignment);
10190
10191 const Register ReturnAddressReg = ScratchReg1;
10192 // DataReg is not needed anymore, using it as a scratch register.
10193 const Register ScratchReg2 = DataReg;
10194
10195 // Save future of main stack exit frame pointer.
10196 masm.computeEffectiveAddress(
10197 Address(masm.getStackPointer(), -int32_t(sizeof(wasm::Frame))),
10198 ScratchReg2);
10199 masm.storePtr(ScratchReg2,
10200 Address(SuspenderDataReg,
10201 wasm::SuspenderObjectData::offsetOfMainExitFP()));
10202
10203 // Load InstanceReg from suspendable stack exit frame.
10204 masm.loadPtr(Address(SuspenderDataReg,
10205 wasm::SuspenderObjectData::offsetOfSuspendableExitFP()),
10206 ScratchReg2);
10207 masm.loadPtr(
10208 Address(ScratchReg2, wasm::FrameWithInstances::callerInstanceOffset()),
10209 ScratchReg2);
10210 masm.storePtr(ScratchReg2, Address(masm.getStackPointer(),
10211 WasmCallerInstanceOffsetBeforeCall));
10212
10213 // Load RA from suspendable stack exit frame.
10214 masm.loadPtr(Address(SuspenderDataReg,
10215 wasm::SuspenderObjectData::offsetOfSuspendableExitFP()),
10216 ScratchReg1);
10217 masm.loadPtr(Address(ScratchReg1, wasm::Frame::returnAddressOffset()),
10218 ReturnAddressReg);
10219
10220 // Call wasm function fast.
10221# ifdef JS_USE_LINK_REGISTER
10222# if defined(JS_CODEGEN_LOONG64)
10223 masm.mov(ReturnAddressReg, ra);
10224# else
10225 masm.mov(ReturnAddressReg, lr);
10226# endif
10227# else
10228 masm.Push(ReturnAddressReg);
10229# endif
10230 // Get funcUncheckedCallEntry() from the function's
10231 // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot.
10232 size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot(
10233 FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT);
10234 masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2);
10235 masm.jump(ScratchReg2);
10236
10237 // About to use valid FramePointer -- restore framePushed.
10238 masm.setFramePushed(framePushed);
10239
10240 // For IsPlausibleStackMapKey check for the following callsite.
10241 masm.wasmTrapInstruction();
10242
10243 // Callsite for return from suspendable stack.
10244 masm.bind(&returnCallsite);
10245 masm.append(desc, *returnCallsite.target());
10246 masm.addCodeLabel(returnCallsite);
10247
10248 masm.assertStackAlignment(WasmStackAlignment);
10249
10250 markSafepointAt(returnCallsite.target()->offset(), lir);
10251 lir->safepoint()->setFramePushedAtStackMapBase(framePushed);
10252 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
10253 // Rooting SuspenderReg.
10254 masm.propagateOOM(
10255 lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender));
10256
10257 masm.freeStackTo(framePushed);
10258
10259 // Push ReturnReg that is passed from ContinueOnSuspended on the stack after,
10260 // the SuspenderReg has been restored (see ScratchReg1 push below).
10261 // (On some platforms SuspenderReg == ReturnReg)
10262 masm.mov(ReturnReg, ScratchReg1);
10263
10264 masm.freeStack(reserve);
10265 masm.Pop(InstanceReg);
10266 masm.Pop(SuspenderReg);
10267
10268 masm.Push(ScratchReg1);
10269
10270 masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2);
10271
10272 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Resume,
10273 SuspenderReg, ScratchReg1);
10274
10275 masm.Pop(ToRegister(lir->output()));
10276
10277#else
10278 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10278); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do {
*((volatile int*)__null) = 10278; __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
10279#endif // ENABLE_WASM_JSPI
10280}
10281
10282void CodeGenerator::visitWasmStackContinueOnSuspendable(
10283 LWasmStackContinueOnSuspendable* lir) {
10284#ifdef ENABLE_WASM_JSPI1
10285 const Register SuspenderReg = lir->suspender()->toRegister().gpr();
10286 const Register ResultReg = lir->result()->toRegister().gpr();
10287 const Register SuspenderDataReg = ABINonArgReg3;
10288
10289# ifdef JS_CODEGEN_ARM64
10290 vixl::UseScratchRegisterScope temps(&masm);
10291 const Register ScratchReg1 = temps.AcquireX().asUnsized();
10292# elif defined(JS_CODEGEN_X86)
10293 const Register ScratchReg1 = ABINonArgReturnReg1;
10294# elif defined(JS_CODEGEN_X641)
10295 const Register ScratchReg1 = ScratchReg;
10296# elif defined(JS_CODEGEN_ARM)
10297 const Register ScratchReg1 = ABINonArgReturnVolatileReg;
10298# elif defined(JS_CODEGEN_LOONG64)
10299 SecondScratchRegisterScope scratch2(masm);
10300 const Register ScratchReg1 = scratch2;
10301# else
10302# error "NYI: scratch register"
10303# endif
10304 const Register ScratchReg2 = ABINonArgReg1;
10305
10306 masm.Push(SuspenderReg);
10307 int32_t framePushedAtSuspender = masm.framePushed();
10308 masm.Push(InstanceReg);
10309
10310 wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch);
10311 CodeLabel returnCallsite;
10312
10313 // Aligning stack before trampoline call.
10314 uint32_t reserve = ComputeByteAlignment(
10315 masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment);
10316 masm.reserveStack(reserve);
10317
10318 masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset(
10319 wasm::SuspenderObjectDataSlot)),
10320 SuspenderDataReg);
10321 masm.storeStackPtr(
10322 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP()));
10323 masm.storePtr(
10324 FramePointer,
10325 Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()));
10326
10327 // Adjust exit frame FP.
10328 masm.loadPtr(Address(SuspenderDataReg,
10329 wasm::SuspenderObjectData::offsetOfSuspendableExitFP()),
10330 ScratchReg1);
10331 masm.storePtr(FramePointer,
10332 Address(ScratchReg1, wasm::Frame::callerFPOffset()));
10333
10334 // Adjust exit frame RA.
10335 masm.mov(&returnCallsite, ScratchReg2);
10336
10337 masm.storePtr(ScratchReg2,
10338 Address(ScratchReg1, wasm::Frame::returnAddressOffset()));
10339 // Adjust exit frame caller instance slot.
10340 masm.storePtr(
10341 InstanceReg,
10342 Address(ScratchReg1, wasm::FrameWithInstances::callerInstanceOffset()));
10343
10344 // Switch stacks to suspendable.
10345 masm.loadStackPtr(Address(
10346 SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP()));
10347 masm.loadPtr(Address(SuspenderDataReg,
10348 wasm::SuspenderObjectData::offsetOfSuspendableFP()),
10349 FramePointer);
10350
10351 masm.assertStackAlignment(WasmStackAlignment);
10352
10353 // The FramePointer is pointing to the same
10354 // place as before switch happened.
10355 uint32_t framePushed = masm.framePushed();
10356
10357 // On different stack, reset framePushed. FramePointer is not valid here.
10358 masm.setFramePushed(0);
10359
10360 // Restore shadow stack area and instance slots.
10361 WasmABIArgGenerator abi;
10362 unsigned reserveBeforeCall = abi.stackBytesConsumedSoFar();
10363 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"
, 10363); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0"
")"); do { *((volatile int*)__null) = 10363; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10364 unsigned argDecrement =
10365 StackDecrementForCall(WasmStackAlignment, 0, reserveBeforeCall);
10366 masm.reserveStack(argDecrement);
10367
10368 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
10369 WasmCallerInstanceOffsetBeforeCall));
10370 masm.storePtr(InstanceReg, Address(masm.getStackPointer(),
10371 WasmCalleeInstanceOffsetBeforeCall));
10372
10373 masm.assertStackAlignment(WasmStackAlignment);
10374
10375 // Transfer results to ReturnReg so it will appear at SwitchToMain return.
10376 masm.mov(ResultReg, ReturnReg);
10377
10378 const Register ReturnAddressReg = ScratchReg1;
10379
10380 // Pretend we just returned from the function.
10381 masm.loadPtr(
10382 Address(SuspenderDataReg,
10383 wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()),
10384 ReturnAddressReg);
10385 masm.jump(ReturnAddressReg);
10386
10387 // About to use valid FramePointer -- restore framePushed.
10388 masm.setFramePushed(framePushed);
10389
10390 // For IsPlausibleStackMapKey check for the following callsite.
10391 masm.wasmTrapInstruction();
10392
10393 // Callsite for return from suspendable stack.
10394 masm.bind(&returnCallsite);
10395 masm.append(desc, *returnCallsite.target());
10396 masm.addCodeLabel(returnCallsite);
10397
10398 masm.assertStackAlignment(WasmStackAlignment);
10399
10400 markSafepointAt(returnCallsite.target()->offset(), lir);
10401 lir->safepoint()->setFramePushedAtStackMapBase(framePushed);
10402 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch);
10403 // Rooting SuspenderReg.
10404 masm.propagateOOM(
10405 lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender));
10406
10407 masm.freeStackTo(framePushed);
10408
10409 masm.freeStack(reserve);
10410 masm.Pop(InstanceReg);
10411 masm.Pop(SuspenderReg);
10412
10413 // Using SuspenderDataReg and ABINonArgReg2 as temps.
10414 masm.switchToWasmInstanceRealm(SuspenderDataReg, ABINonArgReg2);
10415
10416 callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave,
10417 SuspenderReg, ScratchReg1);
10418#else
10419 MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10419); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do {
*((volatile int*)__null) = 10419; __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
10420#endif // ENABLE_WASM_JSPI
10421}
10422
10423void CodeGenerator::visitWasmCallLandingPrePad(LWasmCallLandingPrePad* lir) {
10424 LBlock* block = lir->block();
10425 MWasmCallLandingPrePad* mir = lir->mir();
10426 MBasicBlock* mirBlock = mir->block();
10427 MBasicBlock* callMirBlock = mir->callBlock();
10428
10429 // This block must be the pre-pad successor of the call block. No blocks may
10430 // be inserted between us, such as for critical edge splitting.
10431 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"
, 10432); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
")"); do { *((volatile int*)__null) = 10432; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10432 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"
, 10432); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)"
")"); do { *((volatile int*)__null) = 10432; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10433
10434 // This instruction or a move group must be the first instruction in the
10435 // block. No other instructions may be inserted.
10436 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"
, 10437); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
")"); do { *((volatile int*)__null) = 10437; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10437 *(++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"
, 10437); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)"
")"); do { *((volatile int*)__null) = 10437; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10438
10439 wasm::TryNoteVector& tryNotes = masm.tryNotes();
10440 wasm::TryNote& tryNote = tryNotes[mir->tryNoteIndex()];
10441 // Set the entry point for the call try note to be the beginning of this
10442 // block. The above assertions (and assertions in visitWasmCall) guarantee
10443 // that we are not skipping over instructions that should be executed.
10444 tryNote.setLandingPad(block->label()->offset(), masm.framePushed());
10445}
10446
10447void CodeGenerator::visitWasmCallIndirectAdjunctSafepoint(
10448 LWasmCallIndirectAdjunctSafepoint* lir) {
10449 markSafepointAt(lir->safepointLocation().offset(), lir);
10450 lir->safepoint()->setFramePushedAtStackMapBase(
10451 lir->framePushedAtStackMapBase());
10452}
10453
10454template <typename InstructionWithMaybeTrapSite>
10455void EmitSignalNullCheckTrapSite(MacroAssembler& masm,
10456 InstructionWithMaybeTrapSite* ins,
10457 FaultingCodeOffset fco,
10458 wasm::TrapMachineInsn tmi) {
10459 if (!ins->maybeTrap()) {
10460 return;
10461 }
10462 masm.append(wasm::Trap::NullPointerDereference,
10463 wasm::TrapSite(tmi, fco, *ins->maybeTrap()));
10464}
10465
10466template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex>
10467void CodeGenerator::emitWasmValueLoad(InstructionWithMaybeTrapSite* ins,
10468 MIRType type, MWideningOp wideningOp,
10469 AddressOrBaseIndex addr,
10470 AnyRegister dst) {
10471 FaultingCodeOffset fco;
10472 switch (type) {
10473 case MIRType::Int32:
10474 switch (wideningOp) {
10475 case MWideningOp::None:
10476 fco = masm.load32(addr, dst.gpr());
10477 EmitSignalNullCheckTrapSite(masm, ins, fco,
10478 wasm::TrapMachineInsn::Load32);
10479 break;
10480 case MWideningOp::FromU16:
10481 fco = masm.load16ZeroExtend(addr, dst.gpr());
10482 EmitSignalNullCheckTrapSite(masm, ins, fco,
10483 wasm::TrapMachineInsn::Load16);
10484 break;
10485 case MWideningOp::FromS16:
10486 fco = masm.load16SignExtend(addr, dst.gpr());
10487 EmitSignalNullCheckTrapSite(masm, ins, fco,
10488 wasm::TrapMachineInsn::Load16);
10489 break;
10490 case MWideningOp::FromU8:
10491 fco = masm.load8ZeroExtend(addr, dst.gpr());
10492 EmitSignalNullCheckTrapSite(masm, ins, fco,
10493 wasm::TrapMachineInsn::Load8);
10494 break;
10495 case MWideningOp::FromS8:
10496 fco = masm.load8SignExtend(addr, dst.gpr());
10497 EmitSignalNullCheckTrapSite(masm, ins, fco,
10498 wasm::TrapMachineInsn::Load8);
10499 break;
10500 default:
10501 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"
, 10501); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected widening op in ::visitWasmLoadElement"
")"); do { *((volatile int*)__null) = 10501; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10502 }
10503 break;
10504 case MIRType::Float32:
10505 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"
, 10505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10505; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10506 fco = masm.loadFloat32(addr, dst.fpu());
10507 EmitSignalNullCheckTrapSite(masm, ins, fco,
10508 wasm::TrapMachineInsn::Load32);
10509 break;
10510 case MIRType::Double:
10511 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"
, 10511); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10511; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10512 fco = masm.loadDouble(addr, dst.fpu());
10513 EmitSignalNullCheckTrapSite(masm, ins, fco,
10514 wasm::TrapMachineInsn::Load64);
10515 break;
10516 case MIRType::Pointer:
10517 case MIRType::WasmAnyRef:
10518 case MIRType::WasmArrayData:
10519 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"
, 10519); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10519; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10520 fco = masm.loadPtr(addr, dst.gpr());
10521 EmitSignalNullCheckTrapSite(masm, ins, fco,
10522 wasm::TrapMachineInsnForLoadWord());
10523 break;
10524 default:
10525 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"
, 10525); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueLoad"
")"); do { *((volatile int*)__null) = 10525; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10526 }
10527}
10528
10529template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex>
10530void CodeGenerator::emitWasmValueStore(InstructionWithMaybeTrapSite* ins,
10531 MIRType type, MNarrowingOp narrowingOp,
10532 AnyRegister src,
10533 AddressOrBaseIndex addr) {
10534 FaultingCodeOffset fco;
10535 switch (type) {
10536 case MIRType::Int32:
10537 switch (narrowingOp) {
10538 case MNarrowingOp::None:
10539 fco = masm.store32(src.gpr(), addr);
10540 EmitSignalNullCheckTrapSite(masm, ins, fco,
10541 wasm::TrapMachineInsn::Store32);
10542 break;
10543 case MNarrowingOp::To16:
10544 fco = masm.store16(src.gpr(), addr);
10545 EmitSignalNullCheckTrapSite(masm, ins, fco,
10546 wasm::TrapMachineInsn::Store16);
10547 break;
10548 case MNarrowingOp::To8:
10549 fco = masm.store8(src.gpr(), addr);
10550 EmitSignalNullCheckTrapSite(masm, ins, fco,
10551 wasm::TrapMachineInsn::Store8);
10552 break;
10553 default:
10554 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10554); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 10554; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
10555 }
10556 break;
10557 case MIRType::Float32:
10558 fco = masm.storeFloat32(src.fpu(), addr);
10559 EmitSignalNullCheckTrapSite(masm, ins, fco,
10560 wasm::TrapMachineInsn::Store32);
10561 break;
10562 case MIRType::Double:
10563 fco = masm.storeDouble(src.fpu(), addr);
10564 EmitSignalNullCheckTrapSite(masm, ins, fco,
10565 wasm::TrapMachineInsn::Store64);
10566 break;
10567 case MIRType::Pointer:
10568 // This could be correct, but it would be a new usage, so check carefully.
10569 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"
, 10569); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected type in ::emitWasmValueStore."
")"); do { *((volatile int*)__null) = 10569; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10570 case MIRType::WasmAnyRef:
10571 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"
, 10571); AnnotateMozCrashReason("MOZ_CRASH(" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef."
")"); do { *((volatile int*)__null) = 10571; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10572 default:
10573 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"
, 10573); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueStore"
")"); do { *((volatile int*)__null) = 10573; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10574 }
10575}
10576
10577void CodeGenerator::visitWasmLoadSlot(LWasmLoadSlot* ins) {
10578 MIRType type = ins->type();
10579 MWideningOp wideningOp = ins->wideningOp();
10580 Register container = ToRegister(ins->containerRef());
10581 Address addr(container, ins->offset());
10582 AnyRegister dst = ToAnyRegister(ins->output());
10583
10584#ifdef ENABLE_WASM_SIMD1
10585 if (type == MIRType::Simd128) {
10586 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"
, 10586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10586; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10587 FaultingCodeOffset fco = masm.loadUnalignedSimd128(addr, dst.fpu());
10588 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128);
10589 return;
10590 }
10591#endif
10592 emitWasmValueLoad(ins, type, wideningOp, addr, dst);
10593}
10594
10595void CodeGenerator::visitWasmLoadElement(LWasmLoadElement* ins) {
10596 MIRType type = ins->type();
10597 MWideningOp wideningOp = ins->wideningOp();
10598 Scale scale = ins->scale();
10599 Register base = ToRegister(ins->base());
10600 Register index = ToRegister(ins->index());
10601 AnyRegister dst = ToAnyRegister(ins->output());
10602
10603#ifdef ENABLE_WASM_SIMD1
10604 if (type == MIRType::Simd128) {
10605 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"
, 10605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None"
")"); do { *((volatile int*)__null) = 10605; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10606 FaultingCodeOffset fco;
10607 Register temp = ToRegister(ins->temp0());
10608 masm.movePtr(index, temp);
10609 masm.lshiftPtr(Imm32(4), temp);
10610 fco = masm.loadUnalignedSimd128(BaseIndex(base, temp, Scale::TimesOne),
10611 dst.fpu());
10612 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128);
10613 return;
10614 }
10615#endif
10616 emitWasmValueLoad(ins, type, wideningOp, BaseIndex(base, index, scale), dst);
10617}
10618
10619void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) {
10620 MIRType type = ins->type();
10621 MNarrowingOp narrowingOp = ins->narrowingOp();
10622 Register container = ToRegister(ins->containerRef());
10623 Address addr(container, ins->offset());
10624 AnyRegister src = ToAnyRegister(ins->value());
10625 if (type != MIRType::Int32) {
10626 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"
, 10626); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 10626; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10627 }
10628
10629#ifdef ENABLE_WASM_SIMD1
10630 if (type == MIRType::Simd128) {
10631 FaultingCodeOffset fco = masm.storeUnalignedSimd128(src.fpu(), addr);
10632 EmitSignalNullCheckTrapSite(masm, ins, fco,
10633 wasm::TrapMachineInsn::Store128);
10634 return;
10635 }
10636#endif
10637 emitWasmValueStore(ins, type, narrowingOp, src, addr);
10638}
10639
10640void CodeGenerator::visitWasmStoreElement(LWasmStoreElement* ins) {
10641 MIRType type = ins->type();
10642 MNarrowingOp narrowingOp = ins->narrowingOp();
10643 Scale scale = ins->scale();
10644 Register base = ToRegister(ins->base());
10645 Register index = ToRegister(ins->index());
10646 AnyRegister src = ToAnyRegister(ins->value());
10647 if (type != MIRType::Int32) {
10648 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"
, 10648); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None"
")"); do { *((volatile int*)__null) = 10648; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10649 }
10650
10651#ifdef ENABLE_WASM_SIMD1
10652 if (type == MIRType::Simd128) {
10653 Register temp = ToRegister(ins->temp0());
10654 masm.movePtr(index, temp);
10655 masm.lshiftPtr(Imm32(4), temp);
10656 FaultingCodeOffset fco = masm.storeUnalignedSimd128(
10657 src.fpu(), BaseIndex(base, temp, Scale::TimesOne));
10658 EmitSignalNullCheckTrapSite(masm, ins, fco,
10659 wasm::TrapMachineInsn::Store128);
10660 return;
10661 }
10662#endif
10663 emitWasmValueStore(ins, type, narrowingOp, src,
10664 BaseIndex(base, index, scale));
10665}
10666
10667void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) {
10668 Register elements = ToRegister(ins->elements());
10669 Register index = ToRegister(ins->index());
10670 Register output = ToRegister(ins->output());
10671 masm.loadPtr(BaseIndex(elements, index, ScalePointer), output);
10672}
10673
10674void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) {
10675 masm.movePtr(ToRegister(ins->base()), ToRegister(ins->output()));
10676 masm.addPtr(Imm32(int32_t(ins->offset())), ToRegister(ins->output()));
10677}
10678
10679void CodeGenerator::visitWasmDerivedIndexPointer(
10680 LWasmDerivedIndexPointer* ins) {
10681 Register base = ToRegister(ins->base());
10682 Register index = ToRegister(ins->index());
10683 Register output = ToRegister(ins->output());
10684 masm.computeEffectiveAddress(BaseIndex(base, index, ins->scale()), output);
10685}
10686
10687void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) {
10688 Register instance = ToRegister(ins->instance());
10689 Register valueBase = ToRegister(ins->valueBase());
10690 size_t offset = ins->offset();
10691 Register value = ToRegister(ins->value());
10692 Register temp = ToRegister(ins->temp0());
10693
10694 if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) {
10695 Label skipPreBarrier;
10696 wasm::EmitWasmPreBarrierGuard(masm, instance, temp,
10697 Address(valueBase, offset), &skipPreBarrier,
10698 ins->maybeTrap());
10699 wasm::EmitWasmPreBarrierCallImmediate(masm, instance, temp, valueBase,
10700 offset);
10701 masm.bind(&skipPreBarrier);
10702 }
10703
10704 FaultingCodeOffset fco = masm.storePtr(value, Address(valueBase, offset));
10705 EmitSignalNullCheckTrapSite(masm, ins, fco,
10706 wasm::TrapMachineInsnForStoreWord());
10707 // The postbarrier is handled separately.
10708}
10709
10710void CodeGenerator::visitWasmStoreElementRef(LWasmStoreElementRef* ins) {
10711 Register instance = ToRegister(ins->instance());
10712 Register base = ToRegister(ins->base());
10713 Register index = ToRegister(ins->index());
10714 Register value = ToRegister(ins->value());
10715 Register temp0 = ToTempRegisterOrInvalid(ins->temp0());
10716 Register temp1 = ToTempRegisterOrInvalid(ins->temp1());
10717
10718 BaseIndex addr(base, index, ScalePointer);
10719
10720 if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) {
10721 Label skipPreBarrier;
10722 wasm::EmitWasmPreBarrierGuard(masm, instance, temp0, addr, &skipPreBarrier,
10723 ins->maybeTrap());
10724 wasm::EmitWasmPreBarrierCallIndex(masm, instance, temp0, temp1, addr);
10725 masm.bind(&skipPreBarrier);
10726 }
10727
10728 FaultingCodeOffset fco = masm.storePtr(value, addr);
10729 EmitSignalNullCheckTrapSite(masm, ins, fco,
10730 wasm::TrapMachineInsnForStoreWord());
10731 // The postbarrier is handled separately.
10732}
10733
10734// Out-of-line path to update the store buffer for wasm references.
10735class OutOfLineWasmCallPostWriteBarrierImmediate
10736 : public OutOfLineCodeBase<CodeGenerator> {
10737 LInstruction* lir_;
10738 Register valueBase_;
10739 Register temp_;
10740 uint32_t valueOffset_;
10741
10742 public:
10743 OutOfLineWasmCallPostWriteBarrierImmediate(LInstruction* lir,
10744 Register valueBase, Register temp,
10745 uint32_t valueOffset)
10746 : lir_(lir),
10747 valueBase_(valueBase),
10748 temp_(temp),
10749 valueOffset_(valueOffset) {}
10750
10751 void accept(CodeGenerator* codegen) override {
10752 codegen->visitOutOfLineWasmCallPostWriteBarrierImmediate(this);
10753 }
10754
10755 LInstruction* lir() const { return lir_; }
10756 Register valueBase() const { return valueBase_; }
10757 Register temp() const { return temp_; }
10758 uint32_t valueOffset() const { return valueOffset_; }
10759};
10760
10761void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierImmediate(
10762 OutOfLineWasmCallPostWriteBarrierImmediate* ool) {
10763 saveLiveVolatile(ool->lir());
10764 masm.Push(InstanceReg);
10765 int32_t framePushedAfterInstance = masm.framePushed();
10766
10767 // Fold the value offset into the value base
10768 Register valueAddr = ool->valueBase();
10769 Register temp = ool->temp();
10770 masm.computeEffectiveAddress(Address(valueAddr, ool->valueOffset()), temp);
10771
10772 // Call Instance::postBarrier
10773 masm.setupWasmABICall();
10774 masm.passABIArg(InstanceReg);
10775 masm.passABIArg(temp);
10776 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
10777 masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier,
10778 mozilla::Some(instanceOffset), ABIType::General);
10779
10780 masm.Pop(InstanceReg);
10781 restoreLiveVolatile(ool->lir());
10782
10783 masm.jump(ool->rejoin());
10784}
10785
10786void CodeGenerator::visitWasmPostWriteBarrierImmediate(
10787 LWasmPostWriteBarrierImmediate* lir) {
10788 Register object = ToRegister(lir->object());
10789 Register value = ToRegister(lir->value());
10790 Register valueBase = ToRegister(lir->valueBase());
10791 Register temp = ToRegister(lir->temp0());
10792 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"
, 10792); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg"
")"); do { *((volatile int*)__null) = 10792; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10793 auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierImmediate(
10794 lir, valueBase, temp, lir->valueOffset());
10795 addOutOfLineCode(ool, lir->mir());
10796
10797 wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value,
10798 ool->rejoin());
10799 masm.jump(ool->entry());
10800 masm.bind(ool->rejoin());
10801}
10802
10803// Out-of-line path to update the store buffer for wasm references.
10804class OutOfLineWasmCallPostWriteBarrierIndex
10805 : public OutOfLineCodeBase<CodeGenerator> {
10806 LInstruction* lir_;
10807 Register valueBase_;
10808 Register index_;
10809 Register temp_;
10810 uint32_t elemSize_;
10811
10812 public:
10813 OutOfLineWasmCallPostWriteBarrierIndex(LInstruction* lir, Register valueBase,
10814 Register index, Register temp,
10815 uint32_t elemSize)
10816 : lir_(lir),
10817 valueBase_(valueBase),
10818 index_(index),
10819 temp_(temp),
10820 elemSize_(elemSize) {
10821 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"
, 10822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 10822; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
10822 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"
, 10822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16"
")"); do { *((volatile int*)__null) = 10822; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10823 }
10824
10825 void accept(CodeGenerator* codegen) override {
10826 codegen->visitOutOfLineWasmCallPostWriteBarrierIndex(this);
10827 }
10828
10829 LInstruction* lir() const { return lir_; }
10830 Register valueBase() const { return valueBase_; }
10831 Register index() const { return index_; }
10832 Register temp() const { return temp_; }
10833 uint32_t elemSize() const { return elemSize_; }
10834};
10835
10836void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierIndex(
10837 OutOfLineWasmCallPostWriteBarrierIndex* ool) {
10838 saveLiveVolatile(ool->lir());
10839 masm.Push(InstanceReg);
10840 int32_t framePushedAfterInstance = masm.framePushed();
10841
10842 // Fold the value offset into the value base
10843 Register temp = ool->temp();
10844 if (ool->elemSize() == 16) {
10845 masm.movePtr(ool->index(), temp);
10846 masm.lshiftPtr(Imm32(4), temp);
10847 masm.addPtr(ool->valueBase(), temp);
10848 } else {
10849 masm.computeEffectiveAddress(BaseIndex(ool->valueBase(), ool->index(),
10850 ScaleFromElemWidth(ool->elemSize())),
10851 temp);
10852 }
10853
10854 // Call Instance::postBarrier
10855 masm.setupWasmABICall();
10856 masm.passABIArg(InstanceReg);
10857 masm.passABIArg(temp);
10858 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
10859 masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier,
10860 mozilla::Some(instanceOffset), ABIType::General);
10861
10862 masm.Pop(InstanceReg);
10863 restoreLiveVolatile(ool->lir());
10864
10865 masm.jump(ool->rejoin());
10866}
10867
10868void CodeGenerator::visitWasmPostWriteBarrierIndex(
10869 LWasmPostWriteBarrierIndex* lir) {
10870 Register object = ToRegister(lir->object());
10871 Register value = ToRegister(lir->value());
10872 Register valueBase = ToRegister(lir->valueBase());
10873 Register index = ToRegister(lir->index());
10874 Register temp = ToRegister(lir->temp0());
10875 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"
, 10875); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg"
")"); do { *((volatile int*)__null) = 10875; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10876 auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierIndex(
10877 lir, valueBase, index, temp, lir->elemSize());
10878 addOutOfLineCode(ool, lir->mir());
10879
10880 wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value,
10881 ool->rejoin());
10882 masm.jump(ool->entry());
10883 masm.bind(ool->rejoin());
10884}
10885
10886void CodeGenerator::visitWasmLoadSlotI64(LWasmLoadSlotI64* ins) {
10887 Register container = ToRegister(ins->containerRef());
10888 Address addr(container, ins->offset());
10889 Register64 output = ToOutRegister64(ins);
10890 // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one
10891 // transaction will always trap before the other, so it seems safest to
10892 // register both of them as potentially trapping.
10893#ifdef JS_64BIT1
10894 FaultingCodeOffset fco = masm.load64(addr, output);
10895 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64);
10896#else
10897 FaultingCodeOffsetPair fcop = masm.load64(addr, output);
10898 EmitSignalNullCheckTrapSite(masm, ins, fcop.first,
10899 wasm::TrapMachineInsn::Load32);
10900 EmitSignalNullCheckTrapSite(masm, ins, fcop.second,
10901 wasm::TrapMachineInsn::Load32);
10902#endif
10903}
10904
10905void CodeGenerator::visitWasmLoadElementI64(LWasmLoadElementI64* ins) {
10906 Register base = ToRegister(ins->base());
10907 Register index = ToRegister(ins->index());
10908 BaseIndex addr(base, index, Scale::TimesEight);
10909 Register64 output = ToOutRegister64(ins);
10910 // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one
10911 // transaction will always trap before the other, so it seems safest to
10912 // register both of them as potentially trapping.
10913#ifdef JS_64BIT1
10914 FaultingCodeOffset fco = masm.load64(addr, output);
10915 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64);
10916#else
10917 FaultingCodeOffsetPair fcop = masm.load64(addr, output);
10918 EmitSignalNullCheckTrapSite(masm, ins, fcop.first,
10919 wasm::TrapMachineInsn::Load32);
10920 EmitSignalNullCheckTrapSite(masm, ins, fcop.second,
10921 wasm::TrapMachineInsn::Load32);
10922#endif
10923}
10924
10925void CodeGenerator::visitWasmStoreSlotI64(LWasmStoreSlotI64* ins) {
10926 Register container = ToRegister(ins->containerRef());
10927 Address addr(container, ins->offset());
10928 Register64 value = ToRegister64(ins->value());
10929 // Either 1 or 2 words. As above we register both transactions in the
10930 // 2-word case.
10931#ifdef JS_64BIT1
10932 FaultingCodeOffset fco = masm.store64(value, addr);
10933 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64);
10934#else
10935 FaultingCodeOffsetPair fcop = masm.store64(value, addr);
10936 EmitSignalNullCheckTrapSite(masm, ins, fcop.first,
10937 wasm::TrapMachineInsn::Store32);
10938 EmitSignalNullCheckTrapSite(masm, ins, fcop.second,
10939 wasm::TrapMachineInsn::Store32);
10940#endif
10941}
10942
10943void CodeGenerator::visitWasmStoreElementI64(LWasmStoreElementI64* ins) {
10944 Register base = ToRegister(ins->base());
10945 Register index = ToRegister(ins->index());
10946 BaseIndex addr(base, index, Scale::TimesEight);
10947 Register64 value = ToRegister64(ins->value());
10948 // Either 1 or 2 words. As above we register both transactions in the
10949 // 2-word case.
10950#ifdef JS_64BIT1
10951 FaultingCodeOffset fco = masm.store64(value, addr);
10952 EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64);
10953#else
10954 FaultingCodeOffsetPair fcop = masm.store64(value, addr);
10955 EmitSignalNullCheckTrapSite(masm, ins, fcop.first,
10956 wasm::TrapMachineInsn::Store32);
10957 EmitSignalNullCheckTrapSite(masm, ins, fcop.second,
10958 wasm::TrapMachineInsn::Store32);
10959#endif
10960}
10961
10962void CodeGenerator::visitWasmClampTable64Address(
10963 LWasmClampTable64Address* lir) {
10964#ifdef ENABLE_WASM_MEMORY641
10965 Register64 address = ToRegister64(lir->address());
10966 Register out = ToRegister(lir->output());
10967 masm.wasmClampTable64Address(address, out);
10968#else
10969 MOZ_CRASH("table64 addresses should not be valid without memory64")do { do { } while (false); MOZ_ReportCrash("" "table64 addresses should not be valid without memory64"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 10969); AnnotateMozCrashReason("MOZ_CRASH(" "table64 addresses should not be valid without memory64"
")"); do { *((volatile int*)__null) = 10969; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
10970#endif
10971}
10972
10973void CodeGenerator::visitArrayBufferByteLength(LArrayBufferByteLength* lir) {
10974 Register obj = ToRegister(lir->object());
10975 Register out = ToRegister(lir->output());
10976 masm.loadArrayBufferByteLengthIntPtr(obj, out);
10977}
10978
10979void CodeGenerator::visitArrayBufferViewLength(LArrayBufferViewLength* lir) {
10980 Register obj = ToRegister(lir->object());
10981 Register out = ToRegister(lir->output());
10982 masm.loadArrayBufferViewLengthIntPtr(obj, out);
10983}
10984
10985void CodeGenerator::visitArrayBufferViewByteOffset(
10986 LArrayBufferViewByteOffset* lir) {
10987 Register obj = ToRegister(lir->object());
10988 Register out = ToRegister(lir->output());
10989 masm.loadArrayBufferViewByteOffsetIntPtr(obj, out);
10990}
10991
10992void CodeGenerator::visitArrayBufferViewElements(
10993 LArrayBufferViewElements* lir) {
10994 Register obj = ToRegister(lir->object());
10995 Register out = ToRegister(lir->output());
10996 masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), out);
10997}
10998
10999void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) {
11000 Register obj = ToRegister(lir->object());
11001 Register out = ToRegister(lir->output());
11002
11003 masm.typedArrayElementSize(obj, out);
11004}
11005
11006void CodeGenerator::visitResizableTypedArrayByteOffsetMaybeOutOfBounds(
11007 LResizableTypedArrayByteOffsetMaybeOutOfBounds* lir) {
11008 Register obj = ToRegister(lir->object());
11009 Register out = ToRegister(lir->output());
11010 Register temp = ToRegister(lir->temp0());
11011
11012 masm.loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr(obj, out, temp);
11013}
11014
11015void CodeGenerator::visitResizableTypedArrayLength(
11016 LResizableTypedArrayLength* lir) {
11017 Register obj = ToRegister(lir->object());
11018 Register out = ToRegister(lir->output());
11019 Register temp = ToRegister(lir->temp0());
11020
11021 masm.loadResizableTypedArrayLengthIntPtr(lir->synchronization(), obj, out,
11022 temp);
11023}
11024
11025void CodeGenerator::visitResizableDataViewByteLength(
11026 LResizableDataViewByteLength* lir) {
11027 Register obj = ToRegister(lir->object());
11028 Register out = ToRegister(lir->output());
11029 Register temp = ToRegister(lir->temp0());
11030
11031 masm.loadResizableDataViewByteLengthIntPtr(lir->synchronization(), obj, out,
11032 temp);
11033}
11034
11035void CodeGenerator::visitGrowableSharedArrayBufferByteLength(
11036 LGrowableSharedArrayBufferByteLength* lir) {
11037 Register obj = ToRegister(lir->object());
11038 Register out = ToRegister(lir->output());
11039
11040 // Explicit |byteLength| accesses are seq-consistent atomic loads.
11041 auto sync = Synchronization::Load();
11042
11043 masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out);
11044}
11045
11046void CodeGenerator::visitGuardResizableArrayBufferViewInBounds(
11047 LGuardResizableArrayBufferViewInBounds* lir) {
11048 Register obj = ToRegister(lir->object());
11049 Register temp = ToRegister(lir->temp0());
11050
11051 Label bail;
11052 masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail);
11053 bailoutFrom(&bail, lir->snapshot());
11054}
11055
11056void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached(
11057 LGuardResizableArrayBufferViewInBoundsOrDetached* lir) {
11058 Register obj = ToRegister(lir->object());
11059 Register temp = ToRegister(lir->temp0());
11060
11061 Label done, bail;
11062 masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done);
11063 masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail);
11064 masm.bind(&done);
11065 bailoutFrom(&bail, lir->snapshot());
11066}
11067
11068void CodeGenerator::visitGuardHasAttachedArrayBuffer(
11069 LGuardHasAttachedArrayBuffer* lir) {
11070 Register obj = ToRegister(lir->object());
11071 Register temp = ToRegister(lir->temp0());
11072
11073 Label bail;
11074 masm.branchIfHasDetachedArrayBuffer(obj, temp, &bail);
11075 bailoutFrom(&bail, lir->snapshot());
11076}
11077
11078class OutOfLineGuardNumberToIntPtrIndex
11079 : public OutOfLineCodeBase<CodeGenerator> {
11080 LGuardNumberToIntPtrIndex* lir_;
11081
11082 public:
11083 explicit OutOfLineGuardNumberToIntPtrIndex(LGuardNumberToIntPtrIndex* lir)
11084 : lir_(lir) {}
11085
11086 void accept(CodeGenerator* codegen) override {
11087 codegen->visitOutOfLineGuardNumberToIntPtrIndex(this);
11088 }
11089 LGuardNumberToIntPtrIndex* lir() const { return lir_; }
11090};
11091
11092void CodeGenerator::visitGuardNumberToIntPtrIndex(
11093 LGuardNumberToIntPtrIndex* lir) {
11094 FloatRegister input = ToFloatRegister(lir->input());
11095 Register output = ToRegister(lir->output());
11096
11097 if (!lir->mir()->supportOOB()) {
11098 Label bail;
11099 masm.convertDoubleToPtr(input, output, &bail, false);
11100 bailoutFrom(&bail, lir->snapshot());
11101 return;
11102 }
11103
11104 auto* ool = new (alloc()) OutOfLineGuardNumberToIntPtrIndex(lir);
11105 addOutOfLineCode(ool, lir->mir());
11106
11107 masm.convertDoubleToPtr(input, output, ool->entry(), false);
11108 masm.bind(ool->rejoin());
11109}
11110
11111void CodeGenerator::visitOutOfLineGuardNumberToIntPtrIndex(
11112 OutOfLineGuardNumberToIntPtrIndex* ool) {
11113 // Substitute the invalid index with an arbitrary out-of-bounds index.
11114 masm.movePtr(ImmWord(-1), ToRegister(ool->lir()->output()));
11115 masm.jump(ool->rejoin());
11116}
11117
11118void CodeGenerator::visitStringLength(LStringLength* lir) {
11119 Register input = ToRegister(lir->string());
11120 Register output = ToRegister(lir->output());
11121
11122 masm.loadStringLength(input, output);
11123}
11124
11125void CodeGenerator::visitMinMaxI(LMinMaxI* ins) {
11126 Register first = ToRegister(ins->first());
11127 Register output = ToRegister(ins->output());
11128
11129 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"
, 11129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "first == output"
")"); do { *((volatile int*)__null) = 11129; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11130
11131 Assembler::Condition cond =
11132 ins->mir()->isMax() ? Assembler::GreaterThan : Assembler::LessThan;
11133
11134 if (ins->second()->isConstant()) {
11135 Label done;
11136 masm.branch32(cond, first, Imm32(ToInt32(ins->second())), &done);
11137 masm.move32(Imm32(ToInt32(ins->second())), output);
11138 masm.bind(&done);
11139 } else {
11140 Register second = ToRegister(ins->second());
11141 masm.cmp32Move32(cond, second, first, second, output);
11142 }
11143}
11144
11145void CodeGenerator::visitMinMaxArrayI(LMinMaxArrayI* ins) {
11146 Register array = ToRegister(ins->array());
11147 Register output = ToRegister(ins->output());
11148 Register temp1 = ToRegister(ins->temp1());
11149 Register temp2 = ToRegister(ins->temp2());
11150 Register temp3 = ToRegister(ins->temp3());
11151 bool isMax = ins->isMax();
11152
11153 Label bail;
11154 masm.minMaxArrayInt32(array, output, temp1, temp2, temp3, isMax, &bail);
11155 bailoutFrom(&bail, ins->snapshot());
11156}
11157
11158void CodeGenerator::visitMinMaxArrayD(LMinMaxArrayD* ins) {
11159 Register array = ToRegister(ins->array());
11160 FloatRegister output = ToFloatRegister(ins->output());
11161 Register temp1 = ToRegister(ins->temp1());
11162 Register temp2 = ToRegister(ins->temp2());
11163 FloatRegister floatTemp = ToFloatRegister(ins->floatTemp());
11164 bool isMax = ins->isMax();
11165
11166 Label bail;
11167 masm.minMaxArrayNumber(array, output, floatTemp, temp1, temp2, isMax, &bail);
11168 bailoutFrom(&bail, ins->snapshot());
11169}
11170
11171// For Abs*, lowering will have tied input to output on platforms where that is
11172// sensible, and otherwise left them untied.
11173
11174void CodeGenerator::visitAbsI(LAbsI* ins) {
11175 Register input = ToRegister(ins->input());
11176 Register output = ToRegister(ins->output());
11177
11178 if (ins->mir()->fallible()) {
11179 Label positive;
11180 if (input != output) {
11181 masm.move32(input, output);
11182 }
11183 masm.branchTest32(Assembler::NotSigned, output, output, &positive);
11184 Label bail;
11185 masm.branchNeg32(Assembler::Overflow, output, &bail);
11186 bailoutFrom(&bail, ins->snapshot());
11187 masm.bind(&positive);
11188 } else {
11189 masm.abs32(input, output);
11190 }
11191}
11192
11193void CodeGenerator::visitAbsD(LAbsD* ins) {
11194 masm.absDouble(ToFloatRegister(ins->input()), ToFloatRegister(ins->output()));
11195}
11196
11197void CodeGenerator::visitAbsF(LAbsF* ins) {
11198 masm.absFloat32(ToFloatRegister(ins->input()),
11199 ToFloatRegister(ins->output()));
11200}
11201
11202void CodeGenerator::visitPowII(LPowII* ins) {
11203 Register value = ToRegister(ins->value());
11204 Register power = ToRegister(ins->power());
11205 Register output = ToRegister(ins->output());
11206 Register temp0 = ToRegister(ins->temp0());
11207 Register temp1 = ToRegister(ins->temp1());
11208
11209 Label bailout;
11210 masm.pow32(value, power, output, temp0, temp1, &bailout);
11211 bailoutFrom(&bailout, ins->snapshot());
11212}
11213
11214void CodeGenerator::visitPowI(LPowI* ins) {
11215 FloatRegister value = ToFloatRegister(ins->value());
11216 Register power = ToRegister(ins->power());
11217
11218 using Fn = double (*)(double x, int32_t y);
11219 masm.setupAlignedABICall();
11220 masm.passABIArg(value, ABIType::Float64);
11221 masm.passABIArg(power);
11222
11223 masm.callWithABI<Fn, js::powi>(ABIType::Float64);
11224 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"
, 11224); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 11224; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11225}
11226
11227void CodeGenerator::visitPowD(LPowD* ins) {
11228 FloatRegister value = ToFloatRegister(ins->value());
11229 FloatRegister power = ToFloatRegister(ins->power());
11230
11231 using Fn = double (*)(double x, double y);
11232 masm.setupAlignedABICall();
11233 masm.passABIArg(value, ABIType::Float64);
11234 masm.passABIArg(power, ABIType::Float64);
11235 masm.callWithABI<Fn, ecmaPow>(ABIType::Float64);
11236
11237 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"
, 11237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 11237; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11238}
11239
11240void CodeGenerator::visitPowOfTwoI(LPowOfTwoI* ins) {
11241 Register power = ToRegister(ins->power());
11242 Register output = ToRegister(ins->output());
11243
11244 uint32_t base = ins->base();
11245 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"
, 11245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(base)"
")"); do { *((volatile int*)__null) = 11245; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11246
11247 uint32_t n = mozilla::FloorLog2(base);
11248 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"
, 11248); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n != 0" ")"
); do { *((volatile int*)__null) = 11248; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
11249
11250 // Hacker's Delight, 2nd edition, theorem D2.
11251 auto ceilingDiv = [](uint32_t x, uint32_t y) { return (x + y - 1) / y; };
11252
11253 // Take bailout if |power| is greater-or-equals |log_y(2^31)| or is negative.
11254 // |2^(n*y) < 2^31| must hold, hence |n*y < 31| resp. |y < 31/n|.
11255 //
11256 // Note: it's important for this condition to match the code in CacheIR.cpp
11257 // (CanAttachInt32Pow) to prevent failure loops.
11258 bailoutCmp32(Assembler::AboveOrEqual, power, Imm32(ceilingDiv(31, n)),
11259 ins->snapshot());
11260
11261 // Compute (2^n)^y as 2^(n*y) using repeated shifts. We could directly scale
11262 // |power| and perform a single shift, but due to the lack of necessary
11263 // MacroAssembler functionality, like multiplying a register with an
11264 // immediate, we restrict the number of generated shift instructions when
11265 // lowering this operation.
11266 masm.move32(Imm32(1), output);
11267 do {
11268 masm.lshift32(power, output);
11269 n--;
11270 } while (n > 0);
11271}
11272
11273void CodeGenerator::visitSqrtD(LSqrtD* ins) {
11274 FloatRegister input = ToFloatRegister(ins->input());
11275 FloatRegister output = ToFloatRegister(ins->output());
11276 masm.sqrtDouble(input, output);
11277}
11278
11279void CodeGenerator::visitSqrtF(LSqrtF* ins) {
11280 FloatRegister input = ToFloatRegister(ins->input());
11281 FloatRegister output = ToFloatRegister(ins->output());
11282 masm.sqrtFloat32(input, output);
11283}
11284
11285void CodeGenerator::visitSignI(LSignI* ins) {
11286 Register input = ToRegister(ins->input());
11287 Register output = ToRegister(ins->output());
11288 masm.signInt32(input, output);
11289}
11290
11291void CodeGenerator::visitSignD(LSignD* ins) {
11292 FloatRegister input = ToFloatRegister(ins->input());
11293 FloatRegister output = ToFloatRegister(ins->output());
11294 masm.signDouble(input, output);
11295}
11296
11297void CodeGenerator::visitSignDI(LSignDI* ins) {
11298 FloatRegister input = ToFloatRegister(ins->input());
11299 FloatRegister temp = ToFloatRegister(ins->temp0());
11300 Register output = ToRegister(ins->output());
11301
11302 Label bail;
11303 masm.signDoubleToInt32(input, output, temp, &bail);
11304 bailoutFrom(&bail, ins->snapshot());
11305}
11306
11307void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) {
11308 FloatRegister input = ToFloatRegister(ins->input());
11309 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"
, 11309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 11309; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11310
11311 UnaryMathFunction fun = ins->mir()->function();
11312 UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(fun);
11313
11314 masm.setupAlignedABICall();
11315
11316 masm.passABIArg(input, ABIType::Float64);
11317 masm.callWithABI(DynamicFunction<UnaryMathFunctionType>(funPtr),
11318 ABIType::Float64);
11319}
11320
11321void CodeGenerator::visitMathFunctionF(LMathFunctionF* ins) {
11322 FloatRegister input = ToFloatRegister(ins->input());
11323 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"
, 11323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnFloat32Reg"
")"); do { *((volatile int*)__null) = 11323; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11324
11325 masm.setupAlignedABICall();
11326 masm.passABIArg(input, ABIType::Float32);
11327
11328 using Fn = float (*)(float x);
11329 Fn funptr = nullptr;
11330 CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check;
11331 switch (ins->mir()->function()) {
11332 case UnaryMathFunction::Floor:
11333 funptr = floorf;
11334 check = CheckUnsafeCallWithABI::DontCheckOther;
11335 break;
11336 case UnaryMathFunction::Round:
11337 funptr = math_roundf_impl;
11338 break;
11339 case UnaryMathFunction::Trunc:
11340 funptr = math_truncf_impl;
11341 break;
11342 case UnaryMathFunction::Ceil:
11343 funptr = ceilf;
11344 check = CheckUnsafeCallWithABI::DontCheckOther;
11345 break;
11346 default:
11347 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"
, 11347); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown or unsupported float32 math function"
")"); do { *((volatile int*)__null) = 11347; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
11348 }
11349
11350 masm.callWithABI(DynamicFunction<Fn>(funptr), ABIType::Float32, check);
11351}
11352
11353void CodeGenerator::visitModD(LModD* ins) {
11354 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"
, 11354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 11354; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11355
11356 FloatRegister lhs = ToFloatRegister(ins->lhs());
11357 FloatRegister rhs = ToFloatRegister(ins->rhs());
11358
11359 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"
, 11359); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 11359; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11360
11361 using Fn = double (*)(double a, double b);
11362 masm.setupAlignedABICall();
11363 masm.passABIArg(lhs, ABIType::Float64);
11364 masm.passABIArg(rhs, ABIType::Float64);
11365 masm.callWithABI<Fn, NumberMod>(ABIType::Float64);
11366}
11367
11368void CodeGenerator::visitModPowTwoD(LModPowTwoD* ins) {
11369 FloatRegister lhs = ToFloatRegister(ins->lhs());
11370 uint32_t divisor = ins->divisor();
11371 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"
, 11371); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(divisor)"
")"); do { *((volatile int*)__null) = 11371; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11372
11373 FloatRegister output = ToFloatRegister(ins->output());
11374
11375 // Compute |n % d| using |copysign(n - (d * trunc(n / d)), n)|.
11376 //
11377 // This doesn't work if |d| isn't a power of two, because we may lose too much
11378 // precision. For example |Number.MAX_VALUE % 3 == 2|, but
11379 // |3 * trunc(Number.MAX_VALUE / 3) == Infinity|.
11380
11381 Label done;
11382 {
11383 ScratchDoubleScope scratch(masm);
11384
11385 // Subnormals can lead to performance degradation, which can make calling
11386 // |fmod| faster than this inline implementation. Work around this issue by
11387 // directly returning the input for any value in the interval ]-1, +1[.
11388 Label notSubnormal;
11389 masm.loadConstantDouble(1.0, scratch);
11390 masm.loadConstantDouble(-1.0, output);
11391 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, lhs, scratch,
11392 &notSubnormal);
11393 masm.branchDouble(Assembler::DoubleLessThanOrEqual, lhs, output,
11394 &notSubnormal);
11395
11396 masm.moveDouble(lhs, output);
11397 masm.jump(&done);
11398
11399 masm.bind(&notSubnormal);
11400
11401 if (divisor == 1) {
11402 // The pattern |n % 1 == 0| is used to detect integer numbers. We can skip
11403 // the multiplication by one in this case.
11404 masm.moveDouble(lhs, output);
11405 masm.nearbyIntDouble(RoundingMode::TowardsZero, output, scratch);
11406 masm.subDouble(scratch, output);
11407 } else {
11408 masm.loadConstantDouble(1.0 / double(divisor), scratch);
11409 masm.loadConstantDouble(double(divisor), output);
11410
11411 masm.mulDouble(lhs, scratch);
11412 masm.nearbyIntDouble(RoundingMode::TowardsZero, scratch, scratch);
11413 masm.mulDouble(output, scratch);
11414
11415 masm.moveDouble(lhs, output);
11416 masm.subDouble(scratch, output);
11417 }
11418 }
11419
11420 masm.copySignDouble(output, lhs, output);
11421 masm.bind(&done);
11422}
11423
11424void CodeGenerator::visitWasmBuiltinModD(LWasmBuiltinModD* ins) {
11425 masm.Push(InstanceReg);
11426 int32_t framePushedAfterInstance = masm.framePushed();
11427
11428 FloatRegister lhs = ToFloatRegister(ins->lhs());
11429 FloatRegister rhs = ToFloatRegister(ins->rhs());
11430
11431 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"
, 11431); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 11431; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11432
11433 masm.setupWasmABICall();
11434 masm.passABIArg(lhs, ABIType::Float64);
11435 masm.passABIArg(rhs, ABIType::Float64);
11436
11437 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
11438 masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD,
11439 mozilla::Some(instanceOffset), ABIType::Float64);
11440
11441 masm.Pop(InstanceReg);
11442}
11443
11444void CodeGenerator::visitClzI(LClzI* ins) {
11445 Register input = ToRegister(ins->input());
11446 Register output = ToRegister(ins->output());
11447 bool knownNotZero = ins->mir()->operandIsNeverZero();
11448
11449 masm.clz32(input, output, knownNotZero);
11450}
11451
11452void CodeGenerator::visitCtzI(LCtzI* ins) {
11453 Register input = ToRegister(ins->input());
11454 Register output = ToRegister(ins->output());
11455 bool knownNotZero = ins->mir()->operandIsNeverZero();
11456
11457 masm.ctz32(input, output, knownNotZero);
11458}
11459
11460void CodeGenerator::visitPopcntI(LPopcntI* ins) {
11461 Register input = ToRegister(ins->input());
11462 Register output = ToRegister(ins->output());
11463 Register temp = ToRegister(ins->temp0());
11464
11465 masm.popcnt32(input, output, temp);
11466}
11467
11468void CodeGenerator::visitClzI64(LClzI64* ins) {
11469 Register64 input = ToRegister64(ins->num());
11470 Register64 output = ToOutRegister64(ins);
11471
11472 masm.clz64(input, output);
11473}
11474
11475void CodeGenerator::visitCtzI64(LCtzI64* ins) {
11476 Register64 input = ToRegister64(ins->num());
11477 Register64 output = ToOutRegister64(ins);
11478
11479 masm.ctz64(input, output);
11480}
11481
11482void CodeGenerator::visitPopcntI64(LPopcntI64* ins) {
11483 Register64 input = ToRegister64(ins->num());
11484 Register64 output = ToOutRegister64(ins);
11485 Register temp = ToRegister(ins->temp0());
11486
11487 masm.popcnt64(input, output, temp);
11488}
11489
11490void CodeGenerator::visitBigIntAdd(LBigIntAdd* ins) {
11491 pushArg(ToRegister(ins->rhs()));
11492 pushArg(ToRegister(ins->lhs()));
11493
11494 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11495 callVM<Fn, BigInt::add>(ins);
11496}
11497
11498void CodeGenerator::visitBigIntSub(LBigIntSub* ins) {
11499 pushArg(ToRegister(ins->rhs()));
11500 pushArg(ToRegister(ins->lhs()));
11501
11502 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11503 callVM<Fn, BigInt::sub>(ins);
11504}
11505
11506void CodeGenerator::visitBigIntMul(LBigIntMul* ins) {
11507 pushArg(ToRegister(ins->rhs()));
11508 pushArg(ToRegister(ins->lhs()));
11509
11510 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11511 callVM<Fn, BigInt::mul>(ins);
11512}
11513
11514void CodeGenerator::visitBigIntDiv(LBigIntDiv* ins) {
11515 pushArg(ToRegister(ins->rhs()));
11516 pushArg(ToRegister(ins->lhs()));
11517
11518 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11519 callVM<Fn, BigInt::div>(ins);
11520}
11521
11522void CodeGenerator::visitBigIntMod(LBigIntMod* ins) {
11523 pushArg(ToRegister(ins->rhs()));
11524 pushArg(ToRegister(ins->lhs()));
11525
11526 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11527 callVM<Fn, BigInt::mod>(ins);
11528}
11529
11530void CodeGenerator::visitBigIntPow(LBigIntPow* ins) {
11531 pushArg(ToRegister(ins->rhs()));
11532 pushArg(ToRegister(ins->lhs()));
11533
11534 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11535 callVM<Fn, BigInt::pow>(ins);
11536}
11537
11538void CodeGenerator::visitBigIntBitAnd(LBigIntBitAnd* ins) {
11539 pushArg(ToRegister(ins->rhs()));
11540 pushArg(ToRegister(ins->lhs()));
11541
11542 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11543 callVM<Fn, BigInt::bitAnd>(ins);
11544}
11545
11546void CodeGenerator::visitBigIntBitOr(LBigIntBitOr* ins) {
11547 pushArg(ToRegister(ins->rhs()));
11548 pushArg(ToRegister(ins->lhs()));
11549
11550 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11551 callVM<Fn, BigInt::bitOr>(ins);
11552}
11553
11554void CodeGenerator::visitBigIntBitXor(LBigIntBitXor* ins) {
11555 pushArg(ToRegister(ins->rhs()));
11556 pushArg(ToRegister(ins->lhs()));
11557
11558 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11559 callVM<Fn, BigInt::bitXor>(ins);
11560}
11561
11562void CodeGenerator::visitBigIntLsh(LBigIntLsh* ins) {
11563 pushArg(ToRegister(ins->rhs()));
11564 pushArg(ToRegister(ins->lhs()));
11565
11566 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11567 callVM<Fn, BigInt::lsh>(ins);
11568}
11569
11570void CodeGenerator::visitBigIntRsh(LBigIntRsh* ins) {
11571 pushArg(ToRegister(ins->rhs()));
11572 pushArg(ToRegister(ins->lhs()));
11573
11574 using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt);
11575 callVM<Fn, BigInt::rsh>(ins);
11576}
11577
11578void CodeGenerator::visitBigIntIncrement(LBigIntIncrement* ins) {
11579 pushArg(ToRegister(ins->input()));
11580
11581 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
11582 callVM<Fn, BigInt::inc>(ins);
11583}
11584
11585void CodeGenerator::visitBigIntDecrement(LBigIntDecrement* ins) {
11586 pushArg(ToRegister(ins->input()));
11587
11588 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
11589 callVM<Fn, BigInt::dec>(ins);
11590}
11591
11592void CodeGenerator::visitBigIntNegate(LBigIntNegate* ins) {
11593 Register input = ToRegister(ins->input());
11594 Register temp = ToRegister(ins->temp0());
11595 Register output = ToRegister(ins->output());
11596
11597 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
11598 auto* ool =
11599 oolCallVM<Fn, BigInt::neg>(ins, ArgList(input), StoreRegisterTo(output));
11600
11601 // -0n == 0n
11602 Label lhsNonZero;
11603 masm.branchIfBigIntIsNonZero(input, &lhsNonZero);
11604 masm.movePtr(input, output);
11605 masm.jump(ool->rejoin());
11606 masm.bind(&lhsNonZero);
11607
11608 // Call into the VM when the input uses heap digits.
11609 masm.copyBigIntWithInlineDigits(input, output, temp, initialBigIntHeap(),
11610 ool->entry());
11611
11612 // Flip the sign bit.
11613 masm.xor32(Imm32(BigInt::signBitMask()),
11614 Address(output, BigInt::offsetOfFlags()));
11615
11616 masm.bind(ool->rejoin());
11617}
11618
11619void CodeGenerator::visitBigIntBitNot(LBigIntBitNot* ins) {
11620 pushArg(ToRegister(ins->input()));
11621
11622 using Fn = BigInt* (*)(JSContext*, HandleBigInt);
11623 callVM<Fn, BigInt::bitNot>(ins);
11624}
11625
11626void CodeGenerator::visitBigIntToIntPtr(LBigIntToIntPtr* ins) {
11627 Register input = ToRegister(ins->input());
11628 Register output = ToRegister(ins->output());
11629
11630 Label bail;
11631 masm.loadBigIntPtr(input, output, &bail);
11632 bailoutFrom(&bail, ins->snapshot());
11633}
11634
11635void CodeGenerator::visitIntPtrToBigInt(LIntPtrToBigInt* ins) {
11636 Register input = ToRegister(ins->input());
11637 Register temp = ToRegister(ins->temp0());
11638 Register output = ToRegister(ins->output());
11639
11640 using Fn = BigInt* (*)(JSContext*, intptr_t);
11641 auto* ool = oolCallVM<Fn, JS::BigInt::createFromIntPtr>(
11642 ins, ArgList(input), StoreRegisterTo(output));
11643
11644 masm.newGCBigInt(output, temp, initialBigIntHeap(), ool->entry());
11645 masm.movePtr(input, temp);
11646 masm.initializeBigIntPtr(output, temp);
11647
11648 masm.bind(ool->rejoin());
11649}
11650
11651void CodeGenerator::visitBigIntPtrAdd(LBigIntPtrAdd* ins) {
11652 Register lhs = ToRegister(ins->lhs());
11653 const LAllocation* rhs = ins->rhs();
11654 Register output = ToRegister(ins->output());
11655
11656 if (rhs->isConstant()) {
11657 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
11658 } else {
11659 masm.movePtr(ToRegister(rhs), output);
11660 }
11661
11662 Label bail;
11663 masm.branchAddPtr(Assembler::Overflow, lhs, output, &bail);
11664 bailoutFrom(&bail, ins->snapshot());
11665}
11666
11667void CodeGenerator::visitBigIntPtrSub(LBigIntPtrSub* ins) {
11668 Register lhs = ToRegister(ins->lhs());
11669 Register rhs = ToRegister(ins->rhs());
11670 Register output = ToRegister(ins->output());
11671
11672 Label bail;
11673 masm.movePtr(lhs, output);
11674 masm.branchSubPtr(Assembler::Overflow, rhs, output, &bail);
11675 bailoutFrom(&bail, ins->snapshot());
11676}
11677
11678void CodeGenerator::visitBigIntPtrMul(LBigIntPtrMul* ins) {
11679 Register lhs = ToRegister(ins->lhs());
11680 const LAllocation* rhs = ins->rhs();
11681 Register output = ToRegister(ins->output());
11682
11683 if (rhs->isConstant()) {
11684 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
11685 } else {
11686 masm.movePtr(ToRegister(rhs), output);
11687 }
11688
11689 Label bail;
11690 masm.branchMulPtr(Assembler::Overflow, lhs, output, &bail);
11691 bailoutFrom(&bail, ins->snapshot());
11692}
11693
11694void CodeGenerator::visitBigIntPtrDiv(LBigIntPtrDiv* ins) {
11695 Register lhs = ToRegister(ins->lhs());
11696 Register rhs = ToRegister(ins->rhs());
11697 Register output = ToRegister(ins->output());
11698
11699 // x / 0 throws an error.
11700 Label bail;
11701 if (ins->mir()->canBeDivideByZero()) {
11702 masm.branchPtr(Assembler::Equal, rhs, Imm32(0), &bail);
11703 }
11704
11705 static constexpr auto DigitMin = std::numeric_limits<
11706 mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min();
11707
11708 // Handle an integer overflow from INT{32,64}_MIN / -1.
11709 Label notOverflow;
11710 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), &notOverflow);
11711 masm.branchPtr(Assembler::Equal, rhs, Imm32(-1), &bail);
11712 masm.bind(&notOverflow);
11713
11714 emitBigIntPtrDiv(ins, lhs, rhs, output);
11715
11716 bailoutFrom(&bail, ins->snapshot());
11717}
11718
11719void CodeGenerator::visitBigIntPtrDivPowTwo(LBigIntPtrDivPowTwo* ins) {
11720 Register lhs = ToRegister(ins->lhs());
11721 Register output = ToRegister(ins->output());
11722 int32_t shift = ins->shift();
11723 bool negativeDivisor = ins->negativeDivisor();
11724
11725 masm.movePtr(lhs, output);
11726
11727 if (shift) {
11728 // Adjust the value so that shifting produces a correctly rounded result
11729 // when the numerator is negative.
11730 // See 10-1 "Signed Division by a Known Power of 2" in Henry S. Warren,
11731 // Jr.'s Hacker's Delight.
11732
11733 constexpr size_t bits = BigInt::DigitBits;
11734
11735 if (shift > 1) {
11736 // Copy the sign bit of the numerator. (= (2^bits - 1) or 0)
11737 masm.rshiftPtrArithmetic(Imm32(bits - 1), output);
11738 }
11739
11740 // Divide by 2^(bits - shift)
11741 // i.e. (= (2^bits - 1) / 2^(bits - shift) or 0)
11742 // i.e. (= (2^shift - 1) or 0)
11743 masm.rshiftPtr(Imm32(bits - shift), output);
11744
11745 // If signed, make any 1 bit below the shifted bits to bubble up, such that
11746 // once shifted the value would be rounded towards 0.
11747 masm.addPtr(lhs, output);
11748
11749 masm.rshiftPtrArithmetic(Imm32(shift), output);
11750
11751 if (negativeDivisor) {
11752 masm.negPtr(output);
11753 }
11754 } else if (negativeDivisor) {
11755 Label bail;
11756 masm.branchNegPtr(Assembler::Overflow, output, &bail);
11757 bailoutFrom(&bail, ins->snapshot());
11758 }
11759}
11760
11761void CodeGenerator::visitBigIntPtrMod(LBigIntPtrMod* ins) {
11762 Register lhs = ToRegister(ins->lhs());
11763 Register rhs = ToRegister(ins->rhs());
11764 Register output = ToRegister(ins->output());
11765 Register temp = ToRegister(ins->temp0());
11766
11767 // x % 0 throws an error.
11768 if (ins->mir()->canBeDivideByZero()) {
11769 bailoutCmpPtr(Assembler::Equal, rhs, Imm32(0), ins->snapshot());
11770 }
11771
11772 static constexpr auto DigitMin = std::numeric_limits<
11773 mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min();
11774
11775 masm.movePtr(lhs, temp);
11776
11777 // Handle an integer overflow from INT{32,64}_MIN / -1.
11778 Label notOverflow;
11779 masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), &notOverflow);
11780 masm.branchPtr(Assembler::NotEqual, rhs, Imm32(-1), &notOverflow);
11781 masm.movePtr(ImmWord(0), temp);
11782 masm.bind(&notOverflow);
11783
11784 emitBigIntPtrMod(ins, temp, rhs, output);
11785}
11786
11787void CodeGenerator::visitBigIntPtrModPowTwo(LBigIntPtrModPowTwo* ins) {
11788 Register lhs = ToRegister(ins->lhs());
11789 Register output = ToRegister(ins->output());
11790 Register temp = ToRegister(ins->temp0());
11791 int32_t shift = ins->shift();
11792
11793 masm.movePtr(lhs, output);
11794 masm.movePtr(ImmWord((uintptr_t(1) << shift) - uintptr_t(1)), temp);
11795
11796 // Switch based on sign of the lhs.
11797
11798 // Positive numbers are just a bitmask.
11799 Label negative;
11800 masm.branchTestPtr(Assembler::Signed, lhs, lhs, &negative);
11801
11802 masm.andPtr(temp, output);
11803
11804 Label done;
11805 masm.jump(&done);
11806
11807 // Negative numbers need a negate, bitmask, negate
11808 masm.bind(&negative);
11809
11810 masm.negPtr(output);
11811 masm.andPtr(temp, output);
11812 masm.negPtr(output);
11813
11814 masm.bind(&done);
11815}
11816
11817void CodeGenerator::visitBigIntPtrPow(LBigIntPtrPow* ins) {
11818 Register lhs = ToRegister(ins->lhs());
11819 Register rhs = ToRegister(ins->rhs());
11820 Register output = ToRegister(ins->output());
11821 Register temp0 = ToRegister(ins->temp0());
11822 Register temp1 = ToRegister(ins->temp1());
11823
11824 Label bail;
11825 masm.powPtr(lhs, rhs, output, temp0, temp1, &bail);
11826 bailoutFrom(&bail, ins->snapshot());
11827}
11828
11829void CodeGenerator::visitBigIntPtrBitAnd(LBigIntPtrBitAnd* ins) {
11830 Register lhs = ToRegister(ins->lhs());
11831 const LAllocation* rhs = ins->rhs();
11832 Register output = ToRegister(ins->output());
11833
11834 if (rhs->isConstant()) {
11835 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
11836 } else {
11837 masm.movePtr(ToRegister(rhs), output);
11838 }
11839 masm.andPtr(lhs, output);
11840}
11841
11842void CodeGenerator::visitBigIntPtrBitOr(LBigIntPtrBitOr* ins) {
11843 Register lhs = ToRegister(ins->lhs());
11844 const LAllocation* rhs = ins->rhs();
11845 Register output = ToRegister(ins->output());
11846
11847 if (rhs->isConstant()) {
11848 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
11849 } else {
11850 masm.movePtr(ToRegister(rhs), output);
11851 }
11852 masm.orPtr(lhs, output);
11853}
11854
11855void CodeGenerator::visitBigIntPtrBitXor(LBigIntPtrBitXor* ins) {
11856 Register lhs = ToRegister(ins->lhs());
11857 const LAllocation* rhs = ins->rhs();
11858 Register output = ToRegister(ins->output());
11859
11860 if (rhs->isConstant()) {
11861 masm.movePtr(ImmWord(ToIntPtr(rhs)), output);
11862 } else {
11863 masm.movePtr(ToRegister(rhs), output);
11864 }
11865 masm.xorPtr(lhs, output);
11866}
11867
11868void CodeGenerator::visitBigIntPtrLsh(LBigIntPtrLsh* ins) {
11869 Register lhs = ToRegister(ins->lhs());
11870 Register output = ToRegister(ins->output());
11871 Register temp = ToTempRegisterOrInvalid(ins->temp0());
11872 Register tempShift = ToTempRegisterOrInvalid(ins->temp1());
11873
11874 if (ins->rhs()->isConstant()) {
11875 intptr_t rhs = ToIntPtr(ins->rhs());
11876
11877 if (rhs >= intptr_t(BigInt::DigitBits)) {
11878 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11878); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11878; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11879
11880 // x << DigitBits with x != 0n always exceeds pointer-sized storage.
11881 masm.movePtr(ImmWord(0), output);
11882 bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot());
11883 } else if (rhs <= -intptr_t(BigInt::DigitBits)) {
11884 MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11884); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11884; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11885
11886 // x << -DigitBits == x >> DigitBits, which is either 0n or -1n.
11887 masm.movePtr(lhs, output);
11888 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output);
11889 } else if (rhs <= 0) {
11890 MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11890); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11890; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11891
11892 // |x << -y| is computed as |x >> y|.
11893 masm.movePtr(lhs, output);
11894 masm.rshiftPtrArithmetic(Imm32(-rhs), output);
11895 } else {
11896 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11896); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11896; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11897
11898 masm.movePtr(lhs, output);
11899 masm.lshiftPtr(Imm32(rhs), output);
11900
11901 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
11902 masm.movePtr(output, temp);
11903 masm.rshiftPtrArithmetic(Imm32(rhs), temp);
11904 bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot());
11905 }
11906 } else {
11907 Register rhs = ToRegister(ins->rhs());
11908
11909 Label done, bail;
11910 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11910); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11910; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11911
11912 masm.movePtr(lhs, output);
11913
11914 // 0n << x == 0n
11915 masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done);
11916
11917 // x << DigitBits with x != 0n always exceeds pointer-sized storage.
11918 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(BigInt::DigitBits),
11919 &bail);
11920
11921 // x << -DigitBits == x >> DigitBits, which is either 0n or -1n.
11922 Label shift;
11923 masm.branchPtr(Assembler::GreaterThan, rhs,
11924 Imm32(-int32_t(BigInt::DigitBits)), &shift);
11925 {
11926 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output);
11927 masm.jump(&done);
11928 }
11929 masm.bind(&shift);
11930
11931 // Move |rhs| into the designated shift register.
11932 masm.movePtr(rhs, tempShift);
11933
11934 // |x << -y| is computed as |x >> y|.
11935 Label leftShift;
11936 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &leftShift);
11937 {
11938 masm.negPtr(tempShift);
11939 masm.rshiftPtrArithmetic(tempShift, output);
11940 masm.jump(&done);
11941 }
11942 masm.bind(&leftShift);
11943
11944 masm.lshiftPtr(tempShift, output);
11945
11946 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
11947 masm.movePtr(output, temp);
11948 masm.rshiftPtrArithmetic(tempShift, temp);
11949 masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail);
11950
11951 masm.bind(&done);
11952 bailoutFrom(&bail, ins->snapshot());
11953 }
11954}
11955
11956void CodeGenerator::visitBigIntPtrRsh(LBigIntPtrRsh* ins) {
11957 Register lhs = ToRegister(ins->lhs());
11958 Register output = ToRegister(ins->output());
11959 Register temp = ToTempRegisterOrInvalid(ins->temp0());
11960 Register tempShift = ToTempRegisterOrInvalid(ins->temp1());
11961
11962 if (ins->rhs()->isConstant()) {
11963 intptr_t rhs = ToIntPtr(ins->rhs());
11964
11965 if (rhs <= -intptr_t(BigInt::DigitBits)) {
11966 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11966; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11967
11968 // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage.
11969 masm.movePtr(ImmWord(0), output);
11970 bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot());
11971 } else if (rhs >= intptr_t(BigInt::DigitBits)) {
11972 MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11972; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11973
11974 // x >> DigitBits is either 0n or -1n.
11975 masm.movePtr(lhs, output);
11976 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output);
11977 } else if (rhs < 0) {
11978 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11978); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11978; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11979
11980 // |x >> -y| is computed as |x << y|.
11981 masm.movePtr(lhs, output);
11982 masm.lshiftPtr(Imm32(-rhs), output);
11983
11984 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
11985 masm.movePtr(output, temp);
11986 masm.rshiftPtrArithmetic(Imm32(-rhs), temp);
11987 bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot());
11988 } else {
11989 MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11989); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11989; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11990
11991 masm.movePtr(lhs, output);
11992 masm.rshiftPtrArithmetic(Imm32(rhs), output);
11993 }
11994 } else {
11995 Register rhs = ToRegister(ins->rhs());
11996
11997 Label done, bail;
11998 MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 11998); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()"
")"); do { *((volatile int*)__null) = 11998; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11999
12000 masm.movePtr(lhs, output);
12001
12002 // 0n >> x == 0n
12003 masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done);
12004
12005 // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage.
12006 masm.branchPtr(Assembler::LessThanOrEqual, rhs,
12007 Imm32(-int32_t(BigInt::DigitBits)), &bail);
12008
12009 // x >> DigitBits is either 0n or -1n.
12010 Label shift;
12011 masm.branchPtr(Assembler::LessThan, rhs, Imm32(BigInt::DigitBits), &shift);
12012 {
12013 masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output);
12014 masm.jump(&done);
12015 }
12016 masm.bind(&shift);
12017
12018 // Move |rhs| into the designated shift register.
12019 masm.movePtr(rhs, tempShift);
12020
12021 // |x >> -y| is computed as |x << y|.
12022 Label rightShift;
12023 masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &rightShift);
12024 {
12025 masm.negPtr(tempShift);
12026 masm.lshiftPtr(tempShift, output);
12027
12028 // Check for overflow: ((lhs << rhs) >> rhs) == lhs.
12029 masm.movePtr(output, temp);
12030 masm.rshiftPtrArithmetic(tempShift, temp);
12031 masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail);
12032
12033 masm.jump(&done);
12034 }
12035 masm.bind(&rightShift);
12036
12037 masm.rshiftPtrArithmetic(tempShift, output);
12038
12039 masm.bind(&done);
12040 bailoutFrom(&bail, ins->snapshot());
12041 }
12042}
12043
12044void CodeGenerator::visitBigIntPtrBitNot(LBigIntPtrBitNot* ins) {
12045 Register input = ToRegister(ins->input());
12046 Register output = ToRegister(ins->output());
12047
12048 masm.movePtr(input, output);
12049 masm.notPtr(output);
12050}
12051
12052void CodeGenerator::visitInt32ToStringWithBase(LInt32ToStringWithBase* lir) {
12053 Register input = ToRegister(lir->input());
12054 RegisterOrInt32 base = ToRegisterOrInt32(lir->base());
12055 Register output = ToRegister(lir->output());
12056 Register temp0 = ToRegister(lir->temp0());
12057 Register temp1 = ToRegister(lir->temp1());
12058
12059 bool lowerCase = lir->mir()->lowerCase();
12060
12061 using Fn = JSLinearString* (*)(JSContext*, int32_t, int32_t, bool);
12062 if (base.is<Register>()) {
12063 auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>(
12064 lir, ArgList(input, base.as<Register>(), Imm32(lowerCase)),
12065 StoreRegisterTo(output));
12066
12067 LiveRegisterSet liveRegs = liveVolatileRegs(lir);
12068 masm.loadInt32ToStringWithBase(input, base.as<Register>(), output, temp0,
12069 temp1, gen->runtime->staticStrings(),
12070 liveRegs, lowerCase, ool->entry());
12071 masm.bind(ool->rejoin());
12072 } else {
12073 auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>(
12074 lir, ArgList(input, Imm32(base.as<int32_t>()), Imm32(lowerCase)),
12075 StoreRegisterTo(output));
12076
12077 masm.loadInt32ToStringWithBase(input, base.as<int32_t>(), output, temp0,
12078 temp1, gen->runtime->staticStrings(),
12079 lowerCase, ool->entry());
12080 masm.bind(ool->rejoin());
12081 }
12082}
12083
12084void CodeGenerator::visitNumberParseInt(LNumberParseInt* lir) {
12085 Register string = ToRegister(lir->string());
12086 Register radix = ToRegister(lir->radix());
12087 ValueOperand output = ToOutValue(lir);
12088 Register temp = ToRegister(lir->temp0());
12089
12090#ifdef DEBUG1
12091 Label ok;
12092 masm.branch32(Assembler::Equal, radix, Imm32(0), &ok);
12093 masm.branch32(Assembler::Equal, radix, Imm32(10), &ok);
12094 masm.assumeUnreachable("radix must be 0 or 10 for indexed value fast path");
12095 masm.bind(&ok);
12096#endif
12097
12098 // Use indexed value as fast path if possible.
12099 Label vmCall, done;
12100 masm.loadStringIndexValue(string, temp, &vmCall);
12101 masm.tagValue(JSVAL_TYPE_INT32, temp, output);
12102 masm.jump(&done);
12103 {
12104 masm.bind(&vmCall);
12105
12106 pushArg(radix);
12107 pushArg(string);
12108
12109 using Fn = bool (*)(JSContext*, HandleString, int32_t, MutableHandleValue);
12110 callVM<Fn, js::NumberParseInt>(lir);
12111 }
12112 masm.bind(&done);
12113}
12114
12115void CodeGenerator::visitDoubleParseInt(LDoubleParseInt* lir) {
12116 FloatRegister number = ToFloatRegister(lir->number());
12117 Register output = ToRegister(lir->output());
12118 FloatRegister temp = ToFloatRegister(lir->temp0());
12119
12120 Label bail;
12121 masm.branchDouble(Assembler::DoubleUnordered, number, number, &bail);
12122 masm.branchTruncateDoubleToInt32(number, output, &bail);
12123
12124 Label ok;
12125 masm.branch32(Assembler::NotEqual, output, Imm32(0), &ok);
12126 {
12127 // Accept both +0 and -0 and return 0.
12128 masm.loadConstantDouble(0.0, temp);
12129 masm.branchDouble(Assembler::DoubleEqual, number, temp, &ok);
12130
12131 // Fail if a non-zero input is in the exclusive range (-1, 1.0e-6).
12132 masm.loadConstantDouble(DOUBLE_DECIMAL_IN_SHORTEST_LOW, temp);
12133 masm.branchDouble(Assembler::DoubleLessThan, number, temp, &bail);
12134 }
12135 masm.bind(&ok);
12136
12137 bailoutFrom(&bail, lir->snapshot());
12138}
12139
12140void CodeGenerator::visitFloor(LFloor* lir) {
12141 FloatRegister input = ToFloatRegister(lir->input());
12142 Register output = ToRegister(lir->output());
12143
12144 Label bail;
12145 masm.floorDoubleToInt32(input, output, &bail);
12146 bailoutFrom(&bail, lir->snapshot());
12147}
12148
12149void CodeGenerator::visitFloorF(LFloorF* lir) {
12150 FloatRegister input = ToFloatRegister(lir->input());
12151 Register output = ToRegister(lir->output());
12152
12153 Label bail;
12154 masm.floorFloat32ToInt32(input, output, &bail);
12155 bailoutFrom(&bail, lir->snapshot());
12156}
12157
12158void CodeGenerator::visitCeil(LCeil* lir) {
12159 FloatRegister input = ToFloatRegister(lir->input());
12160 Register output = ToRegister(lir->output());
12161
12162 Label bail;
12163 masm.ceilDoubleToInt32(input, output, &bail);
12164 bailoutFrom(&bail, lir->snapshot());
12165}
12166
12167void CodeGenerator::visitCeilF(LCeilF* lir) {
12168 FloatRegister input = ToFloatRegister(lir->input());
12169 Register output = ToRegister(lir->output());
12170
12171 Label bail;
12172 masm.ceilFloat32ToInt32(input, output, &bail);
12173 bailoutFrom(&bail, lir->snapshot());
12174}
12175
12176void CodeGenerator::visitRound(LRound* lir) {
12177 FloatRegister input = ToFloatRegister(lir->input());
12178 FloatRegister temp = ToFloatRegister(lir->temp0());
12179 Register output = ToRegister(lir->output());
12180
12181 Label bail;
12182 masm.roundDoubleToInt32(input, output, temp, &bail);
12183 bailoutFrom(&bail, lir->snapshot());
12184}
12185
12186void CodeGenerator::visitRoundF(LRoundF* lir) {
12187 FloatRegister input = ToFloatRegister(lir->input());
12188 FloatRegister temp = ToFloatRegister(lir->temp0());
12189 Register output = ToRegister(lir->output());
12190
12191 Label bail;
12192 masm.roundFloat32ToInt32(input, output, temp, &bail);
12193 bailoutFrom(&bail, lir->snapshot());
12194}
12195
12196void CodeGenerator::visitTrunc(LTrunc* lir) {
12197 FloatRegister input = ToFloatRegister(lir->input());
12198 Register output = ToRegister(lir->output());
12199
12200 Label bail;
12201 masm.truncDoubleToInt32(input, output, &bail);
12202 bailoutFrom(&bail, lir->snapshot());
12203}
12204
12205void CodeGenerator::visitTruncF(LTruncF* lir) {
12206 FloatRegister input = ToFloatRegister(lir->input());
12207 Register output = ToRegister(lir->output());
12208
12209 Label bail;
12210 masm.truncFloat32ToInt32(input, output, &bail);
12211 bailoutFrom(&bail, lir->snapshot());
12212}
12213
12214void CodeGenerator::visitCompareS(LCompareS* lir) {
12215 JSOp op = lir->mir()->jsop();
12216 Register left = ToRegister(lir->left());
12217 Register right = ToRegister(lir->right());
12218 Register output = ToRegister(lir->output());
12219
12220 OutOfLineCode* ool = nullptr;
12221
12222 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
12223 if (op == JSOp::Eq || op == JSOp::StrictEq) {
12224 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
12225 lir, ArgList(left, right), StoreRegisterTo(output));
12226 } else if (op == JSOp::Ne || op == JSOp::StrictNe) {
12227 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
12228 lir, ArgList(left, right), StoreRegisterTo(output));
12229 } else if (op == JSOp::Lt) {
12230 ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>(
12231 lir, ArgList(left, right), StoreRegisterTo(output));
12232 } else if (op == JSOp::Le) {
12233 // Push the operands in reverse order for JSOp::Le:
12234 // - |left <= right| is implemented as |right >= left|.
12235 ool =
12236 oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>(
12237 lir, ArgList(right, left), StoreRegisterTo(output));
12238 } else if (op == JSOp::Gt) {
12239 // Push the operands in reverse order for JSOp::Gt:
12240 // - |left > right| is implemented as |right < left|.
12241 ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>(
12242 lir, ArgList(right, left), StoreRegisterTo(output));
12243 } else {
12244 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"
, 12244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ge"
")"); do { *((volatile int*)__null) = 12244; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12245 ool =
12246 oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>(
12247 lir, ArgList(left, right), StoreRegisterTo(output));
12248 }
12249
12250 masm.compareStrings(op, left, right, output, ool->entry());
12251
12252 masm.bind(ool->rejoin());
12253}
12254
12255void CodeGenerator::visitCompareSInline(LCompareSInline* lir) {
12256 JSOp op = lir->mir()->jsop();
12257 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"
, 12257); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12257; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12258
12259 Register input = ToRegister(lir->input());
12260 Register output = ToRegister(lir->output());
12261
12262 const JSLinearString* str = lir->constant();
12263 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"
, 12263); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() > 0"
")"); do { *((volatile int*)__null) = 12263; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12264
12265 OutOfLineCode* ool = nullptr;
12266
12267 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
12268 if (op == JSOp::Eq || op == JSOp::StrictEq) {
12269 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>(
12270 lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output));
12271 } else {
12272 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"
, 12272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ne || op == JSOp::StrictNe"
")"); do { *((volatile int*)__null) = 12272; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12273 ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>(
12274 lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output));
12275 }
12276
12277 Label compareChars;
12278 {
12279 Label notPointerEqual;
12280
12281 // If operands point to the same instance, the strings are trivially equal.
12282 masm.branchPtr(Assembler::NotEqual, input, ImmGCPtr(str), &notPointerEqual);
12283 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
12284 masm.jump(ool->rejoin());
12285
12286 masm.bind(&notPointerEqual);
12287
12288 Label setNotEqualResult;
12289
12290 if (str->isAtom()) {
12291 // Atoms cannot be equal to each other if they point to different strings.
12292 Imm32 atomBit(JSString::ATOM_BIT);
12293 masm.branchTest32(Assembler::NonZero,
12294 Address(input, JSString::offsetOfFlags()), atomBit,
12295 &setNotEqualResult);
12296 }
12297
12298 if (str->hasTwoByteChars()) {
12299 // Pure two-byte strings can't be equal to Latin-1 strings.
12300 JS::AutoCheckCannotGC nogc;
12301 if (!mozilla::IsUtf16Latin1(str->twoByteRange(nogc))) {
12302 masm.branchLatin1String(input, &setNotEqualResult);
12303 }
12304 }
12305
12306 // Strings of different length can never be equal.
12307 masm.branch32(Assembler::NotEqual,
12308 Address(input, JSString::offsetOfLength()),
12309 Imm32(str->length()), &setNotEqualResult);
12310
12311 if (str->isAtom()) {
12312 Label forwardedPtrEqual;
12313 masm.tryFastAtomize(input, output, output, &compareChars);
12314
12315 // We now have two atoms. Just check pointer equality.
12316 masm.branchPtr(Assembler::Equal, output, ImmGCPtr(str),
12317 &forwardedPtrEqual);
12318
12319 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
12320 masm.jump(ool->rejoin());
12321
12322 masm.bind(&forwardedPtrEqual);
12323 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
12324 masm.jump(ool->rejoin());
12325 } else {
12326 masm.jump(&compareChars);
12327 }
12328
12329 masm.bind(&setNotEqualResult);
12330 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
12331 masm.jump(ool->rejoin());
12332 }
12333
12334 masm.bind(&compareChars);
12335
12336 // Load the input string's characters.
12337 Register stringChars = output;
12338 masm.loadStringCharsForCompare(input, str, stringChars, ool->entry());
12339
12340 // Start comparing character by character.
12341 masm.compareStringChars(op, stringChars, str, output);
12342
12343 masm.bind(ool->rejoin());
12344}
12345
12346void CodeGenerator::visitCompareSSingle(LCompareSSingle* lir) {
12347 JSOp op = lir->jsop();
12348 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"
, 12348); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsRelationalOp(op)"
")"); do { *((volatile int*)__null) = 12348; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12349
12350 Register input = ToRegister(lir->input());
12351 Register output = ToRegister(lir->output());
12352 Register temp = ToRegister(lir->temp0());
12353
12354 const JSLinearString* str = lir->constant();
12355 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"
, 12355); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() == 1"
")"); do { *((volatile int*)__null) = 12355; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12356
12357 char16_t ch = str->latin1OrTwoByteChar(0);
12358
12359 masm.movePtr(input, temp);
12360
12361 // Check if the string is empty.
12362 Label compareLength;
12363 masm.branch32(Assembler::Equal, Address(temp, JSString::offsetOfLength()),
12364 Imm32(0), &compareLength);
12365
12366 // The first character is in the left-most rope child.
12367 Label notRope;
12368 masm.branchIfNotRope(temp, &notRope);
12369 {
12370 // Unwind ropes at the start if possible.
12371 Label unwindRope;
12372 masm.bind(&unwindRope);
12373 masm.loadRopeLeftChild(temp, output);
12374 masm.movePtr(output, temp);
12375
12376#ifdef DEBUG1
12377 Label notEmpty;
12378 masm.branch32(Assembler::NotEqual,
12379 Address(temp, JSString::offsetOfLength()), Imm32(0),
12380 &notEmpty);
12381 masm.assumeUnreachable("rope children are non-empty");
12382 masm.bind(&notEmpty);
12383#endif
12384
12385 // Otherwise keep unwinding ropes.
12386 masm.branchIfRope(temp, &unwindRope);
12387 }
12388 masm.bind(&notRope);
12389
12390 // Load the first character into |output|.
12391 auto loadFirstChar = [&](auto encoding) {
12392 masm.loadStringChars(temp, output, encoding);
12393 masm.loadChar(Address(output, 0), output, encoding);
12394 };
12395
12396 Label done;
12397 if (ch <= JSString::MAX_LATIN1_CHAR) {
12398 // Handle both encodings when the search character is Latin-1.
12399 Label twoByte, compare;
12400 masm.branchTwoByteString(temp, &twoByte);
12401
12402 loadFirstChar(CharEncoding::Latin1);
12403 masm.jump(&compare);
12404
12405 masm.bind(&twoByte);
12406 loadFirstChar(CharEncoding::TwoByte);
12407
12408 masm.bind(&compare);
12409 } else {
12410 // The search character is a two-byte character, so it can't be equal to any
12411 // character of a Latin-1 string.
12412 masm.move32(Imm32(int32_t(op == JSOp::Lt || op == JSOp::Le)), output);
12413 masm.branchLatin1String(temp, &done);
12414
12415 loadFirstChar(CharEncoding::TwoByte);
12416 }
12417
12418 // Compare the string length when the search character is equal to the
12419 // input's first character.
12420 masm.branch32(Assembler::Equal, output, Imm32(ch), &compareLength);
12421
12422 // Otherwise compute the result and jump to the end.
12423 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), output, Imm32(ch),
12424 output);
12425 masm.jump(&done);
12426
12427 // Compare the string length to compute the overall result.
12428 masm.bind(&compareLength);
12429 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false),
12430 Address(temp, JSString::offsetOfLength()), Imm32(1), output);
12431
12432 masm.bind(&done);
12433}
12434
12435void CodeGenerator::visitCompareBigInt(LCompareBigInt* lir) {
12436 JSOp op = lir->mir()->jsop();
12437 Register left = ToRegister(lir->left());
12438 Register right = ToRegister(lir->right());
12439 Register temp0 = ToRegister(lir->temp0());
12440 Register temp1 = ToRegister(lir->temp1());
12441 Register temp2 = ToRegister(lir->temp2());
12442 Register output = ToRegister(lir->output());
12443
12444 Label notSame;
12445 Label compareSign;
12446 Label compareLength;
12447 Label compareDigit;
12448
12449 Label* notSameSign;
12450 Label* notSameLength;
12451 Label* notSameDigit;
12452 if (IsEqualityOp(op)) {
12453 notSameSign = &notSame;
12454 notSameLength = &notSame;
12455 notSameDigit = &notSame;
12456 } else {
12457 notSameSign = &compareSign;
12458 notSameLength = &compareLength;
12459 notSameDigit = &compareDigit;
12460 }
12461
12462 masm.equalBigInts(left, right, temp0, temp1, temp2, output, notSameSign,
12463 notSameLength, notSameDigit);
12464
12465 Label done;
12466 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le ||
12467 op == JSOp::Ge),
12468 output);
12469 masm.jump(&done);
12470
12471 if (IsEqualityOp(op)) {
12472 masm.bind(&notSame);
12473 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
12474 } else {
12475 Label invertWhenNegative;
12476
12477 // There are two cases when sign(left) != sign(right):
12478 // 1. sign(left) = positive and sign(right) = negative,
12479 // 2. or the dual case with reversed signs.
12480 //
12481 // For case 1, |left| <cmp> |right| is true for cmp=Gt or cmp=Ge and false
12482 // for cmp=Lt or cmp=Le. Initialize the result for case 1 and handle case 2
12483 // with |invertWhenNegative|.
12484 masm.bind(&compareSign);
12485 masm.move32(Imm32(op == JSOp::Gt || op == JSOp::Ge), output);
12486 masm.jump(&invertWhenNegative);
12487
12488 // For sign(left) = sign(right) and len(digits(left)) != len(digits(right)),
12489 // we have to consider the two cases:
12490 // 1. len(digits(left)) < len(digits(right))
12491 // 2. len(digits(left)) > len(digits(right))
12492 //
12493 // For |left| <cmp> |right| with cmp=Lt:
12494 // Assume both BigInts are positive, then |left < right| is true for case 1
12495 // and false for case 2. When both are negative, the result is reversed.
12496 //
12497 // The other comparison operators can be handled similarly.
12498 //
12499 // |temp0| holds the digits length of the right-hand side operand.
12500 masm.bind(&compareLength);
12501 masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false),
12502 Address(left, BigInt::offsetOfLength()), temp0, output);
12503 masm.jump(&invertWhenNegative);
12504
12505 // Similar to the case above, compare the current digit to determine the
12506 // overall comparison result.
12507 //
12508 // |temp1| points to the current digit of the left-hand side operand.
12509 // |output| holds the current digit of the right-hand side operand.
12510 masm.bind(&compareDigit);
12511 masm.cmpPtrSet(JSOpToCondition(op, /* isSigned = */ false),
12512 Address(temp1, 0), output, output);
12513
12514 Label nonNegative;
12515 masm.bind(&invertWhenNegative);
12516 masm.branchIfBigIntIsNonNegative(left, &nonNegative);
12517 masm.xor32(Imm32(1), output);
12518 masm.bind(&nonNegative);
12519 }
12520
12521 masm.bind(&done);
12522}
12523
12524void CodeGenerator::visitCompareBigIntInt32(LCompareBigIntInt32* lir) {
12525 JSOp op = lir->mir()->jsop();
12526 Register left = ToRegister(lir->left());
12527 Register temp0 = ToRegister(lir->temp0());
12528 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
12529 Register output = ToRegister(lir->output());
12530
12531 Label ifTrue, ifFalse;
12532 if (lir->right()->isConstant()) {
12533 MOZ_ASSERT(temp1 == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 == InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 == InvalidReg))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("temp1 == InvalidReg"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp1 == InvalidReg"
")"); do { *((volatile int*)__null) = 12533; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12534
12535 Imm32 right = Imm32(ToInt32(lir->right()));
12536 masm.compareBigIntAndInt32(op, left, right, temp0, &ifTrue, &ifFalse);
12537 } else {
12538 MOZ_ASSERT(temp1 != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp1 != InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp1 != InvalidReg))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("temp1 != InvalidReg"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12538); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp1 != InvalidReg"
")"); do { *((volatile int*)__null) = 12538; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12539
12540 Register right = ToRegister(lir->right());
12541 masm.compareBigIntAndInt32(op, left, right, temp0, temp1, &ifTrue,
12542 &ifFalse);
12543 }
12544
12545 Label done;
12546 masm.bind(&ifFalse);
12547 masm.move32(Imm32(0), output);
12548 masm.jump(&done);
12549 masm.bind(&ifTrue);
12550 masm.move32(Imm32(1), output);
12551 masm.bind(&done);
12552}
12553
12554void CodeGenerator::visitCompareBigIntInt32AndBranch(
12555 LCompareBigIntInt32AndBranch* lir) {
12556 JSOp op = lir->cmpMir()->jsop();
12557 Register left = ToRegister(lir->left());
12558 Register temp1 = ToRegister(lir->temp1());
12559 Register temp2 = ToTempRegisterOrInvalid(lir->temp2());
12560
12561 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
12562 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
12563
12564 // compareBigIntAndInt32 falls through to the false case. If the next block
12565 // is the true case, negate the comparison so we can fall through.
12566 if (isNextBlock(lir->ifTrue()->lir())) {
12567 op = NegateCompareOp(op);
12568 std::swap(ifTrue, ifFalse);
12569 }
12570
12571 if (lir->right()->isConstant()) {
12572 MOZ_ASSERT(temp2 == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 == InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 == InvalidReg))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("temp2 == InvalidReg"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp2 == InvalidReg"
")"); do { *((volatile int*)__null) = 12572; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12573
12574 Imm32 right = Imm32(ToInt32(lir->right()));
12575 masm.compareBigIntAndInt32(op, left, right, temp1, ifTrue, ifFalse);
12576 } else {
12577 MOZ_ASSERT(temp2 != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(temp2 != InvalidReg)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(temp2 != InvalidReg))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("temp2 != InvalidReg"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12577); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp2 != InvalidReg"
")"); do { *((volatile int*)__null) = 12577; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12578
12579 Register right = ToRegister(lir->right());
12580 masm.compareBigIntAndInt32(op, left, right, temp1, temp2, ifTrue, ifFalse);
12581 }
12582
12583 if (!isNextBlock(lir->ifTrue()->lir())) {
12584 jumpToBlock(lir->ifFalse());
12585 }
12586}
12587
12588void CodeGenerator::visitCompareBigIntDouble(LCompareBigIntDouble* lir) {
12589 JSOp op = lir->mir()->jsop();
12590 Register left = ToRegister(lir->left());
12591 FloatRegister right = ToFloatRegister(lir->right());
12592 Register output = ToRegister(lir->output());
12593
12594 masm.setupAlignedABICall();
12595
12596 // Push the operands in reverse order for JSOp::Le and JSOp::Gt:
12597 // - |left <= right| is implemented as |right >= left|.
12598 // - |left > right| is implemented as |right < left|.
12599 if (op == JSOp::Le || op == JSOp::Gt) {
12600 masm.passABIArg(right, ABIType::Float64);
12601 masm.passABIArg(left);
12602 } else {
12603 masm.passABIArg(left);
12604 masm.passABIArg(right, ABIType::Float64);
12605 }
12606
12607 using FnBigIntNumber = bool (*)(BigInt*, double);
12608 using FnNumberBigInt = bool (*)(double, BigInt*);
12609 switch (op) {
12610 case JSOp::Eq: {
12611 masm.callWithABI<FnBigIntNumber,
12612 jit::BigIntNumberEqual<EqualityKind::Equal>>();
12613 break;
12614 }
12615 case JSOp::Ne: {
12616 masm.callWithABI<FnBigIntNumber,
12617 jit::BigIntNumberEqual<EqualityKind::NotEqual>>();
12618 break;
12619 }
12620 case JSOp::Lt: {
12621 masm.callWithABI<FnBigIntNumber,
12622 jit::BigIntNumberCompare<ComparisonKind::LessThan>>();
12623 break;
12624 }
12625 case JSOp::Gt: {
12626 masm.callWithABI<FnNumberBigInt,
12627 jit::NumberBigIntCompare<ComparisonKind::LessThan>>();
12628 break;
12629 }
12630 case JSOp::Le: {
12631 masm.callWithABI<
12632 FnNumberBigInt,
12633 jit::NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>>();
12634 break;
12635 }
12636 case JSOp::Ge: {
12637 masm.callWithABI<
12638 FnBigIntNumber,
12639 jit::BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>>();
12640 break;
12641 }
12642 default:
12643 MOZ_CRASH("unhandled op")do { do { } while (false); MOZ_ReportCrash("" "unhandled op",
"/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 12643); AnnotateMozCrashReason("MOZ_CRASH(" "unhandled op" ")"
); do { *((volatile int*)__null) = 12643; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
12644 }
12645
12646 masm.storeCallBoolResult(output);
12647}
12648
12649void CodeGenerator::visitCompareBigIntString(LCompareBigIntString* lir) {
12650 JSOp op = lir->mir()->jsop();
12651 Register left = ToRegister(lir->left());
12652 Register right = ToRegister(lir->right());
12653
12654 // Push the operands in reverse order for JSOp::Le and JSOp::Gt:
12655 // - |left <= right| is implemented as |right >= left|.
12656 // - |left > right| is implemented as |right < left|.
12657 if (op == JSOp::Le || op == JSOp::Gt) {
12658 pushArg(left);
12659 pushArg(right);
12660 } else {
12661 pushArg(right);
12662 pushArg(left);
12663 }
12664
12665 using FnBigIntString =
12666 bool (*)(JSContext*, HandleBigInt, HandleString, bool*);
12667 using FnStringBigInt =
12668 bool (*)(JSContext*, HandleString, HandleBigInt, bool*);
12669
12670 switch (op) {
12671 case JSOp::Eq: {
12672 constexpr auto Equal = EqualityKind::Equal;
12673 callVM<FnBigIntString, BigIntStringEqual<Equal>>(lir);
12674 break;
12675 }
12676 case JSOp::Ne: {
12677 constexpr auto NotEqual = EqualityKind::NotEqual;
12678 callVM<FnBigIntString, BigIntStringEqual<NotEqual>>(lir);
12679 break;
12680 }
12681 case JSOp::Lt: {
12682 constexpr auto LessThan = ComparisonKind::LessThan;
12683 callVM<FnBigIntString, BigIntStringCompare<LessThan>>(lir);
12684 break;
12685 }
12686 case JSOp::Gt: {
12687 constexpr auto LessThan = ComparisonKind::LessThan;
12688 callVM<FnStringBigInt, StringBigIntCompare<LessThan>>(lir);
12689 break;
12690 }
12691 case JSOp::Le: {
12692 constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual;
12693 callVM<FnStringBigInt, StringBigIntCompare<GreaterThanOrEqual>>(lir);
12694 break;
12695 }
12696 case JSOp::Ge: {
12697 constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual;
12698 callVM<FnBigIntString, BigIntStringCompare<GreaterThanOrEqual>>(lir);
12699 break;
12700 }
12701 default:
12702 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"
, 12702); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected compare op"
")"); do { *((volatile int*)__null) = 12702; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
12703 }
12704}
12705
12706void CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir) {
12707 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"
, 12708); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12708; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
12708 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"
, 12708); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12708; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12709
12710 JSOp op = lir->mir()->jsop();
12711 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"
, 12711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12711; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12712
12713 const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedV::ValueIndex);
12714 Register output = ToRegister(lir->output());
12715
12716 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
12717 if (!intact) {
12718 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
12719 addOutOfLineCode(ool, lir->mir());
12720
12721 Label* nullOrLikeUndefined = ool->label1();
12722 Label* notNullOrLikeUndefined = ool->label2();
12723
12724 {
12725 ScratchTagScope tag(masm, value);
12726 masm.splitTagForTest(value, tag);
12727
12728 masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
12729 masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
12730
12731 // Check whether it's a truthy object or a falsy object that emulates
12732 // undefined.
12733 masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
12734 }
12735
12736 Register objreg =
12737 masm.extractObject(value, ToTempUnboxRegister(lir->temp0()));
12738 branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined,
12739 notNullOrLikeUndefined, output, ool);
12740 // fall through
12741
12742 Label done;
12743
12744 // It's not null or undefined, and if it's an object it doesn't
12745 // emulate undefined, so it's not like undefined.
12746 masm.move32(Imm32(op == JSOp::Ne), output);
12747 masm.jump(&done);
12748
12749 masm.bind(nullOrLikeUndefined);
12750 masm.move32(Imm32(op == JSOp::Eq), output);
12751
12752 // Both branches meet here.
12753 masm.bind(&done);
12754 } else {
12755 Label nullOrUndefined, notNullOrLikeUndefined;
12756#if defined(DEBUG1) || defined(FUZZING)
12757 Register objreg = Register::Invalid();
12758#endif
12759 {
12760 ScratchTagScope tag(masm, value);
12761 masm.splitTagForTest(value, tag);
12762
12763 masm.branchTestNull(Assembler::Equal, tag, &nullOrUndefined);
12764 masm.branchTestUndefined(Assembler::Equal, tag, &nullOrUndefined);
12765
12766#if defined(DEBUG1) || defined(FUZZING)
12767 // Check whether it's a truthy object or a falsy object that emulates
12768 // undefined.
12769 masm.branchTestObject(Assembler::NotEqual, tag, &notNullOrLikeUndefined);
12770 objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp0()));
12771#endif
12772 }
12773
12774#if defined(DEBUG1) || defined(FUZZING)
12775 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
12776 masm.bind(&notNullOrLikeUndefined);
12777#endif
12778
12779 Label done;
12780
12781 // It's not null or undefined, and if it's an object it doesn't
12782 // emulate undefined.
12783 masm.move32(Imm32(op == JSOp::Ne), output);
12784 masm.jump(&done);
12785
12786 masm.bind(&nullOrUndefined);
12787 masm.move32(Imm32(op == JSOp::Eq), output);
12788
12789 // Both branches meet here.
12790 masm.bind(&done);
12791 }
12792}
12793
12794void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV(
12795 LIsNullOrLikeUndefinedAndBranchV* lir) {
12796 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"
, 12797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12797; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
12797 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"
, 12797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12797; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12798
12799 JSOp op = lir->cmpMir()->jsop();
12800 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"
, 12800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12800; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12801
12802 const ValueOperand value =
12803 ToValue(lir, LIsNullOrLikeUndefinedAndBranchV::Value);
12804
12805 MBasicBlock* ifTrue = lir->ifTrue();
12806 MBasicBlock* ifFalse = lir->ifFalse();
12807
12808 if (op == JSOp::Ne) {
12809 // Swap branches.
12810 std::swap(ifTrue, ifFalse);
12811 }
12812
12813 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
12814
12815 Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
12816 Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
12817
12818 {
12819 ScratchTagScope tag(masm, value);
12820 masm.splitTagForTest(value, tag);
12821
12822 masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
12823 masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
12824
12825 masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
12826 }
12827
12828 bool extractObject = !intact;
Value stored to 'extractObject' during its initialization is never read
12829#if defined(DEBUG1) || defined(FUZZING)
12830 // always extract objreg if we're in debug and
12831 // assertObjectDoesNotEmulateUndefined;
12832 extractObject = true;
12833#endif
12834
12835 Register objreg = Register::Invalid();
12836 Register scratch = ToRegister(lir->temp());
12837 if (extractObject) {
12838 objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
12839 }
12840 if (!intact) {
12841 // Objects that emulate undefined are loosely equal to null/undefined.
12842 OutOfLineTestObject* ool = new (alloc()) OutOfLineTestObject();
12843 addOutOfLineCode(ool, lir->cmpMir());
12844 testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch,
12845 ool);
12846 } else {
12847 assertObjectDoesNotEmulateUndefined(objreg, scratch, lir->cmpMir());
12848 // Bug 1874905. This would be nice to optimize out at the MIR level.
12849 masm.jump(ifFalseLabel);
12850 }
12851}
12852
12853void CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir) {
12854 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"
, 12855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12855; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
12855 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"
, 12855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12855; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12856 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"
, 12856); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->lhs()->type() == MIRType::Object"
")"); do { *((volatile int*)__null) = 12856; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12857
12858 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
12859 JSOp op = lir->mir()->jsop();
12860 Register output = ToRegister(lir->output());
12861 Register objreg = ToRegister(lir->input());
12862 if (!intact) {
12863 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"
, 12864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { *
((volatile int*)__null) = 12864; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
12864 "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"
, 12864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { *
((volatile int*)__null) = 12864; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
12865
12866 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
12867 addOutOfLineCode(ool, lir->mir());
12868
12869 Label* emulatesUndefined = ool->label1();
12870 Label* doesntEmulateUndefined = ool->label2();
12871
12872 branchTestObjectEmulatesUndefined(objreg, emulatesUndefined,
12873 doesntEmulateUndefined, output, ool);
12874
12875 Label done;
12876
12877 masm.move32(Imm32(op == JSOp::Ne), output);
12878 masm.jump(&done);
12879
12880 masm.bind(emulatesUndefined);
12881 masm.move32(Imm32(op == JSOp::Eq), output);
12882 masm.bind(&done);
12883 } else {
12884 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
12885 masm.move32(Imm32(op == JSOp::Ne), output);
12886 }
12887}
12888
12889void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT(
12890 LIsNullOrLikeUndefinedAndBranchT* lir) {
12891 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"
, 12892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12892; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
12892 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"
, 12892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12892; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12893 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"
, 12893); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->lhs()->type() == MIRType::Object"
")"); do { *((volatile int*)__null) = 12893; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12894
12895 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
12896
12897 JSOp op = lir->cmpMir()->jsop();
12898 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"
, 12898); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)"
") (" "Strict equality should have been folded" ")"); do { *
((volatile int*)__null) = 12898; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false)
;
12899
12900 MBasicBlock* ifTrue = lir->ifTrue();
12901 MBasicBlock* ifFalse = lir->ifFalse();
12902
12903 if (op == JSOp::Ne) {
12904 // Swap branches.
12905 std::swap(ifTrue, ifFalse);
12906 }
12907
12908 Register input = ToRegister(lir->getOperand(0));
12909 Register scratch = ToRegister(lir->temp());
12910 Label* ifTrueLabel = getJumpLabelForBranch(ifTrue);
12911 Label* ifFalseLabel = getJumpLabelForBranch(ifFalse);
12912
12913 if (intact) {
12914 // Bug 1874905. Ideally branches like this would be optimized out.
12915 assertObjectDoesNotEmulateUndefined(input, scratch, lir->mir());
12916 masm.jump(ifFalseLabel);
12917 } else {
12918 auto* ool = new (alloc()) OutOfLineTestObject();
12919 addOutOfLineCode(ool, lir->cmpMir());
12920
12921 // Objects that emulate undefined are loosely equal to null/undefined.
12922 testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool);
12923 }
12924}
12925
12926void CodeGenerator::visitIsNull(LIsNull* lir) {
12927 MCompare::CompareType compareType = lir->mir()->compareType();
12928 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"
, 12928); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12928; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12929
12930 JSOp op = lir->mir()->jsop();
12931 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"
, 12931); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12931; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12932
12933 const ValueOperand value = ToValue(lir, LIsNull::ValueIndex);
12934 Register output = ToRegister(lir->output());
12935
12936 Assembler::Condition cond = JSOpToCondition(compareType, op);
12937 masm.testNullSet(cond, value, output);
12938}
12939
12940void CodeGenerator::visitIsUndefined(LIsUndefined* lir) {
12941 MCompare::CompareType compareType = lir->mir()->compareType();
12942 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"
, 12942); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined"
")"); do { *((volatile int*)__null) = 12942; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12943
12944 JSOp op = lir->mir()->jsop();
12945 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"
, 12945); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12945; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12946
12947 const ValueOperand value = ToValue(lir, LIsUndefined::ValueIndex);
12948 Register output = ToRegister(lir->output());
12949
12950 Assembler::Condition cond = JSOpToCondition(compareType, op);
12951 masm.testUndefinedSet(cond, value, output);
12952}
12953
12954void CodeGenerator::visitIsNullAndBranch(LIsNullAndBranch* lir) {
12955 MCompare::CompareType compareType = lir->cmpMir()->compareType();
12956 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"
, 12956); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null"
")"); do { *((volatile int*)__null) = 12956; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12957
12958 JSOp op = lir->cmpMir()->jsop();
12959 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"
, 12959); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12959; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12960
12961 const ValueOperand value = ToValue(lir, LIsNullAndBranch::Value);
12962
12963 Assembler::Condition cond = JSOpToCondition(compareType, op);
12964
12965 MBasicBlock* ifTrue = lir->ifTrue();
12966 MBasicBlock* ifFalse = lir->ifFalse();
12967
12968 if (isNextBlock(ifFalse->lir())) {
12969 masm.branchTestNull(cond, value, getJumpLabelForBranch(ifTrue));
12970 } else {
12971 masm.branchTestNull(Assembler::InvertCondition(cond), value,
12972 getJumpLabelForBranch(ifFalse));
12973 jumpToBlock(ifTrue);
12974 }
12975}
12976
12977void CodeGenerator::visitIsUndefinedAndBranch(LIsUndefinedAndBranch* lir) {
12978 MCompare::CompareType compareType = lir->cmpMir()->compareType();
12979 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"
, 12979); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined"
")"); do { *((volatile int*)__null) = 12979; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12980
12981 JSOp op = lir->cmpMir()->jsop();
12982 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"
, 12982); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)"
")"); do { *((volatile int*)__null) = 12982; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12983
12984 const ValueOperand value = ToValue(lir, LIsUndefinedAndBranch::Value);
12985
12986 Assembler::Condition cond = JSOpToCondition(compareType, op);
12987
12988 MBasicBlock* ifTrue = lir->ifTrue();
12989 MBasicBlock* ifFalse = lir->ifFalse();
12990
12991 if (isNextBlock(ifFalse->lir())) {
12992 masm.branchTestUndefined(cond, value, getJumpLabelForBranch(ifTrue));
12993 } else {
12994 masm.branchTestUndefined(Assembler::InvertCondition(cond), value,
12995 getJumpLabelForBranch(ifFalse));
12996 jumpToBlock(ifTrue);
12997 }
12998}
12999
13000void CodeGenerator::visitSameValueDouble(LSameValueDouble* lir) {
13001 FloatRegister left = ToFloatRegister(lir->left());
13002 FloatRegister right = ToFloatRegister(lir->right());
13003 FloatRegister temp = ToFloatRegister(lir->temp0());
13004 Register output = ToRegister(lir->output());
13005
13006 masm.sameValueDouble(left, right, temp, output);
13007}
13008
13009void CodeGenerator::visitSameValue(LSameValue* lir) {
13010 ValueOperand lhs = ToValue(lir, LSameValue::LhsIndex);
13011 ValueOperand rhs = ToValue(lir, LSameValue::RhsIndex);
13012 Register output = ToRegister(lir->output());
13013
13014 using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*);
13015 OutOfLineCode* ool =
13016 oolCallVM<Fn, SameValue>(lir, ArgList(lhs, rhs), StoreRegisterTo(output));
13017
13018 // First check to see if the values have identical bits.
13019 // This is correct for SameValue because SameValue(NaN,NaN) is true,
13020 // and SameValue(0,-0) is false.
13021 masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(),
13022 ool->entry());
13023 masm.move32(Imm32(1), output);
13024
13025 // If this fails, call SameValue.
13026 masm.bind(ool->rejoin());
13027}
13028
13029void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs,
13030 Register output) {
13031 using Fn =
13032 JSString* (*)(JSContext*, HandleString, HandleString, js::gc::Heap);
13033 OutOfLineCode* ool = oolCallVM<Fn, ConcatStrings<CanGC>>(
13034 lir, ArgList(lhs, rhs, static_cast<Imm32>(int32_t(gc::Heap::Default))),
13035 StoreRegisterTo(output));
13036
13037 JitCode* stringConcatStub =
13038 snapshot_->getZoneStub(JitZone::StubKind::StringConcat);
13039 masm.call(stringConcatStub);
13040 masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
13041
13042 masm.bind(ool->rejoin());
13043}
13044
13045void CodeGenerator::visitConcat(LConcat* lir) {
13046 Register lhs = ToRegister(lir->lhs());
13047 Register rhs = ToRegister(lir->rhs());
13048
13049 Register output = ToRegister(lir->output());
13050
13051 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"
, 13051); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs == CallTempReg0"
")"); do { *((volatile int*)__null) = 13051; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13052 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"
, 13052); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs == CallTempReg1"
")"); do { *((volatile int*)__null) = 13052; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13053 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"
, 13053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp0()) == CallTempReg0"
")"); do { *((volatile int*)__null) = 13053; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13054 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"
, 13054); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp1()) == CallTempReg1"
")"); do { *((volatile int*)__null) = 13054; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13055 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"
, 13055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp2()) == CallTempReg2"
")"); do { *((volatile int*)__null) = 13055; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13056 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"
, 13056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp3()) == CallTempReg3"
")"); do { *((volatile int*)__null) = 13056; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13057 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"
, 13057); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp4()) == CallTempReg4"
")"); do { *((volatile int*)__null) = 13057; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13058 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"
, 13058); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == CallTempReg5"
")"); do { *((volatile int*)__null) = 13058; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13059
13060 emitConcat(lir, lhs, rhs, output);
13061}
13062
13063static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
13064 Register len, Register byteOpScratch,
13065 CharEncoding fromEncoding, CharEncoding toEncoding,
13066 size_t maximumLength = SIZE_MAX(18446744073709551615UL)) {
13067 // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0
13068 // (checked below in debug builds), and when done |to| must point to the
13069 // next available char.
13070
13071#ifdef DEBUG1
13072 Label ok;
13073 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
13074 masm.assumeUnreachable("Length should be greater than 0.");
13075 masm.bind(&ok);
13076
13077 if (maximumLength != SIZE_MAX(18446744073709551615UL)) {
13078 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"
, 13078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maximumLength <= (2147483647)"
") (" "maximum length fits into int32" ")"); do { *((volatile
int*)__null) = 13078; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
13079
13080 Label ok;
13081 masm.branchPtr(Assembler::BelowOrEqual, len, Imm32(maximumLength), &ok);
13082 masm.assumeUnreachable("Length should not exceed maximum length.");
13083 masm.bind(&ok);
13084 }
13085#endif
13086
13087 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"
, 13088); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1"
")"); do { *((volatile int*)__null) = 13088; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
13088 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"
, 13088); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1"
")"); do { *((volatile int*)__null) = 13088; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
13089
13090 size_t fromWidth =
13091 fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
13092 size_t toWidth =
13093 toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t);
13094
13095 // Try to copy multiple characters at once when both encoding are equal.
13096 if (fromEncoding == toEncoding) {
13097 constexpr size_t ptrWidth = sizeof(uintptr_t);
13098
13099 // Copy |width| bytes and then adjust |from| and |to|.
13100 auto copyCharacters = [&](size_t width) {
13101 static_assert(ptrWidth <= 8, "switch handles only up to eight bytes");
13102
13103 switch (width) {
13104 case 1:
13105 masm.load8ZeroExtend(Address(from, 0), byteOpScratch);
13106 masm.store8(byteOpScratch, Address(to, 0));
13107 break;
13108 case 2:
13109 masm.load16ZeroExtend(Address(from, 0), byteOpScratch);
13110 masm.store16(byteOpScratch, Address(to, 0));
13111 break;
13112 case 4:
13113 masm.load32(Address(from, 0), byteOpScratch);
13114 masm.store32(byteOpScratch, Address(to, 0));
13115 break;
13116 case 8:
13117 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"
, 13117); AnnotateMozCrashReason("MOZ_ASSERT" "(" "width == ptrWidth"
")"); do { *((volatile int*)__null) = 13117; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13118 masm.loadPtr(Address(from, 0), byteOpScratch);
13119 masm.storePtr(byteOpScratch, Address(to, 0));
13120 break;
13121 }
13122
13123 masm.addPtr(Imm32(width), from);
13124 masm.addPtr(Imm32(width), to);
13125 };
13126
13127 // First align |len| to pointer width.
13128 Label done;
13129 for (size_t width = fromWidth; width < ptrWidth; width *= 2) {
13130 // Number of characters which fit into |width| bytes.
13131 size_t charsPerWidth = width / fromWidth;
13132
13133 if (charsPerWidth < maximumLength) {
13134 Label next;
13135 masm.branchTest32(Assembler::Zero, len, Imm32(charsPerWidth), &next);
13136
13137 copyCharacters(width);
13138
13139 masm.branchSub32(Assembler::Zero, Imm32(charsPerWidth), len, &done);
13140 masm.bind(&next);
13141 } else if (charsPerWidth == maximumLength) {
13142 copyCharacters(width);
13143 masm.sub32(Imm32(charsPerWidth), len);
13144 }
13145 }
13146
13147 size_t maxInlineLength;
13148 if (fromEncoding == CharEncoding::Latin1) {
13149 maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1;
13150 } else {
13151 maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
13152 }
13153
13154 // Number of characters which fit into a single register.
13155 size_t charsPerPtr = ptrWidth / fromWidth;
13156
13157 // Unroll small loops.
13158 constexpr size_t unrollLoopLimit = 3;
13159 size_t loopCount = std::min(maxInlineLength, maximumLength) / charsPerPtr;
13160
13161#ifdef JS_64BIT1
13162 static constexpr size_t latin1MaxInlineByteLength =
13163 JSFatInlineString::MAX_LENGTH_LATIN1 * sizeof(char);
13164 static constexpr size_t twoByteMaxInlineByteLength =
13165 JSFatInlineString::MAX_LENGTH_TWO_BYTE * sizeof(char16_t);
13166
13167 // |unrollLoopLimit| should be large enough to allow loop unrolling on
13168 // 64-bit targets.
13169 static_assert(latin1MaxInlineByteLength / ptrWidth == unrollLoopLimit,
13170 "Latin-1 loops are unrolled on 64-bit");
13171 static_assert(twoByteMaxInlineByteLength / ptrWidth == unrollLoopLimit,
13172 "Two-byte loops are unrolled on 64-bit");
13173#endif
13174
13175 if (loopCount <= unrollLoopLimit) {
13176 Label labels[unrollLoopLimit];
13177
13178 // Check up front how many characters can be copied.
13179 for (size_t i = 1; i < loopCount; i++) {
13180 masm.branch32(Assembler::Below, len, Imm32((i + 1) * charsPerPtr),
13181 &labels[i]);
13182 }
13183
13184 // Generate the unrolled loop body.
13185 for (size_t i = loopCount; i > 0; i--) {
13186 copyCharacters(ptrWidth);
13187 masm.sub32(Imm32(charsPerPtr), len);
13188
13189 // Jump target for the previous length check.
13190 if (i != 1) {
13191 masm.bind(&labels[i - 1]);
13192 }
13193 }
13194 } else {
13195 Label start;
13196 masm.bind(&start);
13197 copyCharacters(ptrWidth);
13198 masm.branchSub32(Assembler::NonZero, Imm32(charsPerPtr), len, &start);
13199 }
13200
13201 masm.bind(&done);
13202 } else {
13203 Label start;
13204 masm.bind(&start);
13205 masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding);
13206 masm.storeChar(byteOpScratch, Address(to, 0), toEncoding);
13207 masm.addPtr(Imm32(fromWidth), from);
13208 masm.addPtr(Imm32(toWidth), to);
13209 masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start);
13210 }
13211}
13212
13213static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
13214 Register len, Register byteOpScratch,
13215 CharEncoding encoding, size_t maximumLength) {
13216 CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding,
13217 maximumLength);
13218}
13219
13220static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input,
13221 Register destChars, Register temp1,
13222 Register temp2) {
13223 // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may
13224 // have to inflate.
13225
13226 Label isLatin1, done;
13227 masm.loadStringLength(input, temp1);
13228 masm.branchLatin1String(input, &isLatin1);
13229 {
13230 masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
13231 masm.movePtr(temp2, input);
13232 CopyStringChars(masm, destChars, input, temp1, temp2,
13233 CharEncoding::TwoByte);
13234 masm.jump(&done);
13235 }
13236 masm.bind(&isLatin1);
13237 {
13238 masm.loadStringChars(input, temp2, CharEncoding::Latin1);
13239 masm.movePtr(temp2, input);
13240 CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1,
13241 CharEncoding::TwoByte);
13242 }
13243 masm.bind(&done);
13244}
13245
13246static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output,
13247 Register length, Register temp,
13248 gc::Heap initialStringHeap,
13249 Label* failure,
13250 CharEncoding encoding) {
13251#ifdef DEBUG1
13252 size_t maxInlineLength;
13253 if (encoding == CharEncoding::Latin1) {
13254 maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1;
13255 } else {
13256 maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE;
13257 }
13258
13259 Label ok;
13260 masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok);
13261 masm.assumeUnreachable("string length too large to be allocated as inline");
13262 masm.bind(&ok);
13263#endif
13264
13265 size_t maxThinInlineLength;
13266 if (encoding == CharEncoding::Latin1) {
13267 maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1;
13268 } else {
13269 maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE;
13270 }
13271
13272 Label isFat, allocDone;
13273 masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat);
13274 {
13275 uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS;
13276 if (encoding == CharEncoding::Latin1) {
13277 flags |= JSString::LATIN1_CHARS_BIT;
13278 }
13279 masm.newGCString(output, temp, initialStringHeap, failure);
13280 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13281 masm.jump(&allocDone);
13282 }
13283 masm.bind(&isFat);
13284 {
13285 uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS;
13286 if (encoding == CharEncoding::Latin1) {
13287 flags |= JSString::LATIN1_CHARS_BIT;
13288 }
13289 masm.newGCFatInlineString(output, temp, initialStringHeap, failure);
13290 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13291 }
13292 masm.bind(&allocDone);
13293
13294 // Store length.
13295 masm.store32(length, Address(output, JSString::offsetOfLength()));
13296}
13297
13298static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs,
13299 Register output, Register temp1, Register temp2,
13300 Register temp3, gc::Heap initialStringHeap,
13301 Label* failure, CharEncoding encoding) {
13302 JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)",
13303 (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
13304
13305 // State: result length in temp2.
13306
13307 // Ensure both strings are linear.
13308 masm.branchIfRope(lhs, failure);
13309 masm.branchIfRope(rhs, failure);
13310
13311 // Allocate a JSThinInlineString or JSFatInlineString.
13312 AllocateThinOrFatInlineString(masm, output, temp2, temp1, initialStringHeap,
13313 failure, encoding);
13314
13315 // Load chars pointer in temp2.
13316 masm.loadInlineStringCharsForStore(output, temp2);
13317
13318 auto copyChars = [&](Register src) {
13319 if (encoding == CharEncoding::TwoByte) {
13320 CopyStringCharsMaybeInflate(masm, src, temp2, temp1, temp3);
13321 } else {
13322 masm.loadStringLength(src, temp3);
13323 masm.loadStringChars(src, temp1, CharEncoding::Latin1);
13324 masm.movePtr(temp1, src);
13325 CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1);
13326 }
13327 };
13328
13329 // Copy lhs chars. Note that this advances temp2 to point to the next
13330 // char. This also clobbers the lhs register.
13331 copyChars(lhs);
13332
13333 // Copy rhs chars. Clobbers the rhs register.
13334 copyChars(rhs);
13335}
13336
13337void CodeGenerator::visitSubstr(LSubstr* lir) {
13338 Register string = ToRegister(lir->string());
13339 Register begin = ToRegister(lir->begin());
13340 Register length = ToRegister(lir->length());
13341 Register output = ToRegister(lir->output());
13342 Register temp0 = ToRegister(lir->temp0());
13343 Register temp2 = ToRegister(lir->temp2());
13344
13345 // On x86 there are not enough registers. In that case reuse the string
13346 // register as temporary.
13347 Register temp1 =
13348 lir->temp1()->isBogusTemp() ? string : ToRegister(lir->temp1());
13349
13350 size_t maximumLength = SIZE_MAX(18446744073709551615UL);
13351
13352 Range* range = lir->mir()->length()->range();
13353 if (range && range->hasInt32UpperBound()) {
13354 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"
, 13354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "range->upper() >= 0"
")"); do { *((volatile int*)__null) = 13354; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
13355 maximumLength = size_t(range->upper());
13356 }
13357
13358 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <=
13359 JSThinInlineString::MAX_LENGTH_LATIN1);
13360
13361 static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE <=
13362 JSFatInlineString::MAX_LENGTH_LATIN1);
13363
13364 bool tryFatInlineOrDependent =
13365 maximumLength > JSThinInlineString::MAX_LENGTH_TWO_BYTE;
13366 bool tryDependent = maximumLength > JSFatInlineString::MAX_LENGTH_TWO_BYTE;
13367
13368#ifdef DEBUG1
13369 if (maximumLength != SIZE_MAX(18446744073709551615UL)) {
13370 Label ok;
13371 masm.branch32(Assembler::BelowOrEqual, length, Imm32(maximumLength), &ok);
13372 masm.assumeUnreachable("length should not exceed maximum length");
13373 masm.bind(&ok);
13374 }
13375#endif
13376
13377 Label nonZero, nonInput;
13378
13379 // For every edge case use the C++ variant.
13380 // Note: we also use this upon allocation failure in newGCString and
13381 // newGCFatInlineString. To squeeze out even more performance those failures
13382 // can be handled by allocate in ool code and returning to jit code to fill
13383 // in all data.
13384 using Fn = JSString* (*)(JSContext* cx, HandleString str, int32_t begin,
13385 int32_t len);
13386 OutOfLineCode* ool = oolCallVM<Fn, SubstringKernel>(
13387 lir, ArgList(string, begin, length), StoreRegisterTo(output));
13388 Label* slowPath = ool->entry();
13389 Label* done = ool->rejoin();
13390
13391 // Zero length, return emptystring.
13392 masm.branchTest32(Assembler::NonZero, length, length, &nonZero);
13393 const JSAtomState& names = gen->runtime->names();
13394 masm.movePtr(ImmGCPtr(names.empty_), output);
13395 masm.jump(done);
13396
13397 // Substring from 0..|str.length|, return str.
13398 masm.bind(&nonZero);
13399 masm.branch32(Assembler::NotEqual,
13400 Address(string, JSString::offsetOfLength()), length, &nonInput);
13401#ifdef DEBUG1
13402 {
13403 Label ok;
13404 masm.branchTest32(Assembler::Zero, begin, begin, &ok);
13405 masm.assumeUnreachable("length == str.length implies begin == 0");
13406 masm.bind(&ok);
13407 }
13408#endif
13409 masm.movePtr(string, output);
13410 masm.jump(done);
13411
13412 // Use slow path for ropes.
13413 masm.bind(&nonInput);
13414 masm.branchIfRope(string, slowPath);
13415
13416 // Optimize one and two character strings.
13417 Label nonStatic;
13418 masm.branch32(Assembler::Above, length, Imm32(2), &nonStatic);
13419 {
13420 Label loadLengthOne, loadLengthTwo;
13421
13422 auto loadChars = [&](CharEncoding encoding, bool fallthru) {
13423 size_t size = encoding == CharEncoding::Latin1 ? sizeof(JS::Latin1Char)
13424 : sizeof(char16_t);
13425
13426 masm.loadStringChars(string, temp0, encoding);
13427 masm.loadChar(temp0, begin, temp2, encoding);
13428 masm.branch32(Assembler::Equal, length, Imm32(1), &loadLengthOne);
13429 masm.loadChar(temp0, begin, temp0, encoding, int32_t(size));
13430 if (!fallthru) {
13431 masm.jump(&loadLengthTwo);
13432 }
13433 };
13434
13435 Label isLatin1;
13436 masm.branchLatin1String(string, &isLatin1);
13437 loadChars(CharEncoding::TwoByte, /* fallthru = */ false);
13438
13439 masm.bind(&isLatin1);
13440 loadChars(CharEncoding::Latin1, /* fallthru = */ true);
13441
13442 // Try to load a length-two static string.
13443 masm.bind(&loadLengthTwo);
13444 masm.lookupStaticString(temp2, temp0, output, gen->runtime->staticStrings(),
13445 &nonStatic);
13446 masm.jump(done);
13447
13448 // Try to load a length-one static string.
13449 masm.bind(&loadLengthOne);
13450 masm.lookupStaticString(temp2, output, gen->runtime->staticStrings(),
13451 &nonStatic);
13452 masm.jump(done);
13453 }
13454 masm.bind(&nonStatic);
13455
13456 // Allocate either a JSThinInlineString or JSFatInlineString, or jump to
13457 // notInline if we need a dependent string.
13458 Label notInline;
13459 {
13460 static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 <
13461 JSFatInlineString::MAX_LENGTH_LATIN1);
13462 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <
13463 JSFatInlineString::MAX_LENGTH_TWO_BYTE);
13464
13465 // Use temp2 to store the JS(Thin|Fat)InlineString flags. This avoids having
13466 // duplicate newGCString/newGCFatInlineString codegen for Latin1 vs TwoByte
13467 // strings.
13468
13469 Label allocFat, allocDone;
13470 if (tryFatInlineOrDependent) {
13471 Label isLatin1, allocThin;
13472 masm.branchLatin1String(string, &isLatin1);
13473 {
13474 if (tryDependent) {
13475 masm.branch32(Assembler::Above, length,
13476 Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
13477 &notInline);
13478 }
13479 masm.move32(Imm32(0), temp2);
13480 masm.branch32(Assembler::Above, length,
13481 Imm32(JSThinInlineString::MAX_LENGTH_TWO_BYTE),
13482 &allocFat);
13483 masm.jump(&allocThin);
13484 }
13485
13486 masm.bind(&isLatin1);
13487 {
13488 if (tryDependent) {
13489 masm.branch32(Assembler::Above, length,
13490 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1),
13491 &notInline);
13492 }
13493 masm.move32(Imm32(JSString::LATIN1_CHARS_BIT), temp2);
13494 masm.branch32(Assembler::Above, length,
13495 Imm32(JSThinInlineString::MAX_LENGTH_LATIN1), &allocFat);
13496 }
13497
13498 masm.bind(&allocThin);
13499 } else {
13500 masm.load32(Address(string, JSString::offsetOfFlags()), temp2);
13501 masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp2);
13502 }
13503
13504 {
13505 masm.newGCString(output, temp0, initialStringHeap(), slowPath);
13506 masm.or32(Imm32(JSString::INIT_THIN_INLINE_FLAGS), temp2);
13507 }
13508
13509 if (tryFatInlineOrDependent) {
13510 masm.jump(&allocDone);
13511
13512 masm.bind(&allocFat);
13513 {
13514 masm.newGCFatInlineString(output, temp0, initialStringHeap(), slowPath);
13515 masm.or32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), temp2);
13516 }
13517
13518 masm.bind(&allocDone);
13519 }
13520
13521 masm.store32(temp2, Address(output, JSString::offsetOfFlags()));
13522 masm.store32(length, Address(output, JSString::offsetOfLength()));
13523
13524 auto initializeInlineString = [&](CharEncoding encoding) {
13525 masm.loadStringChars(string, temp0, encoding);
13526 masm.addToCharPtr(temp0, begin, encoding);
13527 if (temp1 == string) {
13528 masm.push(string);
13529 }
13530 masm.loadInlineStringCharsForStore(output, temp1);
13531 CopyStringChars(masm, temp1, temp0, length, temp2, encoding,
13532 maximumLength);
13533 masm.loadStringLength(output, length);
13534 if (temp1 == string) {
13535 masm.pop(string);
13536 }
13537 };
13538
13539 Label isInlineLatin1;
13540 masm.branchTest32(Assembler::NonZero, temp2,
13541 Imm32(JSString::LATIN1_CHARS_BIT), &isInlineLatin1);
13542 initializeInlineString(CharEncoding::TwoByte);
13543 masm.jump(done);
13544
13545 masm.bind(&isInlineLatin1);
13546 initializeInlineString(CharEncoding::Latin1);
13547 }
13548
13549 // Handle other cases with a DependentString.
13550 if (tryDependent) {
13551 masm.jump(done);
13552
13553 masm.bind(&notInline);
13554 masm.newGCString(output, temp0, gen->initialStringHeap(), slowPath);
13555 masm.store32(length, Address(output, JSString::offsetOfLength()));
13556
13557 // Note: no post barrier is needed because the dependent string is either
13558 // allocated in the nursery or both strings are tenured (if nursery strings
13559 // are disabled for this zone).
13560 EmitInitDependentStringBase(masm, output, string, temp0, temp2,
13561 /* needsPostBarrier = */ false);
13562
13563 auto initializeDependentString = [&](CharEncoding encoding) {
13564 uint32_t flags = JSString::INIT_DEPENDENT_FLAGS;
13565 if (encoding == CharEncoding::Latin1) {
13566 flags |= JSString::LATIN1_CHARS_BIT;
13567 }
13568 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
13569 masm.loadNonInlineStringChars(string, temp0, encoding);
13570 masm.addToCharPtr(temp0, begin, encoding);
13571 masm.storeNonInlineStringChars(temp0, output);
13572 };
13573
13574 Label isLatin1;
13575 masm.branchLatin1String(string, &isLatin1);
13576 initializeDependentString(CharEncoding::TwoByte);
13577 masm.jump(done);
13578
13579 masm.bind(&isLatin1);
13580 initializeDependentString(CharEncoding::Latin1);
13581 }
13582
13583 masm.bind(done);
13584}
13585
13586JitCode* JitZone::generateStringConcatStub(JSContext* cx) {
13587 JitSpew(JitSpew_Codegen, "# Emitting StringConcat stub");
13588
13589 TempAllocator temp(&cx->tempLifoAlloc());
13590 JitContext jcx(cx);
13591 StackMacroAssembler masm(cx, temp);
13592 AutoCreatedBy acb(masm, "JitZone::generateStringConcatStub");
13593
13594 Register lhs = CallTempReg0;
13595 Register rhs = CallTempReg1;
13596 Register temp1 = CallTempReg2;
13597 Register temp2 = CallTempReg3;
13598 Register temp3 = CallTempReg4;
13599 Register output = CallTempReg5;
13600
13601 Label failure;
13602#ifdef JS_USE_LINK_REGISTER
13603 masm.pushReturnAddress();
13604#endif
13605 masm.Push(FramePointer);
13606 masm.moveStackPtrTo(FramePointer);
13607
13608 // If lhs is empty, return rhs.
13609 Label leftEmpty;
13610 masm.loadStringLength(lhs, temp1);
13611 masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty);
13612
13613 // If rhs is empty, return lhs.
13614 Label rightEmpty;
13615 masm.loadStringLength(rhs, temp2);
13616 masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty);
13617
13618 masm.add32(temp1, temp2);
13619
13620 // Check if we can use a JSInlineString. The result is a Latin1 string if
13621 // lhs and rhs are both Latin1, so we AND the flags.
13622 Label isInlineTwoByte, isInlineLatin1;
13623 masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1);
13624 masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1);
13625
13626 Label isLatin1, notInline;
13627 masm.branchTest32(Assembler::NonZero, temp1,
13628 Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
13629 {
13630 masm.branch32(Assembler::BelowOrEqual, temp2,
13631 Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE),
13632 &isInlineTwoByte);
13633 masm.jump(&notInline);
13634 }
13635 masm.bind(&isLatin1);
13636 {
13637 masm.branch32(Assembler::BelowOrEqual, temp2,
13638 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1);
13639 }
13640 masm.bind(&notInline);
13641
13642 // Keep AND'ed flags in temp1.
13643
13644 // Ensure result length <= JSString::MAX_LENGTH.
13645 masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
13646
13647 // Allocate a new rope, guaranteed to be in the nursery if initialStringHeap
13648 // == gc::Heap::Default. (As a result, no post barriers are needed below.)
13649 masm.newGCString(output, temp3, initialStringHeap, &failure);
13650
13651 // Store rope length and flags. temp1 still holds the result of AND'ing the
13652 // lhs and rhs flags, so we just have to clear the other flags to get our rope
13653 // flags (Latin1 if both lhs and rhs are Latin1).
13654 static_assert(JSString::INIT_ROPE_FLAGS == 0,
13655 "Rope type flags must have no bits set");
13656 masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1);
13657 masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
13658 masm.store32(temp2, Address(output, JSString::offsetOfLength()));
13659
13660 // Store left and right nodes.
13661 masm.storeRopeChildren(lhs, rhs, output);
13662 masm.pop(FramePointer);
13663 masm.ret();
13664
13665 masm.bind(&leftEmpty);
13666 masm.mov(rhs, output);
13667 masm.pop(FramePointer);
13668 masm.ret();
13669
13670 masm.bind(&rightEmpty);
13671 masm.mov(lhs, output);
13672 masm.pop(FramePointer);
13673 masm.ret();
13674
13675 masm.bind(&isInlineTwoByte);
13676 ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
13677 initialStringHeap, &failure, CharEncoding::TwoByte);
13678 masm.pop(FramePointer);
13679 masm.ret();
13680
13681 masm.bind(&isInlineLatin1);
13682 ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3,
13683 initialStringHeap, &failure, CharEncoding::Latin1);
13684 masm.pop(FramePointer);
13685 masm.ret();
13686
13687 masm.bind(&failure);
13688 masm.movePtr(ImmPtr(nullptr), output);
13689 masm.pop(FramePointer);
13690 masm.ret();
13691
13692 Linker linker(masm);
13693 JitCode* code = linker.newCode(cx, CodeKind::Other);
13694
13695 CollectPerfSpewerJitCodeProfile(code, "StringConcatStub");
13696#ifdef MOZ_VTUNE1
13697 vtune::MarkStub(code, "StringConcatStub");
13698#endif
13699
13700 return code;
13701}
13702
13703void JitRuntime::generateLazyLinkStub(MacroAssembler& masm) {
13704 AutoCreatedBy acb(masm, "JitRuntime::generateLazyLinkStub");
13705
13706 lazyLinkStubOffset_ = startTrampolineCode(masm);
13707
13708#ifdef JS_USE_LINK_REGISTER
13709 masm.pushReturnAddress();
13710#endif
13711 masm.Push(FramePointer);
13712 masm.moveStackPtrTo(FramePointer);
13713
13714 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
13715 Register temp0 = regs.takeAny();
13716 Register temp1 = regs.takeAny();
13717 Register temp2 = regs.takeAny();
13718
13719 masm.loadJSContext(temp0);
13720 masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink);
13721 masm.moveStackPtrTo(temp1);
13722
13723 using Fn = uint8_t* (*)(JSContext* cx, LazyLinkExitFrameLayout* frame);
13724 masm.setupUnalignedABICall(temp2);
13725 masm.passABIArg(temp0);
13726 masm.passABIArg(temp1);
13727 masm.callWithABI<Fn, LazyLinkTopActivation>(
13728 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
13729
13730 // Discard exit frame and restore frame pointer.
13731 masm.leaveExitFrame(0);
13732 masm.pop(FramePointer);
13733
13734#ifdef JS_USE_LINK_REGISTER
13735 // Restore the return address such that the emitPrologue function of the
13736 // CodeGenerator can push it back on the stack with pushReturnAddress.
13737 masm.popReturnAddress();
13738#endif
13739 masm.jump(ReturnReg);
13740}
13741
13742void JitRuntime::generateInterpreterStub(MacroAssembler& masm) {
13743 AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterStub");
13744
13745 interpreterStubOffset_ = startTrampolineCode(masm);
13746
13747#ifdef JS_USE_LINK_REGISTER
13748 masm.pushReturnAddress();
13749#endif
13750 masm.Push(FramePointer);
13751 masm.moveStackPtrTo(FramePointer);
13752
13753 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
13754 Register temp0 = regs.takeAny();
13755 Register temp1 = regs.takeAny();
13756 Register temp2 = regs.takeAny();
13757
13758 masm.loadJSContext(temp0);
13759 masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub);
13760 masm.moveStackPtrTo(temp1);
13761
13762 using Fn = bool (*)(JSContext* cx, InterpreterStubExitFrameLayout* frame);
13763 masm.setupUnalignedABICall(temp2);
13764 masm.passABIArg(temp0);
13765 masm.passABIArg(temp1);
13766 masm.callWithABI<Fn, InvokeFromInterpreterStub>(
13767 ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame);
13768
13769 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
13770
13771 // Discard exit frame and restore frame pointer.
13772 masm.leaveExitFrame(0);
13773 masm.pop(FramePointer);
13774
13775 // InvokeFromInterpreterStub stores the return value in argv[0], where the
13776 // caller stored |this|. Subtract |sizeof(void*)| for the frame pointer we
13777 // just popped.
13778 masm.loadValue(Address(masm.getStackPointer(),
13779 JitFrameLayout::offsetOfThis() - sizeof(void*)),
13780 JSReturnOperand);
13781 masm.ret();
13782}
13783
13784void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) {
13785 AutoCreatedBy acb(masm, "JitRuntime::generateDoubleToInt32ValueStub");
13786 doubleToInt32ValueStubOffset_ = startTrampolineCode(masm);
13787
13788 Label done;
13789 masm.branchTestDouble(Assembler::NotEqual, R0, &done);
13790
13791 masm.unboxDouble(R0, FloatReg0);
13792 masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done,
13793 /* negativeZeroCheck = */ false);
13794 masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0);
13795
13796 masm.bind(&done);
13797 masm.abiret();
13798}
13799
13800void CodeGenerator::visitLinearizeString(LLinearizeString* lir) {
13801 Register str = ToRegister(lir->str());
13802 Register output = ToRegister(lir->output());
13803
13804 using Fn = JSLinearString* (*)(JSContext*, JSString*);
13805 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
13806 lir, ArgList(str), StoreRegisterTo(output));
13807
13808 masm.branchIfRope(str, ool->entry());
13809
13810 masm.movePtr(str, output);
13811 masm.bind(ool->rejoin());
13812}
13813
13814void CodeGenerator::visitLinearizeForCharAccess(LLinearizeForCharAccess* lir) {
13815 Register str = ToRegister(lir->str());
13816 Register index = ToRegister(lir->index());
13817 Register output = ToRegister(lir->output());
13818
13819 using Fn = JSLinearString* (*)(JSContext*, JSString*);
13820 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
13821 lir, ArgList(str), StoreRegisterTo(output));
13822
13823 masm.branchIfNotCanLoadStringChar(str, index, output, ool->entry());
13824
13825 masm.movePtr(str, output);
13826 masm.bind(ool->rejoin());
13827}
13828
13829void CodeGenerator::visitLinearizeForCodePointAccess(
13830 LLinearizeForCodePointAccess* lir) {
13831 Register str = ToRegister(lir->str());
13832 Register index = ToRegister(lir->index());
13833 Register output = ToRegister(lir->output());
13834 Register temp = ToRegister(lir->temp0());
13835
13836 using Fn = JSLinearString* (*)(JSContext*, JSString*);
13837 auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>(
13838 lir, ArgList(str), StoreRegisterTo(output));
13839
13840 masm.branchIfNotCanLoadStringCodePoint(str, index, output, temp,
13841 ool->entry());
13842
13843 masm.movePtr(str, output);
13844 masm.bind(ool->rejoin());
13845}
13846
13847void CodeGenerator::visitToRelativeStringIndex(LToRelativeStringIndex* lir) {
13848 Register index = ToRegister(lir->index());
13849 Register length = ToRegister(lir->length());
13850 Register output = ToRegister(lir->output());
13851
13852 masm.move32(Imm32(0), output);
13853 masm.cmp32Move32(Assembler::LessThan, index, Imm32(0), length, output);
13854 masm.add32(index, output);
13855}
13856
13857void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) {
13858 Register str = ToRegister(lir->str());
13859 Register output = ToRegister(lir->output());
13860 Register temp0 = ToRegister(lir->temp0());
13861 Register temp1 = ToRegister(lir->temp1());
13862
13863 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
13864
13865 if (lir->index()->isBogus()) {
13866 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)),
13867 StoreRegisterTo(output));
13868 masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry());
13869 masm.bind(ool->rejoin());
13870 } else {
13871 Register index = ToRegister(lir->index());
13872
13873 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index),
13874 StoreRegisterTo(output));
13875 masm.loadStringChar(str, index, output, temp0, temp1, ool->entry());
13876 masm.bind(ool->rejoin());
13877 }
13878}
13879
13880void CodeGenerator::visitCharCodeAtOrNegative(LCharCodeAtOrNegative* lir) {
13881 Register str = ToRegister(lir->str());
13882 Register output = ToRegister(lir->output());
13883 Register temp0 = ToRegister(lir->temp0());
13884 Register temp1 = ToRegister(lir->temp1());
13885
13886 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
13887
13888 // Return -1 for out-of-bounds access.
13889 masm.move32(Imm32(-1), output);
13890
13891 if (lir->index()->isBogus()) {
13892 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)),
13893 StoreRegisterTo(output));
13894
13895 masm.branch32(Assembler::Equal, Address(str, JSString::offsetOfLength()),
13896 Imm32(0), ool->rejoin());
13897 masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry());
13898 masm.bind(ool->rejoin());
13899 } else {
13900 Register index = ToRegister(lir->index());
13901
13902 auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index),
13903 StoreRegisterTo(output));
13904
13905 masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
13906 temp0, ool->rejoin());
13907 masm.loadStringChar(str, index, output, temp0, temp1, ool->entry());
13908 masm.bind(ool->rejoin());
13909 }
13910}
13911
13912void CodeGenerator::visitCodePointAt(LCodePointAt* lir) {
13913 Register str = ToRegister(lir->str());
13914 Register index = ToRegister(lir->index());
13915 Register output = ToRegister(lir->output());
13916 Register temp0 = ToRegister(lir->temp0());
13917 Register temp1 = ToRegister(lir->temp1());
13918
13919 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
13920 auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index),
13921 StoreRegisterTo(output));
13922
13923 masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry());
13924 masm.bind(ool->rejoin());
13925}
13926
13927void CodeGenerator::visitCodePointAtOrNegative(LCodePointAtOrNegative* lir) {
13928 Register str = ToRegister(lir->str());
13929 Register index = ToRegister(lir->index());
13930 Register output = ToRegister(lir->output());
13931 Register temp0 = ToRegister(lir->temp0());
13932 Register temp1 = ToRegister(lir->temp1());
13933
13934 using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*);
13935 auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index),
13936 StoreRegisterTo(output));
13937
13938 // Return -1 for out-of-bounds access.
13939 masm.move32(Imm32(-1), output);
13940
13941 masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()),
13942 temp0, ool->rejoin());
13943 masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry());
13944 masm.bind(ool->rejoin());
13945}
13946
13947void CodeGenerator::visitNegativeToNaN(LNegativeToNaN* lir) {
13948 Register input = ToRegister(lir->input());
13949 ValueOperand output = ToOutValue(lir);
13950
13951 masm.tagValue(JSVAL_TYPE_INT32, input, output);
13952
13953 Label done;
13954 masm.branchTest32(Assembler::NotSigned, input, input, &done);
13955 masm.moveValue(JS::NaNValue(), output);
13956 masm.bind(&done);
13957}
13958
13959void CodeGenerator::visitNegativeToUndefined(LNegativeToUndefined* lir) {
13960 Register input = ToRegister(lir->input());
13961 ValueOperand output = ToOutValue(lir);
13962
13963 masm.tagValue(JSVAL_TYPE_INT32, input, output);
13964
13965 Label done;
13966 masm.branchTest32(Assembler::NotSigned, input, input, &done);
13967 masm.moveValue(JS::UndefinedValue(), output);
13968 masm.bind(&done);
13969}
13970
13971void CodeGenerator::visitFromCharCode(LFromCharCode* lir) {
13972 Register code = ToRegister(lir->code());
13973 Register output = ToRegister(lir->output());
13974
13975 using Fn = JSLinearString* (*)(JSContext*, int32_t);
13976 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
13977 StoreRegisterTo(output));
13978
13979 // OOL path if code >= UNIT_STATIC_LIMIT.
13980 masm.lookupStaticString(code, output, gen->runtime->staticStrings(),
13981 ool->entry());
13982
13983 masm.bind(ool->rejoin());
13984}
13985
13986void CodeGenerator::visitFromCharCodeEmptyIfNegative(
13987 LFromCharCodeEmptyIfNegative* lir) {
13988 Register code = ToRegister(lir->code());
13989 Register output = ToRegister(lir->output());
13990
13991 using Fn = JSLinearString* (*)(JSContext*, int32_t);
13992 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
13993 StoreRegisterTo(output));
13994
13995 // Return the empty string for negative inputs.
13996 const JSAtomState& names = gen->runtime->names();
13997 masm.movePtr(ImmGCPtr(names.empty_), output);
13998 masm.branchTest32(Assembler::Signed, code, code, ool->rejoin());
13999
14000 // OOL path if code >= UNIT_STATIC_LIMIT.
14001 masm.lookupStaticString(code, output, gen->runtime->staticStrings(),
14002 ool->entry());
14003
14004 masm.bind(ool->rejoin());
14005}
14006
14007void CodeGenerator::visitFromCharCodeUndefinedIfNegative(
14008 LFromCharCodeUndefinedIfNegative* lir) {
14009 Register code = ToRegister(lir->code());
14010 ValueOperand output = ToOutValue(lir);
14011 Register temp = output.scratchReg();
14012
14013 using Fn = JSLinearString* (*)(JSContext*, int32_t);
14014 auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code),
14015 StoreRegisterTo(temp));
14016
14017 // Return |undefined| for negative inputs.
14018 Label done;
14019 masm.moveValue(UndefinedValue(), output);
14020 masm.branchTest32(Assembler::Signed, code, code, &done);
14021
14022 // OOL path if code >= UNIT_STATIC_LIMIT.
14023 masm.lookupStaticString(code, temp, gen->runtime->staticStrings(),
14024 ool->entry());
14025
14026 masm.bind(ool->rejoin());
14027 masm.tagValue(JSVAL_TYPE_STRING, temp, output);
14028
14029 masm.bind(&done);
14030}
14031
14032void CodeGenerator::visitFromCodePoint(LFromCodePoint* lir) {
14033 Register codePoint = ToRegister(lir->codePoint());
14034 Register output = ToRegister(lir->output());
14035 Register temp0 = ToRegister(lir->temp0());
14036 Register temp1 = ToRegister(lir->temp1());
14037 LSnapshot* snapshot = lir->snapshot();
14038
14039 // The OOL path is only taken when we can't allocate the inline string.
14040 using Fn = JSLinearString* (*)(JSContext*, char32_t);
14041 auto* ool = oolCallVM<Fn, js::StringFromCodePoint>(lir, ArgList(codePoint),
14042 StoreRegisterTo(output));
14043
14044 Label isTwoByte;
14045 Label* done = ool->rejoin();
14046
14047 static_assert(
14048 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
14049 "Latin-1 strings can be loaded from static strings");
14050
14051 {
14052 masm.lookupStaticString(codePoint, output, gen->runtime->staticStrings(),
14053 &isTwoByte);
14054 masm.jump(done);
14055 }
14056 masm.bind(&isTwoByte);
14057 {
14058 // Use a bailout if the input is not a valid code point, because
14059 // MFromCodePoint is movable and it'd be observable when a moved
14060 // fromCodePoint throws an exception before its actual call site.
14061 bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax),
14062 snapshot);
14063
14064 // Allocate a JSThinInlineString.
14065 {
14066 static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE >= 2,
14067 "JSThinInlineString can hold a supplementary code point");
14068
14069 uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS;
14070 masm.newGCString(output, temp0, gen->initialStringHeap(), ool->entry());
14071 masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags()));
14072 }
14073
14074 Label isSupplementary;
14075 masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(unicode::NonBMPMin),
14076 &isSupplementary);
14077 {
14078 // Store length.
14079 masm.store32(Imm32(1), Address(output, JSString::offsetOfLength()));
14080
14081 // Load chars pointer in temp0.
14082 masm.loadInlineStringCharsForStore(output, temp0);
14083
14084 masm.store16(codePoint, Address(temp0, 0));
14085
14086 masm.jump(done);
14087 }
14088 masm.bind(&isSupplementary);
14089 {
14090 // Store length.
14091 masm.store32(Imm32(2), Address(output, JSString::offsetOfLength()));
14092
14093 // Load chars pointer in temp0.
14094 masm.loadInlineStringCharsForStore(output, temp0);
14095
14096 // Inlined unicode::LeadSurrogate(uint32_t).
14097 masm.move32(codePoint, temp1);
14098 masm.rshift32(Imm32(10), temp1);
14099 masm.add32(Imm32(unicode::LeadSurrogateMin - (unicode::NonBMPMin >> 10)),
14100 temp1);
14101
14102 masm.store16(temp1, Address(temp0, 0));
14103
14104 // Inlined unicode::TrailSurrogate(uint32_t).
14105 masm.move32(codePoint, temp1);
14106 masm.and32(Imm32(0x3FF), temp1);
14107 masm.or32(Imm32(unicode::TrailSurrogateMin), temp1);
14108
14109 masm.store16(temp1, Address(temp0, sizeof(char16_t)));
14110 }
14111 }
14112
14113 masm.bind(done);
14114}
14115
14116void CodeGenerator::visitStringIncludes(LStringIncludes* lir) {
14117 pushArg(ToRegister(lir->searchString()));
14118 pushArg(ToRegister(lir->string()));
14119
14120 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14121 callVM<Fn, js::StringIncludes>(lir);
14122}
14123
14124template <typename LIns>
14125static void CallStringMatch(MacroAssembler& masm, LIns* lir, OutOfLineCode* ool,
14126 LiveRegisterSet volatileRegs) {
14127 Register string = ToRegister(lir->string());
14128 Register output = ToRegister(lir->output());
14129 Register tempLength = ToRegister(lir->temp0());
14130 Register tempChars = ToRegister(lir->temp1());
14131 Register maybeTempPat = ToTempRegisterOrInvalid(lir->temp2());
14132
14133 const JSLinearString* searchString = lir->searchString();
14134 size_t length = searchString->length();
14135 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"
, 14135); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length == 1 || length == 2"
")"); do { *((volatile int*)__null) = 14135; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14136
14137 // The additional temp register is only needed when searching for two
14138 // pattern characters.
14139 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"
, 14139); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeTempPat != InvalidReg"
")"); do { *((volatile int*)__null) = 14139; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
14140
14141 if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) {
14142 masm.move32(Imm32(0), output);
14143 } else {
14144 masm.move32(Imm32(-1), output);
14145 }
14146
14147 masm.loadStringLength(string, tempLength);
14148
14149 // Can't be a substring when the string is smaller than the search string.
14150 Label done;
14151 masm.branch32(Assembler::Below, tempLength, Imm32(length), ool->rejoin());
14152
14153 bool searchStringIsPureTwoByte = false;
14154 if (searchString->hasTwoByteChars()) {
14155 JS::AutoCheckCannotGC nogc;
14156 searchStringIsPureTwoByte =
14157 !mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc));
14158 }
14159
14160 // Pure two-byte strings can't occur in a Latin-1 string.
14161 if (searchStringIsPureTwoByte) {
14162 masm.branchLatin1String(string, ool->rejoin());
14163 }
14164
14165 // Slow path when we need to linearize the string.
14166 masm.branchIfRope(string, ool->entry());
14167
14168 Label restoreVolatile;
14169
14170 auto callMatcher = [&](CharEncoding encoding) {
14171 masm.loadStringChars(string, tempChars, encoding);
14172
14173 LiveGeneralRegisterSet liveRegs;
14174 if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) {
14175 // Save |tempChars| to compute the result index.
14176 liveRegs.add(tempChars);
14177
14178#ifdef DEBUG1
14179 // Save |tempLength| in debug-mode for assertions.
14180 liveRegs.add(tempLength);
14181#endif
14182
14183 // Exclude non-volatile registers.
14184 liveRegs.set() = GeneralRegisterSet::Intersect(
14185 liveRegs.set(), GeneralRegisterSet::Volatile());
14186
14187 masm.PushRegsInMask(liveRegs);
14188 }
14189
14190 if (length == 1) {
14191 char16_t pat = searchString->latin1OrTwoByteChar(0);
14192 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"
, 14193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 14193; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
14193 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"
, 14193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 14193; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
14194
14195 masm.move32(Imm32(pat), output);
14196
14197 masm.setupAlignedABICall();
14198 masm.passABIArg(tempChars);
14199 masm.passABIArg(output);
14200 masm.passABIArg(tempLength);
14201 if (encoding == CharEncoding::Latin1) {
14202 using Fn = const char* (*)(const char*, char, size_t);
14203 masm.callWithABI<Fn, mozilla::SIMD::memchr8>(
14204 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
14205 } else {
14206 using Fn = const char16_t* (*)(const char16_t*, char16_t, size_t);
14207 masm.callWithABI<Fn, mozilla::SIMD::memchr16>(
14208 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
14209 }
14210 } else {
14211 char16_t pat0 = searchString->latin1OrTwoByteChar(0);
14212 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"
, 14213); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 14213; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
14213 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"
, 14213); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 14213; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
14214
14215 char16_t pat1 = searchString->latin1OrTwoByteChar(1);
14216 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"
, 14217); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 14217; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
14217 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"
, 14217); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR"
")"); do { *((volatile int*)__null) = 14217; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
14218
14219 masm.move32(Imm32(pat0), output);
14220 masm.move32(Imm32(pat1), maybeTempPat);
14221
14222 masm.setupAlignedABICall();
14223 masm.passABIArg(tempChars);
14224 masm.passABIArg(output);
14225 masm.passABIArg(maybeTempPat);
14226 masm.passABIArg(tempLength);
14227 if (encoding == CharEncoding::Latin1) {
14228 using Fn = const char* (*)(const char*, char, char, size_t);
14229 masm.callWithABI<Fn, mozilla::SIMD::memchr2x8>(
14230 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
14231 } else {
14232 using Fn =
14233 const char16_t* (*)(const char16_t*, char16_t, char16_t, size_t);
14234 masm.callWithABI<Fn, mozilla::SIMD::memchr2x16>(
14235 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
14236 }
14237 }
14238
14239 masm.storeCallPointerResult(output);
14240
14241 // Convert to string index for `indexOf`.
14242 if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) {
14243 // Restore |tempChars|. (And in debug mode |tempLength|.)
14244 masm.PopRegsInMask(liveRegs);
14245
14246 Label found;
14247 masm.branchPtr(Assembler::NotEqual, output, ImmPtr(nullptr), &found);
14248 {
14249 masm.move32(Imm32(-1), output);
14250 masm.jump(&restoreVolatile);
14251 }
14252 masm.bind(&found);
14253
14254#ifdef DEBUG1
14255 // Check lower bound.
14256 Label lower;
14257 masm.branchPtr(Assembler::AboveOrEqual, output, tempChars, &lower);
14258 masm.assumeUnreachable("result pointer below string chars");
14259 masm.bind(&lower);
14260
14261 // Compute the end position of the characters.
14262 auto scale = encoding == CharEncoding::Latin1 ? TimesOne : TimesTwo;
14263 masm.computeEffectiveAddress(BaseIndex(tempChars, tempLength, scale),
14264 tempLength);
14265
14266 // Check upper bound.
14267 Label upper;
14268 masm.branchPtr(Assembler::Below, output, tempLength, &upper);
14269 masm.assumeUnreachable("result pointer above string chars");
14270 masm.bind(&upper);
14271#endif
14272
14273 masm.subPtr(tempChars, output);
14274
14275 if (encoding == CharEncoding::TwoByte) {
14276 masm.rshiftPtr(Imm32(1), output);
14277 }
14278 }
14279 };
14280
14281 volatileRegs.takeUnchecked(output);
14282 volatileRegs.takeUnchecked(tempLength);
14283 volatileRegs.takeUnchecked(tempChars);
14284 if (maybeTempPat != InvalidReg) {
14285 volatileRegs.takeUnchecked(maybeTempPat);
14286 }
14287 masm.PushRegsInMask(volatileRegs);
14288
14289 // Handle the case when the input is a Latin-1 string.
14290 if (!searchStringIsPureTwoByte) {
14291 Label twoByte;
14292 masm.branchTwoByteString(string, &twoByte);
14293 {
14294 callMatcher(CharEncoding::Latin1);
14295 masm.jump(&restoreVolatile);
14296 }
14297 masm.bind(&twoByte);
14298 }
14299
14300 // Handle the case when the input is a two-byte string.
14301 callMatcher(CharEncoding::TwoByte);
14302
14303 masm.bind(&restoreVolatile);
14304 masm.PopRegsInMask(volatileRegs);
14305
14306 // Convert to bool for `includes`.
14307 if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) {
14308 masm.cmpPtrSet(Assembler::NotEqual, output, ImmPtr(nullptr), output);
14309 }
14310
14311 masm.bind(ool->rejoin());
14312}
14313
14314void CodeGenerator::visitStringIncludesSIMD(LStringIncludesSIMD* lir) {
14315 Register string = ToRegister(lir->string());
14316 Register output = ToRegister(lir->output());
14317 const JSLinearString* searchString = lir->searchString();
14318
14319 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14320 auto* ool = oolCallVM<Fn, js::StringIncludes>(
14321 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
14322
14323 CallStringMatch(masm, lir, ool, liveVolatileRegs(lir));
14324}
14325
14326void CodeGenerator::visitStringIndexOf(LStringIndexOf* lir) {
14327 pushArg(ToRegister(lir->searchString()));
14328 pushArg(ToRegister(lir->string()));
14329
14330 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
14331 callVM<Fn, js::StringIndexOf>(lir);
14332}
14333
14334void CodeGenerator::visitStringIndexOfSIMD(LStringIndexOfSIMD* lir) {
14335 Register string = ToRegister(lir->string());
14336 Register output = ToRegister(lir->output());
14337 const JSLinearString* searchString = lir->searchString();
14338
14339 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
14340 auto* ool = oolCallVM<Fn, js::StringIndexOf>(
14341 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
14342
14343 CallStringMatch(masm, lir, ool, liveVolatileRegs(lir));
14344}
14345
14346void CodeGenerator::visitStringLastIndexOf(LStringLastIndexOf* lir) {
14347 pushArg(ToRegister(lir->searchString()));
14348 pushArg(ToRegister(lir->string()));
14349
14350 using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*);
14351 callVM<Fn, js::StringLastIndexOf>(lir);
14352}
14353
14354void CodeGenerator::visitStringStartsWith(LStringStartsWith* lir) {
14355 pushArg(ToRegister(lir->searchString()));
14356 pushArg(ToRegister(lir->string()));
14357
14358 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14359 callVM<Fn, js::StringStartsWith>(lir);
14360}
14361
14362void CodeGenerator::visitStringStartsWithInline(LStringStartsWithInline* lir) {
14363 Register string = ToRegister(lir->string());
14364 Register output = ToRegister(lir->output());
14365 Register temp = ToRegister(lir->temp0());
14366
14367 const JSLinearString* searchString = lir->searchString();
14368
14369 size_t length = searchString->length();
14370 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"
, 14370); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0"
")"); do { *((volatile int*)__null) = 14370; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14371
14372 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14373 auto* ool = oolCallVM<Fn, js::StringStartsWith>(
14374 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
14375
14376 masm.move32(Imm32(0), output);
14377
14378 // Can't be a prefix when the string is smaller than the search string.
14379 masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()),
14380 Imm32(length), ool->rejoin());
14381
14382 // Unwind ropes at the start if possible.
14383 Label compare;
14384 masm.movePtr(string, temp);
14385 masm.branchIfNotRope(temp, &compare);
14386
14387 Label unwindRope;
14388 masm.bind(&unwindRope);
14389 masm.loadRopeLeftChild(temp, output);
14390 masm.movePtr(output, temp);
14391
14392 // If the left child is smaller than the search string, jump into the VM to
14393 // linearize the string.
14394 masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()),
14395 Imm32(length), ool->entry());
14396
14397 // Otherwise keep unwinding ropes.
14398 masm.branchIfRope(temp, &unwindRope);
14399
14400 masm.bind(&compare);
14401
14402 // If operands point to the same instance, it's trivially a prefix.
14403 Label notPointerEqual;
14404 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString),
14405 &notPointerEqual);
14406 masm.move32(Imm32(1), output);
14407 masm.jump(ool->rejoin());
14408 masm.bind(&notPointerEqual);
14409
14410 if (searchString->hasTwoByteChars()) {
14411 // Pure two-byte strings can't be a prefix of Latin-1 strings.
14412 JS::AutoCheckCannotGC nogc;
14413 if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) {
14414 Label compareChars;
14415 masm.branchTwoByteString(temp, &compareChars);
14416 masm.move32(Imm32(0), output);
14417 masm.jump(ool->rejoin());
14418 masm.bind(&compareChars);
14419 }
14420 }
14421
14422 // Load the input string's characters.
14423 Register stringChars = output;
14424 masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry());
14425
14426 // Start comparing character by character.
14427 masm.compareStringChars(JSOp::Eq, stringChars, searchString, output);
14428
14429 masm.bind(ool->rejoin());
14430}
14431
14432void CodeGenerator::visitStringEndsWith(LStringEndsWith* lir) {
14433 pushArg(ToRegister(lir->searchString()));
14434 pushArg(ToRegister(lir->string()));
14435
14436 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14437 callVM<Fn, js::StringEndsWith>(lir);
14438}
14439
14440void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) {
14441 Register string = ToRegister(lir->string());
14442 Register output = ToRegister(lir->output());
14443 Register temp = ToRegister(lir->temp0());
14444
14445 const JSLinearString* searchString = lir->searchString();
14446
14447 size_t length = searchString->length();
14448 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"
, 14448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0"
")"); do { *((volatile int*)__null) = 14448; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14449
14450 using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*);
14451 auto* ool = oolCallVM<Fn, js::StringEndsWith>(
14452 lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output));
14453
14454 masm.move32(Imm32(0), output);
14455
14456 // Can't be a suffix when the string is smaller than the search string.
14457 masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()),
14458 Imm32(length), ool->rejoin());
14459
14460 // Unwind ropes at the end if possible.
14461 Label compare;
14462 masm.movePtr(string, temp);
14463 masm.branchIfNotRope(temp, &compare);
14464
14465 Label unwindRope;
14466 masm.bind(&unwindRope);
14467 masm.loadRopeRightChild(temp, output);
14468 masm.movePtr(output, temp);
14469
14470 // If the right child is smaller than the search string, jump into the VM to
14471 // linearize the string.
14472 masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()),
14473 Imm32(length), ool->entry());
14474
14475 // Otherwise keep unwinding ropes.
14476 masm.branchIfRope(temp, &unwindRope);
14477
14478 masm.bind(&compare);
14479
14480 // If operands point to the same instance, it's trivially a suffix.
14481 Label notPointerEqual;
14482 masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString),
14483 &notPointerEqual);
14484 masm.move32(Imm32(1), output);
14485 masm.jump(ool->rejoin());
14486 masm.bind(&notPointerEqual);
14487
14488 CharEncoding encoding = searchString->hasLatin1Chars()
14489 ? CharEncoding::Latin1
14490 : CharEncoding::TwoByte;
14491 if (encoding == CharEncoding::TwoByte) {
14492 // Pure two-byte strings can't be a suffix of Latin-1 strings.
14493 JS::AutoCheckCannotGC nogc;
14494 if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) {
14495 Label compareChars;
14496 masm.branchTwoByteString(temp, &compareChars);
14497 masm.move32(Imm32(0), output);
14498 masm.jump(ool->rejoin());
14499 masm.bind(&compareChars);
14500 }
14501 }
14502
14503 // Load the input string's characters.
14504 Register stringChars = output;
14505 masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry());
14506
14507 // Move string-char pointer to the suffix string.
14508 masm.loadStringLength(temp, temp);
14509 masm.sub32(Imm32(length), temp);
14510 masm.addToCharPtr(stringChars, temp, encoding);
14511
14512 // Start comparing character by character.
14513 masm.compareStringChars(JSOp::Eq, stringChars, searchString, output);
14514
14515 masm.bind(ool->rejoin());
14516}
14517
14518void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) {
14519 Register string = ToRegister(lir->string());
14520 Register output = ToRegister(lir->output());
14521 Register temp0 = ToRegister(lir->temp0());
14522 Register temp1 = ToRegister(lir->temp1());
14523 Register temp2 = ToRegister(lir->temp2());
14524
14525 // On x86 there are not enough registers. In that case reuse the string
14526 // register as a temporary.
14527 Register temp3 =
14528 lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3());
14529 Register temp4 = ToRegister(lir->temp4());
14530
14531 using Fn = JSLinearString* (*)(JSContext*, JSString*);
14532 OutOfLineCode* ool = oolCallVM<Fn, js::StringToLowerCase>(
14533 lir, ArgList(string), StoreRegisterTo(output));
14534
14535 // Take the slow path if the string isn't a linear Latin-1 string.
14536 Imm32 linearLatin1Bits(JSString::LINEAR_BIT | JSString::LATIN1_CHARS_BIT);
14537 Register flags = temp0;
14538 masm.load32(Address(string, JSString::offsetOfFlags()), flags);
14539 masm.and32(linearLatin1Bits, flags);
14540 masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry());
14541
14542 Register length = temp0;
14543 masm.loadStringLength(string, length);
14544
14545 // Return the input if it's the empty string.
14546 Label notEmptyString;
14547 masm.branch32(Assembler::NotEqual, length, Imm32(0), &notEmptyString);
14548 {
14549 masm.movePtr(string, output);
14550 masm.jump(ool->rejoin());
14551 }
14552 masm.bind(&notEmptyString);
14553
14554 Register inputChars = temp1;
14555 masm.loadStringChars(string, inputChars, CharEncoding::Latin1);
14556
14557 Register toLowerCaseTable = temp2;
14558 masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable);
14559
14560 // Single element strings can be directly retrieved from static strings cache.
14561 Label notSingleElementString;
14562 masm.branch32(Assembler::NotEqual, length, Imm32(1), &notSingleElementString);
14563 {
14564 Register current = temp4;
14565
14566 masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1);
14567 masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne),
14568 current);
14569 masm.lookupStaticString(current, output, gen->runtime->staticStrings());
14570
14571 masm.jump(ool->rejoin());
14572 }
14573 masm.bind(&notSingleElementString);
14574
14575 // Use the OOL-path when the string is too long. This prevents scanning long
14576 // strings which have upper case characters only near the end a second time in
14577 // the VM.
14578 constexpr int32_t MaxInlineLength = 64;
14579 masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry());
14580
14581 {
14582 // Check if there are any characters which need to be converted.
14583 //
14584 // This extra loop gives a small performance improvement for strings which
14585 // are already lower cased and lets us avoid calling into the runtime for
14586 // non-inline, all lower case strings. But more importantly it avoids
14587 // repeated inline allocation failures:
14588 // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the
14589 // |js::StringToLowerCase| runtime function when the result string can't be
14590 // allocated inline. And |js::StringToLowerCase| directly returns the input
14591 // string when no characters need to be converted. That means it won't
14592 // trigger GC to clear up the free nursery space, so the next toLowerCase()
14593 // call will again fail to inline allocate the result string.
14594 Label hasUpper;
14595 {
14596 Register checkInputChars = output;
14597 masm.movePtr(inputChars, checkInputChars);
14598
14599 Register current = temp4;
14600
14601 Label start;
14602 masm.bind(&start);
14603 masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1);
14604 masm.branch8(Assembler::NotEqual,
14605 BaseIndex(toLowerCaseTable, current, TimesOne), current,
14606 &hasUpper);
14607 masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars);
14608 masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start);
14609
14610 // Input is already in lower case.
14611 masm.movePtr(string, output);
14612 masm.jump(ool->rejoin());
14613 }
14614 masm.bind(&hasUpper);
14615
14616 // |length| was clobbered above, reload.
14617 masm.loadStringLength(string, length);
14618
14619 // Call into the runtime when we can't create an inline string.
14620 masm.branch32(Assembler::Above, length,
14621 Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry());
14622
14623 AllocateThinOrFatInlineString(masm, output, length, temp4,
14624 initialStringHeap(), ool->entry(),
14625 CharEncoding::Latin1);
14626
14627 if (temp3 == string) {
14628 masm.push(string);
14629 }
14630
14631 Register outputChars = temp3;
14632 masm.loadInlineStringCharsForStore(output, outputChars);
14633
14634 {
14635 Register current = temp4;
14636
14637 Label start;
14638 masm.bind(&start);
14639 masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1);
14640 masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne),
14641 current);
14642 masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1);
14643 masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars);
14644 masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars);
14645 masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start);
14646 }
14647
14648 if (temp3 == string) {
14649 masm.pop(string);
14650 }
14651 }
14652
14653 masm.bind(ool->rejoin());
14654}
14655
14656void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) {
14657 pushArg(ToRegister(lir->string()));
14658
14659 using Fn = JSLinearString* (*)(JSContext*, JSString*);
14660 callVM<Fn, js::StringToUpperCase>(lir);
14661}
14662
14663void CodeGenerator::visitCharCodeToLowerCase(LCharCodeToLowerCase* lir) {
14664 Register code = ToRegister(lir->code());
14665 Register output = ToRegister(lir->output());
14666 Register temp = ToRegister(lir->temp0());
14667
14668 using Fn = JSString* (*)(JSContext*, int32_t);
14669 auto* ool = oolCallVM<Fn, jit::CharCodeToLowerCase>(lir, ArgList(code),
14670 StoreRegisterTo(output));
14671
14672 constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1;
14673
14674 // OOL path if code >= NonLatin1Min.
14675 masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry());
14676
14677 // Convert to lower case.
14678 masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), temp);
14679 masm.load8ZeroExtend(BaseIndex(temp, code, TimesOne), temp);
14680
14681 // Load static string for lower case character.
14682 masm.lookupStaticString(temp, output, gen->runtime->staticStrings());
14683
14684 masm.bind(ool->rejoin());
14685}
14686
14687void CodeGenerator::visitCharCodeToUpperCase(LCharCodeToUpperCase* lir) {
14688 Register code = ToRegister(lir->code());
14689 Register output = ToRegister(lir->output());
14690 Register temp = ToRegister(lir->temp0());
14691
14692 using Fn = JSString* (*)(JSContext*, int32_t);
14693 auto* ool = oolCallVM<Fn, jit::CharCodeToUpperCase>(lir, ArgList(code),
14694 StoreRegisterTo(output));
14695
14696 constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1;
14697
14698 // OOL path if code >= NonLatin1Min.
14699 masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry());
14700
14701 // Most one element Latin-1 strings can be directly retrieved from the
14702 // static strings cache, except the following three characters:
14703 //
14704 // 1. ToUpper(U+00B5) = 0+039C
14705 // 2. ToUpper(U+00FF) = 0+0178
14706 // 3. ToUpper(U+00DF) = 0+0053 0+0053
14707 masm.branch32(Assembler::Equal, code, Imm32(unicode::MICRO_SIGN),
14708 ool->entry());
14709 masm.branch32(Assembler::Equal, code,
14710 Imm32(unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS),
14711 ool->entry());
14712 masm.branch32(Assembler::Equal, code,
14713 Imm32(unicode::LATIN_SMALL_LETTER_SHARP_S), ool->entry());
14714
14715 // Inline unicode::ToUpperCase (without the special case for ASCII characters)
14716
14717 constexpr size_t shift = unicode::CharInfoShift;
14718
14719 // code >> shift
14720 masm.move32(code, temp);
14721 masm.rshift32(Imm32(shift), temp);
14722
14723 // index = index1[code >> shift];
14724 masm.movePtr(ImmPtr(unicode::index1), output);
14725 masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp);
14726
14727 // (code & ((1 << shift) - 1)
14728 masm.move32(code, output);
14729 masm.and32(Imm32((1 << shift) - 1), output);
14730
14731 // (index << shift) + (code & ((1 << shift) - 1))
14732 masm.lshift32(Imm32(shift), temp);
14733 masm.add32(output, temp);
14734
14735 // index = index2[(index << shift) + (code & ((1 << shift) - 1))]
14736 masm.movePtr(ImmPtr(unicode::index2), output);
14737 masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp);
14738
14739 // Compute |index * 6| through |(index * 3) * TimesTwo|.
14740 static_assert(sizeof(unicode::CharacterInfo) == 6);
14741 masm.mulBy3(temp, temp);
14742
14743 // upperCase = js_charinfo[index].upperCase
14744 masm.movePtr(ImmPtr(unicode::js_charinfo), output);
14745 masm.load16ZeroExtend(BaseIndex(output, temp, TimesTwo,
14746 offsetof(unicode::CharacterInfo, upperCase)__builtin_offsetof(unicode::CharacterInfo, upperCase)),
14747 temp);
14748
14749 // uint16_t(ch) + upperCase
14750 masm.add32(code, temp);
14751
14752 // Clear any high bits added when performing the unsigned 16-bit addition
14753 // through a signed 32-bit addition.
14754 masm.move8ZeroExtend(temp, temp);
14755
14756 // Load static string for upper case character.
14757 masm.lookupStaticString(temp, output, gen->runtime->staticStrings());
14758
14759 masm.bind(ool->rejoin());
14760}
14761
14762void CodeGenerator::visitStringTrimStartIndex(LStringTrimStartIndex* lir) {
14763 Register string = ToRegister(lir->string());
14764 Register output = ToRegister(lir->output());
14765
14766 auto volatileRegs = liveVolatileRegs(lir);
14767 volatileRegs.takeUnchecked(output);
14768
14769 masm.PushRegsInMask(volatileRegs);
14770
14771 using Fn = int32_t (*)(const JSString*);
14772 masm.setupAlignedABICall();
14773 masm.passABIArg(string);
14774 masm.callWithABI<Fn, jit::StringTrimStartIndex>();
14775 masm.storeCallInt32Result(output);
14776
14777 masm.PopRegsInMask(volatileRegs);
14778}
14779
14780void CodeGenerator::visitStringTrimEndIndex(LStringTrimEndIndex* lir) {
14781 Register string = ToRegister(lir->string());
14782 Register start = ToRegister(lir->start());
14783 Register output = ToRegister(lir->output());
14784
14785 auto volatileRegs = liveVolatileRegs(lir);
14786 volatileRegs.takeUnchecked(output);
14787
14788 masm.PushRegsInMask(volatileRegs);
14789
14790 using Fn = int32_t (*)(const JSString*, int32_t);
14791 masm.setupAlignedABICall();
14792 masm.passABIArg(string);
14793 masm.passABIArg(start);
14794 masm.callWithABI<Fn, jit::StringTrimEndIndex>();
14795 masm.storeCallInt32Result(output);
14796
14797 masm.PopRegsInMask(volatileRegs);
14798}
14799
14800void CodeGenerator::visitStringSplit(LStringSplit* lir) {
14801 pushArg(Imm32(INT32_MAX(2147483647)));
14802 pushArg(ToRegister(lir->separator()));
14803 pushArg(ToRegister(lir->string()));
14804
14805 using Fn = ArrayObject* (*)(JSContext*, HandleString, HandleString, uint32_t);
14806 callVM<Fn, js::StringSplitString>(lir);
14807}
14808
14809void CodeGenerator::visitInitializedLength(LInitializedLength* lir) {
14810 Address initLength(ToRegister(lir->elements()),
14811 ObjectElements::offsetOfInitializedLength());
14812 masm.load32(initLength, ToRegister(lir->output()));
14813}
14814
14815void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) {
14816 Address initLength(ToRegister(lir->elements()),
14817 ObjectElements::offsetOfInitializedLength());
14818 SetLengthFromIndex(masm, lir->index(), initLength);
14819}
14820
14821void CodeGenerator::visitNotI(LNotI* lir) {
14822 Register input = ToRegister(lir->input());
14823 Register output = ToRegister(lir->output());
14824
14825 masm.cmp32Set(Assembler::Equal, input, Imm32(0), output);
14826}
14827
14828void CodeGenerator::visitNotIPtr(LNotIPtr* lir) {
14829 Register input = ToRegister(lir->input());
14830 Register output = ToRegister(lir->output());
14831
14832 masm.cmpPtrSet(Assembler::Equal, input, ImmWord(0), output);
14833}
14834
14835void CodeGenerator::visitNotI64(LNotI64* lir) {
14836 Register64 input = ToRegister64(lir->inputI64());
14837 Register output = ToRegister(lir->output());
14838
14839 masm.cmp64Set(Assembler::Equal, input, Imm64(0), output);
14840}
14841
14842void CodeGenerator::visitNotBI(LNotBI* lir) {
14843 Register input = ToRegister(lir->input());
14844 Register output = ToRegister(lir->output());
14845
14846 masm.cmp32Set(Assembler::Equal, Address(input, BigInt::offsetOfLength()),
14847 Imm32(0), output);
14848}
14849
14850void CodeGenerator::visitNotO(LNotO* lir) {
14851 Register objreg = ToRegister(lir->input());
14852 Register output = ToRegister(lir->output());
14853
14854 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
14855 if (intact) {
14856 // Bug 1874905: It would be fantastic if this could be optimized out.
14857 assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir());
14858 masm.move32(Imm32(0), output);
14859 } else {
14860 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
14861 addOutOfLineCode(ool, lir->mir());
14862
14863 Label* ifEmulatesUndefined = ool->label1();
14864 Label* ifDoesntEmulateUndefined = ool->label2();
14865
14866 branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined,
14867 ifDoesntEmulateUndefined, output, ool);
14868 // fall through
14869
14870 Label join;
14871
14872 masm.move32(Imm32(0), output);
14873 masm.jump(&join);
14874
14875 masm.bind(ifEmulatesUndefined);
14876 masm.move32(Imm32(1), output);
14877
14878 masm.bind(&join);
14879 }
14880}
14881
14882void CodeGenerator::visitNotV(LNotV* lir) {
14883 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
14884 addOutOfLineCode(ool, lir->mir());
14885
14886 Label* ifTruthy = ool->label1();
14887 Label* ifFalsy = ool->label2();
14888
14889 ValueOperand input = ToValue(lir, LNotV::InputIndex);
14890 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
14891 FloatRegister floatTemp = ToFloatRegister(lir->temp0());
14892 Register output = ToRegister(lir->output());
14893 const TypeDataList& observedTypes = lir->mir()->observedTypes();
14894
14895 testValueTruthy(input, tempToUnbox, output, floatTemp, observedTypes,
14896 ifTruthy, ifFalsy, ool);
14897
14898 Label join;
14899
14900 // Note that the testValueTruthy call above may choose to fall through
14901 // to ifTruthy instead of branching there.
14902 masm.bind(ifTruthy);
14903 masm.move32(Imm32(0), output);
14904 masm.jump(&join);
14905
14906 masm.bind(ifFalsy);
14907 masm.move32(Imm32(1), output);
14908
14909 // both branches meet here.
14910 masm.bind(&join);
14911}
14912
14913void CodeGenerator::visitBoundsCheck(LBoundsCheck* lir) {
14914 const LAllocation* index = lir->index();
14915 const LAllocation* length = lir->length();
14916 LSnapshot* snapshot = lir->snapshot();
14917
14918 MIRType type = lir->mir()->type();
14919
14920 auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) {
14921 if (type == MIRType::Int32) {
14922 bailoutCmp32(cond, lhs, rhs, snapshot);
14923 } else {
14924 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"
, 14924); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14924; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14925 bailoutCmpPtr(cond, lhs, rhs, snapshot);
14926 }
14927 };
14928
14929 auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs,
14930 int32_t rhs) {
14931 if (type == MIRType::Int32) {
14932 bailoutCmp32(cond, lhs, Imm32(rhs), snapshot);
14933 } else {
14934 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"
, 14934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14934; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14935 bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot);
14936 }
14937 };
14938
14939 if (index->isConstant()) {
14940 // Use uint32 so that the comparison is unsigned.
14941 uint32_t idx = ToInt32(index);
14942 if (length->isConstant()) {
14943 uint32_t len = ToInt32(lir->length());
14944 if (idx < len) {
14945 return;
14946 }
14947 bailout(snapshot);
14948 return;
14949 }
14950
14951 if (length->isRegister()) {
14952 bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), idx);
14953 } else {
14954 bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), idx);
14955 }
14956 return;
14957 }
14958
14959 Register indexReg = ToRegister(index);
14960 if (length->isConstant()) {
14961 bailoutCmpConstant(Assembler::AboveOrEqual, indexReg, ToInt32(length));
14962 } else if (length->isRegister()) {
14963 bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), indexReg);
14964 } else {
14965 bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), indexReg);
14966 }
14967}
14968
14969void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) {
14970 int32_t min = lir->mir()->minimum();
14971 int32_t max = lir->mir()->maximum();
14972 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"
, 14972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "max >= min"
")"); do { *((volatile int*)__null) = 14972; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14973
14974 LSnapshot* snapshot = lir->snapshot();
14975 MIRType type = lir->mir()->type();
14976
14977 const LAllocation* length = lir->length();
14978 Register temp = ToRegister(lir->getTemp(0));
14979
14980 auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) {
14981 if (type == MIRType::Int32) {
14982 bailoutCmp32(cond, lhs, rhs, snapshot);
14983 } else {
14984 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"
, 14984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14984; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14985 bailoutCmpPtr(cond, lhs, rhs, snapshot);
14986 }
14987 };
14988
14989 auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs,
14990 int32_t rhs) {
14991 if (type == MIRType::Int32) {
14992 bailoutCmp32(cond, lhs, Imm32(rhs), snapshot);
14993 } else {
14994 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"
, 14994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 14994; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
14995 bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot);
14996 }
14997 };
14998
14999 if (lir->index()->isConstant()) {
15000 int32_t nmin, nmax;
15001 int32_t index = ToInt32(lir->index());
15002 if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) {
15003 if (length->isRegister()) {
15004 bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax);
15005 } else {
15006 bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), nmax);
15007 }
15008 return;
15009 }
15010 masm.mov(ImmWord(index), temp);
15011 } else {
15012 masm.mov(ToRegister(lir->index()), temp);
15013 }
15014
15015 // If the minimum and maximum differ then do an underflow check first.
15016 // If the two are the same then doing an unsigned comparison on the
15017 // length will also catch a negative index.
15018 if (min != max) {
15019 if (min != 0) {
15020 Label bail;
15021 if (type == MIRType::Int32) {
15022 masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail);
15023 } else {
15024 masm.branchAddPtr(Assembler::Overflow, Imm32(min), temp, &bail);
15025 }
15026 bailoutFrom(&bail, snapshot);
15027 }
15028
15029 bailoutCmpConstant(Assembler::LessThan, temp, 0);
15030
15031 if (min != 0) {
15032 int32_t diff;
15033 if (SafeSub(max, min, &diff)) {
15034 max = diff;
15035 } else {
15036 if (type == MIRType::Int32) {
15037 masm.sub32(Imm32(min), temp);
15038 } else {
15039 masm.subPtr(Imm32(min), temp);
15040 }
15041 }
15042 }
15043 }
15044
15045 // Compute the maximum possible index. No overflow check is needed when
15046 // max > 0. We can only wraparound to a negative number, which will test as
15047 // larger than all nonnegative numbers in the unsigned comparison, and the
15048 // length is required to be nonnegative (else testing a negative length
15049 // would succeed on any nonnegative index).
15050 if (max != 0) {
15051 if (max < 0) {
15052 Label bail;
15053 if (type == MIRType::Int32) {
15054 masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail);
15055 } else {
15056 masm.branchAddPtr(Assembler::Overflow, Imm32(max), temp, &bail);
15057 }
15058 bailoutFrom(&bail, snapshot);
15059 } else {
15060 if (type == MIRType::Int32) {
15061 masm.add32(Imm32(max), temp);
15062 } else {
15063 masm.addPtr(Imm32(max), temp);
15064 }
15065 }
15066 }
15067
15068 if (length->isRegister()) {
15069 bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), temp);
15070 } else {
15071 bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), temp);
15072 }
15073}
15074
15075void CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir) {
15076 int32_t min = lir->mir()->minimum();
15077 bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min),
15078 lir->snapshot());
15079}
15080
15081void CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir) {
15082 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"
, 15082); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitOptions.spectreIndexMasking"
")"); do { *((volatile int*)__null) = 15082; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15083
15084 const LAllocation* length = lir->length();
15085 Register index = ToRegister(lir->index());
15086 Register output = ToRegister(lir->output());
15087
15088 if (lir->mir()->type() == MIRType::Int32) {
15089 if (length->isRegister()) {
15090 masm.spectreMaskIndex32(index, ToRegister(length), output);
15091 } else {
15092 masm.spectreMaskIndex32(index, ToAddress(length), output);
15093 }
15094 } else {
15095 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"
, 15095); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 15095; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15096 if (length->isRegister()) {
15097 masm.spectreMaskIndexPtr(index, ToRegister(length), output);
15098 } else {
15099 masm.spectreMaskIndexPtr(index, ToAddress(length), output);
15100 }
15101 }
15102}
15103
15104class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator> {
15105 LInstruction* ins_;
15106
15107 public:
15108 explicit OutOfLineStoreElementHole(LInstruction* ins) : ins_(ins) {
15109 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"
, 15109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->isStoreElementHoleV() || ins->isStoreElementHoleT()"
")"); do { *((volatile int*)__null) = 15109; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15110 }
15111
15112 void accept(CodeGenerator* codegen) override {
15113 codegen->visitOutOfLineStoreElementHole(this);
15114 }
15115
15116 MStoreElementHole* mir() const {
15117 return ins_->isStoreElementHoleV() ? ins_->toStoreElementHoleV()->mir()
15118 : ins_->toStoreElementHoleT()->mir();
15119 }
15120 LInstruction* ins() const { return ins_; }
15121};
15122
15123void CodeGenerator::emitStoreHoleCheck(Register elements,
15124 const LAllocation* index,
15125 LSnapshot* snapshot) {
15126 Label bail;
15127 if (index->isConstant()) {
15128 Address dest(elements, ToInt32(index) * sizeof(js::Value));
15129 masm.branchTestMagic(Assembler::Equal, dest, &bail);
15130 } else {
15131 BaseObjectElementIndex dest(elements, ToRegister(index));
15132 masm.branchTestMagic(Assembler::Equal, dest, &bail);
15133 }
15134 bailoutFrom(&bail, snapshot);
15135}
15136
15137void CodeGenerator::emitStoreElementTyped(const LAllocation* value,
15138 MIRType valueType, Register elements,
15139 const LAllocation* index) {
15140 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"
, 15140); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole"
")"); do { *((volatile int*)__null) = 15140; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15141 ConstantOrRegister v = ToConstantOrRegister(value, valueType);
15142 if (index->isConstant()) {
15143 Address dest(elements, ToInt32(index) * sizeof(js::Value));
15144 masm.storeUnboxedValue(v, valueType, dest);
15145 } else {
15146 BaseObjectElementIndex dest(elements, ToRegister(index));
15147 masm.storeUnboxedValue(v, valueType, dest);
15148 }
15149}
15150
15151void CodeGenerator::visitStoreElementT(LStoreElementT* store) {
15152 Register elements = ToRegister(store->elements());
15153 const LAllocation* index = store->index();
15154
15155 if (store->mir()->needsBarrier()) {
15156 emitPreBarrier(elements, index);
15157 }
15158
15159 if (store->mir()->needsHoleCheck()) {
15160 emitStoreHoleCheck(elements, index, store->snapshot());
15161 }
15162
15163 emitStoreElementTyped(store->value(), store->mir()->value()->type(), elements,
15164 index);
15165}
15166
15167void CodeGenerator::visitStoreElementV(LStoreElementV* lir) {
15168 const ValueOperand value = ToValue(lir, LStoreElementV::Value);
15169 Register elements = ToRegister(lir->elements());
15170 const LAllocation* index = lir->index();
15171
15172 if (lir->mir()->needsBarrier()) {
15173 emitPreBarrier(elements, index);
15174 }
15175
15176 if (lir->mir()->needsHoleCheck()) {
15177 emitStoreHoleCheck(elements, index, lir->snapshot());
15178 }
15179
15180 if (lir->index()->isConstant()) {
15181 Address dest(elements, ToInt32(lir->index()) * sizeof(js::Value));
15182 masm.storeValue(value, dest);
15183 } else {
15184 BaseObjectElementIndex dest(elements, ToRegister(lir->index()));
15185 masm.storeValue(value, dest);
15186 }
15187}
15188
15189void CodeGenerator::visitStoreHoleValueElement(LStoreHoleValueElement* lir) {
15190 Register elements = ToRegister(lir->elements());
15191 Register index = ToRegister(lir->index());
15192
15193 Address elementsFlags(elements, ObjectElements::offsetOfFlags());
15194 masm.or32(Imm32(ObjectElements::NON_PACKED), elementsFlags);
15195
15196 BaseObjectElementIndex element(elements, index);
15197 masm.storeValue(MagicValue(JS_ELEMENTS_HOLE), element);
15198}
15199
15200void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) {
15201 auto* ool = new (alloc()) OutOfLineStoreElementHole(lir);
15202 addOutOfLineCode(ool, lir->mir());
15203
15204 Register obj = ToRegister(lir->object());
15205 Register elements = ToRegister(lir->elements());
15206 Register index = ToRegister(lir->index());
15207 Register temp = ToRegister(lir->temp0());
15208
15209 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
15210 masm.spectreBoundsCheck32(index, initLength, temp, ool->entry());
15211
15212 emitPreBarrier(elements, lir->index());
15213
15214 masm.bind(ool->rejoin());
15215 emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), elements,
15216 lir->index());
15217
15218 if (ValueNeedsPostBarrier(lir->mir()->value())) {
15219 LiveRegisterSet regs = liveVolatileRegs(lir);
15220 ConstantOrRegister val =
15221 ToConstantOrRegister(lir->value(), lir->mir()->value()->type());
15222 emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp, val);
15223 }
15224}
15225
15226void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) {
15227 auto* ool = new (alloc()) OutOfLineStoreElementHole(lir);
15228 addOutOfLineCode(ool, lir->mir());
15229
15230 Register obj = ToRegister(lir->object());
15231 Register elements = ToRegister(lir->elements());
15232 Register index = ToRegister(lir->index());
15233 const ValueOperand value = ToValue(lir, LStoreElementHoleV::ValueIndex);
15234 Register temp = ToRegister(lir->temp0());
15235
15236 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
15237 masm.spectreBoundsCheck32(index, initLength, temp, ool->entry());
15238
15239 emitPreBarrier(elements, lir->index());
15240
15241 masm.bind(ool->rejoin());
15242 masm.storeValue(value, BaseObjectElementIndex(elements, index));
15243
15244 if (ValueNeedsPostBarrier(lir->mir()->value())) {
15245 LiveRegisterSet regs = liveVolatileRegs(lir);
15246 emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp,
15247 ConstantOrRegister(value));
15248 }
15249}
15250
15251void CodeGenerator::visitOutOfLineStoreElementHole(
15252 OutOfLineStoreElementHole* ool) {
15253 Register object, elements, index;
15254 LInstruction* ins = ool->ins();
15255 mozilla::Maybe<ConstantOrRegister> value;
15256 Register temp;
15257
15258 if (ins->isStoreElementHoleV()) {
15259 LStoreElementHoleV* store = ins->toStoreElementHoleV();
15260 object = ToRegister(store->object());
15261 elements = ToRegister(store->elements());
15262 index = ToRegister(store->index());
15263 value.emplace(
15264 TypedOrValueRegister(ToValue(store, LStoreElementHoleV::ValueIndex)));
15265 temp = ToRegister(store->temp0());
15266 } else {
15267 LStoreElementHoleT* store = ins->toStoreElementHoleT();
15268 object = ToRegister(store->object());
15269 elements = ToRegister(store->elements());
15270 index = ToRegister(store->index());
15271 if (store->value()->isConstant()) {
15272 value.emplace(
15273 ConstantOrRegister(store->value()->toConstant()->toJSValue()));
15274 } else {
15275 MIRType valueType = store->mir()->value()->type();
15276 value.emplace(
15277 TypedOrValueRegister(valueType, ToAnyRegister(store->value())));
15278 }
15279 temp = ToRegister(store->temp0());
15280 }
15281
15282 Address initLength(elements, ObjectElements::offsetOfInitializedLength());
15283
15284 // We're out-of-bounds. We only handle the index == initlength case.
15285 // If index > initializedLength, bail out. Note that this relies on the
15286 // condition flags sticking from the incoming branch.
15287 // Also note: this branch does not need Spectre mitigations, doing that for
15288 // the capacity check below is sufficient.
15289 Label allocElement, addNewElement;
15290#if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \
15291 defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64)
15292 // Had to reimplement for MIPS because there are no flags.
15293 bailoutCmp32(Assembler::NotEqual, initLength, index, ins->snapshot());
15294#else
15295 bailoutIf(Assembler::NotEqual, ins->snapshot());
15296#endif
15297
15298 // If index < capacity, we can add a dense element inline. If not, we need
15299 // to allocate more elements first.
15300 masm.spectreBoundsCheck32(
15301 index, Address(elements, ObjectElements::offsetOfCapacity()), temp,
15302 &allocElement);
15303 masm.jump(&addNewElement);
15304
15305 masm.bind(&allocElement);
15306
15307 // Save all live volatile registers, except |temp|.
15308 LiveRegisterSet liveRegs = liveVolatileRegs(ins);
15309 liveRegs.takeUnchecked(temp);
15310 masm.PushRegsInMask(liveRegs);
15311
15312 masm.setupAlignedABICall();
15313 masm.loadJSContext(temp);
15314 masm.passABIArg(temp);
15315 masm.passABIArg(object);
15316
15317 using Fn = bool (*)(JSContext*, NativeObject*);
15318 masm.callWithABI<Fn, NativeObject::addDenseElementPure>();
15319 masm.storeCallPointerResult(temp);
15320
15321 masm.PopRegsInMask(liveRegs);
15322 bailoutIfFalseBool(temp, ins->snapshot());
15323
15324 // Load the reallocated elements pointer.
15325 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elements);
15326
15327 masm.bind(&addNewElement);
15328
15329 // Increment initLength
15330 masm.add32(Imm32(1), initLength);
15331
15332 // If length is now <= index, increment length too.
15333 Label skipIncrementLength;
15334 Address length(elements, ObjectElements::offsetOfLength());
15335 masm.branch32(Assembler::Above, length, index, &skipIncrementLength);
15336 masm.add32(Imm32(1), length);
15337 masm.bind(&skipIncrementLength);
15338
15339 // Jump to the inline path where we will store the value.
15340 // We rejoin after the prebarrier, because the memory is uninitialized.
15341 masm.jump(ool->rejoin());
15342}
15343
15344void CodeGenerator::visitArrayPopShift(LArrayPopShift* lir) {
15345 Register obj = ToRegister(lir->object());
15346 Register temp1 = ToRegister(lir->temp0());
15347 Register temp2 = ToRegister(lir->temp1());
15348 ValueOperand out = ToOutValue(lir);
15349
15350 Label bail;
15351 if (lir->mir()->mode() == MArrayPopShift::Pop) {
15352 masm.packedArrayPop(obj, out, temp1, temp2, &bail);
15353 } else {
15354 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"
, 15354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MArrayPopShift::Shift"
")"); do { *((volatile int*)__null) = 15354; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15355 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
15356 masm.packedArrayShift(obj, out, temp1, temp2, volatileRegs, &bail);
15357 }
15358 bailoutFrom(&bail, lir->snapshot());
15359}
15360
15361class OutOfLineArrayPush : public OutOfLineCodeBase<CodeGenerator> {
15362 LArrayPush* ins_;
15363
15364 public:
15365 explicit OutOfLineArrayPush(LArrayPush* ins) : ins_(ins) {}
15366
15367 void accept(CodeGenerator* codegen) override {
15368 codegen->visitOutOfLineArrayPush(this);
15369 }
15370
15371 LArrayPush* ins() const { return ins_; }
15372};
15373
15374void CodeGenerator::visitArrayPush(LArrayPush* lir) {
15375 Register obj = ToRegister(lir->object());
15376 Register elementsTemp = ToRegister(lir->temp0());
15377 Register length = ToRegister(lir->output());
15378 ValueOperand value = ToValue(lir, LArrayPush::ValueIndex);
15379 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
15380
15381 auto* ool = new (alloc()) OutOfLineArrayPush(lir);
15382 addOutOfLineCode(ool, lir->mir());
15383
15384 // Load obj->elements in elementsTemp.
15385 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
15386
15387 Address initLengthAddr(elementsTemp,
15388 ObjectElements::offsetOfInitializedLength());
15389 Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength());
15390 Address capacityAddr(elementsTemp, ObjectElements::offsetOfCapacity());
15391
15392 // Bail out if length != initLength.
15393 masm.load32(lengthAddr, length);
15394 bailoutCmp32(Assembler::NotEqual, initLengthAddr, length, lir->snapshot());
15395
15396 // If length < capacity, we can add a dense element inline. If not, we
15397 // need to allocate more elements.
15398 masm.spectreBoundsCheck32(length, capacityAddr, spectreTemp, ool->entry());
15399 masm.bind(ool->rejoin());
15400
15401 // Store the value.
15402 masm.storeValue(value, BaseObjectElementIndex(elementsTemp, length));
15403
15404 // Update length and initialized length.
15405 masm.add32(Imm32(1), length);
15406 masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
15407 masm.store32(length, Address(elementsTemp,
15408 ObjectElements::offsetOfInitializedLength()));
15409
15410 if (ValueNeedsPostBarrier(lir->mir()->value())) {
15411 LiveRegisterSet regs = liveVolatileRegs(lir);
15412 regs.addUnchecked(length);
15413 emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->output()->output(),
15414 elementsTemp, ConstantOrRegister(value),
15415 /* indexDiff = */ -1);
15416 }
15417}
15418
15419void CodeGenerator::visitOutOfLineArrayPush(OutOfLineArrayPush* ool) {
15420 LArrayPush* ins = ool->ins();
15421
15422 Register object = ToRegister(ins->object());
15423 Register temp = ToRegister(ins->temp0());
15424
15425 LiveRegisterSet liveRegs = liveVolatileRegs(ins);
15426 liveRegs.takeUnchecked(temp);
15427 liveRegs.addUnchecked(ToRegister(ins->output()));
15428 liveRegs.addUnchecked(ToValue(ins, LArrayPush::ValueIndex));
15429
15430 masm.PushRegsInMask(liveRegs);
15431
15432 masm.setupAlignedABICall();
15433 masm.loadJSContext(temp);
15434 masm.passABIArg(temp);
15435 masm.passABIArg(object);
15436
15437 using Fn = bool (*)(JSContext*, NativeObject* obj);
15438 masm.callWithABI<Fn, NativeObject::addDenseElementPure>();
15439 masm.storeCallPointerResult(temp);
15440
15441 masm.PopRegsInMask(liveRegs);
15442 bailoutIfFalseBool(temp, ins->snapshot());
15443
15444 // Load the reallocated elements pointer.
15445 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
15446
15447 masm.jump(ool->rejoin());
15448}
15449
15450void CodeGenerator::visitArraySlice(LArraySlice* lir) {
15451 Register object = ToRegister(lir->object());
15452 Register begin = ToRegister(lir->begin());
15453 Register end = ToRegister(lir->end());
15454 Register temp0 = ToRegister(lir->temp0());
15455 Register temp1 = ToRegister(lir->temp1());
15456
15457 Label call, fail;
15458
15459 Label bail;
15460 masm.branchArrayIsNotPacked(object, temp0, temp1, &bail);
15461 bailoutFrom(&bail, lir->snapshot());
15462
15463 // Try to allocate an object.
15464 TemplateObject templateObject(lir->mir()->templateObj());
15465 masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(),
15466 &fail);
15467
15468 masm.jump(&call);
15469 {
15470 masm.bind(&fail);
15471 masm.movePtr(ImmPtr(nullptr), temp0);
15472 }
15473 masm.bind(&call);
15474
15475 pushArg(temp0);
15476 pushArg(end);
15477 pushArg(begin);
15478 pushArg(object);
15479
15480 using Fn =
15481 JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
15482 callVM<Fn, ArraySliceDense>(lir);
15483}
15484
15485void CodeGenerator::visitArgumentsSlice(LArgumentsSlice* lir) {
15486 Register object = ToRegister(lir->object());
15487 Register begin = ToRegister(lir->begin());
15488 Register end = ToRegister(lir->end());
15489 Register temp0 = ToRegister(lir->temp0());
15490 Register temp1 = ToRegister(lir->temp1());
15491
15492 Label call, fail;
15493
15494 // Try to allocate an object.
15495 TemplateObject templateObject(lir->mir()->templateObj());
15496 masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(),
15497 &fail);
15498
15499 masm.jump(&call);
15500 {
15501 masm.bind(&fail);
15502 masm.movePtr(ImmPtr(nullptr), temp0);
15503 }
15504 masm.bind(&call);
15505
15506 pushArg(temp0);
15507 pushArg(end);
15508 pushArg(begin);
15509 pushArg(object);
15510
15511 using Fn =
15512 JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject);
15513 callVM<Fn, ArgumentsSliceDense>(lir);
15514}
15515
15516#ifdef DEBUG1
15517void CodeGenerator::emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin,
15518 const RegisterOrInt32& count,
15519 Register numActualArgs) {
15520 // |begin| must be positive or zero.
15521 if (begin.is<Register>()) {
15522 Label beginOk;
15523 masm.branch32(Assembler::GreaterThanOrEqual, begin.as<Register>(), Imm32(0),
15524 &beginOk);
15525 masm.assumeUnreachable("begin < 0");
15526 masm.bind(&beginOk);
15527 } else {
15528 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"
, 15528); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() >= 0"
")"); do { *((volatile int*)__null) = 15528; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15529 }
15530
15531 // |count| must be positive or zero.
15532 if (count.is<Register>()) {
15533 Label countOk;
15534 masm.branch32(Assembler::GreaterThanOrEqual, count.as<Register>(), Imm32(0),
15535 &countOk);
15536 masm.assumeUnreachable("count < 0");
15537 masm.bind(&countOk);
15538 } else {
15539 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"
, 15539); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count.as<int32_t>() >= 0"
")"); do { *((volatile int*)__null) = 15539; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15540 }
15541
15542 // |begin| must be less-or-equal to |numActualArgs|.
15543 Label argsBeginOk;
15544 if (begin.is<Register>()) {
15545 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(),
15546 &argsBeginOk);
15547 } else {
15548 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
15549 Imm32(begin.as<int32_t>()), &argsBeginOk);
15550 }
15551 masm.assumeUnreachable("begin <= numActualArgs");
15552 masm.bind(&argsBeginOk);
15553
15554 // |count| must be less-or-equal to |numActualArgs|.
15555 Label argsCountOk;
15556 if (count.is<Register>()) {
15557 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, count.as<Register>(),
15558 &argsCountOk);
15559 } else {
15560 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
15561 Imm32(count.as<int32_t>()), &argsCountOk);
15562 }
15563 masm.assumeUnreachable("count <= numActualArgs");
15564 masm.bind(&argsCountOk);
15565
15566 // |begin| and |count| must be preserved, but |numActualArgs| can be changed.
15567 //
15568 // Pre-condition: |count| <= |numActualArgs|
15569 // Condition to test: |begin + count| <= |numActualArgs|
15570 // Transform to: |begin| <= |numActualArgs - count|
15571 if (count.is<Register>()) {
15572 masm.subPtr(count.as<Register>(), numActualArgs);
15573 } else {
15574 masm.subPtr(Imm32(count.as<int32_t>()), numActualArgs);
15575 }
15576
15577 // |begin + count| must be less-or-equal to |numActualArgs|.
15578 Label argsBeginCountOk;
15579 if (begin.is<Register>()) {
15580 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(),
15581 &argsBeginCountOk);
15582 } else {
15583 masm.branchPtr(Assembler::AboveOrEqual, numActualArgs,
15584 Imm32(begin.as<int32_t>()), &argsBeginCountOk);
15585 }
15586 masm.assumeUnreachable("begin + count <= numActualArgs");
15587 masm.bind(&argsBeginCountOk);
15588}
15589#endif
15590
15591template <class ArgumentsSlice>
15592void CodeGenerator::emitNewArray(ArgumentsSlice* lir,
15593 const RegisterOrInt32& count, Register output,
15594 Register temp) {
15595 using Fn = ArrayObject* (*)(JSContext*, int32_t);
15596 auto* ool = count.match(
15597 [&](Register count) {
15598 return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>(
15599 lir, ArgList(count), StoreRegisterTo(output));
15600 },
15601 [&](int32_t count) {
15602 return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>(
15603 lir, ArgList(Imm32(count)), StoreRegisterTo(output));
15604 });
15605
15606 TemplateObject templateObject(lir->mir()->templateObj());
15607 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"
, 15607); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject.isArrayObject()"
")"); do { *((volatile int*)__null) = 15607; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15608
15609 auto templateNativeObj = templateObject.asTemplateNativeObject();
15610 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"
, 15610); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getArrayLength() == 0"
")"); do { *((volatile int*)__null) = 15610; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15611 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"
, 15611); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getDenseInitializedLength() == 0"
")"); do { *((volatile int*)__null) = 15611; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15612 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"
, 15612); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateNativeObj.hasDynamicElements()"
")"); do { *((volatile int*)__null) = 15612; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15613
15614 // Check array capacity. Call into the VM if the template object's capacity
15615 // is too small.
15616 bool tryAllocate = count.match(
15617 [&](Register count) {
15618 masm.branch32(Assembler::Above, count,
15619 Imm32(templateNativeObj.getDenseCapacity()),
15620 ool->entry());
15621 return true;
15622 },
15623 [&](int32_t count) {
15624 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"
, 15624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count >= 0"
")"); do { *((volatile int*)__null) = 15624; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
15625 if (uint32_t(count) > templateNativeObj.getDenseCapacity()) {
15626 masm.jump(ool->entry());
15627 return false;
15628 }
15629 return true;
15630 });
15631
15632 if (tryAllocate) {
15633 // Try to allocate an object.
15634 masm.createGCObject(output, temp, templateObject, lir->mir()->initialHeap(),
15635 ool->entry());
15636
15637 auto setInitializedLengthAndLength = [&](auto count) {
15638 const int elementsOffset = NativeObject::offsetOfFixedElements();
15639
15640 // Update initialized length.
15641 Address initLength(
15642 output, elementsOffset + ObjectElements::offsetOfInitializedLength());
15643 masm.store32(count, initLength);
15644
15645 // Update length.
15646 Address length(output, elementsOffset + ObjectElements::offsetOfLength());
15647 masm.store32(count, length);
15648 };
15649
15650 // The array object was successfully created. Set the length and initialized
15651 // length and then proceed to fill the elements.
15652 count.match([&](Register count) { setInitializedLengthAndLength(count); },
15653 [&](int32_t count) {
15654 if (count > 0) {
15655 setInitializedLengthAndLength(Imm32(count));
15656 }
15657 });
15658 }
15659
15660 masm.bind(ool->rejoin());
15661}
15662
15663void CodeGenerator::visitFrameArgumentsSlice(LFrameArgumentsSlice* lir) {
15664 Register begin = ToRegister(lir->begin());
15665 Register count = ToRegister(lir->count());
15666 Register temp = ToRegister(lir->temp0());
15667 Register output = ToRegister(lir->output());
15668
15669#ifdef DEBUG1
15670 masm.loadNumActualArgs(FramePointer, temp);
15671 emitAssertArgumentsSliceBounds(RegisterOrInt32(begin), RegisterOrInt32(count),
15672 temp);
15673#endif
15674
15675 emitNewArray(lir, RegisterOrInt32(count), output, temp);
15676
15677 Label done;
15678 masm.branch32(Assembler::Equal, count, Imm32(0), &done);
15679 {
15680 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
15681 allRegs.take(begin);
15682 allRegs.take(count);
15683 allRegs.take(temp);
15684 allRegs.take(output);
15685
15686 ValueOperand value = allRegs.takeAnyValue();
15687
15688 LiveRegisterSet liveRegs;
15689 liveRegs.add(output);
15690 liveRegs.add(begin);
15691 liveRegs.add(value);
15692
15693 masm.PushRegsInMask(liveRegs);
15694
15695 // Initialize all elements.
15696
15697 Register elements = output;
15698 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
15699
15700 Register argIndex = begin;
15701
15702 Register index = temp;
15703 masm.move32(Imm32(0), index);
15704
15705 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
15706 BaseValueIndex argPtr(FramePointer, argIndex, argvOffset);
15707
15708 Label loop;
15709 masm.bind(&loop);
15710
15711 masm.loadValue(argPtr, value);
15712
15713 // We don't need a pre-barrier, because the element at |index| is guaranteed
15714 // to be a non-GC thing (either uninitialized memory or the magic hole
15715 // value).
15716 masm.storeValue(value, BaseObjectElementIndex(elements, index));
15717
15718 masm.add32(Imm32(1), index);
15719 masm.add32(Imm32(1), argIndex);
15720
15721 masm.branch32(Assembler::LessThan, index, count, &loop);
15722
15723 masm.PopRegsInMask(liveRegs);
15724
15725 // Emit a post-write barrier if |output| is tenured.
15726 //
15727 // We expect that |output| is nursery allocated, so it isn't worth the
15728 // trouble to check if no frame argument is a nursery thing, which would
15729 // allow to omit the post-write barrier.
15730 masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done);
15731
15732 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
15733 volatileRegs.takeUnchecked(temp);
15734 if (output.volatile_()) {
15735 volatileRegs.addUnchecked(output);
15736 }
15737
15738 masm.PushRegsInMask(volatileRegs);
15739 emitPostWriteBarrier(output);
15740 masm.PopRegsInMask(volatileRegs);
15741 }
15742 masm.bind(&done);
15743}
15744
15745CodeGenerator::RegisterOrInt32 CodeGenerator::ToRegisterOrInt32(
15746 const LAllocation* allocation) {
15747 if (allocation->isConstant()) {
15748 return RegisterOrInt32(allocation->toConstant()->toInt32());
15749 }
15750 return RegisterOrInt32(ToRegister(allocation));
15751}
15752
15753void CodeGenerator::visitInlineArgumentsSlice(LInlineArgumentsSlice* lir) {
15754 RegisterOrInt32 begin = ToRegisterOrInt32(lir->begin());
15755 RegisterOrInt32 count = ToRegisterOrInt32(lir->count());
15756 Register temp = ToRegister(lir->temp());
15757 Register output = ToRegister(lir->output());
15758
15759 uint32_t numActuals = lir->mir()->numActuals();
15760
15761#ifdef DEBUG1
15762 masm.move32(Imm32(numActuals), temp);
15763
15764 emitAssertArgumentsSliceBounds(begin, count, temp);
15765#endif
15766
15767 emitNewArray(lir, count, output, temp);
15768
15769 // We're done if there are no actual arguments.
15770 if (numActuals == 0) {
15771 return;
15772 }
15773
15774 // Check if any arguments have to be copied.
15775 Label done;
15776 if (count.is<Register>()) {
15777 masm.branch32(Assembler::Equal, count.as<Register>(), Imm32(0), &done);
15778 } else if (count.as<int32_t>() == 0) {
15779 return;
15780 }
15781
15782 auto getArg = [&](uint32_t i) {
15783 return toConstantOrRegister(lir, LInlineArgumentsSlice::ArgIndex(i),
15784 lir->mir()->getArg(i)->type());
15785 };
15786
15787 auto storeArg = [&](uint32_t i, auto dest) {
15788 // We don't need a pre-barrier because the element at |index| is guaranteed
15789 // to be a non-GC thing (either uninitialized memory or the magic hole
15790 // value).
15791 masm.storeConstantOrRegister(getArg(i), dest);
15792 };
15793
15794 // Initialize all elements.
15795 if (numActuals == 1) {
15796 // There's exactly one argument. We've checked that |count| is non-zero,
15797 // which implies that |begin| must be zero.
15798 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"
, 15798); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() == 0"
")"); do { *((volatile int*)__null) = 15798; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
15799
15800 Register elements = temp;
15801 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
15802
15803 storeArg(0, Address(elements, 0));
15804 } else if (begin.is<Register>()) {
15805 // There is more than one argument and |begin| isn't a compile-time
15806 // constant. Iterate through 0..numActuals to search for |begin| and then
15807 // start copying |count| arguments from that index.
15808
15809 LiveGeneralRegisterSet liveRegs;
15810 liveRegs.add(output);
15811 liveRegs.add(begin.as<Register>());
15812
15813 masm.PushRegsInMask(liveRegs);
15814
15815 Register elements = output;
15816 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
15817
15818 Register argIndex = begin.as<Register>();
15819
15820 Register index = temp;
15821 masm.move32(Imm32(0), index);
15822
15823 Label doneLoop;
15824 for (uint32_t i = 0; i < numActuals; ++i) {
15825 Label next;
15826 masm.branch32(Assembler::NotEqual, argIndex, Imm32(i), &next);
15827
15828 storeArg(i, BaseObjectElementIndex(elements, index));
15829
15830 masm.add32(Imm32(1), index);
15831 masm.add32(Imm32(1), argIndex);
15832
15833 if (count.is<Register>()) {
15834 masm.branch32(Assembler::GreaterThanOrEqual, index,
15835 count.as<Register>(), &doneLoop);
15836 } else {
15837 masm.branch32(Assembler::GreaterThanOrEqual, index,
15838 Imm32(count.as<int32_t>()), &doneLoop);
15839 }
15840
15841 masm.bind(&next);
15842 }
15843 masm.bind(&doneLoop);
15844
15845 masm.PopRegsInMask(liveRegs);
15846 } else {
15847 // There is more than one argument and |begin| is a compile-time constant.
15848
15849 Register elements = temp;
15850 masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements);
15851
15852 int32_t argIndex = begin.as<int32_t>();
15853
15854 int32_t index = 0;
15855
15856 Label doneLoop;
15857 for (uint32_t i = argIndex; i < numActuals; ++i) {
15858 storeArg(i, Address(elements, index * sizeof(Value)));
15859
15860 index += 1;
15861
15862 if (count.is<Register>()) {
15863 masm.branch32(Assembler::LessThanOrEqual, count.as<Register>(),
15864 Imm32(index), &doneLoop);
15865 } else {
15866 if (index >= count.as<int32_t>()) {
15867 break;
15868 }
15869 }
15870 }
15871 masm.bind(&doneLoop);
15872 }
15873
15874 // Determine if we have to emit post-write barrier.
15875 //
15876 // If either |begin| or |count| is a constant, use their value directly.
15877 // Otherwise assume we copy all inline arguments from 0..numActuals.
15878 bool postWriteBarrier = false;
15879 uint32_t actualBegin = begin.match([](Register) { return 0; },
15880 [](int32_t value) { return value; });
15881 uint32_t actualCount =
15882 count.match([=](Register) { return numActuals; },
15883 [](int32_t value) -> uint32_t { return value; });
15884 for (uint32_t i = 0; i < actualCount; ++i) {
15885 ConstantOrRegister arg = getArg(actualBegin + i);
15886 if (arg.constant()) {
15887 Value v = arg.value();
15888 if (v.isGCThing() && IsInsideNursery(v.toGCThing())) {
15889 postWriteBarrier = true;
15890 }
15891 } else {
15892 MIRType type = arg.reg().type();
15893 if (type == MIRType::Value || NeedsPostBarrier(type)) {
15894 postWriteBarrier = true;
15895 }
15896 }
15897 }
15898
15899 // Emit a post-write barrier if |output| is tenured and we couldn't
15900 // determine at compile-time that no barrier is needed.
15901 if (postWriteBarrier) {
15902 masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done);
15903
15904 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
15905 volatileRegs.takeUnchecked(temp);
15906 if (output.volatile_()) {
15907 volatileRegs.addUnchecked(output);
15908 }
15909
15910 masm.PushRegsInMask(volatileRegs);
15911 emitPostWriteBarrier(output);
15912 masm.PopRegsInMask(volatileRegs);
15913 }
15914
15915 masm.bind(&done);
15916}
15917
15918void CodeGenerator::visitNormalizeSliceTerm(LNormalizeSliceTerm* lir) {
15919 Register value = ToRegister(lir->value());
15920 Register length = ToRegister(lir->length());
15921 Register output = ToRegister(lir->output());
15922
15923 masm.move32(value, output);
15924
15925 Label positive;
15926 masm.branch32(Assembler::GreaterThanOrEqual, value, Imm32(0), &positive);
15927
15928 Label done;
15929 masm.add32(length, output);
15930 masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(0), &done);
15931 masm.move32(Imm32(0), output);
15932 masm.jump(&done);
15933
15934 masm.bind(&positive);
15935 masm.cmp32Move32(Assembler::LessThan, length, value, length, output);
15936
15937 masm.bind(&done);
15938}
15939
15940void CodeGenerator::visitArrayJoin(LArrayJoin* lir) {
15941 Label skipCall;
15942
15943 Register output = ToRegister(lir->output());
15944 Register sep = ToRegister(lir->separator());
15945 Register array = ToRegister(lir->array());
15946 Register temp = ToRegister(lir->temp0());
15947
15948 // Fast path for simple length <= 1 cases.
15949 {
15950 masm.loadPtr(Address(array, NativeObject::offsetOfElements()), temp);
15951 Address length(temp, ObjectElements::offsetOfLength());
15952 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
15953
15954 // Check for length == 0
15955 Label notEmpty;
15956 masm.branch32(Assembler::NotEqual, length, Imm32(0), &notEmpty);
15957 const JSAtomState& names = gen->runtime->names();
15958 masm.movePtr(ImmGCPtr(names.empty_), output);
15959 masm.jump(&skipCall);
15960
15961 masm.bind(&notEmpty);
15962 Label notSingleString;
15963 // Check for length == 1, initializedLength >= 1, arr[0].isString()
15964 masm.branch32(Assembler::NotEqual, length, Imm32(1), &notSingleString);
15965 masm.branch32(Assembler::LessThan, initLength, Imm32(1), &notSingleString);
15966
15967 Address elem0(temp, 0);
15968 masm.branchTestString(Assembler::NotEqual, elem0, &notSingleString);
15969
15970 // At this point, 'output' can be used as a scratch register, since we're
15971 // guaranteed to succeed.
15972 masm.unboxString(elem0, output);
15973 masm.jump(&skipCall);
15974 masm.bind(&notSingleString);
15975 }
15976
15977 pushArg(sep);
15978 pushArg(array);
15979
15980 using Fn = JSString* (*)(JSContext*, HandleObject, HandleString);
15981 callVM<Fn, jit::ArrayJoin>(lir);
15982 masm.bind(&skipCall);
15983}
15984
15985void CodeGenerator::visitObjectKeys(LObjectKeys* lir) {
15986 Register object = ToRegister(lir->object());
15987
15988 pushArg(object);
15989
15990 using Fn = JSObject* (*)(JSContext*, HandleObject);
15991 callVM<Fn, jit::ObjectKeys>(lir);
15992}
15993
15994void CodeGenerator::visitObjectKeysLength(LObjectKeysLength* lir) {
15995 Register object = ToRegister(lir->object());
15996
15997 pushArg(object);
15998
15999 using Fn = bool (*)(JSContext*, HandleObject, int32_t*);
16000 callVM<Fn, jit::ObjectKeysLength>(lir);
16001}
16002
16003void CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir) {
16004 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16005 TypedOrValueRegister val =
16006 toConstantOrRegister(lir, LGetIteratorCache::ValueIndex,
16007 lir->mir()->value()->type())
16008 .reg();
16009 Register output = ToRegister(lir->output());
16010 Register temp0 = ToRegister(lir->temp0());
16011 Register temp1 = ToRegister(lir->temp1());
16012
16013 IonGetIteratorIC ic(liveRegs, val, output, temp0, temp1);
16014 addIC(lir, allocateIC(ic));
16015}
16016
16017void CodeGenerator::visitOptimizeSpreadCallCache(
16018 LOptimizeSpreadCallCache* lir) {
16019 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16020 ValueOperand val = ToValue(lir, LOptimizeSpreadCallCache::ValueIndex);
16021 ValueOperand output = ToOutValue(lir);
16022 Register temp = ToRegister(lir->temp0());
16023
16024 IonOptimizeSpreadCallIC ic(liveRegs, val, output, temp);
16025 addIC(lir, allocateIC(ic));
16026}
16027
16028void CodeGenerator::visitCloseIterCache(LCloseIterCache* lir) {
16029 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16030 Register iter = ToRegister(lir->iter());
16031 Register temp = ToRegister(lir->temp0());
16032 CompletionKind kind = CompletionKind(lir->mir()->completionKind());
16033
16034 IonCloseIterIC ic(liveRegs, iter, temp, kind);
16035 addIC(lir, allocateIC(ic));
16036}
16037
16038void CodeGenerator::visitOptimizeGetIteratorCache(
16039 LOptimizeGetIteratorCache* lir) {
16040 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
16041 ValueOperand val = ToValue(lir, LOptimizeGetIteratorCache::ValueIndex);
16042 Register output = ToRegister(lir->output());
16043 Register temp = ToRegister(lir->temp0());
16044
16045 IonOptimizeGetIteratorIC ic(liveRegs, val, output, temp);
16046 addIC(lir, allocateIC(ic));
16047}
16048
16049void CodeGenerator::visitIteratorMore(LIteratorMore* lir) {
16050 const Register obj = ToRegister(lir->iterator());
16051 const ValueOperand output = ToOutValue(lir);
16052 const Register temp = ToRegister(lir->temp0());
16053
16054 masm.iteratorMore(obj, output, temp);
16055}
16056
16057void CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch* lir) {
16058 ValueOperand input = ToValue(lir, LIsNoIterAndBranch::Input);
16059 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
16060 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
16061
16062 masm.branchTestMagic(Assembler::Equal, input, ifTrue);
16063
16064 if (!isNextBlock(lir->ifFalse()->lir())) {
16065 masm.jump(ifFalse);
16066 }
16067}
16068
16069void CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) {
16070 const Register obj = ToRegister(lir->object());
16071 const Register temp0 = ToRegister(lir->temp0());
16072 const Register temp1 = ToRegister(lir->temp1());
16073 const Register temp2 = ToRegister(lir->temp2());
16074
16075 masm.iteratorClose(obj, temp0, temp1, temp2);
16076}
16077
16078void CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) {
16079 // read number of actual arguments from the JS frame.
16080 Register argc = ToRegister(lir->output());
16081 masm.loadNumActualArgs(FramePointer, argc);
16082}
16083
16084void CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) {
16085 ValueOperand result = ToOutValue(lir);
16086 const LAllocation* index = lir->index();
16087 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
16088
16089 // This instruction is used to access actual arguments and formal arguments.
16090 // The number of Values on the stack is |max(numFormals, numActuals)|, so we
16091 // assert |index < numFormals || index < numActuals| in debug builds.
16092 DebugOnly<size_t> numFormals = gen->outerInfo().script()->function()->nargs();
16093
16094 if (index->isConstant()) {
16095 int32_t i = index->toConstant()->toInt32();
16096#ifdef DEBUG1
16097 if (uint32_t(i) >= numFormals) {
16098 Label ok;
16099 Register argc = result.scratchReg();
16100 masm.loadNumActualArgs(FramePointer, argc);
16101 masm.branch32(Assembler::Above, argc, Imm32(i), &ok);
16102 masm.assumeUnreachable("Invalid argument index");
16103 masm.bind(&ok);
16104 }
16105#endif
16106 Address argPtr(FramePointer, sizeof(Value) * i + argvOffset);
16107 masm.loadValue(argPtr, result);
16108 } else {
16109 Register i = ToRegister(index);
16110#ifdef DEBUG1
16111 Label ok;
16112 Register argc = result.scratchReg();
16113 masm.branch32(Assembler::Below, i, Imm32(numFormals), &ok);
16114 masm.loadNumActualArgs(FramePointer, argc);
16115 masm.branch32(Assembler::Above, argc, i, &ok);
16116 masm.assumeUnreachable("Invalid argument index");
16117 masm.bind(&ok);
16118#endif
16119 BaseValueIndex argPtr(FramePointer, i, argvOffset);
16120 masm.loadValue(argPtr, result);
16121 }
16122}
16123
16124void CodeGenerator::visitGetFrameArgumentHole(LGetFrameArgumentHole* lir) {
16125 ValueOperand result = ToOutValue(lir);
16126 Register index = ToRegister(lir->index());
16127 Register length = ToRegister(lir->length());
16128 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0());
16129 size_t argvOffset = JitFrameLayout::offsetOfActualArgs();
16130
16131 Label outOfBounds, done;
16132 masm.spectreBoundsCheck32(index, length, spectreTemp, &outOfBounds);
16133
16134 BaseValueIndex argPtr(FramePointer, index, argvOffset);
16135 masm.loadValue(argPtr, result);
16136 masm.jump(&done);
16137
16138 masm.bind(&outOfBounds);
16139 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
16140 masm.moveValue(UndefinedValue(), result);
16141
16142 masm.bind(&done);
16143}
16144
16145void CodeGenerator::visitRest(LRest* lir) {
16146 Register numActuals = ToRegister(lir->numActuals());
16147 Register temp0 = ToRegister(lir->temp0());
16148 Register temp1 = ToRegister(lir->temp1());
16149 Register temp2 = ToRegister(lir->temp2());
16150 Register temp3 = ToRegister(lir->temp3());
16151 unsigned numFormals = lir->mir()->numFormals();
16152
16153 constexpr uint32_t arrayCapacity = 2;
16154
16155 if (Shape* shape = lir->mir()->shape()) {
16156 uint32_t arrayLength = 0;
16157 gc::AllocKind allocKind = GuessArrayGCKind(arrayCapacity);
16158 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"
, 16158); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)"
")"); do { *((volatile int*)__null) = 16158; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16159 allocKind = ForegroundToBackgroundAllocKind(allocKind);
16160 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"
, 16161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
")"); do { *((volatile int*)__null) = 16161; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16161 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"
, 16161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER"
")"); do { *((volatile int*)__null) = 16161; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16162
16163 Label joinAlloc, failAlloc;
16164 masm.movePtr(ImmGCPtr(shape), temp0);
16165 masm.createArrayWithFixedElements(temp2, temp0, temp1, InvalidReg,
16166 arrayLength, arrayCapacity, 0, 0,
16167 allocKind, gc::Heap::Default, &failAlloc);
16168 masm.jump(&joinAlloc);
16169 {
16170 masm.bind(&failAlloc);
16171 masm.movePtr(ImmPtr(nullptr), temp2);
16172 }
16173 masm.bind(&joinAlloc);
16174 } else {
16175 masm.movePtr(ImmPtr(nullptr), temp2);
16176 }
16177
16178 // Set temp1 to the address of the first actual argument.
16179 size_t actualsOffset = JitFrameLayout::offsetOfActualArgs();
16180 masm.computeEffectiveAddress(Address(FramePointer, actualsOffset), temp1);
16181
16182 // Compute array length: max(numActuals - numFormals, 0).
16183 Register lengthReg;
16184 if (numFormals) {
16185 lengthReg = temp0;
16186 Label emptyLength, joinLength;
16187 masm.branch32(Assembler::LessThanOrEqual, numActuals, Imm32(numFormals),
16188 &emptyLength);
16189 {
16190 masm.move32(numActuals, lengthReg);
16191 masm.sub32(Imm32(numFormals), lengthReg);
16192
16193 // Skip formal arguments.
16194 masm.addPtr(Imm32(sizeof(Value) * numFormals), temp1);
16195
16196 masm.jump(&joinLength);
16197 }
16198 masm.bind(&emptyLength);
16199 {
16200 masm.move32(Imm32(0), lengthReg);
16201
16202 // Leave temp1 pointed to the start of actuals() when the rest-array
16203 // length is zero. We don't use |actuals() + numFormals| because
16204 // |numFormals| can be any non-negative int32 value when this MRest was
16205 // created from scalar replacement optimizations. And it seems
16206 // questionable to compute a Value* pointer which points to who knows
16207 // where.
16208 }
16209 masm.bind(&joinLength);
16210 } else {
16211 // Use numActuals directly when there are no formals.
16212 lengthReg = numActuals;
16213 }
16214
16215 // Try to initialize the array elements.
16216 Label vmCall, done;
16217 if (lir->mir()->shape()) {
16218 // Call into C++ if we failed to allocate an array or there are more than
16219 // |arrayCapacity| elements.
16220 masm.branchTestPtr(Assembler::Zero, temp2, temp2, &vmCall);
16221 masm.branch32(Assembler::Above, lengthReg, Imm32(arrayCapacity), &vmCall);
16222
16223 // The array must be nursery allocated so no post barrier is needed.
16224#ifdef DEBUG1
16225 Label ok;
16226 masm.branchPtrInNurseryChunk(Assembler::Equal, temp2, temp3, &ok);
16227 masm.assumeUnreachable("Unexpected tenured object for LRest");
16228 masm.bind(&ok);
16229#endif
16230
16231 Label initialized;
16232 masm.branch32(Assembler::Equal, lengthReg, Imm32(0), &initialized);
16233
16234 // Store length and initializedLength.
16235 Register elements = temp3;
16236 masm.loadPtr(Address(temp2, NativeObject::offsetOfElements()), elements);
16237 Address lengthAddr(elements, ObjectElements::offsetOfLength());
16238 Address initLengthAddr(elements,
16239 ObjectElements::offsetOfInitializedLength());
16240 masm.store32(lengthReg, lengthAddr);
16241 masm.store32(lengthReg, initLengthAddr);
16242
16243 // Store either one or two elements. This may clobber lengthReg (temp0).
16244 static_assert(arrayCapacity == 2, "code handles 1 or 2 elements");
16245 Label storeFirst;
16246 masm.branch32(Assembler::Equal, lengthReg, Imm32(1), &storeFirst);
16247 masm.storeValue(Address(temp1, sizeof(Value)),
16248 Address(elements, sizeof(Value)), temp0);
16249 masm.bind(&storeFirst);
16250 masm.storeValue(Address(temp1, 0), Address(elements, 0), temp0);
16251
16252 // Done.
16253 masm.bind(&initialized);
16254 masm.movePtr(temp2, ReturnReg);
16255 masm.jump(&done);
16256 }
16257
16258 masm.bind(&vmCall);
16259
16260 pushArg(temp2);
16261 pushArg(temp1);
16262 pushArg(lengthReg);
16263
16264 using Fn =
16265 ArrayObject* (*)(JSContext*, uint32_t, Value*, Handle<ArrayObject*>);
16266 callVM<Fn, InitRestParameter>(lir);
16267
16268 masm.bind(&done);
16269}
16270
16271// Create a stackmap from the given safepoint, with the structure:
16272//
16273// <reg dump, if any>
16274// | ++ <body (general spill)>
16275// | | ++ <space for Frame>
16276// | | ++ <inbound args>
16277// | | |
16278// Lowest Addr Highest Addr
16279// |
16280// framePushedAtStackMapBase
16281//
16282// The caller owns the resulting stackmap. This assumes a grow-down stack.
16283//
16284// For non-debug builds, if the stackmap would contain no pointers, no
16285// stackmap is created, and nullptr is returned. For a debug build, a
16286// stackmap is always created and returned.
16287//
16288// Depending on the type of safepoint, the stackmap may need to account for
16289// spilled registers. WasmSafepointKind::LirCall corresponds to LIR nodes where
16290// isCall() == true, for which the register allocator will spill/restore all
16291// live registers at the LIR level - in this case, the LSafepoint sees only live
16292// values on the stack, never in registers. WasmSafepointKind::CodegenCall, on
16293// the other hand, is for LIR nodes which may manually spill/restore live
16294// registers in codegen, in which case the stackmap must account for this. Traps
16295// also require tracking of live registers, but spilling is handled by the trap
16296// mechanism.
16297static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint,
16298 const RegisterOffsets& trapExitLayout,
16299 size_t trapExitLayoutNumWords,
16300 size_t nInboundStackArgBytes,
16301 wasm::StackMap** result) {
16302 // Ensure this is defined on all return paths.
16303 *result = nullptr;
16304
16305 // The size of the wasm::Frame itself.
16306 const size_t nFrameBytes = sizeof(wasm::Frame);
16307
16308 // This is the number of bytes spilled for live registers, outside of a trap.
16309 // For traps, trapExitLayout and trapExitLayoutNumWords will be used.
16310 const size_t nRegisterDumpBytes =
16311 MacroAssembler::PushRegsInMaskSizeInBytes(safepoint.liveRegs());
16312
16313 // As mentioned above, for WasmSafepointKind::LirCall, register spills and
16314 // restores are handled at the LIR level and there should therefore be no live
16315 // registers to handle here.
16316 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"
, 16317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0"
")"); do { *((volatile int*)__null) = 16317; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
16317 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"
, 16317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0"
")"); do { *((volatile int*)__null) = 16317; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
16318 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"
, 16318); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 16318; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16319
16320 // This is the number of bytes in the general spill area, below the Frame.
16321 const size_t nBodyBytes = safepoint.framePushedAtStackMapBase();
16322
16323 // The stack map owns any alignment padding around inbound stack args.
16324 const size_t nInboundStackArgBytesAligned =
16325 wasm::AlignStackArgAreaSize(nInboundStackArgBytes);
16326
16327 // This is the number of bytes in the general spill area, the Frame, and the
16328 // incoming args, but not including any register dump area.
16329 const size_t nNonRegisterBytes =
16330 nBodyBytes + nFrameBytes + nInboundStackArgBytesAligned;
16331 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"
, 16331); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nNonRegisterBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 16331; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16332
16333 // This is the number of bytes in the register dump area, if any, below the
16334 // general spill area.
16335 const size_t nRegisterBytes =
16336 (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap)
16337 ? (trapExitLayoutNumWords * sizeof(void*))
16338 : nRegisterDumpBytes;
16339
16340 // This is the total number of bytes covered by the map.
16341 const size_t nTotalBytes = nNonRegisterBytes + nRegisterBytes;
16342
16343#ifndef DEBUG1
16344 bool needStackMap = !(safepoint.wasmAnyRefRegs().empty() &&
16345 safepoint.wasmAnyRefSlots().empty() &&
16346 safepoint.slotsOrElementsSlots().empty());
16347
16348 // There are no references, and this is a non-debug build, so don't bother
16349 // building the stackmap.
16350 if (!needStackMap) {
16351 return true;
16352 }
16353#endif
16354
16355 wasm::StackMap* stackMap =
16356 wasm::StackMap::create(nTotalBytes / sizeof(void*));
16357 if (!stackMap) {
16358 return false;
16359 }
16360 if (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) {
16361 stackMap->setExitStubWords(trapExitLayoutNumWords);
16362 }
16363
16364 // REG DUMP AREA, if any.
16365 size_t regDumpWords = 0;
16366 const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs();
16367 const LiveGeneralRegisterSet slotsOrElementsRegs =
16368 safepoint.slotsOrElementsRegs();
16369 const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union(
16370 wasmAnyRefRegs.set(), slotsOrElementsRegs.set()));
16371 GeneralRegisterForwardIterator refRegsIter(refRegs);
16372 switch (safepoint.wasmSafepointKind()) {
16373 case WasmSafepointKind::LirCall:
16374 case WasmSafepointKind::StackSwitch:
16375 case WasmSafepointKind::CodegenCall: {
16376 size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*);
16377 regDumpWords += spilledNumWords;
16378
16379 for (; refRegsIter.more(); ++refRegsIter) {
16380 Register reg = *refRegsIter;
16381 size_t offsetFromSpillBase =
16382 safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) /
16383 sizeof(void*);
16384 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"
, 16385); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
")"); do { *((volatile int*)__null) = 16385; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16385 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"
, 16385); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords"
")"); do { *((volatile int*)__null) = 16385; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16386 size_t index = spilledNumWords - offsetFromSpillBase;
16387
16388 if (wasmAnyRefRegs.has(reg)) {
16389 stackMap->set(index, wasm::StackMap::AnyRef);
16390 } else {
16391 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"
, 16391); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)"
")"); do { *((volatile int*)__null) = 16391; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16392 stackMap->set(index, wasm::StackMap::ArrayDataPointer);
16393 }
16394 }
16395 // Float and vector registers do not have to be handled; they cannot
16396 // contain wasm anyrefs, and they are spilled after general-purpose
16397 // registers. Gprs are therefore closest to the spill base and thus their
16398 // offset calculation does not need to account for other spills.
16399 } break;
16400 case WasmSafepointKind::Trap: {
16401 regDumpWords += trapExitLayoutNumWords;
16402
16403 for (; refRegsIter.more(); ++refRegsIter) {
16404 Register reg = *refRegsIter;
16405 size_t offsetFromTop = trapExitLayout.getOffset(reg);
16406
16407 // If this doesn't hold, the associated register wasn't saved by
16408 // the trap exit stub. Better to crash now than much later, in
16409 // some obscure place, and possibly with security consequences.
16410 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"
, 16410); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "offsetFromTop < trapExitLayoutNumWords"
")"); do { *((volatile int*)__null) = 16410; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16411
16412 // offsetFromTop is an offset in words down from the highest
16413 // address in the exit stub save area. Switch it around to be an
16414 // offset up from the bottom of the (integer register) save area.
16415 size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop;
16416
16417 if (wasmAnyRefRegs.has(reg)) {
16418 stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef);
16419 } else {
16420 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"
, 16420); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)"
")"); do { *((volatile int*)__null) = 16420; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16421 stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer);
16422 }
16423 }
16424 } break;
16425 default:
16426 MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16426); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")"
); do { *((volatile int*)__null) = 16426; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
16427 }
16428
16429 // Ensure other reg/slot collections on LSafepoint are empty.
16430 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"
, 16430); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.gcRegs().empty() && safepoint.gcSlots().empty()"
")"); do { *((volatile int*)__null) = 16430; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16431#ifdef JS_NUNBOX32
16432 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"
, 16432); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.nunboxParts().empty()"
")"); do { *((volatile int*)__null) = 16432; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16433#elif JS_PUNBOX641
16434 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"
, 16434); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.valueRegs().empty() && safepoint.valueSlots().empty()"
")"); do { *((volatile int*)__null) = 16434; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16435#endif
16436
16437 // BODY (GENERAL SPILL) AREA and FRAME and INCOMING ARGS
16438 // Deal with roots on the stack.
16439 const LSafepoint::SlotList& wasmAnyRefSlots = safepoint.wasmAnyRefSlots();
16440 for (SafepointSlotEntry wasmAnyRefSlot : wasmAnyRefSlots) {
16441 // The following needs to correspond with JitFrameLayout::slotRef
16442 // wasmAnyRefSlot.stack == 0 means the slot is in the args area
16443 if (wasmAnyRefSlot.stack) {
16444 // It's a slot in the body allocation, so .slot is interpreted
16445 // as an index downwards from the Frame*
16446 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"
, 16446); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot <= nBodyBytes"
")"); do { *((volatile int*)__null) = 16446; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16447 uint32_t offsetInBytes = nBodyBytes - wasmAnyRefSlot.slot;
16448 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"
, 16448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 16448; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16449 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
16450 wasm::StackMap::AnyRef);
16451 } else {
16452 // It's an argument slot
16453 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"
, 16453); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot < nInboundStackArgBytes"
")"); do { *((volatile int*)__null) = 16453; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16454 uint32_t offsetInBytes = nBodyBytes + nFrameBytes + wasmAnyRefSlot.slot;
16455 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"
, 16455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 16455; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16456 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
16457 wasm::StackMap::AnyRef);
16458 }
16459 }
16460
16461 // Track array data pointers on the stack
16462 const LSafepoint::SlotList& slots = safepoint.slotsOrElementsSlots();
16463 for (SafepointSlotEntry slot : slots) {
16464 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"
, 16464); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack"
")"); do { *((volatile int*)__null) = 16464; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16465
16466 // It's a slot in the body allocation, so .slot is interpreted
16467 // as an index downwards from the Frame*
16468 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"
, 16468); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes"
")"); do { *((volatile int*)__null) = 16468; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16469 uint32_t offsetInBytes = nBodyBytes - slot.slot;
16470 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"
, 16470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0"
")"); do { *((volatile int*)__null) = 16470; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16471 stackMap->set(regDumpWords + offsetInBytes / sizeof(void*),
16472 wasm::StackMap::Kind::ArrayDataPointer);
16473 }
16474
16475 // Record in the map, how far down from the highest address the Frame* is.
16476 // Take the opportunity to check that we haven't marked any part of the
16477 // Frame itself as a pointer.
16478 stackMap->setFrameOffsetFromTop((nInboundStackArgBytesAligned + nFrameBytes) /
16479 sizeof(void*));
16480#ifdef DEBUG1
16481 for (uint32_t i = 0; i < nFrameBytes / sizeof(void*); i++) {
16482 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"
, 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { *((volatile int*)__null) = 16484; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16483 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"
, 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { *((volatile int*)__null) = 16484; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
16484 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"
, 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD"
")"); do { *((volatile int*)__null) = 16484; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16485 }
16486#endif
16487
16488 *result = stackMap;
16489 return true;
16490}
16491
16492bool CodeGenerator::generateWasm(wasm::CallIndirectId callIndirectId,
16493 const wasm::TrapSiteDesc& entryTrapSiteDesc,
16494 const wasm::ArgTypeVector& argTypes,
16495 const RegisterOffsets& trapExitLayout,
16496 size_t trapExitLayoutNumWords,
16497 wasm::FuncOffsets* offsets,
16498 wasm::StackMaps* stackMaps,
16499 wasm::Decoder* decoder) {
16500 AutoCreatedBy acb(masm, "CodeGenerator::generateWasm");
16501
16502 JitSpew(JitSpew_Codegen, "# Emitting wasm code");
16503
16504 size_t nInboundStackArgBytes = StackArgAreaSizeUnaligned(argTypes);
16505 inboundStackArgBytes_ = nInboundStackArgBytes;
16506
16507 wasm::GenerateFunctionPrologue(masm, callIndirectId, mozilla::Nothing(),
16508 offsets);
16509
16510 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"
, 16510); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0"
")"); do { *((volatile int*)__null) = 16510; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16511
16512 // Very large frames are implausible, probably an attack.
16513 if (frameSize() > wasm::MaxFrameSize) {
16514 return decoder->fail(decoder->beginOffset(), "stack frame is too large");
16515 }
16516
16517 if (omitOverRecursedCheck()) {
16518 masm.reserveStack(frameSize());
16519 } else {
16520 std::pair<CodeOffset, uint32_t> pair =
16521 masm.wasmReserveStackChecked(frameSize(), entryTrapSiteDesc);
16522 CodeOffset trapInsnOffset = pair.first;
16523 size_t nBytesReservedBeforeTrap = pair.second;
16524
16525 wasm::StackMap* functionEntryStackMap = nullptr;
16526 if (!CreateStackMapForFunctionEntryTrap(
16527 argTypes, trapExitLayout, trapExitLayoutNumWords,
16528 nBytesReservedBeforeTrap, nInboundStackArgBytes,
16529 &functionEntryStackMap)) {
16530 return false;
16531 }
16532
16533 // In debug builds, we'll always have a stack map, even if there are no
16534 // refs to track.
16535 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"
, 16535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "functionEntryStackMap"
")"); do { *((volatile int*)__null) = 16535; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16536
16537 if (functionEntryStackMap &&
16538 !stackMaps->add((uint8_t*)(uintptr_t)trapInsnOffset.offset(),
16539 functionEntryStackMap)) {
16540 functionEntryStackMap->destroy();
16541 return false;
16542 }
16543 }
16544
16545 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"
, 16545); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 16545; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16546
16547 if (!generateBody()) {
16548 return false;
16549 }
16550
16551 masm.bind(&returnLabel_);
16552 wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets);
16553
16554 if (!generateOutOfLineCode()) {
16555 return false;
16556 }
16557
16558 masm.flush();
16559 if (masm.oom()) {
16560 return false;
16561 }
16562
16563 offsets->end = masm.currentOffset();
16564
16565 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"
, 16565); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!masm.failureLabel()->used()"
")"); do { *((volatile int*)__null) = 16565; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16566 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"
, 16566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.listSize() == 0"
")"); do { *((volatile int*)__null) = 16566; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16567 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"
, 16567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.RVATableSize() == 0"
")"); do { *((volatile int*)__null) = 16567; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16568 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"
, 16568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size() == 0"
")"); do { *((volatile int*)__null) = 16568; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16569 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"
, 16569); AnnotateMozCrashReason("MOZ_ASSERT" "(" "graph.numConstants() == 0"
")"); do { *((volatile int*)__null) = 16569; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16570 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"
, 16570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osiIndices_.empty()"
")"); do { *((volatile int*)__null) = 16570; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16571 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"
, 16571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "icList_.empty()"
")"); do { *((volatile int*)__null) = 16571; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16572 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"
, 16572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoints_.size() == 0"
")"); do { *((volatile int*)__null) = 16572; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16573 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"
, 16573); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!scriptCounts_"
")"); do { *((volatile int*)__null) = 16573; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16574
16575 // Convert the safepoints to stackmaps and add them to our running
16576 // collection thereof.
16577 for (CodegenSafepointIndex& index : safepointIndices_) {
16578 wasm::StackMap* stackMap = nullptr;
16579 if (!CreateStackMapFromLSafepoint(*index.safepoint(), trapExitLayout,
16580 trapExitLayoutNumWords,
16581 nInboundStackArgBytes, &stackMap)) {
16582 return false;
16583 }
16584
16585 // In debug builds, we'll always have a stack map.
16586 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"
, 16586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap" ")"
); do { *((volatile int*)__null) = 16586; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
16587 if (!stackMap) {
16588 continue;
16589 }
16590
16591 if (!stackMaps->add((uint8_t*)(uintptr_t)index.displacement(), stackMap)) {
16592 stackMap->destroy();
16593 return false;
16594 }
16595 }
16596
16597 return true;
16598}
16599
16600bool CodeGenerator::generate(const WarpSnapshot* snapshot) {
16601 AutoCreatedBy acb(masm, "CodeGenerator::generate");
16602
16603 MOZ_ASSERT(snapshot)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(snapshot)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(snapshot))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("snapshot", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshot" ")"
); do { *((volatile int*)__null) = 16603; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
16604 snapshot_ = snapshot;
16605
16606 JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%u:%u",
16607 gen->outerInfo().script()->filename(),
16608 gen->outerInfo().script()->lineno(),
16609 gen->outerInfo().script()->column().oneOriginValue());
16610
16611 // Initialize native code table with an entry to the start of
16612 // top-level script.
16613 InlineScriptTree* tree = gen->outerInfo().inlineScriptTree();
16614 jsbytecode* startPC = tree->script()->code();
16615 BytecodeSite* startSite = new (gen->alloc()) BytecodeSite(tree, startPC);
16616 if (!addNativeToBytecodeEntry(startSite)) {
16617 return false;
16618 }
16619
16620 if (!safepoints_.init(gen->alloc())) {
16621 return false;
16622 }
16623
16624 size_t maxSafepointIndices =
16625 graph.numSafepoints() + graph.extraSafepointUses();
16626 if (!safepointIndices_.reserve(maxSafepointIndices)) {
16627 return false;
16628 }
16629 if (!osiIndices_.reserve(graph.numSafepoints())) {
16630 return false;
16631 }
16632
16633 perfSpewer_.recordOffset(masm, "Prologue");
16634 if (!generatePrologue()) {
16635 return false;
16636 }
16637
16638 // Reset native => bytecode map table with top-level script and startPc.
16639 if (!addNativeToBytecodeEntry(startSite)) {
16640 return false;
16641 }
16642
16643 if (!generateBody()) {
16644 return false;
16645 }
16646
16647 // Reset native => bytecode map table with top-level script and startPc.
16648 if (!addNativeToBytecodeEntry(startSite)) {
16649 return false;
16650 }
16651
16652 perfSpewer_.recordOffset(masm, "Epilogue");
16653 if (!generateEpilogue()) {
16654 return false;
16655 }
16656
16657 // Reset native => bytecode map table with top-level script and startPc.
16658 if (!addNativeToBytecodeEntry(startSite)) {
16659 return false;
16660 }
16661
16662 perfSpewer_.recordOffset(masm, "InvalidateEpilogue");
16663 generateInvalidateEpilogue();
16664
16665 // native => bytecode entries for OOL code will be added
16666 // by CodeGeneratorShared::generateOutOfLineCode
16667 perfSpewer_.recordOffset(masm, "OOLCode");
16668 if (!generateOutOfLineCode()) {
16669 return false;
16670 }
16671
16672 // Add terminal entry.
16673 if (!addNativeToBytecodeEntry(startSite)) {
16674 return false;
16675 }
16676
16677 // Dump Native to bytecode entries to spew.
16678 dumpNativeToBytecodeEntries();
16679
16680 // We encode safepoints after the OSI-point offsets have been determined.
16681 if (!encodeSafepoints()) {
16682 return false;
16683 }
16684
16685 // If this assertion trips, then you have multiple things to do:
16686 //
16687 // This assertion will report if a safepoint is used multiple times for the
16688 // same instruction. To fix this assertion make sure to call
16689 // `lirGraph_.addExtraSafepointUses(..);` in the Lowering phase.
16690 //
16691 // However, this non-worrying issue might hide a more dramatic security issue,
16692 // which is that having multiple encoding of a safepoint in a single LIR
16693 // instruction is not safe, unless:
16694 //
16695 // - The multiple uses of the safepoints are in different code path. i-e
16696 // there should be not single execution trace making use of multiple
16697 // calls within a single instruction.
16698 //
16699 // - There is enough space to encode data in-place of the call instruction.
16700 // Such that a patched-call site does not corrupt the code path on another
16701 // execution trace.
16702 //
16703 // This issue is caused by the way invalidation works, to keep the code alive
16704 // when invalidated code is only referenced by the stack. This works by
16705 // storing data in-place of the calling code, which thus becomes unsafe to
16706 // execute.
16707 MOZ_ASSERT(safepointIndices_.length() <= maxSafepointIndices)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(safepointIndices_.length() <= maxSafepointIndices
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(safepointIndices_.length() <= maxSafepointIndices
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"safepointIndices_.length() <= maxSafepointIndices", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16707); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepointIndices_.length() <= maxSafepointIndices"
")"); do { *((volatile int*)__null) = 16707; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16708
16709 // For each instruction with a safepoint, we have an OSI point inserted after
16710 // which handles bailouts in case of invalidation of the code.
16711 MOZ_ASSERT(osiIndices_.length() == graph.numSafepoints())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(osiIndices_.length() == graph.numSafepoints())>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(osiIndices_.length() == graph.numSafepoints()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("osiIndices_.length() == graph.numSafepoints()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osiIndices_.length() == graph.numSafepoints()"
")"); do { *((volatile int*)__null) = 16711; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16712
16713 return !masm.oom();
16714}
16715
16716static bool AddInlinedCompilations(JSContext* cx, HandleScript script,
16717 IonCompilationId compilationId,
16718 const WarpSnapshot* snapshot,
16719 bool* isValid) {
16720 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"
, 16720); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*isValid"
")"); do { *((volatile int*)__null) = 16720; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16721 RecompileInfo recompileInfo(script, compilationId);
16722
16723 JitZone* jitZone = cx->zone()->jitZone();
16724
16725 for (const auto* scriptSnapshot : snapshot->scripts()) {
16726 JSScript* inlinedScript = scriptSnapshot->script();
16727 if (inlinedScript == script) {
16728 continue;
16729 }
16730
16731 // TODO(post-Warp): This matches FinishCompilation and is necessary to
16732 // ensure in-progress compilations are canceled when an inlined functon
16733 // becomes a debuggee. See the breakpoint-14.js jit-test.
16734 // When TI is gone, try to clean this up by moving AddInlinedCompilations to
16735 // WarpOracle so that we can handle this as part of addPendingRecompile
16736 // instead of requiring this separate check.
16737 if (inlinedScript->isDebuggee()) {
16738 *isValid = false;
16739 return true;
16740 }
16741
16742 if (!jitZone->addInlinedCompilation(recompileInfo, inlinedScript)) {
16743 return false;
16744 }
16745 }
16746
16747 *isValid = true;
16748 return true;
16749}
16750
16751struct EmulatesUndefinedDependency final : public CompilationDependency {
16752 explicit EmulatesUndefinedDependency()
16753 : CompilationDependency(CompilationDependency::Type::EmulatesUndefined) {
16754 };
16755
16756 virtual bool operator==(CompilationDependency& dep) {
16757 // Since the emulates undefined fuse is runtime wide, they are all equal
16758 return dep.type == type;
16759 }
16760
16761 virtual bool checkDependency(JSContext* cx) {
16762 return cx->runtime()->hasSeenObjectEmulateUndefinedFuse.ref().intact();
16763 }
16764
16765 virtual bool registerDependency(JSContext* cx, HandleScript script) {
16766 MOZ_ASSERT(checkDependency(cx))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(checkDependency(cx))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(checkDependency(cx)))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("checkDependency(cx)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16766); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checkDependency(cx)"
")"); do { *((volatile int*)__null) = 16766; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16767 return cx->runtime()
16768 ->hasSeenObjectEmulateUndefinedFuse.ref()
16769 .addFuseDependency(cx, script);
16770 }
16771
16772 virtual UniquePtr<CompilationDependency> clone() {
16773 return MakeUnique<EmulatesUndefinedDependency>();
16774 }
16775};
16776
16777bool CodeGenerator::addHasSeenObjectEmulateUndefinedFuseDependency() {
16778 EmulatesUndefinedDependency dep;
16779 return mirGen().tracker.addDependency(dep);
16780}
16781
16782bool CodeGenerator::link(JSContext* cx) {
16783 AutoCreatedBy acb(masm, "CodeGenerator::link");
16784
16785 // We cancel off-thread Ion compilations in a few places during GC, but if
16786 // this compilation was performed off-thread it will already have been
16787 // removed from the relevant lists by this point. Don't allow GC here.
16788 JS::AutoAssertNoGC nogc(cx);
16789
16790 RootedScript script(cx, gen->outerInfo().script());
16791 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"
, 16791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!script->hasIonScript()"
")"); do { *((volatile int*)__null) = 16791; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16792
16793 if (scriptCounts_ && !script->hasScriptCounts() &&
16794 !script->initScriptCounts(cx)) {
16795 return false;
16796 }
16797
16798 JitZone* jitZone = cx->zone()->jitZone();
16799
16800 IonCompilationId compilationId =
16801 cx->runtime()->jitRuntime()->nextCompilationId();
16802 jitZone->currentCompilationIdRef().emplace(compilationId);
16803 auto resetCurrentId = mozilla::MakeScopeExit(
16804 [jitZone] { jitZone->currentCompilationIdRef().reset(); });
16805
16806 // Record constraints. If an error occured, returns false and potentially
16807 // prevent future compilations. Otherwise, if an invalidation occured, then
16808 // skip the current compilation.
16809 bool isValid = false;
16810
16811 // If an inlined script is invalidated (for example, by attaching
16812 // a debugger), we must also invalidate the parent IonScript.
16813 if (!AddInlinedCompilations(cx, script, compilationId, snapshot_, &isValid)) {
16814 return false;
16815 }
16816
16817 // This compilation is no longer valid; don't proceed, but return true as this
16818 // isn't an error case either.
16819 if (!isValid) {
16820 return true;
16821 }
16822
16823 CompilationDependencyTracker& tracker = mirGen().tracker;
16824 // Make sure we're using the same realm as this context.
16825 MOZ_ASSERT(mirGen().realm->realmPtr() == cx->realm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mirGen().realm->realmPtr() == cx->realm())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mirGen().realm->realmPtr() == cx->realm()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("mirGen().realm->realmPtr() == cx->realm()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 16825); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mirGen().realm->realmPtr() == cx->realm()"
")"); do { *((volatile int*)__null) = 16825; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
16826 if (!tracker.checkDependencies(cx)) {
16827 return true;
16828 }
16829
16830 for (auto& dep : tracker.dependencies) {
16831 if (!dep->registerDependency(cx, script)) {
16832 return false; // Should we make sure we only return false on OOM and then
16833 // eat the OOM here?
16834 }
16835 }
16836
16837 uint32_t argumentSlots = (gen->outerInfo().nargs() + 1) * sizeof(Value);
16838
16839 size_t numNurseryObjects = snapshot_->nurseryObjects().length();
16840
16841 IonScript* ionScript = IonScript::New(
16842 cx, compilationId, graph.localSlotsSize(), argumentSlots, frameDepth_,
16843 snapshots_.listSize(), snapshots_.RVATableSize(), recovers_.size(),
16844 graph.numConstants(), numNurseryObjects, safepointIndices_.length(),
16845 osiIndices_.length(), icList_.length(), runtimeData_.length(),
16846 safepoints_.size());
16847 if (!ionScript) {
16848 return false;
16849 }
16850#ifdef DEBUG1
16851 ionScript->setICHash(snapshot_->icHash());
16852#endif
16853
16854 auto freeIonScript = mozilla::MakeScopeExit([&ionScript] {
16855 // Use js_free instead of IonScript::Destroy: the cache list is still
16856 // uninitialized.
16857 js_free(ionScript);
16858 });
16859
16860 Linker linker(masm);
16861 JitCode* code = linker.newCode(cx, CodeKind::Ion);
16862 if (!code) {
16863 return false;
16864 }
16865
16866 // Encode native to bytecode map if profiling is enabled.
16867 if (isProfilerInstrumentationEnabled()) {
16868 // Generate native-to-bytecode main table.
16869 IonEntry::ScriptList scriptList;
16870 if (!generateCompactNativeToBytecodeMap(cx, code, scriptList)) {
16871 return false;
16872 }
16873
16874 uint8_t* ionTableAddr =
16875 ((uint8_t*)nativeToBytecodeMap_.get()) + nativeToBytecodeTableOffset_;
16876 JitcodeIonTable* ionTable = (JitcodeIonTable*)ionTableAddr;
16877
16878 // Construct the IonEntry that will go into the global table.
16879 auto entry = MakeJitcodeGlobalEntry<IonEntry>(
16880 cx, code, code->raw(), code->rawEnd(), std::move(scriptList), ionTable);
16881 if (!entry) {
16882 return false;
16883 }
16884 (void)nativeToBytecodeMap_.release(); // Table is now owned by |entry|.
16885
16886 // Add entry to the global table.
16887 JitcodeGlobalTable* globalTable =
16888 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
16889 if (!globalTable->addEntry(std::move(entry))) {
16890 return false;
16891 }
16892
16893 // Mark the jitcode as having a bytecode map.
16894 code->setHasBytecodeMap();
16895 } else {
16896 // Add a dumy jitcodeGlobalTable entry.
16897 auto entry = MakeJitcodeGlobalEntry<DummyEntry>(cx, code, code->raw(),
16898 code->rawEnd());
16899 if (!entry) {
16900 return false;
16901 }
16902
16903 // Add entry to the global table.
16904 JitcodeGlobalTable* globalTable =
16905 cx->runtime()->jitRuntime()->getJitcodeGlobalTable();
16906 if (!globalTable->addEntry(std::move(entry))) {
16907 return false;
16908 }
16909
16910 // Mark the jitcode as having a bytecode map.
16911 code->setHasBytecodeMap();
16912 }
16913
16914 ionScript->setMethod(code);
16915
16916 // If the Gecko Profiler is enabled, mark IonScript as having been
16917 // instrumented accordingly.
16918 if (isProfilerInstrumentationEnabled()) {
16919 ionScript->setHasProfilingInstrumentation();
16920 }
16921
16922 Assembler::PatchDataWithValueCheck(
16923 CodeLocationLabel(code, invalidateEpilogueData_), ImmPtr(ionScript),
16924 ImmPtr((void*)-1));
16925
16926 for (CodeOffset offset : ionScriptLabels_) {
16927 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, offset),
16928 ImmPtr(ionScript), ImmPtr((void*)-1));
16929 }
16930
16931 for (NurseryObjectLabel label : ionNurseryObjectLabels_) {
16932 void* entry = ionScript->addressOfNurseryObject(label.nurseryIndex);
16933 Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset),
16934 ImmPtr(entry), ImmPtr((void*)-1));
16935 }
16936
16937 // for generating inline caches during the execution.
16938 if (runtimeData_.length()) {
16939 ionScript->copyRuntimeData(&runtimeData_[0]);
16940 }
16941 if (icList_.length()) {
16942 ionScript->copyICEntries(&icList_[0]);
16943 }
16944
16945 for (size_t i = 0; i < icInfo_.length(); i++) {
16946 IonIC& ic = ionScript->getICFromIndex(i);
16947 Assembler::PatchDataWithValueCheck(
16948 CodeLocationLabel(code, icInfo_[i].icOffsetForJump),
16949 ImmPtr(ic.codeRawPtr()), ImmPtr((void*)-1));
16950 Assembler::PatchDataWithValueCheck(
16951 CodeLocationLabel(code, icInfo_[i].icOffsetForPush), ImmPtr(&ic),
16952 ImmPtr((void*)-1));
16953 }
16954
16955 JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)", (void*)ionScript,
16956 (void*)code->raw());
16957
16958 ionScript->setInvalidationEpilogueDataOffset(
16959 invalidateEpilogueData_.offset());
16960 if (jsbytecode* osrPc = gen->outerInfo().osrPc()) {
16961 ionScript->setOsrPc(osrPc);
16962 ionScript->setOsrEntryOffset(getOsrEntryOffset());
16963 }
16964 ionScript->setInvalidationEpilogueOffset(invalidate_.offset());
16965
16966 perfSpewer_.saveProfile(cx, script, code);
16967
16968#ifdef MOZ_VTUNE1
16969 vtune::MarkScript(code, script, "ion");
16970#endif
16971
16972 // Set a Ion counter hint for this script.
16973 if (cx->runtime()->jitRuntime()->hasJitHintsMap()) {
16974 JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap();
16975 jitHints->recordIonCompilation(script);
16976 }
16977
16978 // for marking during GC.
16979 if (safepointIndices_.length()) {
16980 ionScript->copySafepointIndices(&safepointIndices_[0]);
16981 }
16982 if (safepoints_.size()) {
16983 ionScript->copySafepoints(&safepoints_);
16984 }
16985
16986 // for recovering from an Ion Frame.
16987 if (osiIndices_.length()) {
16988 ionScript->copyOsiIndices(&osiIndices_[0]);
16989 }
16990 if (snapshots_.listSize()) {
16991 ionScript->copySnapshots(&snapshots_);
16992 }
16993 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"
, 16993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size()"
")"); do { *((volatile int*)__null) = 16993; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
16994 if (recovers_.size()) {
16995 ionScript->copyRecovers(&recovers_);
16996 }
16997 if (graph.numConstants()) {
16998 const Value* vp = graph.constantPool();
16999 ionScript->copyConstants(vp);
17000 for (size_t i = 0; i < graph.numConstants(); i++) {
17001 const Value& v = vp[i];
17002 if (v.isGCThing()) {
17003 if (gc::StoreBuffer* sb = v.toGCThing()->storeBuffer()) {
17004 sb->putWholeCell(script);
17005 break;
17006 }
17007 }
17008 }
17009 }
17010
17011 // Attach any generated script counts to the script.
17012 if (IonScriptCounts* counts = extractScriptCounts()) {
17013 script->addIonCounts(counts);
17014 }
17015 // WARNING: Code after this point must be infallible!
17016
17017 // Copy the list of nursery objects. Note that the store buffer can add
17018 // HeapPtr edges that must be cleared in IonScript::Destroy. See the
17019 // infallibility warning above.
17020 const auto& nurseryObjects = snapshot_->nurseryObjects();
17021 for (size_t i = 0; i < nurseryObjects.length(); i++) {
17022 ionScript->nurseryObjects()[i].init(nurseryObjects[i]);
17023 }
17024
17025 // Transfer ownership of the IonScript to the JitScript. At this point enough
17026 // of the IonScript must be initialized for IonScript::Destroy to work.
17027 freeIonScript.release();
17028 script->jitScript()->setIonScript(script, ionScript);
17029
17030 return true;
17031}
17032
17033// An out-of-line path to convert a boxed int32 to either a float or double.
17034class OutOfLineUnboxFloatingPoint : public OutOfLineCodeBase<CodeGenerator> {
17035 LUnboxFloatingPoint* unboxFloatingPoint_;
17036
17037 public:
17038 explicit OutOfLineUnboxFloatingPoint(LUnboxFloatingPoint* unboxFloatingPoint)
17039 : unboxFloatingPoint_(unboxFloatingPoint) {}
17040
17041 void accept(CodeGenerator* codegen) override {
17042 codegen->visitOutOfLineUnboxFloatingPoint(this);
17043 }
17044
17045 LUnboxFloatingPoint* unboxFloatingPoint() const {
17046 return unboxFloatingPoint_;
17047 }
17048};
17049
17050void CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint* lir) {
17051 const ValueOperand box = ToValue(lir, LUnboxFloatingPoint::Input);
17052 const LDefinition* result = lir->output();
17053
17054 // Out-of-line path to convert int32 to double or bailout
17055 // if this instruction is fallible.
17056 OutOfLineUnboxFloatingPoint* ool =
17057 new (alloc()) OutOfLineUnboxFloatingPoint(lir);
17058 addOutOfLineCode(ool, lir->mir());
17059
17060 FloatRegister resultReg = ToFloatRegister(result);
17061 masm.branchTestDouble(Assembler::NotEqual, box, ool->entry());
17062 masm.unboxDouble(box, resultReg);
17063 if (lir->type() == MIRType::Float32) {
17064 masm.convertDoubleToFloat32(resultReg, resultReg);
17065 }
17066 masm.bind(ool->rejoin());
17067}
17068
17069void CodeGenerator::visitOutOfLineUnboxFloatingPoint(
17070 OutOfLineUnboxFloatingPoint* ool) {
17071 LUnboxFloatingPoint* ins = ool->unboxFloatingPoint();
17072 const ValueOperand value = ToValue(ins, LUnboxFloatingPoint::Input);
17073
17074 if (ins->mir()->fallible()) {
17075 Label bail;
17076 masm.branchTestInt32(Assembler::NotEqual, value, &bail);
17077 bailoutFrom(&bail, ins->snapshot());
17078 }
17079 if (ins->type() == MIRType::Float32) {
17080 masm.convertInt32ToFloat32(value.payloadOrValueReg(),
17081 ToFloatRegister(ins->output()));
17082 } else {
17083 masm.convertInt32ToDouble(value.payloadOrValueReg(),
17084 ToFloatRegister(ins->output()));
17085 }
17086 masm.jump(ool->rejoin());
17087}
17088
17089void CodeGenerator::visitCallBindVar(LCallBindVar* lir) {
17090 pushArg(ToRegister(lir->environmentChain()));
17091
17092 using Fn = JSObject* (*)(JSContext*, JSObject*);
17093 callVM<Fn, BindVarOperation>(lir);
17094}
17095
17096void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) {
17097 Register obj = ToRegister(lir->getOperand(0));
17098 ValueOperand idVal = ToValue(lir, LMegamorphicSetElement::IndexIndex);
17099 ValueOperand value = ToValue(lir, LMegamorphicSetElement::ValueIndex);
17100
17101 Register temp0 = ToRegister(lir->temp0());
17102 // See comment in LIROps.yaml (x86 is short on registers)
17103#ifndef JS_CODEGEN_X86
17104 Register temp1 = ToRegister(lir->temp1());
17105 Register temp2 = ToRegister(lir->temp2());
17106#endif
17107
17108 // The instruction is marked as call-instruction so only these registers are
17109 // live.
17110 LiveRegisterSet liveRegs;
17111 liveRegs.addUnchecked(obj);
17112 liveRegs.addUnchecked(idVal);
17113 liveRegs.addUnchecked(value);
17114 liveRegs.addUnchecked(temp0);
17115#ifndef JS_CODEGEN_X86
17116 liveRegs.addUnchecked(temp1);
17117 liveRegs.addUnchecked(temp2);
17118#endif
17119
17120 Label cacheHit, done;
17121#ifdef JS_CODEGEN_X86
17122 masm.emitMegamorphicCachedSetSlot(
17123 idVal, obj, temp0, value, liveRegs, &cacheHit,
17124 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
17125 EmitPreBarrier(masm, addr, mirType);
17126 });
17127#else
17128 masm.emitMegamorphicCachedSetSlot(
17129 idVal, obj, temp0, temp1, temp2, value, liveRegs, &cacheHit,
17130 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
17131 EmitPreBarrier(masm, addr, mirType);
17132 });
17133#endif
17134
17135 pushArg(Imm32(lir->mir()->strict()));
17136 pushArg(ToValue(lir, LMegamorphicSetElement::ValueIndex));
17137 pushArg(ToValue(lir, LMegamorphicSetElement::IndexIndex));
17138 pushArg(obj);
17139
17140 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
17141 callVM<Fn, js::jit::SetElementMegamorphic<true>>(lir);
17142
17143 masm.jump(&done);
17144 masm.bind(&cacheHit);
17145
17146 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
17147 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
17148
17149 // Note: because this is a call-instruction, no registers need to be saved.
17150 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"
, 17150); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->isCall()"
")"); do { *((volatile int*)__null) = 17150; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17151 emitPostWriteBarrier(obj);
17152
17153 masm.bind(&done);
17154}
17155
17156void CodeGenerator::visitLoadScriptedProxyHandler(
17157 LLoadScriptedProxyHandler* ins) {
17158 Register obj = ToRegister(ins->getOperand(0));
17159 Register output = ToRegister(ins->output());
17160
17161 masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), output);
17162
17163 Label bail;
17164 Address handlerAddr(output, js::detail::ProxyReservedSlots::offsetOfSlot(
17165 ScriptedProxyHandler::HANDLER_EXTRA));
17166 masm.fallibleUnboxObject(handlerAddr, output, &bail);
17167 bailoutFrom(&bail, ins->snapshot());
17168}
17169
17170#ifdef JS_PUNBOX641
17171void CodeGenerator::visitCheckScriptedProxyGetResult(
17172 LCheckScriptedProxyGetResult* ins) {
17173 ValueOperand target = ToValue(ins, LCheckScriptedProxyGetResult::TargetIndex);
17174 ValueOperand value = ToValue(ins, LCheckScriptedProxyGetResult::ValueIndex);
17175 ValueOperand id = ToValue(ins, LCheckScriptedProxyGetResult::IdIndex);
17176 Register scratch = ToRegister(ins->temp0());
17177 Register scratch2 = ToRegister(ins->temp1());
17178
17179 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue,
17180 MutableHandleValue);
17181 OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>(
17182 ins, ArgList(scratch, id, value), StoreValueTo(value));
17183
17184 masm.unboxObject(target, scratch);
17185 masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch,
17186 scratch2, ool->entry());
17187 masm.bind(ool->rejoin());
17188}
17189#endif
17190
17191void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) {
17192 ValueOperand id = ToValue(ins, LIdToStringOrSymbol::IdIndex);
17193 ValueOperand output = ToOutValue(ins);
17194 Register scratch = ToRegister(ins->temp0());
17195
17196 masm.moveValue(id, output);
17197
17198 Label done, callVM;
17199 Label bail;
17200 {
17201 ScratchTagScope tag(masm, output);
17202 masm.splitTagForTest(output, tag);
17203 masm.branchTestString(Assembler::Equal, tag, &done);
17204 masm.branchTestSymbol(Assembler::Equal, tag, &done);
17205 masm.branchTestInt32(Assembler::NotEqual, tag, &bail);
17206 }
17207
17208 masm.unboxInt32(output, scratch);
17209
17210 using Fn = JSLinearString* (*)(JSContext*, int);
17211 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
17212 ins, ArgList(scratch), StoreRegisterTo(output.scratchReg()));
17213
17214 masm.lookupStaticIntString(scratch, output.scratchReg(),
17215 gen->runtime->staticStrings(), ool->entry());
17216
17217 masm.bind(ool->rejoin());
17218 masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output);
17219 masm.bind(&done);
17220
17221 bailoutFrom(&bail, ins->snapshot());
17222}
17223
17224void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) {
17225 const Register obj = ToRegister(ins->getOperand(0));
17226 size_t slot = ins->mir()->slot();
17227 ValueOperand result = ToOutValue(ins);
17228
17229 masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result);
17230}
17231
17232void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) {
17233 const Register obj = ToRegister(ins->getOperand(0));
17234 size_t slot = ins->mir()->slot();
17235 AnyRegister result = ToAnyRegister(ins->getDef(0));
17236 MIRType type = ins->mir()->type();
17237
17238 masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)),
17239 type, result);
17240}
17241
17242template <typename T>
17243static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type,
17244 bool fallible, AnyRegister dest, Label* fail) {
17245 if (type == MIRType::Double) {
17246 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"
, 17246); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.isFloat()"
")"); do { *((volatile int*)__null) = 17246; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17247 masm.ensureDouble(src, dest.fpu(), fail);
17248 return;
17249 }
17250 if (fallible) {
17251 switch (type) {
17252 case MIRType::Int32:
17253 masm.fallibleUnboxInt32(src, dest.gpr(), fail);
17254 break;
17255 case MIRType::Boolean:
17256 masm.fallibleUnboxBoolean(src, dest.gpr(), fail);
17257 break;
17258 case MIRType::Object:
17259 masm.fallibleUnboxObject(src, dest.gpr(), fail);
17260 break;
17261 case MIRType::String:
17262 masm.fallibleUnboxString(src, dest.gpr(), fail);
17263 break;
17264 case MIRType::Symbol:
17265 masm.fallibleUnboxSymbol(src, dest.gpr(), fail);
17266 break;
17267 case MIRType::BigInt:
17268 masm.fallibleUnboxBigInt(src, dest.gpr(), fail);
17269 break;
17270 default:
17271 MOZ_CRASH("Unexpected MIRType")do { do { } while (false); MOZ_ReportCrash("" "Unexpected MIRType"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 17271); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected MIRType"
")"); do { *((volatile int*)__null) = 17271; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
17272 }
17273 return;
17274 }
17275 masm.loadUnboxedValue(src, type, dest);
17276}
17277
17278void CodeGenerator::visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* ins) {
17279 const MLoadFixedSlotAndUnbox* mir = ins->mir();
17280 MIRType type = mir->type();
17281 Register input = ToRegister(ins->object());
17282 AnyRegister result = ToAnyRegister(ins->output());
17283 size_t slot = mir->slot();
17284
17285 Address address(input, NativeObject::getFixedSlotOffset(slot));
17286
17287 Label bail;
17288 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail);
17289 if (mir->fallible()) {
17290 bailoutFrom(&bail, ins->snapshot());
17291 }
17292}
17293
17294void CodeGenerator::visitLoadDynamicSlotAndUnbox(
17295 LLoadDynamicSlotAndUnbox* ins) {
17296 const MLoadDynamicSlotAndUnbox* mir = ins->mir();
17297 MIRType type = mir->type();
17298 Register input = ToRegister(ins->slots());
17299 AnyRegister result = ToAnyRegister(ins->output());
17300 size_t slot = mir->slot();
17301
17302 Address address(input, slot * sizeof(JS::Value));
17303
17304 Label bail;
17305 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail);
17306 if (mir->fallible()) {
17307 bailoutFrom(&bail, ins->snapshot());
17308 }
17309}
17310
17311void CodeGenerator::visitLoadElementAndUnbox(LLoadElementAndUnbox* ins) {
17312 const MLoadElementAndUnbox* mir = ins->mir();
17313 MIRType type = mir->type();
17314 Register elements = ToRegister(ins->elements());
17315 AnyRegister result = ToAnyRegister(ins->output());
17316
17317 Label bail;
17318 if (ins->index()->isConstant()) {
17319 NativeObject::elementsSizeMustNotOverflow();
17320 int32_t offset = ToInt32(ins->index()) * sizeof(Value);
17321 Address address(elements, offset);
17322 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail);
17323 } else {
17324 BaseObjectElementIndex address(elements, ToRegister(ins->index()));
17325 EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail);
17326 }
17327
17328 if (mir->fallible()) {
17329 bailoutFrom(&bail, ins->snapshot());
17330 }
17331}
17332
17333class OutOfLineAtomizeSlot : public OutOfLineCodeBase<CodeGenerator> {
17334 LInstruction* lir_;
17335 Register stringReg_;
17336 Address slotAddr_;
17337 TypedOrValueRegister dest_;
17338
17339 public:
17340 OutOfLineAtomizeSlot(LInstruction* lir, Register stringReg, Address slotAddr,
17341 TypedOrValueRegister dest)
17342 : lir_(lir), stringReg_(stringReg), slotAddr_(slotAddr), dest_(dest) {}
17343
17344 void accept(CodeGenerator* codegen) final {
17345 codegen->visitOutOfLineAtomizeSlot(this);
17346 }
17347 LInstruction* lir() const { return lir_; }
17348 Register stringReg() const { return stringReg_; }
17349 Address slotAddr() const { return slotAddr_; }
17350 TypedOrValueRegister dest() const { return dest_; }
17351};
17352
17353void CodeGenerator::visitOutOfLineAtomizeSlot(OutOfLineAtomizeSlot* ool) {
17354 LInstruction* lir = ool->lir();
17355 Register stringReg = ool->stringReg();
17356 Address slotAddr = ool->slotAddr();
17357 TypedOrValueRegister dest = ool->dest();
17358
17359 // This code is called with a non-atomic string in |stringReg|.
17360 // When it returns, |stringReg| contains an unboxed pointer to an
17361 // atomized version of that string, and |slotAddr| contains a
17362 // StringValue pointing to that atom. If |dest| is a ValueOperand,
17363 // it contains the same StringValue; otherwise we assert that |dest|
17364 // is |stringReg|.
17365
17366 saveLive(lir);
17367 pushArg(stringReg);
17368
17369 using Fn = JSAtom* (*)(JSContext*, JSString*);
17370 callVM<Fn, js::AtomizeString>(lir);
17371 StoreRegisterTo(stringReg).generate(this);
17372 restoreLiveIgnore(lir, StoreRegisterTo(stringReg).clobbered());
17373
17374 if (dest.hasValue()) {
17375 masm.moveValue(
17376 TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)),
17377 dest.valueReg());
17378 } else {
17379 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"
, 17379); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg"
")"); do { *((volatile int*)__null) = 17379; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17380 }
17381
17382 emitPreBarrier(slotAddr);
17383 masm.storeTypedOrValue(dest, slotAddr);
17384
17385 // We don't need a post-barrier because atoms aren't nursery-allocated.
17386#ifdef DEBUG1
17387 // We need a temp register for the nursery check. Spill something.
17388 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
17389 allRegs.take(stringReg);
17390 Register temp = allRegs.takeAny();
17391 masm.push(temp);
17392
17393 Label tenured;
17394 masm.branchPtrInNurseryChunk(Assembler::NotEqual, stringReg, temp, &tenured);
17395 masm.assumeUnreachable("AtomizeString returned a nursery pointer");
17396 masm.bind(&tenured);
17397
17398 masm.pop(temp);
17399#endif
17400
17401 masm.jump(ool->rejoin());
17402}
17403
17404void CodeGenerator::emitMaybeAtomizeSlot(LInstruction* ins, Register stringReg,
17405 Address slotAddr,
17406 TypedOrValueRegister dest) {
17407 OutOfLineAtomizeSlot* ool =
17408 new (alloc()) OutOfLineAtomizeSlot(ins, stringReg, slotAddr, dest);
17409 addOutOfLineCode(ool, ins->mirRaw()->toInstruction());
17410 masm.branchTest32(Assembler::NonZero,
17411 Address(stringReg, JSString::offsetOfFlags()),
17412 Imm32(JSString::ATOM_BIT), ool->rejoin());
17413
17414 masm.branchTest32(Assembler::Zero,
17415 Address(stringReg, JSString::offsetOfFlags()),
17416 Imm32(JSString::ATOM_REF_BIT), ool->entry());
17417 masm.loadPtr(Address(stringReg, JSAtomRefString::offsetOfAtom()), stringReg);
17418
17419 if (dest.hasValue()) {
17420 masm.moveValue(
17421 TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)),
17422 dest.valueReg());
17423 } else {
17424 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"
, 17424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg"
")"); do { *((volatile int*)__null) = 17424; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17425 }
17426
17427 emitPreBarrier(slotAddr);
17428 masm.storeTypedOrValue(dest, slotAddr);
17429
17430 masm.bind(ool->rejoin());
17431}
17432
17433void CodeGenerator::visitLoadFixedSlotAndAtomize(
17434 LLoadFixedSlotAndAtomize* ins) {
17435 Register obj = ToRegister(ins->getOperand(0));
17436 Register temp = ToRegister(ins->temp0());
17437 size_t slot = ins->mir()->slot();
17438 ValueOperand result = ToOutValue(ins);
17439
17440 Address slotAddr(obj, NativeObject::getFixedSlotOffset(slot));
17441 masm.loadValue(slotAddr, result);
17442
17443 Label notString;
17444 masm.branchTestString(Assembler::NotEqual, result, &notString);
17445 masm.unboxString(result, temp);
17446 emitMaybeAtomizeSlot(ins, temp, slotAddr, result);
17447 masm.bind(&notString);
17448}
17449
17450void CodeGenerator::visitLoadDynamicSlotAndAtomize(
17451 LLoadDynamicSlotAndAtomize* ins) {
17452 ValueOperand result = ToOutValue(ins);
17453 Register temp = ToRegister(ins->temp0());
17454 Register base = ToRegister(ins->input());
17455 int32_t offset = ins->mir()->slot() * sizeof(js::Value);
17456
17457 Address slotAddr(base, offset);
17458 masm.loadValue(slotAddr, result);
17459
17460 Label notString;
17461 masm.branchTestString(Assembler::NotEqual, result, &notString);
17462 masm.unboxString(result, temp);
17463 emitMaybeAtomizeSlot(ins, temp, slotAddr, result);
17464 masm.bind(&notString);
17465}
17466
17467void CodeGenerator::visitLoadFixedSlotUnboxAndAtomize(
17468 LLoadFixedSlotUnboxAndAtomize* ins) {
17469 const MLoadFixedSlotAndUnbox* mir = ins->mir();
17470 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"
, 17470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String"
")"); do { *((volatile int*)__null) = 17470; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17471 Register input = ToRegister(ins->object());
17472 AnyRegister result = ToAnyRegister(ins->output());
17473 size_t slot = mir->slot();
17474
17475 Address slotAddr(input, NativeObject::getFixedSlotOffset(slot));
17476
17477 Label bail;
17478 EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result,
17479 &bail);
17480 emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr,
17481 TypedOrValueRegister(MIRType::String, result));
17482
17483 if (mir->fallible()) {
17484 bailoutFrom(&bail, ins->snapshot());
17485 }
17486}
17487
17488void CodeGenerator::visitLoadDynamicSlotUnboxAndAtomize(
17489 LLoadDynamicSlotUnboxAndAtomize* ins) {
17490 const MLoadDynamicSlotAndUnbox* mir = ins->mir();
17491 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"
, 17491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String"
")"); do { *((volatile int*)__null) = 17491; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
17492 Register input = ToRegister(ins->slots());
17493 AnyRegister result = ToAnyRegister(ins->output());
17494 size_t slot = mir->slot();
17495
17496 Address slotAddr(input, slot * sizeof(JS::Value));
17497
17498 Label bail;
17499 EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result,
17500 &bail);
17501 emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr,
17502 TypedOrValueRegister(MIRType::String, result));
17503
17504 if (mir->fallible()) {
17505 bailoutFrom(&bail, ins->snapshot());
17506 }
17507}
17508
17509void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) {
17510 const Register obj = ToRegister(ins->getOperand(0));
17511 const ValueOperand value = ToValue(ins, LAddAndStoreSlot::ValueIndex);
17512 const Register maybeTemp = ToTempRegisterOrInvalid(ins->temp0());
17513
17514 Shape* shape = ins->mir()->shape();
17515 masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) {
17516 EmitPreBarrier(masm, addr, MIRType::Shape);
17517 });
17518
17519 // Perform the store. No pre-barrier required since this is a new
17520 // initialization.
17521
17522 uint32_t offset = ins->mir()->slotOffset();
17523 if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) {
17524 Address slot(obj, offset);
17525 masm.storeValue(value, slot);
17526 } else {
17527 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), maybeTemp);
17528 Address slot(maybeTemp, offset);
17529 masm.storeValue(value, slot);
17530 }
17531}
17532
17533void CodeGenerator::visitAllocateAndStoreSlot(LAllocateAndStoreSlot* ins) {
17534 const Register obj = ToRegister(ins->getOperand(0));
17535 const ValueOperand value = ToValue(ins, LAllocateAndStoreSlot::ValueIndex);
17536 const Register temp0 = ToRegister(ins->temp0());
17537 const Register temp1 = ToRegister(ins->temp1());
17538
17539 masm.Push(obj);
17540 masm.Push(value);
17541
17542 using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount);
17543 masm.setupAlignedABICall();
17544 masm.loadJSContext(temp0);
17545 masm.passABIArg(temp0);
17546 masm.passABIArg(obj);
17547 masm.move32(Imm32(ins->mir()->numNewSlots()), temp1);
17548 masm.passABIArg(temp1);
17549 masm.callWithABI<Fn, NativeObject::growSlotsPure>();
17550 masm.storeCallPointerResult(temp0);
17551
17552 masm.Pop(value);
17553 masm.Pop(obj);
17554
17555 bailoutIfFalseBool(temp0, ins->snapshot());
17556
17557 masm.storeObjShape(ins->mir()->shape(), obj,
17558 [](MacroAssembler& masm, const Address& addr) {
17559 EmitPreBarrier(masm, addr, MIRType::Shape);
17560 });
17561
17562 // Perform the store. No pre-barrier required since this is a new
17563 // initialization.
17564 masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0);
17565 Address slot(temp0, ins->mir()->slotOffset());
17566 masm.storeValue(value, slot);
17567}
17568
17569void CodeGenerator::visitAddSlotAndCallAddPropHook(
17570 LAddSlotAndCallAddPropHook* ins) {
17571 const Register obj = ToRegister(ins->object());
17572 const ValueOperand value =
17573 ToValue(ins, LAddSlotAndCallAddPropHook::ValueIndex);
17574
17575 pushArg(ImmGCPtr(ins->mir()->shape()));
17576 pushArg(value);
17577 pushArg(obj);
17578
17579 using Fn =
17580 bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, Handle<Shape*>);
17581 callVM<Fn, AddSlotAndCallAddPropHook>(ins);
17582}
17583
17584void CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV* ins) {
17585 const Register obj = ToRegister(ins->getOperand(0));
17586 size_t slot = ins->mir()->slot();
17587
17588 const ValueOperand value = ToValue(ins, LStoreFixedSlotV::ValueIndex);
17589
17590 Address address(obj, NativeObject::getFixedSlotOffset(slot));
17591 if (ins->mir()->needsBarrier()) {
17592 emitPreBarrier(address);
17593 }
17594
17595 masm.storeValue(value, address);
17596}
17597
17598void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) {
17599 const Register obj = ToRegister(ins->getOperand(0));
17600 size_t slot = ins->mir()->slot();
17601
17602 const LAllocation* value = ins->value();
17603 MIRType valueType = ins->mir()->value()->type();
17604
17605 Address address(obj, NativeObject::getFixedSlotOffset(slot));
17606 if (ins->mir()->needsBarrier()) {
17607 emitPreBarrier(address);
17608 }
17609
17610 ConstantOrRegister nvalue =
17611 value->isConstant()
17612 ? ConstantOrRegister(value->toConstant()->toJSValue())
17613 : TypedOrValueRegister(valueType, ToAnyRegister(value));
17614 masm.storeConstantOrRegister(nvalue, address);
17615}
17616
17617void CodeGenerator::visitGetNameCache(LGetNameCache* ins) {
17618 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17619 Register envChain = ToRegister(ins->envObj());
17620 ValueOperand output = ToOutValue(ins);
17621 Register temp = ToRegister(ins->temp0());
17622
17623 IonGetNameIC ic(liveRegs, envChain, output, temp);
17624 addIC(ins, allocateIC(ic));
17625}
17626
17627void CodeGenerator::addGetPropertyCache(LInstruction* ins,
17628 LiveRegisterSet liveRegs,
17629 TypedOrValueRegister value,
17630 const ConstantOrRegister& id,
17631 ValueOperand output) {
17632 CacheKind kind = CacheKind::GetElem;
17633 if (id.constant() && id.value().isString()) {
17634 JSString* idString = id.value().toString();
17635 if (idString->isAtom() && !idString->asAtom().isIndex()) {
17636 kind = CacheKind::GetProp;
17637 }
17638 }
17639 IonGetPropertyIC cache(kind, liveRegs, value, id, output);
17640 addIC(ins, allocateIC(cache));
17641}
17642
17643void CodeGenerator::addSetPropertyCache(LInstruction* ins,
17644 LiveRegisterSet liveRegs,
17645 Register objReg, Register temp,
17646 const ConstantOrRegister& id,
17647 const ConstantOrRegister& value,
17648 bool strict) {
17649 CacheKind kind = CacheKind::SetElem;
17650 if (id.constant() && id.value().isString()) {
17651 JSString* idString = id.value().toString();
17652 if (idString->isAtom() && !idString->asAtom().isIndex()) {
17653 kind = CacheKind::SetProp;
17654 }
17655 }
17656 IonSetPropertyIC cache(kind, liveRegs, objReg, temp, id, value, strict);
17657 addIC(ins, allocateIC(cache));
17658}
17659
17660ConstantOrRegister CodeGenerator::toConstantOrRegister(LInstruction* lir,
17661 size_t n, MIRType type) {
17662 if (type == MIRType::Value) {
17663 return TypedOrValueRegister(ToValue(lir, n));
17664 }
17665
17666 const LAllocation* value = lir->getOperand(n);
17667 if (value->isConstant()) {
17668 return ConstantOrRegister(value->toConstant()->toJSValue());
17669 }
17670
17671 return TypedOrValueRegister(type, ToAnyRegister(value));
17672}
17673
17674void CodeGenerator::visitGetPropertyCache(LGetPropertyCache* ins) {
17675 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17676 TypedOrValueRegister value =
17677 toConstantOrRegister(ins, LGetPropertyCache::ValueIndex,
17678 ins->mir()->value()->type())
17679 .reg();
17680 ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCache::IdIndex,
17681 ins->mir()->idval()->type());
17682 ValueOperand output = ToOutValue(ins);
17683 addGetPropertyCache(ins, liveRegs, value, id, output);
17684}
17685
17686void CodeGenerator::visitGetPropSuperCache(LGetPropSuperCache* ins) {
17687 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17688 Register obj = ToRegister(ins->obj());
17689 TypedOrValueRegister receiver =
17690 toConstantOrRegister(ins, LGetPropSuperCache::ReceiverIndex,
17691 ins->mir()->receiver()->type())
17692 .reg();
17693 ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCache::IdIndex,
17694 ins->mir()->idval()->type());
17695 ValueOperand output = ToOutValue(ins);
17696
17697 CacheKind kind = CacheKind::GetElemSuper;
17698 if (id.constant() && id.value().isString()) {
17699 JSString* idString = id.value().toString();
17700 if (idString->isAtom() && !idString->asAtom().isIndex()) {
17701 kind = CacheKind::GetPropSuper;
17702 }
17703 }
17704
17705 IonGetPropSuperIC cache(kind, liveRegs, obj, receiver, id, output);
17706 addIC(ins, allocateIC(cache));
17707}
17708
17709void CodeGenerator::visitBindNameCache(LBindNameCache* ins) {
17710 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17711 Register envChain = ToRegister(ins->environmentChain());
17712 Register output = ToRegister(ins->output());
17713 Register temp = ToRegister(ins->temp0());
17714
17715 IonBindNameIC ic(liveRegs, envChain, output, temp);
17716 addIC(ins, allocateIC(ic));
17717}
17718
17719void CodeGenerator::visitHasOwnCache(LHasOwnCache* ins) {
17720 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17721 TypedOrValueRegister value =
17722 toConstantOrRegister(ins, LHasOwnCache::ValueIndex,
17723 ins->mir()->value()->type())
17724 .reg();
17725 TypedOrValueRegister id = toConstantOrRegister(ins, LHasOwnCache::IdIndex,
17726 ins->mir()->idval()->type())
17727 .reg();
17728 Register output = ToRegister(ins->output());
17729
17730 IonHasOwnIC cache(liveRegs, value, id, output);
17731 addIC(ins, allocateIC(cache));
17732}
17733
17734void CodeGenerator::visitCheckPrivateFieldCache(LCheckPrivateFieldCache* ins) {
17735 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17736 TypedOrValueRegister value =
17737 toConstantOrRegister(ins, LCheckPrivateFieldCache::ValueIndex,
17738 ins->mir()->value()->type())
17739 .reg();
17740 TypedOrValueRegister id =
17741 toConstantOrRegister(ins, LCheckPrivateFieldCache::IdIndex,
17742 ins->mir()->idval()->type())
17743 .reg();
17744 Register output = ToRegister(ins->output());
17745
17746 IonCheckPrivateFieldIC cache(liveRegs, value, id, output);
17747 addIC(ins, allocateIC(cache));
17748}
17749
17750void CodeGenerator::visitNewPrivateName(LNewPrivateName* ins) {
17751 pushArg(ImmGCPtr(ins->mir()->name()));
17752
17753 using Fn = JS::Symbol* (*)(JSContext*, Handle<JSAtom*>);
17754 callVM<Fn, NewPrivateName>(ins);
17755}
17756
17757void CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty* lir) {
17758 pushArg(ImmGCPtr(lir->mir()->name()));
17759 pushArg(ToValue(lir, LCallDeleteProperty::ValueIndex));
17760
17761 using Fn = bool (*)(JSContext*, HandleValue, Handle<PropertyName*>, bool*);
17762 if (lir->mir()->strict()) {
17763 callVM<Fn, DelPropOperation<true>>(lir);
17764 } else {
17765 callVM<Fn, DelPropOperation<false>>(lir);
17766 }
17767}
17768
17769void CodeGenerator::visitCallDeleteElement(LCallDeleteElement* lir) {
17770 pushArg(ToValue(lir, LCallDeleteElement::IndexIndex));
17771 pushArg(ToValue(lir, LCallDeleteElement::ValueIndex));
17772
17773 using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*);
17774 if (lir->mir()->strict()) {
17775 callVM<Fn, DelElemOperation<true>>(lir);
17776 } else {
17777 callVM<Fn, DelElemOperation<false>>(lir);
17778 }
17779}
17780
17781void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) {
17782 Register obj = ToRegister(lir->object());
17783 Register iterObj = ToRegister(lir->output());
17784 Register temp = ToRegister(lir->temp0());
17785 Register temp2 = ToRegister(lir->temp1());
17786 Register temp3 = ToRegister(lir->temp2());
17787
17788 using Fn = PropertyIteratorObject* (*)(JSContext*, HandleObject);
17789 OutOfLineCode* ool = (lir->mir()->wantsIndices())
17790 ? oolCallVM<Fn, GetIteratorWithIndices>(
17791 lir, ArgList(obj), StoreRegisterTo(iterObj))
17792 : oolCallVM<Fn, GetIterator>(
17793 lir, ArgList(obj), StoreRegisterTo(iterObj));
17794
17795 masm.maybeLoadIteratorFromShape(obj, iterObj, temp, temp2, temp3,
17796 ool->entry());
17797
17798 Register nativeIter = temp;
17799 masm.loadPrivate(
17800 Address(iterObj, PropertyIteratorObject::offsetOfIteratorSlot()),
17801 nativeIter);
17802
17803 if (lir->mir()->wantsIndices()) {
17804 // At least one consumer of the output of this iterator has been optimized
17805 // to use iterator indices. If the cached iterator doesn't include indices,
17806 // but it was marked to indicate that we can create them if needed, then we
17807 // do a VM call to replace the cached iterator with a fresh iterator
17808 // including indices.
17809 masm.branchNativeIteratorIndices(Assembler::Equal, nativeIter, temp2,
17810 NativeIteratorIndices::AvailableOnRequest,
17811 ool->entry());
17812 }
17813
17814 Address iterFlagsAddr(nativeIter, NativeIterator::offsetOfFlagsAndCount());
17815 masm.storePtr(
17816 obj, Address(nativeIter, NativeIterator::offsetOfObjectBeingIterated()));
17817 masm.or32(Imm32(NativeIterator::Flags::Active), iterFlagsAddr);
17818
17819 Register enumeratorsAddr = temp2;
17820 masm.movePtr(ImmPtr(lir->mir()->enumeratorsAddr()), enumeratorsAddr);
17821 masm.registerIterator(enumeratorsAddr, nativeIter, temp3);
17822
17823 // Generate post-write barrier for storing to |iterObj->objectBeingIterated_|.
17824 // We already know that |iterObj| is tenured, so we only have to check |obj|.
17825 Label skipBarrier;
17826 masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp2, &skipBarrier);
17827 {
17828 LiveRegisterSet save = liveVolatileRegs(lir);
17829 save.takeUnchecked(temp);
17830 save.takeUnchecked(temp2);
17831 save.takeUnchecked(temp3);
17832 if (iterObj.volatile_()) {
17833 save.addUnchecked(iterObj);
17834 }
17835
17836 masm.PushRegsInMask(save);
17837 emitPostWriteBarrier(iterObj);
17838 masm.PopRegsInMask(save);
17839 }
17840 masm.bind(&skipBarrier);
17841
17842 masm.bind(ool->rejoin());
17843}
17844
17845void CodeGenerator::visitValueToIterator(LValueToIterator* lir) {
17846 pushArg(ToValue(lir, LValueToIterator::ValueIndex));
17847
17848 using Fn = PropertyIteratorObject* (*)(JSContext*, HandleValue);
17849 callVM<Fn, ValueToIterator>(lir);
17850}
17851
17852void CodeGenerator::visitIteratorHasIndicesAndBranch(
17853 LIteratorHasIndicesAndBranch* lir) {
17854 Register iterator = ToRegister(lir->iterator());
17855 Register object = ToRegister(lir->object());
17856 Register temp = ToRegister(lir->temp());
17857 Register temp2 = ToRegister(lir->temp2());
17858 Label* ifTrue = getJumpLabelForBranch(lir->ifTrue());
17859 Label* ifFalse = getJumpLabelForBranch(lir->ifFalse());
17860
17861 // Check that the iterator has indices available.
17862 Address nativeIterAddr(iterator,
17863 PropertyIteratorObject::offsetOfIteratorSlot());
17864 masm.loadPrivate(nativeIterAddr, temp);
17865 masm.branchNativeIteratorIndices(Assembler::NotEqual, temp, temp2,
17866 NativeIteratorIndices::Valid, ifFalse);
17867
17868 // Guard that the first shape stored in the iterator matches the current
17869 // shape of the iterated object.
17870 Address firstShapeAddr(temp, NativeIterator::offsetOfFirstShape());
17871 masm.loadPtr(firstShapeAddr, temp);
17872 masm.branchTestObjShape(Assembler::NotEqual, object, temp, temp2, object,
17873 ifFalse);
17874
17875 if (!isNextBlock(lir->ifTrue()->lir())) {
17876 masm.jump(ifTrue);
17877 }
17878}
17879
17880void CodeGenerator::visitLoadSlotByIteratorIndex(
17881 LLoadSlotByIteratorIndex* lir) {
17882 Register object = ToRegister(lir->object());
17883 Register iterator = ToRegister(lir->iterator());
17884 Register temp = ToRegister(lir->temp0());
17885 Register temp2 = ToRegister(lir->temp1());
17886 ValueOperand result = ToOutValue(lir);
17887
17888 masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2);
17889
17890 Label notDynamicSlot, notFixedSlot, done;
17891 masm.branch32(Assembler::NotEqual, temp2,
17892 Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)),
17893 &notDynamicSlot);
17894 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
17895 masm.loadValue(BaseValueIndex(temp2, temp), result);
17896 masm.jump(&done);
17897
17898 masm.bind(&notDynamicSlot);
17899 masm.branch32(Assembler::NotEqual, temp2,
17900 Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot);
17901 // Fixed slot
17902 masm.loadValue(BaseValueIndex(object, temp, sizeof(NativeObject)), result);
17903 masm.jump(&done);
17904 masm.bind(&notFixedSlot);
17905
17906#ifdef DEBUG1
17907 Label kindOkay;
17908 masm.branch32(Assembler::Equal, temp2,
17909 Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay);
17910 masm.assumeUnreachable("Invalid PropertyIndex::Kind");
17911 masm.bind(&kindOkay);
17912#endif
17913
17914 // Dense element
17915 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2);
17916 Label indexOkay;
17917 Address initLength(temp2, ObjectElements::offsetOfInitializedLength());
17918 masm.branch32(Assembler::Above, initLength, temp, &indexOkay);
17919 masm.assumeUnreachable("Dense element out of bounds");
17920 masm.bind(&indexOkay);
17921
17922 masm.loadValue(BaseObjectElementIndex(temp2, temp), result);
17923 masm.bind(&done);
17924}
17925
17926void CodeGenerator::visitStoreSlotByIteratorIndex(
17927 LStoreSlotByIteratorIndex* lir) {
17928 Register object = ToRegister(lir->object());
17929 Register iterator = ToRegister(lir->iterator());
17930 ValueOperand value = ToValue(lir, LStoreSlotByIteratorIndex::ValueIndex);
17931 Register temp = ToRegister(lir->temp0());
17932 Register temp2 = ToRegister(lir->temp1());
17933
17934 masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2);
17935
17936 Label notDynamicSlot, notFixedSlot, done, doStore;
17937 masm.branch32(Assembler::NotEqual, temp2,
17938 Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)),
17939 &notDynamicSlot);
17940 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
17941 masm.computeEffectiveAddress(BaseValueIndex(temp2, temp), temp);
17942 masm.jump(&doStore);
17943
17944 masm.bind(&notDynamicSlot);
17945 masm.branch32(Assembler::NotEqual, temp2,
17946 Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), &notFixedSlot);
17947 // Fixed slot
17948 masm.computeEffectiveAddress(
17949 BaseValueIndex(object, temp, sizeof(NativeObject)), temp);
17950 masm.jump(&doStore);
17951 masm.bind(&notFixedSlot);
17952
17953#ifdef DEBUG1
17954 Label kindOkay;
17955 masm.branch32(Assembler::Equal, temp2,
17956 Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay);
17957 masm.assumeUnreachable("Invalid PropertyIndex::Kind");
17958 masm.bind(&kindOkay);
17959#endif
17960
17961 // Dense element
17962 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2);
17963 Label indexOkay;
17964 Address initLength(temp2, ObjectElements::offsetOfInitializedLength());
17965 masm.branch32(Assembler::Above, initLength, temp, &indexOkay);
17966 masm.assumeUnreachable("Dense element out of bounds");
17967 masm.bind(&indexOkay);
17968
17969 BaseObjectElementIndex elementAddress(temp2, temp);
17970 masm.computeEffectiveAddress(elementAddress, temp);
17971
17972 masm.bind(&doStore);
17973 Address storeAddress(temp, 0);
17974 emitPreBarrier(storeAddress);
17975 masm.storeValue(value, storeAddress);
17976
17977 masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp2, &done);
17978 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp2, &done);
17979
17980 saveVolatile(temp2);
17981 emitPostWriteBarrier(object);
17982 restoreVolatile(temp2);
17983
17984 masm.bind(&done);
17985}
17986
17987void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) {
17988 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
17989 Register objReg = ToRegister(ins->object());
17990 Register temp = ToRegister(ins->temp0());
17991
17992 ConstantOrRegister id = toConstantOrRegister(ins, LSetPropertyCache::IdIndex,
17993 ins->mir()->idval()->type());
17994 ConstantOrRegister value = toConstantOrRegister(
17995 ins, LSetPropertyCache::ValueIndex, ins->mir()->value()->type());
17996
17997 addSetPropertyCache(ins, liveRegs, objReg, temp, id, value,
17998 ins->mir()->strict());
17999}
18000
18001void CodeGenerator::visitThrow(LThrow* lir) {
18002 pushArg(ToValue(lir, LThrow::ValueIndex));
18003
18004 using Fn = bool (*)(JSContext*, HandleValue);
18005 callVM<Fn, js::ThrowOperation>(lir);
18006}
18007
18008void CodeGenerator::visitThrowWithStack(LThrowWithStack* lir) {
18009 pushArg(ToValue(lir, LThrowWithStack::StackIndex));
18010 pushArg(ToValue(lir, LThrowWithStack::ValueIndex));
18011
18012 using Fn = bool (*)(JSContext*, HandleValue, HandleValue);
18013 callVM<Fn, js::ThrowWithStackOperation>(lir);
18014}
18015
18016class OutOfLineTypeOfV : public OutOfLineCodeBase<CodeGenerator> {
18017 LTypeOfV* ins_;
18018
18019 public:
18020 explicit OutOfLineTypeOfV(LTypeOfV* ins) : ins_(ins) {}
18021
18022 void accept(CodeGenerator* codegen) override {
18023 codegen->visitOutOfLineTypeOfV(this);
18024 }
18025 LTypeOfV* ins() const { return ins_; }
18026};
18027
18028void CodeGenerator::emitTypeOfJSType(JSValueType type, Register output) {
18029 switch (type) {
18030 case JSVAL_TYPE_OBJECT:
18031 masm.move32(Imm32(JSTYPE_OBJECT), output);
18032 break;
18033 case JSVAL_TYPE_DOUBLE:
18034 case JSVAL_TYPE_INT32:
18035 masm.move32(Imm32(JSTYPE_NUMBER), output);
18036 break;
18037 case JSVAL_TYPE_BOOLEAN:
18038 masm.move32(Imm32(JSTYPE_BOOLEAN), output);
18039 break;
18040 case JSVAL_TYPE_UNDEFINED:
18041 masm.move32(Imm32(JSTYPE_UNDEFINED), output);
18042 break;
18043 case JSVAL_TYPE_NULL:
18044 masm.move32(Imm32(JSTYPE_OBJECT), output);
18045 break;
18046 case JSVAL_TYPE_STRING:
18047 masm.move32(Imm32(JSTYPE_STRING), output);
18048 break;
18049 case JSVAL_TYPE_SYMBOL:
18050 masm.move32(Imm32(JSTYPE_SYMBOL), output);
18051 break;
18052 case JSVAL_TYPE_BIGINT:
18053 masm.move32(Imm32(JSTYPE_BIGINT), output);
18054 break;
18055 default:
18056 MOZ_CRASH("Unsupported JSValueType")do { do { } while (false); MOZ_ReportCrash("" "Unsupported JSValueType"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18056); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported JSValueType"
")"); do { *((volatile int*)__null) = 18056; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18057 }
18058}
18059
18060void CodeGenerator::emitTypeOfCheck(JSValueType type, Register tag,
18061 Register output, Label* done,
18062 Label* oolObject) {
18063 Label notMatch;
18064 switch (type) {
18065 case JSVAL_TYPE_OBJECT:
18066 // The input may be a callable object (result is "function") or
18067 // may emulate undefined (result is "undefined"). Use an OOL path.
18068 masm.branchTestObject(Assembler::Equal, tag, oolObject);
18069 return;
18070 case JSVAL_TYPE_DOUBLE:
18071 case JSVAL_TYPE_INT32:
18072 masm.branchTestNumber(Assembler::NotEqual, tag, &notMatch);
18073 break;
18074 default:
18075 masm.branchTestType(Assembler::NotEqual, tag, type, &notMatch);
18076 break;
18077 }
18078
18079 emitTypeOfJSType(type, output);
18080 masm.jump(done);
18081 masm.bind(&notMatch);
18082}
18083
18084void CodeGenerator::visitTypeOfV(LTypeOfV* lir) {
18085 const ValueOperand value = ToValue(lir, LTypeOfV::InputIndex);
18086 Register output = ToRegister(lir->output());
18087 Register tag = masm.extractTag(value, output);
18088
18089 Label done;
18090
18091 auto* ool = new (alloc()) OutOfLineTypeOfV(lir);
18092 addOutOfLineCode(ool, lir->mir());
18093
18094 const std::initializer_list<JSValueType> defaultOrder = {
18095 JSVAL_TYPE_OBJECT, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_UNDEFINED,
18096 JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_STRING,
18097 JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
18098
18099 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
18100
18101 // Generate checks for previously observed types first.
18102 // The TypeDataList is sorted by descending frequency.
18103 for (auto& observed : lir->mir()->observedTypes()) {
18104 JSValueType type = observed.type();
18105
18106 // Unify number types.
18107 if (type == JSVAL_TYPE_INT32) {
18108 type = JSVAL_TYPE_DOUBLE;
18109 }
18110
18111 remaining -= type;
18112
18113 emitTypeOfCheck(type, tag, output, &done, ool->entry());
18114 }
18115
18116 // Generate checks for remaining types.
18117 for (auto type : defaultOrder) {
18118 if (!remaining.contains(type)) {
18119 continue;
18120 }
18121 remaining -= type;
18122
18123 if (remaining.isEmpty() && type != JSVAL_TYPE_OBJECT) {
18124 // We can skip the check for the last remaining type, unless the type is
18125 // JSVAL_TYPE_OBJECT, which may have to go through the OOL path.
18126#ifdef DEBUG1
18127 emitTypeOfCheck(type, tag, output, &done, ool->entry());
18128 masm.assumeUnreachable("Unexpected Value type in visitTypeOfV");
18129#else
18130 emitTypeOfJSType(type, output);
18131#endif
18132 } else {
18133 emitTypeOfCheck(type, tag, output, &done, ool->entry());
18134 }
18135 }
18136 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"
, 18136); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()"
")"); do { *((volatile int*)__null) = 18136; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18137
18138 masm.bind(&done);
18139 masm.bind(ool->rejoin());
18140}
18141
18142void CodeGenerator::emitTypeOfObject(Register obj, Register output,
18143 Label* done) {
18144 Label slowCheck, isObject, isCallable, isUndefined;
18145 masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable,
18146 &isUndefined);
18147
18148 masm.bind(&isCallable);
18149 masm.move32(Imm32(JSTYPE_FUNCTION), output);
18150 masm.jump(done);
18151
18152 masm.bind(&isUndefined);
18153 masm.move32(Imm32(JSTYPE_UNDEFINED), output);
18154 masm.jump(done);
18155
18156 masm.bind(&isObject);
18157 masm.move32(Imm32(JSTYPE_OBJECT), output);
18158 masm.jump(done);
18159
18160 masm.bind(&slowCheck);
18161
18162 saveVolatile(output);
18163 using Fn = JSType (*)(JSObject*);
18164 masm.setupAlignedABICall();
18165 masm.passABIArg(obj);
18166 masm.callWithABI<Fn, js::TypeOfObject>();
18167 masm.storeCallInt32Result(output);
18168 restoreVolatile(output);
18169}
18170
18171void CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) {
18172 LTypeOfV* ins = ool->ins();
18173
18174 ValueOperand input = ToValue(ins, LTypeOfV::InputIndex);
18175 Register temp = ToTempUnboxRegister(ins->temp0());
18176 Register output = ToRegister(ins->output());
18177
18178 Register obj = masm.extractObject(input, temp);
18179 emitTypeOfObject(obj, output, ool->rejoin());
18180 masm.jump(ool->rejoin());
18181}
18182
18183void CodeGenerator::visitTypeOfO(LTypeOfO* lir) {
18184 Register obj = ToRegister(lir->object());
18185 Register output = ToRegister(lir->output());
18186
18187 Label done;
18188 emitTypeOfObject(obj, output, &done);
18189 masm.bind(&done);
18190}
18191
18192void CodeGenerator::visitTypeOfName(LTypeOfName* lir) {
18193 Register input = ToRegister(lir->input());
18194 Register output = ToRegister(lir->output());
18195
18196#ifdef DEBUG1
18197 Label ok;
18198 masm.branch32(Assembler::Below, input, Imm32(JSTYPE_LIMIT), &ok);
18199 masm.assumeUnreachable("bad JSType");
18200 masm.bind(&ok);
18201#endif
18202
18203 static_assert(JSTYPE_UNDEFINED == 0);
18204
18205 masm.movePtr(ImmPtr(&gen->runtime->names().undefined), output);
18206 masm.loadPtr(BaseIndex(output, input, ScalePointer), output);
18207}
18208
18209class OutOfLineTypeOfIsNonPrimitiveV : public OutOfLineCodeBase<CodeGenerator> {
18210 LTypeOfIsNonPrimitiveV* ins_;
18211
18212 public:
18213 explicit OutOfLineTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* ins)
18214 : ins_(ins) {}
18215
18216 void accept(CodeGenerator* codegen) override {
18217 codegen->visitOutOfLineTypeOfIsNonPrimitiveV(this);
18218 }
18219 auto* ins() const { return ins_; }
18220};
18221
18222class OutOfLineTypeOfIsNonPrimitiveO : public OutOfLineCodeBase<CodeGenerator> {
18223 LTypeOfIsNonPrimitiveO* ins_;
18224
18225 public:
18226 explicit OutOfLineTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* ins)
18227 : ins_(ins) {}
18228
18229 void accept(CodeGenerator* codegen) override {
18230 codegen->visitOutOfLineTypeOfIsNonPrimitiveO(this);
18231 }
18232 auto* ins() const { return ins_; }
18233};
18234
18235void CodeGenerator::emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj,
18236 Register output) {
18237 saveVolatile(output);
18238 using Fn = JSType (*)(JSObject*);
18239 masm.setupAlignedABICall();
18240 masm.passABIArg(obj);
18241 masm.callWithABI<Fn, js::TypeOfObject>();
18242 masm.storeCallInt32Result(output);
18243 restoreVolatile(output);
18244
18245 auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false);
18246 masm.cmp32Set(cond, output, Imm32(mir->jstype()), output);
18247}
18248
18249void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveV(
18250 OutOfLineTypeOfIsNonPrimitiveV* ool) {
18251 auto* ins = ool->ins();
18252 ValueOperand input = ToValue(ins, LTypeOfIsNonPrimitiveV::InputIndex);
18253 Register output = ToRegister(ins->output());
18254 Register temp = ToTempUnboxRegister(ins->temp0());
18255
18256 Register obj = masm.extractObject(input, temp);
18257
18258 emitTypeOfIsObjectOOL(ins->mir(), obj, output);
18259
18260 masm.jump(ool->rejoin());
18261}
18262
18263void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveO(
18264 OutOfLineTypeOfIsNonPrimitiveO* ool) {
18265 auto* ins = ool->ins();
18266 Register input = ToRegister(ins->input());
18267 Register output = ToRegister(ins->output());
18268
18269 emitTypeOfIsObjectOOL(ins->mir(), input, output);
18270
18271 masm.jump(ool->rejoin());
18272}
18273
18274void CodeGenerator::emitTypeOfIsObject(MTypeOfIs* mir, Register obj,
18275 Register output, Label* success,
18276 Label* fail, Label* slowCheck) {
18277 Label* isObject = fail;
18278 Label* isFunction = fail;
18279 Label* isUndefined = fail;
18280
18281 switch (mir->jstype()) {
18282 case JSTYPE_UNDEFINED:
18283 isUndefined = success;
18284 break;
18285
18286 case JSTYPE_OBJECT:
18287 isObject = success;
18288 break;
18289
18290 case JSTYPE_FUNCTION:
18291 isFunction = success;
18292 break;
18293
18294 case JSTYPE_STRING:
18295 case JSTYPE_NUMBER:
18296 case JSTYPE_BOOLEAN:
18297 case JSTYPE_SYMBOL:
18298 case JSTYPE_BIGINT:
18299#ifdef ENABLE_RECORD_TUPLE
18300 case JSTYPE_RECORD:
18301 case JSTYPE_TUPLE:
18302#endif
18303 case JSTYPE_LIMIT:
18304 MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18304); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type"
")"); do { *((volatile int*)__null) = 18304; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18305 }
18306
18307 masm.typeOfObject(obj, output, slowCheck, isObject, isFunction, isUndefined);
18308
18309 auto op = mir->jsop();
18310
18311 Label done;
18312 masm.bind(fail);
18313 masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output);
18314 masm.jump(&done);
18315 masm.bind(success);
18316 masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output);
18317 masm.bind(&done);
18318}
18319
18320void CodeGenerator::visitTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* lir) {
18321 ValueOperand input = ToValue(lir, LTypeOfIsNonPrimitiveV::InputIndex);
18322 Register output = ToRegister(lir->output());
18323 Register temp = ToTempUnboxRegister(lir->temp0());
18324
18325 auto* mir = lir->mir();
18326
18327 auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveV(lir);
18328 addOutOfLineCode(ool, mir);
18329
18330 Label success, fail;
18331
18332 switch (mir->jstype()) {
18333 case JSTYPE_UNDEFINED: {
18334 ScratchTagScope tag(masm, input);
18335 masm.splitTagForTest(input, tag);
18336
18337 masm.branchTestUndefined(Assembler::Equal, tag, &success);
18338 masm.branchTestObject(Assembler::NotEqual, tag, &fail);
18339 break;
18340 }
18341
18342 case JSTYPE_OBJECT: {
18343 ScratchTagScope tag(masm, input);
18344 masm.splitTagForTest(input, tag);
18345
18346 masm.branchTestNull(Assembler::Equal, tag, &success);
18347 masm.branchTestObject(Assembler::NotEqual, tag, &fail);
18348 break;
18349 }
18350
18351 case JSTYPE_FUNCTION: {
18352 masm.branchTestObject(Assembler::NotEqual, input, &fail);
18353 break;
18354 }
18355
18356 case JSTYPE_STRING:
18357 case JSTYPE_NUMBER:
18358 case JSTYPE_BOOLEAN:
18359 case JSTYPE_SYMBOL:
18360 case JSTYPE_BIGINT:
18361#ifdef ENABLE_RECORD_TUPLE
18362 case JSTYPE_RECORD:
18363 case JSTYPE_TUPLE:
18364#endif
18365 case JSTYPE_LIMIT:
18366 MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18366); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type"
")"); do { *((volatile int*)__null) = 18366; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18367 }
18368
18369 Register obj = masm.extractObject(input, temp);
18370
18371 emitTypeOfIsObject(mir, obj, output, &success, &fail, ool->entry());
18372
18373 masm.bind(ool->rejoin());
18374}
18375
18376void CodeGenerator::visitTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* lir) {
18377 Register input = ToRegister(lir->input());
18378 Register output = ToRegister(lir->output());
18379
18380 auto* mir = lir->mir();
18381
18382 auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveO(lir);
18383 addOutOfLineCode(ool, mir);
18384
18385 Label success, fail;
18386 emitTypeOfIsObject(mir, input, output, &success, &fail, ool->entry());
18387
18388 masm.bind(ool->rejoin());
18389}
18390
18391void CodeGenerator::visitTypeOfIsPrimitive(LTypeOfIsPrimitive* lir) {
18392 ValueOperand input = ToValue(lir, LTypeOfIsPrimitive::InputIndex);
18393 Register output = ToRegister(lir->output());
18394
18395 auto* mir = lir->mir();
18396 auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false);
18397
18398 switch (mir->jstype()) {
18399 case JSTYPE_STRING:
18400 masm.testStringSet(cond, input, output);
18401 break;
18402 case JSTYPE_NUMBER:
18403 masm.testNumberSet(cond, input, output);
18404 break;
18405 case JSTYPE_BOOLEAN:
18406 masm.testBooleanSet(cond, input, output);
18407 break;
18408 case JSTYPE_SYMBOL:
18409 masm.testSymbolSet(cond, input, output);
18410 break;
18411 case JSTYPE_BIGINT:
18412 masm.testBigIntSet(cond, input, output);
18413 break;
18414
18415 case JSTYPE_UNDEFINED:
18416 case JSTYPE_OBJECT:
18417 case JSTYPE_FUNCTION:
18418#ifdef ENABLE_RECORD_TUPLE
18419 case JSTYPE_RECORD:
18420 case JSTYPE_TUPLE:
18421#endif
18422 case JSTYPE_LIMIT:
18423 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"
, 18423); AnnotateMozCrashReason("MOZ_CRASH(" "Non-primitive type"
")"); do { *((volatile int*)__null) = 18423; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18424 }
18425}
18426
18427void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) {
18428 pushArg(ToValue(lir, LToAsyncIter::NextMethodIndex));
18429 pushArg(ToRegister(lir->iterator()));
18430
18431 using Fn = JSObject* (*)(JSContext*, HandleObject, HandleValue);
18432 callVM<Fn, js::CreateAsyncFromSyncIterator>(lir);
18433}
18434
18435void CodeGenerator::visitToPropertyKeyCache(LToPropertyKeyCache* lir) {
18436 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
18437 ValueOperand input = ToValue(lir, LToPropertyKeyCache::InputIndex);
18438 ValueOperand output = ToOutValue(lir);
18439
18440 IonToPropertyKeyIC ic(liveRegs, input, output);
18441 addIC(lir, allocateIC(ic));
18442}
18443
18444void CodeGenerator::visitLoadElementV(LLoadElementV* load) {
18445 Register elements = ToRegister(load->elements());
18446 const ValueOperand out = ToOutValue(load);
18447
18448 if (load->index()->isConstant()) {
18449 NativeObject::elementsSizeMustNotOverflow();
18450 int32_t offset = ToInt32(load->index()) * sizeof(Value);
18451 masm.loadValue(Address(elements, offset), out);
18452 } else {
18453 masm.loadValue(BaseObjectElementIndex(elements, ToRegister(load->index())),
18454 out);
18455 }
18456
18457 Label testMagic;
18458 masm.branchTestMagic(Assembler::Equal, out, &testMagic);
18459 bailoutFrom(&testMagic, load->snapshot());
18460}
18461
18462void CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) {
18463 Register elements = ToRegister(lir->elements());
18464 Register index = ToRegister(lir->index());
18465 Register initLength = ToRegister(lir->initLength());
18466 const ValueOperand out = ToOutValue(lir);
18467
18468 const MLoadElementHole* mir = lir->mir();
18469
18470 // If the index is out of bounds, load |undefined|. Otherwise, load the
18471 // value.
18472 Label outOfBounds, done;
18473 masm.spectreBoundsCheck32(index, initLength, out.scratchReg(), &outOfBounds);
18474
18475 masm.loadValue(BaseObjectElementIndex(elements, index), out);
18476
18477 // If the value wasn't a hole, we're done. Otherwise, we'll load undefined.
18478 masm.branchTestMagic(Assembler::NotEqual, out, &done);
18479
18480 if (mir->needsNegativeIntCheck()) {
18481 Label loadUndefined;
18482 masm.jump(&loadUndefined);
18483
18484 masm.bind(&outOfBounds);
18485
18486 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
18487
18488 masm.bind(&loadUndefined);
18489 } else {
18490 masm.bind(&outOfBounds);
18491 }
18492 masm.moveValue(UndefinedValue(), out);
18493
18494 masm.bind(&done);
18495}
18496
18497void CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) {
18498 Register elements = ToRegister(lir->elements());
18499 Register temp0 = ToTempRegisterOrInvalid(lir->temp0());
18500 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
18501 AnyRegister out = ToAnyRegister(lir->output());
18502
18503 const MLoadUnboxedScalar* mir = lir->mir();
18504
18505 Scalar::Type storageType = mir->storageType();
18506
18507 LiveRegisterSet volatileRegs;
18508 if (MacroAssembler::LoadRequiresCall(storageType)) {
18509 volatileRegs = liveVolatileRegs(lir);
18510 }
18511
18512 Label fail;
18513 if (lir->index()->isConstant()) {
18514 Address source =
18515 ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment());
18516 masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail,
18517 volatileRegs);
18518 } else {
18519 BaseIndex source(elements, ToRegister(lir->index()),
18520 ScaleFromScalarType(storageType), mir->offsetAdjustment());
18521 masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail,
18522 volatileRegs);
18523 }
18524
18525 if (fail.used()) {
18526 bailoutFrom(&fail, lir->snapshot());
18527 }
18528}
18529
18530void CodeGenerator::visitLoadUnboxedInt64(LLoadUnboxedInt64* lir) {
18531 Register elements = ToRegister(lir->elements());
18532 Register64 out = ToOutRegister64(lir);
18533
18534 const MLoadUnboxedScalar* mir = lir->mir();
18535
18536 Scalar::Type storageType = mir->storageType();
18537
18538 if (lir->index()->isConstant()) {
18539 Address source =
18540 ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment());
18541 masm.load64(source, out);
18542 } else {
18543 BaseIndex source(elements, ToRegister(lir->index()),
18544 ScaleFromScalarType(storageType), mir->offsetAdjustment());
18545 masm.load64(source, out);
18546 }
18547}
18548
18549void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) {
18550 Register elements = ToRegister(lir->elements());
18551 const LAllocation* littleEndian = lir->littleEndian();
18552 Register temp1 = ToTempRegisterOrInvalid(lir->temp1());
18553 Register temp2 = ToTempRegisterOrInvalid(lir->temp2());
18554 Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64());
18555 AnyRegister out = ToAnyRegister(lir->output());
18556
18557 const MLoadDataViewElement* mir = lir->mir();
18558 Scalar::Type storageType = mir->storageType();
18559
18560 LiveRegisterSet volatileRegs;
18561 if (MacroAssembler::LoadRequiresCall(storageType)) {
18562 volatileRegs = liveVolatileRegs(lir);
18563 }
18564
18565 BaseIndex source(elements, ToRegister(lir->index()), TimesOne);
18566
18567 bool noSwap = littleEndian->isConstant() &&
18568 ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1;
18569
18570 // Directly load if no byte swap is needed and the platform supports unaligned
18571 // accesses for the access. (Such support is assumed for integer types.)
18572 if (noSwap && (!Scalar::isFloatingType(storageType) ||
18573 MacroAssembler::SupportsFastUnalignedFPAccesses())) {
18574 Label fail;
18575 masm.loadFromTypedArray(storageType, source, out, temp1, temp2, &fail,
18576 volatileRegs);
18577
18578 if (fail.used()) {
18579 bailoutFrom(&fail, lir->snapshot());
18580 }
18581 return;
18582 }
18583
18584 // Load the value into a gpr register.
18585 switch (storageType) {
18586 case Scalar::Int16:
18587 masm.load16UnalignedSignExtend(source, out.gpr());
18588 break;
18589 case Scalar::Uint16:
18590 masm.load16UnalignedZeroExtend(source, out.gpr());
18591 break;
18592 case Scalar::Int32:
18593 masm.load32Unaligned(source, out.gpr());
18594 break;
18595 case Scalar::Uint32:
18596 masm.load32Unaligned(source, out.isFloat() ? temp1 : out.gpr());
18597 break;
18598 case Scalar::Float16:
18599 masm.load16UnalignedZeroExtend(source, temp1);
18600 break;
18601 case Scalar::Float32:
18602 masm.load32Unaligned(source, temp1);
18603 break;
18604 case Scalar::Float64:
18605 masm.load64Unaligned(source, temp64);
18606 break;
18607 case Scalar::Int8:
18608 case Scalar::Uint8:
18609 case Scalar::Uint8Clamped:
18610 case Scalar::BigInt64:
18611 case Scalar::BigUint64:
18612 default:
18613 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"
, 18613); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18613; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18614 }
18615
18616 if (!noSwap) {
18617 // Swap the bytes in the loaded value.
18618 Label skip;
18619 if (!littleEndian->isConstant()) {
18620 masm.branch32(
18621 MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal,
18622 ToRegister(littleEndian), Imm32(0), &skip);
18623 }
18624
18625 switch (storageType) {
18626 case Scalar::Int16:
18627 masm.byteSwap16SignExtend(out.gpr());
18628 break;
18629 case Scalar::Uint16:
18630 masm.byteSwap16ZeroExtend(out.gpr());
18631 break;
18632 case Scalar::Int32:
18633 masm.byteSwap32(out.gpr());
18634 break;
18635 case Scalar::Uint32:
18636 masm.byteSwap32(out.isFloat() ? temp1 : out.gpr());
18637 break;
18638 case Scalar::Float16:
18639 masm.byteSwap16ZeroExtend(temp1);
18640 break;
18641 case Scalar::Float32:
18642 masm.byteSwap32(temp1);
18643 break;
18644 case Scalar::Float64:
18645 masm.byteSwap64(temp64);
18646 break;
18647 case Scalar::Int8:
18648 case Scalar::Uint8:
18649 case Scalar::Uint8Clamped:
18650 case Scalar::BigInt64:
18651 case Scalar::BigUint64:
18652 default:
18653 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"
, 18653); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18653; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18654 }
18655
18656 if (skip.used()) {
18657 masm.bind(&skip);
18658 }
18659 }
18660
18661 // Move the value into the output register.
18662 switch (storageType) {
18663 case Scalar::Int16:
18664 case Scalar::Uint16:
18665 case Scalar::Int32:
18666 break;
18667 case Scalar::Uint32:
18668 if (out.isFloat()) {
18669 masm.convertUInt32ToDouble(temp1, out.fpu());
18670 } else {
18671 // Bail out if the value doesn't fit into a signed int32 value. This
18672 // is what allows MLoadDataViewElement to have a type() of
18673 // MIRType::Int32 for UInt32 array loads.
18674 bailoutTest32(Assembler::Signed, out.gpr(), out.gpr(), lir->snapshot());
18675 }
18676 break;
18677 case Scalar::Float16:
18678 masm.moveGPRToFloat16(temp1, out.fpu(), temp2, volatileRegs);
18679 masm.canonicalizeFloat(out.fpu());
18680 break;
18681 case Scalar::Float32:
18682 masm.moveGPRToFloat32(temp1, out.fpu());
18683 masm.canonicalizeFloat(out.fpu());
18684 break;
18685 case Scalar::Float64:
18686 masm.moveGPR64ToDouble(temp64, out.fpu());
18687 masm.canonicalizeDouble(out.fpu());
18688 break;
18689 case Scalar::Int8:
18690 case Scalar::Uint8:
18691 case Scalar::Uint8Clamped:
18692 case Scalar::BigInt64:
18693 case Scalar::BigUint64:
18694 default:
18695 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"
, 18695); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 18695; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18696 }
18697}
18698
18699void CodeGenerator::visitLoadDataViewElement64(LLoadDataViewElement64* lir) {
18700 Register elements = ToRegister(lir->elements());
18701 const LAllocation* littleEndian = lir->littleEndian();
18702 Register64 out = ToOutRegister64(lir);
18703
18704 MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->storageType()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Scalar::isBigIntType(lir->mir()->storageType()
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(Scalar::isBigIntType(lir->mir()->storageType()
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("Scalar::isBigIntType(lir->mir()->storageType())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18704); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->storageType())"
")"); do { *((volatile int*)__null) = 18704; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18705
18706 BaseIndex source(elements, ToRegister(lir->index()), TimesOne);
18707
18708 bool noSwap = littleEndian->isConstant() &&
18709 ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1;
18710
18711 // Load the value into a register.
18712 masm.load64Unaligned(source, out);
18713
18714 if (!noSwap) {
18715 // Swap the bytes in the loaded value.
18716 Label skip;
18717 if (!littleEndian->isConstant()) {
18718 masm.branch32(
18719 MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal,
18720 ToRegister(littleEndian), Imm32(0), &skip);
18721 }
18722
18723 masm.byteSwap64(out);
18724
18725 if (skip.used()) {
18726 masm.bind(&skip);
18727 }
18728 }
18729}
18730
18731void CodeGenerator::visitLoadTypedArrayElementHole(
18732 LLoadTypedArrayElementHole* lir) {
18733 Register elements = ToRegister(lir->elements());
18734 Register index = ToRegister(lir->index());
18735 Register length = ToRegister(lir->length());
18736 Register temp = ToTempRegisterOrInvalid(lir->temp0());
18737 const ValueOperand out = ToOutValue(lir);
18738
18739 Register scratch = out.scratchReg();
18740
18741 // Load undefined if index >= length.
18742 Label outOfBounds, done;
18743 masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds);
18744
18745 Scalar::Type arrayType = lir->mir()->arrayType();
18746
18747 LiveRegisterSet volatileRegs;
18748 if (MacroAssembler::LoadRequiresCall(arrayType)) {
18749 volatileRegs = liveVolatileRegs(lir);
18750 }
18751
18752 Label fail;
18753 BaseIndex source(elements, index, ScaleFromScalarType(arrayType));
18754 MacroAssembler::Uint32Mode uint32Mode =
18755 lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble
18756 : MacroAssembler::Uint32Mode::FailOnDouble;
18757 masm.loadFromTypedArray(arrayType, source, out, uint32Mode, temp, &fail,
18758 volatileRegs);
18759 masm.jump(&done);
18760
18761 masm.bind(&outOfBounds);
18762 masm.moveValue(UndefinedValue(), out);
18763
18764 if (fail.used()) {
18765 bailoutFrom(&fail, lir->snapshot());
18766 }
18767
18768 masm.bind(&done);
18769}
18770
18771void CodeGenerator::visitLoadTypedArrayElementHoleBigInt(
18772 LLoadTypedArrayElementHoleBigInt* lir) {
18773 Register elements = ToRegister(lir->elements());
18774 Register index = ToRegister(lir->index());
18775 Register length = ToRegister(lir->length());
18776 const ValueOperand out = ToOutValue(lir);
18777
18778 Register temp = ToRegister(lir->temp());
18779
18780 // On x86 there are not enough registers. In that case reuse the output
18781 // registers as temporaries.
18782#ifdef JS_CODEGEN_X86
18783 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"
, 18783); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->temp64().isBogusTemp()"
")"); do { *((volatile int*)__null) = 18783; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
18784 Register64 temp64 = out.toRegister64();
18785#else
18786 Register64 temp64 = ToRegister64(lir->temp64());
18787#endif
18788
18789 // Load undefined if index >= length.
18790 Label outOfBounds, done;
18791 masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds);
18792
18793 Scalar::Type arrayType = lir->mir()->arrayType();
18794 BaseIndex source(elements, index, ScaleFromScalarType(arrayType));
18795 masm.load64(source, temp64);
18796
18797#ifdef JS_CODEGEN_X86
18798 Register bigInt = temp;
18799 Register maybeTemp = InvalidReg;
18800#else
18801 Register bigInt = out.scratchReg();
18802 Register maybeTemp = temp;
18803#endif
18804 emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp);
18805
18806 masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out);
18807 masm.jump(&done);
18808
18809 masm.bind(&outOfBounds);
18810 masm.moveValue(UndefinedValue(), out);
18811
18812 masm.bind(&done);
18813}
18814
18815template <SwitchTableType tableType>
18816class OutOfLineSwitch : public OutOfLineCodeBase<CodeGenerator> {
18817 using LabelsVector = Vector<Label, 0, JitAllocPolicy>;
18818 using CodeLabelsVector = Vector<CodeLabel, 0, JitAllocPolicy>;
18819 LabelsVector labels_;
18820 CodeLabelsVector codeLabels_;
18821 CodeLabel start_;
18822 bool isOutOfLine_;
18823
18824 void accept(CodeGenerator* codegen) override {
18825 codegen->visitOutOfLineSwitch(this);
18826 }
18827
18828 public:
18829 explicit OutOfLineSwitch(TempAllocator& alloc)
18830 : labels_(alloc), codeLabels_(alloc), isOutOfLine_(false) {}
18831
18832 CodeLabel* start() { return &start_; }
18833
18834 CodeLabelsVector& codeLabels() { return codeLabels_; }
18835 LabelsVector& labels() { return labels_; }
18836
18837 void jumpToCodeEntries(MacroAssembler& masm, Register index, Register temp) {
18838 Register base;
18839 if (tableType == SwitchTableType::Inline) {
18840#if defined(JS_CODEGEN_ARM)
18841 base = ::js::jit::pc;
18842#else
18843 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"
, 18843); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::Inline"
")"); do { *((volatile int*)__null) = 18843; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18844#endif
18845 } else {
18846#if defined(JS_CODEGEN_ARM)
18847 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"
, 18847); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine"
")"); do { *((volatile int*)__null) = 18847; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18848#else
18849 masm.mov(start(), temp);
18850 base = temp;
18851#endif
18852 }
18853 BaseIndex jumpTarget(base, index, ScalePointer);
18854 masm.branchToComputedAddress(jumpTarget);
18855 }
18856
18857 // Register an entry in the switch table.
18858 void addTableEntry(MacroAssembler& masm) {
18859 if ((!isOutOfLine_ && tableType == SwitchTableType::Inline) ||
18860 (isOutOfLine_ && tableType == SwitchTableType::OutOfLine)) {
18861 CodeLabel cl;
18862 masm.writeCodePointer(&cl);
18863 masm.propagateOOM(codeLabels_.append(std::move(cl)));
18864 }
18865 }
18866 // Register the code, to which the table will jump to.
18867 void addCodeEntry(MacroAssembler& masm) {
18868 Label entry;
18869 masm.bind(&entry);
18870 masm.propagateOOM(labels_.append(std::move(entry)));
18871 }
18872
18873 void setOutOfLine() { isOutOfLine_ = true; }
18874};
18875
18876template <SwitchTableType tableType>
18877void CodeGenerator::visitOutOfLineSwitch(
18878 OutOfLineSwitch<tableType>* jumpTable) {
18879 jumpTable->setOutOfLine();
18880 auto& labels = jumpTable->labels();
18881
18882 if (tableType == SwitchTableType::OutOfLine) {
18883#if defined(JS_CODEGEN_ARM)
18884 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"
, 18884); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine"
")"); do { *((volatile int*)__null) = 18884; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
18885#elif defined(JS_CODEGEN_NONE)
18886 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 18886); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 18886; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
18887#else
18888
18889# if defined(JS_CODEGEN_ARM64)
18890 AutoForbidPoolsAndNops afp(
18891 &masm,
18892 (labels.length() + 1) * (sizeof(void*) / vixl::kInstructionSize));
18893# endif
18894
18895 masm.haltingAlign(sizeof(void*));
18896
18897 // Bind the address of the jump table and reserve the space for code
18898 // pointers to jump in the newly generated code.
18899 masm.bind(jumpTable->start());
18900 masm.addCodeLabel(*jumpTable->start());
18901 for (size_t i = 0, e = labels.length(); i < e; i++) {
18902 jumpTable->addTableEntry(masm);
18903 }
18904#endif
18905 }
18906
18907 // Register all reserved pointers of the jump table to target labels. The
18908 // entries of the jump table need to be absolute addresses and thus must be
18909 // patched after codegen is finished.
18910 auto& codeLabels = jumpTable->codeLabels();
18911 for (size_t i = 0, e = codeLabels.length(); i < e; i++) {
18912 auto& cl = codeLabels[i];
18913 cl.target()->bind(labels[i].offset());
18914 masm.addCodeLabel(cl);
18915 }
18916}
18917
18918template void CodeGenerator::visitOutOfLineSwitch(
18919 OutOfLineSwitch<SwitchTableType::Inline>* jumpTable);
18920template void CodeGenerator::visitOutOfLineSwitch(
18921 OutOfLineSwitch<SwitchTableType::OutOfLine>* jumpTable);
18922
18923template <typename T>
18924static inline void StoreToTypedArray(MacroAssembler& masm,
18925 Scalar::Type writeType,
18926 const LAllocation* value, const T& dest,
18927 Register temp,
18928 LiveRegisterSet volatileRegs) {
18929 if (Scalar::isFloatingType(writeType)) {
18930 masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, temp,
18931 volatileRegs);
18932 } else {
18933 if (value->isConstant()) {
18934 masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest);
18935 } else {
18936 masm.storeToTypedIntArray(writeType, ToRegister(value), dest);
18937 }
18938 }
18939}
18940
18941void CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) {
18942 Register elements = ToRegister(lir->elements());
18943 Register temp = ToTempRegisterOrInvalid(lir->temp0());
18944 const LAllocation* value = lir->value();
18945
18946 const MStoreUnboxedScalar* mir = lir->mir();
18947
18948 Scalar::Type writeType = mir->writeType();
18949
18950 LiveRegisterSet volatileRegs;
18951 if (MacroAssembler::StoreRequiresCall(writeType)) {
18952 volatileRegs = liveVolatileRegs(lir);
18953 }
18954
18955 if (lir->index()->isConstant()) {
18956 Address dest = ToAddress(elements, lir->index(), writeType);
18957 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
18958 } else {
18959 BaseIndex dest(elements, ToRegister(lir->index()),
18960 ScaleFromScalarType(writeType));
18961 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
18962 }
18963}
18964
18965void CodeGenerator::visitStoreUnboxedInt64(LStoreUnboxedInt64* lir) {
18966 Register elements = ToRegister(lir->elements());
18967 Register64 value = ToRegister64(lir->value());
18968
18969 Scalar::Type writeType = lir->mir()->writeType();
18970
18971 if (lir->index()->isConstant()) {
18972 Address dest = ToAddress(elements, lir->index(), writeType);
18973 masm.storeToTypedBigIntArray(writeType, value, dest);
18974 } else {
18975 BaseIndex dest(elements, ToRegister(lir->index()),
18976 ScaleFromScalarType(writeType));
18977 masm.storeToTypedBigIntArray(writeType, value, dest);
18978 }
18979}
18980
18981void CodeGenerator::visitStoreDataViewElement(LStoreDataViewElement* lir) {
18982 Register elements = ToRegister(lir->elements());
18983 const LAllocation* value = lir->value();
18984 const LAllocation* littleEndian = lir->littleEndian();
18985 Register temp = ToTempRegisterOrInvalid(lir->temp());
18986 Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64());
18987
18988 const MStoreDataViewElement* mir = lir->mir();
18989 Scalar::Type writeType = mir->writeType();
18990
18991 LiveRegisterSet volatileRegs;
18992 if (MacroAssembler::StoreRequiresCall(writeType)) {
18993 volatileRegs = liveVolatileRegs(lir);
18994 }
18995
18996 BaseIndex dest(elements, ToRegister(lir->index()), TimesOne);
18997
18998 bool noSwap = littleEndian->isConstant() &&
18999 ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1;
19000
19001 // Directly store if no byte swap is needed and the platform supports
19002 // unaligned accesses for the access. (Such support is assumed for integer
19003 // types.)
19004 if (noSwap && (!Scalar::isFloatingType(writeType) ||
19005 MacroAssembler::SupportsFastUnalignedFPAccesses())) {
19006 StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs);
19007 return;
19008 }
19009
19010 // Load the value into a gpr register.
19011 switch (writeType) {
19012 case Scalar::Int16:
19013 case Scalar::Uint16:
19014 case Scalar::Int32:
19015 case Scalar::Uint32:
19016 if (value->isConstant()) {
19017 masm.move32(Imm32(ToInt32(value)), temp);
19018 } else {
19019 masm.move32(ToRegister(value), temp);
19020 }
19021 break;
19022 case Scalar::Float16: {
19023 FloatRegister fvalue = ToFloatRegister(value);
19024 masm.canonicalizeFloatIfDeterministic(fvalue);
19025 masm.moveFloat16ToGPR(fvalue, temp, volatileRegs);
19026 break;
19027 }
19028 case Scalar::Float32: {
19029 FloatRegister fvalue = ToFloatRegister(value);
19030 masm.canonicalizeFloatIfDeterministic(fvalue);
19031 masm.moveFloat32ToGPR(fvalue, temp);
19032 break;
19033 }
19034 case Scalar::Float64: {
19035 FloatRegister fvalue = ToFloatRegister(value);
19036 masm.canonicalizeDoubleIfDeterministic(fvalue);
19037 masm.moveDoubleToGPR64(fvalue, temp64);
19038 break;
19039 }
19040 case Scalar::Int8:
19041 case Scalar::Uint8:
19042 case Scalar::Uint8Clamped:
19043 case Scalar::BigInt64:
19044 case Scalar::BigUint64:
19045 default:
19046 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"
, 19046); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 19046; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
19047 }
19048
19049 if (!noSwap) {
19050 // Swap the bytes in the loaded value.
19051 Label skip;
19052 if (!littleEndian->isConstant()) {
19053 masm.branch32(
19054 MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal,
19055 ToRegister(littleEndian), Imm32(0), &skip);
19056 }
19057
19058 switch (writeType) {
19059 case Scalar::Int16:
19060 masm.byteSwap16SignExtend(temp);
19061 break;
19062 case Scalar::Uint16:
19063 case Scalar::Float16:
19064 masm.byteSwap16ZeroExtend(temp);
19065 break;
19066 case Scalar::Int32:
19067 case Scalar::Uint32:
19068 case Scalar::Float32:
19069 masm.byteSwap32(temp);
19070 break;
19071 case Scalar::Float64:
19072 masm.byteSwap64(temp64);
19073 break;
19074 case Scalar::Int8:
19075 case Scalar::Uint8:
19076 case Scalar::Uint8Clamped:
19077 case Scalar::BigInt64:
19078 case Scalar::BigUint64:
19079 default:
19080 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"
, 19080); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 19080; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
19081 }
19082
19083 if (skip.used()) {
19084 masm.bind(&skip);
19085 }
19086 }
19087
19088 // Store the value into the destination.
19089 switch (writeType) {
19090 case Scalar::Int16:
19091 case Scalar::Uint16:
19092 case Scalar::Float16:
19093 masm.store16Unaligned(temp, dest);
19094 break;
19095 case Scalar::Int32:
19096 case Scalar::Uint32:
19097 case Scalar::Float32:
19098 masm.store32Unaligned(temp, dest);
19099 break;
19100 case Scalar::Float64:
19101 masm.store64Unaligned(temp64, dest);
19102 break;
19103 case Scalar::Int8:
19104 case Scalar::Uint8:
19105 case Scalar::Uint8Clamped:
19106 case Scalar::BigInt64:
19107 case Scalar::BigUint64:
19108 default:
19109 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"
, 19109); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type"
")"); do { *((volatile int*)__null) = 19109; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
19110 }
19111}
19112
19113void CodeGenerator::visitStoreDataViewElement64(LStoreDataViewElement64* lir) {
19114 Register elements = ToRegister(lir->elements());
19115 Register64 value = ToRegister64(lir->value());
19116 const LAllocation* littleEndian = lir->littleEndian();
19117 Register64 temp = ToTempRegister64OrInvalid(lir->temp());
19118
19119 MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->writeType()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(Scalar::isBigIntType(lir->mir()->writeType()))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(Scalar::isBigIntType(lir->mir()->writeType()))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Scalar::isBigIntType(lir->mir()->writeType())"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 19119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->writeType())"
")"); do { *((volatile int*)__null) = 19119; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19120
19121 BaseIndex dest(elements, ToRegister(lir->index()), TimesOne);
19122
19123 bool noSwap = littleEndian->isConstant() &&
19124 ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1;
19125
19126 if (!noSwap) {
19127 // Preserve the input value.
19128 if (temp != Register64::Invalid()) {
19129 masm.move64(value, temp);
19130 value = temp;
19131 } else {
19132 masm.Push(value);
19133 }
19134
19135 // Swap the bytes in the loaded value.
19136 Label skip;
19137 if (!littleEndian->isConstant()) {
19138 masm.branch32(
19139 MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal,
19140 ToRegister(littleEndian), Imm32(0), &skip);
19141 }
19142
19143 masm.byteSwap64(value);
19144
19145 if (skip.used()) {
19146 masm.bind(&skip);
19147 }
19148 }
19149
19150 // Store the value into the destination.
19151 masm.store64Unaligned(value, dest);
19152
19153 // Restore |value| if it was modified.
19154 if (!noSwap && temp == Register64::Invalid()) {
19155 masm.Pop(value);
19156 }
19157}
19158
19159void CodeGenerator::visitStoreTypedArrayElementHole(
19160 LStoreTypedArrayElementHole* lir) {
19161 Register elements = ToRegister(lir->elements());
19162 const LAllocation* value = lir->value();
19163
19164 Scalar::Type arrayType = lir->mir()->arrayType();
19165
19166 Register index = ToRegister(lir->index());
19167 const LAllocation* length = lir->length();
19168 Register temp = ToTempRegisterOrInvalid(lir->temp0());
19169
19170 LiveRegisterSet volatileRegs;
19171 if (MacroAssembler::StoreRequiresCall(arrayType)) {
19172 volatileRegs = liveVolatileRegs(lir);
19173 }
19174
19175 Label skip;
19176 if (length->isRegister()) {
19177 masm.spectreBoundsCheckPtr(index, ToRegister(length), temp, &skip);
19178 } else {
19179 masm.spectreBoundsCheckPtr(index, ToAddress(length), temp, &skip);
19180 }
19181
19182 BaseIndex dest(elements, index, ScaleFromScalarType(arrayType));
19183 StoreToTypedArray(masm, arrayType, value, dest, temp, volatileRegs);
19184
19185 masm.bind(&skip);
19186}
19187
19188void CodeGenerator::visitStoreTypedArrayElementHoleInt64(
19189 LStoreTypedArrayElementHoleInt64* lir) {
19190 Register elements = ToRegister(lir->elements());
19191 Register64 value = ToRegister64(lir->value());
19192
19193 Scalar::Type arrayType = lir->mir()->arrayType();
19194
19195 Register index = ToRegister(lir->index());
19196 const LAllocation* length = lir->length();
19197 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp());
19198
19199 Label skip;
19200 if (length->isRegister()) {
19201 masm.spectreBoundsCheckPtr(index, ToRegister(length), spectreTemp, &skip);
19202 } else {
19203 masm.spectreBoundsCheckPtr(index, ToAddress(length), spectreTemp, &skip);
19204 }
19205
19206 BaseIndex dest(elements, index, ScaleFromScalarType(arrayType));
19207 masm.storeToTypedBigIntArray(arrayType, value, dest);
19208
19209 masm.bind(&skip);
19210}
19211
19212void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) {
19213 masm.memoryBarrier(ins->type());
19214}
19215
19216void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) {
19217 Register value = ToRegister(lir->value());
19218 Register output = ToRegister(lir->output());
19219
19220 masm.atomicIsLockFreeJS(value, output);
19221}
19222
19223void CodeGenerator::visitAtomicPause(LAtomicPause* lir) { masm.atomicPause(); }
19224
19225void CodeGenerator::visitClampIToUint8(LClampIToUint8* lir) {
19226 Register output = ToRegister(lir->output());
19227 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"
, 19227); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == ToRegister(lir->input())"
")"); do { *((volatile int*)__null) = 19227; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19228 masm.clampIntToUint8(output);
19229}
19230
19231void CodeGenerator::visitClampDToUint8(LClampDToUint8* lir) {
19232 FloatRegister input = ToFloatRegister(lir->input());
19233 Register output = ToRegister(lir->output());
19234 masm.clampDoubleToUint8(input, output);
19235}
19236
19237void CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) {
19238 ValueOperand operand = ToValue(lir, LClampVToUint8::InputIndex);
19239 FloatRegister tempFloat = ToFloatRegister(lir->temp0());
19240 Register output = ToRegister(lir->output());
19241
19242 using Fn = bool (*)(JSContext*, JSString*, double*);
19243 OutOfLineCode* oolString = oolCallVM<Fn, StringToNumber>(
19244 lir, ArgList(output), StoreFloatRegisterTo(tempFloat));
19245 Label* stringEntry = oolString->entry();
19246 Label* stringRejoin = oolString->rejoin();
19247
19248 Label fails;
19249 masm.clampValueToUint8(operand, stringEntry, stringRejoin, output, tempFloat,
19250 output, &fails);
19251
19252 bailoutFrom(&fails, lir->snapshot());
19253}
19254
19255void CodeGenerator::visitInCache(LInCache* ins) {
19256 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
19257
19258 ConstantOrRegister key =
19259 toConstantOrRegister(ins, LInCache::LhsIndex, ins->mir()->key()->type());
19260 Register object = ToRegister(ins->rhs());
19261 Register output = ToRegister(ins->output());
19262 Register temp = ToRegister(ins->temp0());
19263
19264 IonInIC cache(liveRegs, key, object, output, temp);
19265 addIC(ins, allocateIC(cache));
19266}
19267
19268void CodeGenerator::visitInArray(LInArray* lir) {
19269 const MInArray* mir = lir->mir();
19270 Register elements = ToRegister(lir->elements());
19271 Register initLength = ToRegister(lir->initLength());
19272 Register output = ToRegister(lir->output());
19273
19274 Label falseBranch, done, trueBranch;
19275
19276 if (lir->index()->isConstant()) {
19277 int32_t index = ToInt32(lir->index());
19278
19279 if (index < 0) {
19280 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"
, 19280); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->needsNegativeIntCheck()"
")"); do { *((volatile int*)__null) = 19280; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19281 bailout(lir->snapshot());
19282 return;
19283 }
19284
19285 masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index),
19286 &falseBranch);
19287
19288 NativeObject::elementsSizeMustNotOverflow();
19289 Address address = Address(elements, index * sizeof(Value));
19290 masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
19291 } else {
19292 Register index = ToRegister(lir->index());
19293
19294 Label negativeIntCheck;
19295 Label* failedInitLength = &falseBranch;
19296 if (mir->needsNegativeIntCheck()) {
19297 failedInitLength = &negativeIntCheck;
19298 }
19299
19300 masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength);
19301
19302 BaseObjectElementIndex address(elements, index);
19303 masm.branchTestMagic(Assembler::Equal, address, &falseBranch);
19304
19305 if (mir->needsNegativeIntCheck()) {
19306 masm.jump(&trueBranch);
19307 masm.bind(&negativeIntCheck);
19308
19309 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
19310
19311 masm.jump(&falseBranch);
19312 }
19313 }
19314
19315 masm.bind(&trueBranch);
19316 masm.move32(Imm32(1), output);
19317 masm.jump(&done);
19318
19319 masm.bind(&falseBranch);
19320 masm.move32(Imm32(0), output);
19321 masm.bind(&done);
19322}
19323
19324void CodeGenerator::visitGuardElementNotHole(LGuardElementNotHole* lir) {
19325 Register elements = ToRegister(lir->elements());
19326 const LAllocation* index = lir->index();
19327
19328 Label testMagic;
19329 if (index->isConstant()) {
19330 Address address(elements, ToInt32(index) * sizeof(js::Value));
19331 masm.branchTestMagic(Assembler::Equal, address, &testMagic);
19332 } else {
19333 BaseObjectElementIndex address(elements, ToRegister(index));
19334 masm.branchTestMagic(Assembler::Equal, address, &testMagic);
19335 }
19336 bailoutFrom(&testMagic, lir->snapshot());
19337}
19338
19339void CodeGenerator::visitInstanceOfO(LInstanceOfO* ins) {
19340 Register protoReg = ToRegister(ins->rhs());
19341 emitInstanceOf(ins, protoReg);
19342}
19343
19344void CodeGenerator::visitInstanceOfV(LInstanceOfV* ins) {
19345 Register protoReg = ToRegister(ins->rhs());
19346 emitInstanceOf(ins, protoReg);
19347}
19348
19349void CodeGenerator::emitInstanceOf(LInstruction* ins, Register protoReg) {
19350 // This path implements fun_hasInstance when the function's prototype is
19351 // known to be the object in protoReg
19352
19353 Label done;
19354 Register output = ToRegister(ins->getDef(0));
19355
19356 // If the lhs is a primitive, the result is false.
19357 Register objReg;
19358 if (ins->isInstanceOfV()) {
19359 Label isObject;
19360 ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex);
19361 masm.branchTestObject(Assembler::Equal, lhsValue, &isObject);
19362 masm.mov(ImmWord(0), output);
19363 masm.jump(&done);
19364 masm.bind(&isObject);
19365 objReg = masm.extractObject(lhsValue, output);
19366 } else {
19367 objReg = ToRegister(ins->toInstanceOfO()->lhs());
19368 }
19369
19370 // Crawl the lhs's prototype chain in a loop to search for prototypeObject.
19371 // This follows the main loop of js::IsPrototypeOf, though additionally breaks
19372 // out of the loop on Proxy::LazyProto.
19373
19374 // Load the lhs's prototype.
19375 masm.loadObjProto(objReg, output);
19376
19377 Label testLazy;
19378 {
19379 Label loopPrototypeChain;
19380 masm.bind(&loopPrototypeChain);
19381
19382 // Test for the target prototype object.
19383 Label notPrototypeObject;
19384 masm.branchPtr(Assembler::NotEqual, output, protoReg, &notPrototypeObject);
19385 masm.mov(ImmWord(1), output);
19386 masm.jump(&done);
19387 masm.bind(&notPrototypeObject);
19388
19389 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"
, 19389); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 19389; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19390
19391 // Test for nullptr or Proxy::LazyProto
19392 masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy);
19393
19394 // Load the current object's prototype.
19395 masm.loadObjProto(output, output);
19396
19397 masm.jump(&loopPrototypeChain);
19398 }
19399
19400 // Make a VM call if an object with a lazy proto was found on the prototype
19401 // chain. This currently occurs only for cross compartment wrappers, which
19402 // we do not expect to be compared with non-wrapper functions from this
19403 // compartment. Otherwise, we stopped on a nullptr prototype and the output
19404 // register is already correct.
19405
19406 using Fn = bool (*)(JSContext*, HandleObject, JSObject*, bool*);
19407 auto* ool = oolCallVM<Fn, IsPrototypeOf>(ins, ArgList(protoReg, objReg),
19408 StoreRegisterTo(output));
19409
19410 // Regenerate the original lhs object for the VM call.
19411 Label regenerate, *lazyEntry;
19412 if (objReg != output) {
19413 lazyEntry = ool->entry();
19414 } else {
19415 masm.bind(&regenerate);
19416 lazyEntry = &regenerate;
19417 if (ins->isInstanceOfV()) {
19418 ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex);
19419 objReg = masm.extractObject(lhsValue, output);
19420 } else {
19421 objReg = ToRegister(ins->toInstanceOfO()->lhs());
19422 }
19423 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"
, 19423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "objReg == output"
")"); do { *((volatile int*)__null) = 19423; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19424 masm.jump(ool->entry());
19425 }
19426
19427 masm.bind(&testLazy);
19428 masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry);
19429
19430 masm.bind(&done);
19431 masm.bind(ool->rejoin());
19432}
19433
19434void CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins) {
19435 // The Lowering ensures that RHS is an object, and that LHS is a value.
19436 LiveRegisterSet liveRegs = ins->safepoint()->liveRegs();
19437 TypedOrValueRegister lhs =
19438 TypedOrValueRegister(ToValue(ins, LInstanceOfCache::LHS));
19439 Register rhs = ToRegister(ins->rhs());
19440 Register output = ToRegister(ins->output());
19441
19442 IonInstanceOfIC ic(liveRegs, lhs, rhs, output);
19443 addIC(ins, allocateIC(ic));
19444}
19445
19446void CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) {
19447 const Register JSContextReg = ToRegister(ins->getJSContextReg());
19448 const Register ObjectReg = ToRegister(ins->getObjectReg());
19449 const Register PrivateReg = ToRegister(ins->getPrivReg());
19450 const Register ValueReg = ToRegister(ins->getValueReg());
19451
19452 Label haveValue;
19453 if (ins->mir()->valueMayBeInSlot()) {
19454 size_t slot = ins->mir()->domMemberSlotIndex();
19455 // It's a bit annoying to redo these slot calculations, which duplcate
19456 // LSlots and a few other things like that, but I'm not sure there's a
19457 // way to reuse those here.
19458 //
19459 // If this ever gets fixed to work with proxies (by not assuming that
19460 // reserved slot indices, which is what domMemberSlotIndex() returns,
19461 // match fixed slot indices), we can reenable MGetDOMProperty for
19462 // proxies in IonBuilder.
19463 if (slot < NativeObject::MAX_FIXED_SLOTS) {
19464 masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)),
19465 JSReturnOperand);
19466 } else {
19467 // It's a dynamic slot.
19468 slot -= NativeObject::MAX_FIXED_SLOTS;
19469 // Use PrivateReg as a scratch register for the slots pointer.
19470 masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()),
19471 PrivateReg);
19472 masm.loadValue(Address(PrivateReg, slot * sizeof(js::Value)),
19473 JSReturnOperand);
19474 }
19475 masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue);
19476 }
19477
19478 DebugOnly<uint32_t> initialStack = masm.framePushed();
19479
19480 masm.checkStackAlignment();
19481
19482 // Make space for the outparam. Pre-initialize it to UndefinedValue so we
19483 // can trace it at GC time.
19484 masm.Push(UndefinedValue());
19485 // We pass the pointer to our out param as an instance of
19486 // JSJitGetterCallArgs, since on the binary level it's the same thing.
19487 static_assert(sizeof(JSJitGetterCallArgs) == sizeof(Value*));
19488 masm.moveStackPtrTo(ValueReg);
19489
19490 masm.Push(ObjectReg);
19491
19492 LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
19493
19494 // Rooting will happen at GC time.
19495 masm.moveStackPtrTo(ObjectReg);
19496
19497 Realm* getterRealm = ins->mir()->getterRealm();
19498 if (gen->realm->realmPtr() != getterRealm) {
19499 // We use JSContextReg as scratch register here.
19500 masm.switchToRealm(getterRealm, JSContextReg);
19501 }
19502
19503 uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
19504 masm.loadJSContext(JSContextReg);
19505 masm.enterFakeExitFrame(JSContextReg, JSContextReg,
19506 ExitFrameType::IonDOMGetter);
19507
19508 markSafepointAt(safepointOffset, ins);
19509
19510 masm.setupAlignedABICall();
19511 masm.loadJSContext(JSContextReg);
19512 masm.passABIArg(JSContextReg);
19513 masm.passABIArg(ObjectReg);
19514 masm.passABIArg(PrivateReg);
19515 masm.passABIArg(ValueReg);
19516 ensureOsiSpace();
19517 masm.callWithABI(DynamicFunction<JSJitGetterOp>(ins->mir()->fun()),
19518 ABIType::General,
19519 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
19520
19521 if (ins->mir()->isInfallible()) {
19522 masm.loadValue(Address(masm.getStackPointer(),
19523 IonDOMExitFrameLayout::offsetOfResult()),
19524 JSReturnOperand);
19525 } else {
19526 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
19527
19528 masm.loadValue(Address(masm.getStackPointer(),
19529 IonDOMExitFrameLayout::offsetOfResult()),
19530 JSReturnOperand);
19531 }
19532
19533 // Switch back to the current realm if needed. Note: if the getter threw an
19534 // exception, the exception handler will do this.
19535 if (gen->realm->realmPtr() != getterRealm) {
19536 static_assert(!JSReturnOperand.aliases(ReturnReg),
19537 "Clobbering ReturnReg should not affect the return value");
19538 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
19539 }
19540
19541 // Until C++ code is instrumented against Spectre, prevent speculative
19542 // execution from returning any private data.
19543 if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) {
19544 masm.speculationBarrier();
19545 }
19546
19547 masm.adjustStack(IonDOMExitFrameLayout::Size());
19548
19549 masm.bind(&haveValue);
19550
19551 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"
, 19551); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 19551; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19552}
19553
19554void CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins) {
19555 // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to
19556 // use an LLoadFixedSlotV or some subclass of it for this case: that would
19557 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
19558 // we'd have to duplicate a bunch of stuff we now get for free from
19559 // MGetDOMProperty.
19560 //
19561 // If this ever gets fixed to work with proxies (by not assuming that
19562 // reserved slot indices, which is what domMemberSlotIndex() returns,
19563 // match fixed slot indices), we can reenable MGetDOMMember for
19564 // proxies in IonBuilder.
19565 Register object = ToRegister(ins->object());
19566 size_t slot = ins->mir()->domMemberSlotIndex();
19567 ValueOperand result = ToOutValue(ins);
19568
19569 masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)),
19570 result);
19571}
19572
19573void CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT* ins) {
19574 // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to
19575 // use an LLoadFixedSlotT or some subclass of it for this case: that would
19576 // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then
19577 // we'd have to duplicate a bunch of stuff we now get for free from
19578 // MGetDOMProperty.
19579 //
19580 // If this ever gets fixed to work with proxies (by not assuming that
19581 // reserved slot indices, which is what domMemberSlotIndex() returns,
19582 // match fixed slot indices), we can reenable MGetDOMMember for
19583 // proxies in IonBuilder.
19584 Register object = ToRegister(ins->object());
19585 size_t slot = ins->mir()->domMemberSlotIndex();
19586 AnyRegister result = ToAnyRegister(ins->getDef(0));
19587 MIRType type = ins->mir()->type();
19588
19589 masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)),
19590 type, result);
19591}
19592
19593void CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) {
19594 const Register JSContextReg = ToRegister(ins->getJSContextReg());
19595 const Register ObjectReg = ToRegister(ins->getObjectReg());
19596 const Register PrivateReg = ToRegister(ins->getPrivReg());
19597 const Register ValueReg = ToRegister(ins->getValueReg());
19598
19599 DebugOnly<uint32_t> initialStack = masm.framePushed();
19600
19601 masm.checkStackAlignment();
19602
19603 // Push the argument. Rooting will happen at GC time.
19604 ValueOperand argVal = ToValue(ins, LSetDOMProperty::Value);
19605 masm.Push(argVal);
19606 // We pass the pointer to our out param as an instance of
19607 // JSJitGetterCallArgs, since on the binary level it's the same thing.
19608 static_assert(sizeof(JSJitSetterCallArgs) == sizeof(Value*));
19609 masm.moveStackPtrTo(ValueReg);
19610
19611 masm.Push(ObjectReg);
19612
19613 LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind());
19614
19615 // Rooting will happen at GC time.
19616 masm.moveStackPtrTo(ObjectReg);
19617
19618 Realm* setterRealm = ins->mir()->setterRealm();
19619 if (gen->realm->realmPtr() != setterRealm) {
19620 // We use JSContextReg as scratch register here.
19621 masm.switchToRealm(setterRealm, JSContextReg);
19622 }
19623
19624 uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
19625 masm.loadJSContext(JSContextReg);
19626 masm.enterFakeExitFrame(JSContextReg, JSContextReg,
19627 ExitFrameType::IonDOMSetter);
19628
19629 markSafepointAt(safepointOffset, ins);
19630
19631 masm.setupAlignedABICall();
19632 masm.loadJSContext(JSContextReg);
19633 masm.passABIArg(JSContextReg);
19634 masm.passABIArg(ObjectReg);
19635 masm.passABIArg(PrivateReg);
19636 masm.passABIArg(ValueReg);
19637 ensureOsiSpace();
19638 masm.callWithABI(DynamicFunction<JSJitSetterOp>(ins->mir()->fun()),
19639 ABIType::General,
19640 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
19641
19642 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
19643
19644 // Switch back to the current realm if needed. Note: if the setter threw an
19645 // exception, the exception handler will do this.
19646 if (gen->realm->realmPtr() != setterRealm) {
19647 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
19648 }
19649
19650 masm.adjustStack(IonDOMExitFrameLayout::Size());
19651
19652 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"
, 19652); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 19652; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
19653}
19654
19655void CodeGenerator::visitLoadDOMExpandoValue(LLoadDOMExpandoValue* ins) {
19656 Register proxy = ToRegister(ins->proxy());
19657 ValueOperand out = ToOutValue(ins);
19658
19659 masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()),
19660 out.scratchReg());
19661 masm.loadValue(Address(out.scratchReg(),
19662 js::detail::ProxyReservedSlots::offsetOfPrivateSlot()),
19663 out);
19664}
19665
19666void CodeGenerator::visitLoadDOMExpandoValueGuardGeneration(
19667 LLoadDOMExpandoValueGuardGeneration* ins) {
19668 Register proxy = ToRegister(ins->proxy());
19669 ValueOperand out = ToOutValue(ins);
19670
19671 Label bail;
19672 masm.loadDOMExpandoValueGuardGeneration(proxy, out,
19673 ins->mir()->expandoAndGeneration(),
19674 ins->mir()->generation(), &bail);
19675 bailoutFrom(&bail, ins->snapshot());
19676}
19677
19678void CodeGenerator::visitLoadDOMExpandoValueIgnoreGeneration(
19679 LLoadDOMExpandoValueIgnoreGeneration* ins) {
19680 Register proxy = ToRegister(ins->proxy());
19681 ValueOperand out = ToOutValue(ins);
19682
19683 masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()),
19684 out.scratchReg());
19685
19686 // Load the ExpandoAndGeneration* from the PrivateValue.
19687 masm.loadPrivate(
19688 Address(out.scratchReg(),
19689 js::detail::ProxyReservedSlots::offsetOfPrivateSlot()),
19690 out.scratchReg());
19691
19692 // Load expandoAndGeneration->expando into the output Value register.
19693 masm.loadValue(
19694 Address(out.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), out);
19695}
19696
19697void CodeGenerator::visitGuardDOMExpandoMissingOrGuardShape(
19698 LGuardDOMExpandoMissingOrGuardShape* ins) {
19699 Register temp = ToRegister(ins->temp0());
19700 ValueOperand input =
19701 ToValue(ins, LGuardDOMExpandoMissingOrGuardShape::InputIndex);
19702
19703 Label done;
19704 masm.branchTestUndefined(Assembler::Equal, input, &done);
19705
19706 masm.debugAssertIsObject(input);
19707 masm.unboxObject(input, temp);
19708 // The expando object is not used in this case, so we don't need Spectre
19709 // mitigations.
19710 Label bail;
19711 masm.branchTestObjShapeNoSpectreMitigations(Assembler::NotEqual, temp,
19712 ins->mir()->shape(), &bail);
19713 bailoutFrom(&bail, ins->snapshot());
19714
19715 masm.bind(&done);
19716}
19717
19718class OutOfLineIsCallable : public OutOfLineCodeBase<CodeGenerator> {
19719 Register object_;
19720 Register output_;
19721
19722 public:
19723 OutOfLineIsCallable(Register object, Register output)
19724 : object_(object), output_(output) {}
19725
19726 void accept(CodeGenerator* codegen) override {
19727 codegen->visitOutOfLineIsCallable(this);
19728 }
19729 Register object() const { return object_; }
19730 Register output() const { return output_; }
19731};
19732
19733void CodeGenerator::visitIsCallableO(LIsCallableO* ins) {
19734 Register object = ToRegister(ins->object());
19735 Register output = ToRegister(ins->output());
19736
19737 OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(object, output);
19738 addOutOfLineCode(ool, ins->mir());
19739
19740 masm.isCallable(object, output, ool->entry());
19741
19742 masm.bind(ool->rejoin());
19743}
19744
19745void CodeGenerator::visitIsCallableV(LIsCallableV* ins) {
19746 ValueOperand val = ToValue(ins, LIsCallableV::ObjectIndex);
19747 Register output = ToRegister(ins->output());
19748 Register temp = ToRegister(ins->temp0());
19749
19750 Label notObject;
19751 masm.fallibleUnboxObject(val, temp, &notObject);
19752
19753 OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(temp, output);
19754 addOutOfLineCode(ool, ins->mir());
19755
19756 masm.isCallable(temp, output, ool->entry());
19757 masm.jump(ool->rejoin());
19758
19759 masm.bind(&notObject);
19760 masm.move32(Imm32(0), output);
19761
19762 masm.bind(ool->rejoin());
19763}
19764
19765void CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool) {
19766 Register object = ool->object();
19767 Register output = ool->output();
19768
19769 saveVolatile(output);
19770 using Fn = bool (*)(JSObject* obj);
19771 masm.setupAlignedABICall();
19772 masm.passABIArg(object);
19773 masm.callWithABI<Fn, ObjectIsCallable>();
19774 masm.storeCallBoolResult(output);
19775 restoreVolatile(output);
19776 masm.jump(ool->rejoin());
19777}
19778
19779class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator> {
19780 LIsConstructor* ins_;
19781
19782 public:
19783 explicit OutOfLineIsConstructor(LIsConstructor* ins) : ins_(ins) {}
19784
19785 void accept(CodeGenerator* codegen) override {
19786 codegen->visitOutOfLineIsConstructor(this);
19787 }
19788 LIsConstructor* ins() const { return ins_; }
19789};
19790
19791void CodeGenerator::visitIsConstructor(LIsConstructor* ins) {
19792 Register object = ToRegister(ins->object());
19793 Register output = ToRegister(ins->output());
19794
19795 OutOfLineIsConstructor* ool = new (alloc()) OutOfLineIsConstructor(ins);
19796 addOutOfLineCode(ool, ins->mir());
19797
19798 masm.isConstructor(object, output, ool->entry());
19799
19800 masm.bind(ool->rejoin());
19801}
19802
19803void CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool) {
19804 LIsConstructor* ins = ool->ins();
19805 Register object = ToRegister(ins->object());
19806 Register output = ToRegister(ins->output());
19807
19808 saveVolatile(output);
19809 using Fn = bool (*)(JSObject* obj);
19810 masm.setupAlignedABICall();
19811 masm.passABIArg(object);
19812 masm.callWithABI<Fn, ObjectIsConstructor>();
19813 masm.storeCallBoolResult(output);
19814 restoreVolatile(output);
19815 masm.jump(ool->rejoin());
19816}
19817
19818void CodeGenerator::visitIsCrossRealmArrayConstructor(
19819 LIsCrossRealmArrayConstructor* ins) {
19820 Register object = ToRegister(ins->object());
19821 Register output = ToRegister(ins->output());
19822
19823 masm.setIsCrossRealmArrayConstructor(object, output);
19824}
19825
19826static void EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool,
19827 Register obj, Register output,
19828 Label* notArray = nullptr) {
19829 masm.loadObjClassUnsafe(obj, output);
19830
19831 Label isArray;
19832 masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_),
19833 &isArray);
19834
19835 // Branch to OOL path if it's a proxy.
19836 masm.branchTestClassIsProxy(true, output, ool->entry());
19837
19838 if (notArray) {
19839 masm.bind(notArray);
19840 }
19841 masm.move32(Imm32(0), output);
19842 masm.jump(ool->rejoin());
19843
19844 masm.bind(&isArray);
19845 masm.move32(Imm32(1), output);
19846
19847 masm.bind(ool->rejoin());
19848}
19849
19850void CodeGenerator::visitIsArrayO(LIsArrayO* lir) {
19851 Register object = ToRegister(lir->object());
19852 Register output = ToRegister(lir->output());
19853
19854 using Fn = bool (*)(JSContext*, HandleObject, bool*);
19855 OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>(
19856 lir, ArgList(object), StoreRegisterTo(output));
19857 EmitObjectIsArray(masm, ool, object, output);
19858}
19859
19860void CodeGenerator::visitIsArrayV(LIsArrayV* lir) {
19861 ValueOperand val = ToValue(lir, LIsArrayV::ValueIndex);
19862 Register output = ToRegister(lir->output());
19863 Register temp = ToRegister(lir->temp0());
19864
19865 Label notArray;
19866 masm.fallibleUnboxObject(val, temp, &notArray);
19867
19868 using Fn = bool (*)(JSContext*, HandleObject, bool*);
19869 OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>(
19870 lir, ArgList(temp), StoreRegisterTo(output));
19871 EmitObjectIsArray(masm, ool, temp, output, &notArray);
19872}
19873
19874void CodeGenerator::visitIsTypedArray(LIsTypedArray* lir) {
19875 Register object = ToRegister(lir->object());
19876 Register output = ToRegister(lir->output());
19877
19878 OutOfLineCode* ool = nullptr;
19879 if (lir->mir()->isPossiblyWrapped()) {
19880 using Fn = bool (*)(JSContext*, JSObject*, bool*);
19881 ool = oolCallVM<Fn, jit::IsPossiblyWrappedTypedArray>(
19882 lir, ArgList(object), StoreRegisterTo(output));
19883 }
19884
19885 Label notTypedArray;
19886 Label done;
19887
19888 masm.loadObjClassUnsafe(object, output);
19889 masm.branchIfClassIsNotTypedArray(output, &notTypedArray);
19890
19891 masm.move32(Imm32(1), output);
19892 masm.jump(&done);
19893 masm.bind(&notTypedArray);
19894 if (ool) {
19895 Label notProxy;
19896 masm.branchTestClassIsProxy(false, output, &notProxy);
19897 masm.branchTestProxyHandlerFamily(Assembler::Equal, object, output,
19898 &Wrapper::family, ool->entry());
19899 masm.bind(&notProxy);
19900 }
19901 masm.move32(Imm32(0), output);
19902 masm.bind(&done);
19903 if (ool) {
19904 masm.bind(ool->rejoin());
19905 }
19906}
19907
19908void CodeGenerator::visitIsObject(LIsObject* ins) {
19909 Register output = ToRegister(ins->output());
19910 ValueOperand value = ToValue(ins, LIsObject::ObjectIndex);
19911 masm.testObjectSet(Assembler::Equal, value, output);
19912}
19913
19914void CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins) {
19915 ValueOperand value = ToValue(ins, LIsObjectAndBranch::Input);
19916
19917 MBasicBlock* ifTrue = ins->ifTrue();
19918 MBasicBlock* ifFalse = ins->ifFalse();
19919
19920 if (isNextBlock(ifFalse->lir())) {
19921 masm.branchTestObject(Assembler::Equal, value,
19922 getJumpLabelForBranch(ifTrue));
19923 } else {
19924 masm.branchTestObject(Assembler::NotEqual, value,
19925 getJumpLabelForBranch(ifFalse));
19926 jumpToBlock(ifTrue);
19927 }
19928}
19929
19930void CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins) {
19931 Register output = ToRegister(ins->output());
19932 ValueOperand value = ToValue(ins, LIsNullOrUndefined::InputIndex);
19933
19934 Label isNotNull, done;
19935 masm.branchTestNull(Assembler::NotEqual, value, &isNotNull);
19936
19937 masm.move32(Imm32(1), output);
19938 masm.jump(&done);
19939
19940 masm.bind(&isNotNull);
19941 masm.testUndefinedSet(Assembler::Equal, value, output);
19942
19943 masm.bind(&done);
19944}
19945
19946void CodeGenerator::visitIsNullOrUndefinedAndBranch(
19947 LIsNullOrUndefinedAndBranch* ins) {
19948 Label* ifTrue = getJumpLabelForBranch(ins->ifTrue());
19949 Label* ifFalse = getJumpLabelForBranch(ins->ifFalse());
19950 ValueOperand value = ToValue(ins, LIsNullOrUndefinedAndBranch::Input);
19951
19952 ScratchTagScope tag(masm, value);
19953 masm.splitTagForTest(value, tag);
19954
19955 masm.branchTestNull(Assembler::Equal, tag, ifTrue);
19956 masm.branchTestUndefined(Assembler::Equal, tag, ifTrue);
19957
19958 if (!isNextBlock(ins->ifFalse()->lir())) {
19959 masm.jump(ifFalse);
19960 }
19961}
19962
19963void CodeGenerator::loadOutermostJSScript(Register reg) {
19964 // The "outermost" JSScript means the script that we are compiling
19965 // basically; this is not always the script associated with the
19966 // current basic block, which might be an inlined script.
19967
19968 MIRGraph& graph = current->mir()->graph();
19969 MBasicBlock* entryBlock = graph.entryBlock();
19970 masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg);
19971}
19972
19973void CodeGenerator::loadJSScriptForBlock(MBasicBlock* block, Register reg) {
19974 // The current JSScript means the script for the current
19975 // basic block. This may be an inlined script.
19976
19977 JSScript* script = block->info().script();
19978 masm.movePtr(ImmGCPtr(script), reg);
19979}
19980
19981void CodeGenerator::visitHasClass(LHasClass* ins) {
19982 Register lhs = ToRegister(ins->lhs());
19983 Register output = ToRegister(ins->output());
19984
19985 masm.loadObjClassUnsafe(lhs, output);
19986 masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()),
19987 output);
19988}
19989
19990void CodeGenerator::visitGuardToClass(LGuardToClass* ins) {
19991 Register lhs = ToRegister(ins->lhs());
19992 Register temp = ToRegister(ins->temp0());
19993
19994 // branchTestObjClass may zero the object register on speculative paths
19995 // (we should have a defineReuseInput allocation in this case).
19996 Register spectreRegToZero = lhs;
19997
19998 Label notEqual;
19999
20000 masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(),
20001 temp, spectreRegToZero, &notEqual);
20002
20003 // Can't return null-return here, so bail.
20004 bailoutFrom(&notEqual, ins->snapshot());
20005}
20006
20007void CodeGenerator::visitGuardToEitherClass(LGuardToEitherClass* ins) {
20008 Register lhs = ToRegister(ins->lhs());
20009 Register temp = ToRegister(ins->temp0());
20010
20011 // branchTestObjClass may zero the object register on speculative paths
20012 // (we should have a defineReuseInput allocation in this case).
20013 Register spectreRegToZero = lhs;
20014
20015 Label notEqual;
20016
20017 masm.branchTestObjClass(Assembler::NotEqual, lhs,
20018 {ins->mir()->getClass1(), ins->mir()->getClass2()},
20019 temp, spectreRegToZero, &notEqual);
20020
20021 // Can't return null-return here, so bail.
20022 bailoutFrom(&notEqual, ins->snapshot());
20023}
20024
20025void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) {
20026 Register lhs = ToRegister(ins->lhs());
20027 Register temp = ToRegister(ins->temp0());
20028
20029 // branchTestObjClass may zero the object register on speculative paths
20030 // (we should have a defineReuseInput allocation in this case).
20031 Register spectreRegToZero = lhs;
20032
20033 Label notEqual;
20034
20035 masm.branchTestObjIsFunction(Assembler::NotEqual, lhs, temp, spectreRegToZero,
20036 &notEqual);
20037
20038 // Can't return null-return here, so bail.
20039 bailoutFrom(&notEqual, ins->snapshot());
20040}
20041
20042void CodeGenerator::visitObjectClassToString(LObjectClassToString* lir) {
20043 Register obj = ToRegister(lir->lhs());
20044 Register temp = ToRegister(lir->temp0());
20045
20046 using Fn = JSString* (*)(JSContext*, JSObject*);
20047 masm.setupAlignedABICall();
20048 masm.loadJSContext(temp);
20049 masm.passABIArg(temp);
20050 masm.passABIArg(obj);
20051 masm.callWithABI<Fn, js::ObjectClassToString>();
20052
20053 bailoutCmpPtr(Assembler::Equal, ReturnReg, ImmWord(0), lir->snapshot());
20054}
20055
20056void CodeGenerator::visitWasmParameter(LWasmParameter* lir) {}
20057
20058void CodeGenerator::visitWasmParameterI64(LWasmParameterI64* lir) {}
20059
20060void CodeGenerator::visitWasmReturn(LWasmReturn* lir) {
20061 // Don't emit a jump to the return label if this is the last block.
20062 if (current->mir() != *gen->graph().poBegin()) {
20063 masm.jump(&returnLabel_);
20064 }
20065}
20066
20067void CodeGenerator::visitWasmReturnI64(LWasmReturnI64* lir) {
20068 // Don't emit a jump to the return label if this is the last block.
20069 if (current->mir() != *gen->graph().poBegin()) {
20070 masm.jump(&returnLabel_);
20071 }
20072}
20073
20074void CodeGenerator::visitWasmReturnVoid(LWasmReturnVoid* lir) {
20075 // Don't emit a jump to the return label if this is the last block.
20076 if (current->mir() != *gen->graph().poBegin()) {
20077 masm.jump(&returnLabel_);
20078 }
20079}
20080
20081void CodeGenerator::emitAssertRangeI(MIRType type, const Range* r,
20082 Register input) {
20083 // Check the lower bound.
20084 if (r->hasInt32LowerBound() && r->lower() > INT32_MIN(-2147483647-1)) {
20085 Label success;
20086 if (type == MIRType::Int32 || type == MIRType::Boolean) {
20087 masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()),
20088 &success);
20089 } else {
20090 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"
, 20090); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 20090; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20091 masm.branchPtr(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()),
20092 &success);
20093 }
20094 masm.assumeUnreachable(
20095 "Integer input should be equal or higher than Lowerbound.");
20096 masm.bind(&success);
20097 }
20098
20099 // Check the upper bound.
20100 if (r->hasInt32UpperBound() && r->upper() < INT32_MAX(2147483647)) {
20101 Label success;
20102 if (type == MIRType::Int32 || type == MIRType::Boolean) {
20103 masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()),
20104 &success);
20105 } else {
20106 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"
, 20106); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr"
")"); do { *((volatile int*)__null) = 20106; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20107 masm.branchPtr(Assembler::LessThanOrEqual, input, Imm32(r->upper()),
20108 &success);
20109 }
20110 masm.assumeUnreachable(
20111 "Integer input should be lower or equal than Upperbound.");
20112 masm.bind(&success);
20113 }
20114
20115 // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and
20116 // r->exponent(), there's nothing to check, because if we ended up in the
20117 // integer range checking code, the value is already in an integer register
20118 // in the integer range.
20119}
20120
20121void CodeGenerator::emitAssertRangeD(const Range* r, FloatRegister input,
20122 FloatRegister temp) {
20123 // Check the lower bound.
20124 if (r->hasInt32LowerBound()) {
20125 Label success;
20126 masm.loadConstantDouble(r->lower(), temp);
20127 if (r->canBeNaN()) {
20128 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
20129 }
20130 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp,
20131 &success);
20132 masm.assumeUnreachable(
20133 "Double input should be equal or higher than Lowerbound.");
20134 masm.bind(&success);
20135 }
20136 // Check the upper bound.
20137 if (r->hasInt32UpperBound()) {
20138 Label success;
20139 masm.loadConstantDouble(r->upper(), temp);
20140 if (r->canBeNaN()) {
20141 masm.branchDouble(Assembler::DoubleUnordered, input, input, &success);
20142 }
20143 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success);
20144 masm.assumeUnreachable(
20145 "Double input should be lower or equal than Upperbound.");
20146 masm.bind(&success);
20147 }
20148
20149 // This code does not yet check r->canHaveFractionalPart(). This would require
20150 // new assembler interfaces to make rounding instructions available.
20151
20152 if (!r->canBeNegativeZero()) {
20153 Label success;
20154
20155 // First, test for being equal to 0.0, which also includes -0.0.
20156 masm.loadConstantDouble(0.0, temp);
20157 masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp,
20158 &success);
20159
20160 // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is
20161 // -Infinity instead of Infinity.
20162 masm.loadConstantDouble(1.0, temp);
20163 masm.divDouble(input, temp);
20164 masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success);
20165
20166 masm.assumeUnreachable("Input shouldn't be negative zero.");
20167
20168 masm.bind(&success);
20169 }
20170
20171 if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() &&
20172 r->exponent() < FloatingPoint<double>::kExponentBias) {
20173 // Check the bounds implied by the maximum exponent.
20174 Label exponentLoOk;
20175 masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp);
20176 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk);
20177 masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp,
20178 &exponentLoOk);
20179 masm.assumeUnreachable("Check for exponent failed.");
20180 masm.bind(&exponentLoOk);
20181
20182 Label exponentHiOk;
20183 masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp);
20184 masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk);
20185 masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp,
20186 &exponentHiOk);
20187 masm.assumeUnreachable("Check for exponent failed.");
20188 masm.bind(&exponentHiOk);
20189 } else if (!r->hasInt32Bounds() && !r->canBeNaN()) {
20190 // If we think the value can't be NaN, check that it isn't.
20191 Label notnan;
20192 masm.branchDouble(Assembler::DoubleOrdered, input, input, &notnan);
20193 masm.assumeUnreachable("Input shouldn't be NaN.");
20194 masm.bind(&notnan);
20195
20196 // If we think the value also can't be an infinity, check that it isn't.
20197 if (!r->canBeInfiniteOrNaN()) {
20198 Label notposinf;
20199 masm.loadConstantDouble(PositiveInfinity<double>(), temp);
20200 masm.branchDouble(Assembler::DoubleLessThan, input, temp, &notposinf);
20201 masm.assumeUnreachable("Input shouldn't be +Inf.");
20202 masm.bind(&notposinf);
20203
20204 Label notneginf;
20205 masm.loadConstantDouble(NegativeInfinity<double>(), temp);
20206 masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, &notneginf);
20207 masm.assumeUnreachable("Input shouldn't be -Inf.");
20208 masm.bind(&notneginf);
20209 }
20210 }
20211}
20212
20213void CodeGenerator::visitAssertClass(LAssertClass* ins) {
20214 Register obj = ToRegister(ins->input());
20215 Register temp = ToRegister(ins->getTemp(0));
20216
20217 Label success;
20218 if (ins->mir()->getClass() == &FunctionClass) {
20219 // Allow both possible function classes here.
20220 masm.branchTestObjIsFunctionNoSpectreMitigations(Assembler::Equal, obj,
20221 temp, &success);
20222 } else {
20223 masm.branchTestObjClassNoSpectreMitigations(
20224 Assembler::Equal, obj, ins->mir()->getClass(), temp, &success);
20225 }
20226 masm.assumeUnreachable("Wrong KnownClass during run-time");
20227 masm.bind(&success);
20228}
20229
20230void CodeGenerator::visitAssertShape(LAssertShape* ins) {
20231 Register obj = ToRegister(ins->input());
20232
20233 Label success;
20234 masm.branchTestObjShapeNoSpectreMitigations(Assembler::Equal, obj,
20235 ins->mir()->shape(), &success);
20236 masm.assumeUnreachable("Wrong Shape during run-time");
20237 masm.bind(&success);
20238}
20239
20240void CodeGenerator::visitAssertRangeI(LAssertRangeI* ins) {
20241 Register input = ToRegister(ins->input());
20242 const Range* r = ins->range();
20243
20244 emitAssertRangeI(ins->mir()->input()->type(), r, input);
20245}
20246
20247void CodeGenerator::visitAssertRangeD(LAssertRangeD* ins) {
20248 FloatRegister input = ToFloatRegister(ins->input());
20249 FloatRegister temp = ToFloatRegister(ins->temp());
20250 const Range* r = ins->range();
20251
20252 emitAssertRangeD(r, input, temp);
20253}
20254
20255void CodeGenerator::visitAssertRangeF(LAssertRangeF* ins) {
20256 FloatRegister input = ToFloatRegister(ins->input());
20257 FloatRegister temp = ToFloatRegister(ins->temp());
20258 FloatRegister temp2 = ToFloatRegister(ins->temp2());
20259
20260 const Range* r = ins->range();
20261
20262 masm.convertFloat32ToDouble(input, temp);
20263 emitAssertRangeD(r, temp, temp2);
20264}
20265
20266void CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) {
20267 const Range* r = ins->range();
20268 const ValueOperand value = ToValue(ins, LAssertRangeV::Input);
20269 Label done;
20270
20271 {
20272 ScratchTagScope tag(masm, value);
20273 masm.splitTagForTest(value, tag);
20274
20275 {
20276 Label isNotInt32;
20277 masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32);
20278 {
20279 ScratchTagScopeRelease _(&tag);
20280 Register unboxInt32 = ToTempUnboxRegister(ins->temp());
20281 Register input = masm.extractInt32(value, unboxInt32);
20282 emitAssertRangeI(MIRType::Int32, r, input);
20283 masm.jump(&done);
20284 }
20285 masm.bind(&isNotInt32);
20286 }
20287
20288 {
20289 Label isNotDouble;
20290 masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble);
20291 {
20292 ScratchTagScopeRelease _(&tag);
20293 FloatRegister input = ToFloatRegister(ins->floatTemp1());
20294 FloatRegister temp = ToFloatRegister(ins->floatTemp2());
20295 masm.unboxDouble(value, input);
20296 emitAssertRangeD(r, input, temp);
20297 masm.jump(&done);
20298 }
20299 masm.bind(&isNotDouble);
20300 }
20301 }
20302
20303 masm.assumeUnreachable("Incorrect range for Value.");
20304 masm.bind(&done);
20305}
20306
20307void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) {
20308 using Fn = bool (*)(JSContext*);
20309 OutOfLineCode* ool =
20310 oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing());
20311
20312 const void* interruptAddr = gen->runtime->addressOfInterruptBits();
20313 masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0),
20314 ool->entry());
20315 masm.bind(ool->rejoin());
20316}
20317
20318void CodeGenerator::visitOutOfLineResumableWasmTrap(
20319 OutOfLineResumableWasmTrap* ool) {
20320 LInstruction* lir = ool->lir();
20321 masm.wasmTrap(ool->trap(), ool->trapSiteDesc());
20322
20323 markSafepointAt(masm.currentOffset(), lir);
20324
20325 // Note that masm.framePushed() doesn't include the register dump area.
20326 // That will be taken into account when the StackMap is created from the
20327 // LSafepoint.
20328 lir->safepoint()->setFramePushedAtStackMapBase(ool->framePushed());
20329 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::Trap);
20330
20331 masm.jump(ool->rejoin());
20332}
20333
20334void CodeGenerator::visitOutOfLineAbortingWasmTrap(
20335 OutOfLineAbortingWasmTrap* ool) {
20336 masm.wasmTrap(ool->trap(), ool->trapSiteDesc());
20337}
20338
20339void CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir) {
20340 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"
, 20340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20340; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20341
20342 OutOfLineResumableWasmTrap* ool = new (alloc()) OutOfLineResumableWasmTrap(
20343 lir, masm.framePushed(), lir->mir()->trapSiteDesc(),
20344 wasm::Trap::CheckInterrupt);
20345 addOutOfLineCode(ool, lir->mir());
20346 masm.branch32(
20347 Assembler::NotEqual,
20348 Address(ToRegister(lir->instance()), wasm::Instance::offsetOfInterrupt()),
20349 Imm32(0), ool->entry());
20350 masm.bind(ool->rejoin());
20351}
20352
20353void CodeGenerator::visitWasmTrap(LWasmTrap* lir) {
20354 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"
, 20354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20354; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20355 const MWasmTrap* mir = lir->mir();
20356
20357 masm.wasmTrap(mir->trap(), mir->trapSiteDesc());
20358}
20359
20360void CodeGenerator::visitWasmTrapIfNull(LWasmTrapIfNull* lir) {
20361 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"
, 20361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20361; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20362 const MWasmTrapIfNull* mir = lir->mir();
20363 Label nonNull;
20364 Register ref = ToRegister(lir->ref());
20365
20366 masm.branchWasmAnyRefIsNull(false, ref, &nonNull);
20367 masm.wasmTrap(mir->trap(), mir->trapSiteDesc());
20368 masm.bind(&nonNull);
20369}
20370
20371void CodeGenerator::visitWasmRefIsSubtypeOfAbstract(
20372 LWasmRefIsSubtypeOfAbstract* ins) {
20373 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"
, 20373); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20373; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20374
20375 const MWasmRefIsSubtypeOfAbstract* mir = ins->mir();
20376 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"
, 20376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()"
")"); do { *((volatile int*)__null) = 20376; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20377
20378 Register ref = ToRegister(ins->ref());
20379 Register superSTV = Register::Invalid();
20380 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
20381 Register scratch2 = Register::Invalid();
20382 Register result = ToRegister(ins->output());
20383 Label onSuccess;
20384 Label onFail;
20385 Label join;
20386 masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(),
20387 &onSuccess, /*onSuccess=*/true, superSTV,
20388 scratch1, scratch2);
20389 masm.bind(&onFail);
20390 masm.xor32(result, result);
20391 masm.jump(&join);
20392 masm.bind(&onSuccess);
20393 masm.move32(Imm32(1), result);
20394 masm.bind(&join);
20395}
20396
20397void CodeGenerator::visitWasmRefIsSubtypeOfConcrete(
20398 LWasmRefIsSubtypeOfConcrete* ins) {
20399 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"
, 20399); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20399; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20400
20401 const MWasmRefIsSubtypeOfConcrete* mir = ins->mir();
20402 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"
, 20402); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()"
")"); do { *((volatile int*)__null) = 20402; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20403
20404 Register ref = ToRegister(ins->ref());
20405 Register superSTV = ToRegister(ins->superSTV());
20406 Register scratch1 = ToRegister(ins->temp0());
20407 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
20408 Register result = ToRegister(ins->output());
20409 Label onSuccess;
20410 Label join;
20411 masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(),
20412 &onSuccess, /*onSuccess=*/true, superSTV,
20413 scratch1, scratch2);
20414 masm.move32(Imm32(0), result);
20415 masm.jump(&join);
20416 masm.bind(&onSuccess);
20417 masm.move32(Imm32(1), result);
20418 masm.bind(&join);
20419}
20420
20421void CodeGenerator::visitWasmRefIsSubtypeOfAbstractAndBranch(
20422 LWasmRefIsSubtypeOfAbstractAndBranch* ins) {
20423 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"
, 20423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20423; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20424 Register ref = ToRegister(ins->ref());
20425 Register scratch1 = ToTempRegisterOrInvalid(ins->temp0());
20426 Label* onSuccess = getJumpLabelForBranch(ins->ifTrue());
20427 Label* onFail = getJumpLabelForBranch(ins->ifFalse());
20428 masm.branchWasmRefIsSubtype(
20429 ref, ins->sourceType(), ins->destType(), onSuccess, /*onSuccess=*/true,
20430 Register::Invalid(), scratch1, Register::Invalid());
20431 masm.jump(onFail);
20432}
20433
20434void CodeGenerator::visitWasmRefIsSubtypeOfConcreteAndBranch(
20435 LWasmRefIsSubtypeOfConcreteAndBranch* ins) {
20436 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"
, 20436); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20436; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20437 Register ref = ToRegister(ins->ref());
20438 Register superSTV = ToRegister(ins->superSTV());
20439 Register scratch1 = ToRegister(ins->temp0());
20440 Register scratch2 = ToTempRegisterOrInvalid(ins->temp1());
20441 Label* onSuccess = getJumpLabelForBranch(ins->ifTrue());
20442 Label* onFail = getJumpLabelForBranch(ins->ifFalse());
20443 masm.branchWasmRefIsSubtype(ref, ins->sourceType(), ins->destType(),
20444 onSuccess, /*onSuccess=*/true, superSTV, scratch1,
20445 scratch2);
20446 masm.jump(onFail);
20447}
20448
20449void CodeGenerator::callWasmStructAllocFun(
20450 LInstruction* lir, wasm::SymbolicAddress fun, Register typeDefData,
20451 Register output, const wasm::TrapSiteDesc& trapSiteDesc) {
20452 MOZ_ASSERT(fun == wasm::SymbolicAddress::StructNewIL_true ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun
== wasm::SymbolicAddress::StructNewIL_false || fun == wasm::
SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress
::StructNewOOL_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false
|| fun == wasm::SymbolicAddress::StructNewOOL_true || fun ==
wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
")"); do { *((volatile int*)__null) = 20455; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20453 fun == wasm::SymbolicAddress::StructNewIL_false ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun
== wasm::SymbolicAddress::StructNewIL_false || fun == wasm::
SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress
::StructNewOOL_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false
|| fun == wasm::SymbolicAddress::StructNewOOL_true || fun ==
wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
")"); do { *((volatile int*)__null) = 20455; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20454 fun == wasm::SymbolicAddress::StructNewOOL_true ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun
== wasm::SymbolicAddress::StructNewIL_false || fun == wasm::
SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress
::StructNewOOL_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false
|| fun == wasm::SymbolicAddress::StructNewOOL_true || fun ==
wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
")"); do { *((volatile int*)__null) = 20455; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20455 fun == wasm::SymbolicAddress::StructNewOOL_false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun
== wasm::SymbolicAddress::StructNewIL_false || fun == wasm::
SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress
::StructNewOOL_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false
|| fun == wasm::SymbolicAddress::StructNewOOL_true || fun ==
wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false"
")"); do { *((volatile int*)__null) = 20455; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20456 MOZ_ASSERT(wasm::SASigStructNewIL_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_true.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20457; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20457 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_true.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20457; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20458 MOZ_ASSERT(wasm::SASigStructNewIL_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_false.
failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20459); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20459; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20459 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_false.
failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20459); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20459; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20460 MOZ_ASSERT(wasm::SASigStructNewOOL_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_true.
failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20461); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20461; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20461 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_true.
failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20461); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20461; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20462 MOZ_ASSERT(wasm::SASigStructNewOOL_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_false
.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20463); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20463; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20463 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_false
.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20463); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20463; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20464
20465 masm.Push(InstanceReg);
20466 int32_t framePushedAfterInstance = masm.framePushed();
20467 saveLive(lir);
20468
20469 masm.setupWasmABICall();
20470 masm.passABIArg(InstanceReg);
20471 masm.passABIArg(typeDefData);
20472 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
20473 CodeOffset offset =
20474 masm.callWithABI(trapSiteDesc.bytecodeOffset, fun,
20475 mozilla::Some(instanceOffset), ABIType::General);
20476 masm.storeCallPointerResult(output);
20477
20478 markSafepointAt(offset.offset(), lir);
20479 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance);
20480 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall);
20481
20482 restoreLive(lir);
20483 masm.Pop(InstanceReg);
20484#if JS_CODEGEN_ARM64
20485 masm.syncStackPtr();
20486#endif
20487
20488 masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr,
20489 trapSiteDesc);
20490}
20491
20492// Out-of-line path to allocate wasm GC structs
20493class OutOfLineWasmNewStruct : public OutOfLineCodeBase<CodeGenerator> {
20494 LInstruction* lir_;
20495 wasm::SymbolicAddress fun_;
20496 Register typeDefData_;
20497 Register output_;
20498 wasm::TrapSiteDesc trapSiteDesc_;
20499
20500 public:
20501 OutOfLineWasmNewStruct(LInstruction* lir, wasm::SymbolicAddress fun,
20502 Register typeDefData, Register output,
20503 const wasm::TrapSiteDesc& trapSiteDesc)
20504 : lir_(lir),
20505 fun_(fun),
20506 typeDefData_(typeDefData),
20507 output_(output),
20508 trapSiteDesc_(trapSiteDesc) {}
20509
20510 void accept(CodeGenerator* codegen) override {
20511 codegen->visitOutOfLineWasmNewStruct(this);
20512 }
20513
20514 LInstruction* lir() const { return lir_; }
20515 wasm::SymbolicAddress fun() const { return fun_; }
20516 Register typeDefData() const { return typeDefData_; }
20517 Register output() const { return output_; }
20518 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
20519};
20520
20521void CodeGenerator::visitOutOfLineWasmNewStruct(OutOfLineWasmNewStruct* ool) {
20522 callWasmStructAllocFun(ool->lir(), ool->fun(), ool->typeDefData(),
20523 ool->output(), ool->trapSiteDesc());
20524 masm.jump(ool->rejoin());
20525}
20526
20527void CodeGenerator::visitWasmNewStructObject(LWasmNewStructObject* lir) {
20528 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"
, 20528); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20528; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20529
20530 MWasmNewStructObject* mir = lir->mir();
20531
20532 Register typeDefData = ToRegister(lir->typeDefData());
20533 Register output = ToRegister(lir->output());
20534
20535 if (mir->isOutline()) {
20536 wasm::SymbolicAddress fun = mir->zeroFields()
20537 ? wasm::SymbolicAddress::StructNewOOL_true
20538 : wasm::SymbolicAddress::StructNewOOL_false;
20539 callWasmStructAllocFun(lir, fun, typeDefData, output, mir->trapSiteDesc());
20540 } else {
20541 wasm::SymbolicAddress fun = mir->zeroFields()
20542 ? wasm::SymbolicAddress::StructNewIL_true
20543 : wasm::SymbolicAddress::StructNewIL_false;
20544
20545 Register instance = ToRegister(lir->instance());
20546 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"
, 20546); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg"
")"); do { *((volatile int*)__null) = 20546; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20547
20548 auto* ool = new (alloc()) OutOfLineWasmNewStruct(
20549 lir, fun, typeDefData, output, mir->trapSiteDesc());
20550 addOutOfLineCode(ool, lir->mir());
20551
20552 Register temp1 = ToRegister(lir->temp0());
20553 Register temp2 = ToRegister(lir->temp1());
20554 masm.wasmNewStructObject(instance, output, typeDefData, temp1, temp2,
20555 ool->entry(), mir->allocKind(), mir->zeroFields());
20556
20557 masm.bind(ool->rejoin());
20558 }
20559}
20560
20561void CodeGenerator::callWasmArrayAllocFun(
20562 LInstruction* lir, wasm::SymbolicAddress fun, Register numElements,
20563 Register typeDefData, Register output,
20564 const wasm::TrapSiteDesc& trapSiteDesc) {
20565 MOZ_ASSERT(fun == wasm::SymbolicAddress::ArrayNew_true ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::ArrayNew_true || fun ==
wasm::SymbolicAddress::ArrayNew_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
")"); do { *((volatile int*)__null) = 20566; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20566 fun == wasm::SymbolicAddress::ArrayNew_false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun == wasm::SymbolicAddress::ArrayNew_true || fun ==
wasm::SymbolicAddress::ArrayNew_false)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress
::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false"
")"); do { *((volatile int*)__null) = 20566; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20567 MOZ_ASSERT(wasm::SASigArrayNew_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_true.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20568; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20568 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_true.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20568; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20569 MOZ_ASSERT(wasm::SASigArrayNew_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_false.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20570; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
20570 wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode
::FailOnNullPtr)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_false.failureMode
== wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr"
")"); do { *((volatile int*)__null) = 20570; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20571
20572 masm.Push(InstanceReg);
20573 int32_t framePushedAfterInstance = masm.framePushed();
20574 saveLive(lir);
20575
20576 masm.setupWasmABICall();
20577 masm.passABIArg(InstanceReg);
20578 masm.passABIArg(numElements);
20579 masm.passABIArg(typeDefData);
20580 int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance;
20581 CodeOffset offset =
20582 masm.callWithABI(trapSiteDesc.bytecodeOffset, fun,
20583 mozilla::Some(instanceOffset), ABIType::General);
20584 masm.storeCallPointerResult(output);
20585
20586 markSafepointAt(offset.offset(), lir);
20587 lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance);
20588 lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall);
20589
20590 restoreLive(lir);
20591 masm.Pop(InstanceReg);
20592#if JS_CODEGEN_ARM64
20593 masm.syncStackPtr();
20594#endif
20595
20596 masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr,
20597 trapSiteDesc);
20598}
20599
20600// Out-of-line path to allocate wasm GC arrays
20601class OutOfLineWasmNewArray : public OutOfLineCodeBase<CodeGenerator> {
20602 LInstruction* lir_;
20603 wasm::SymbolicAddress fun_;
20604 Register numElementsReg_;
20605 mozilla::Maybe<uint32_t> numElements_;
20606 Register typeDefData_;
20607 Register output_;
20608 wasm::TrapSiteDesc trapSiteDesc_;
20609
20610 public:
20611 OutOfLineWasmNewArray(LInstruction* lir, wasm::SymbolicAddress fun,
20612 Register numElementsReg,
20613 mozilla::Maybe<uint32_t> numElements,
20614 Register typeDefData, Register output,
20615 const wasm::TrapSiteDesc& trapSiteDesc)
20616 : lir_(lir),
20617 fun_(fun),
20618 numElementsReg_(numElementsReg),
20619 numElements_(numElements),
20620 typeDefData_(typeDefData),
20621 output_(output),
20622 trapSiteDesc_(trapSiteDesc) {}
20623
20624 void accept(CodeGenerator* codegen) override {
20625 codegen->visitOutOfLineWasmNewArray(this);
20626 }
20627
20628 LInstruction* lir() const { return lir_; }
20629 wasm::SymbolicAddress fun() const { return fun_; }
20630 Register numElementsReg() const { return numElementsReg_; }
20631 mozilla::Maybe<uint32_t> numElements() const { return numElements_; }
20632 Register typeDefData() const { return typeDefData_; }
20633 Register output() const { return output_; }
20634 const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; }
20635};
20636
20637void CodeGenerator::visitOutOfLineWasmNewArray(OutOfLineWasmNewArray* ool) {
20638 if (ool->numElements().isSome()) {
20639 masm.move32(Imm32(ool->numElements().value()), ool->numElementsReg());
20640 }
20641 callWasmArrayAllocFun(ool->lir(), ool->fun(), ool->numElementsReg(),
20642 ool->typeDefData(), ool->output(), ool->trapSiteDesc());
20643 masm.jump(ool->rejoin());
20644}
20645
20646void CodeGenerator::visitWasmNewArrayObject(LWasmNewArrayObject* lir) {
20647 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"
, 20647); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 20647; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20648
20649 MWasmNewArrayObject* mir = lir->mir();
20650
20651 Register typeDefData = ToRegister(lir->typeDefData());
20652 Register output = ToRegister(lir->output());
20653 Register temp1 = ToRegister(lir->temp0());
20654 Register temp2 = ToRegister(lir->temp1());
20655
20656 wasm::SymbolicAddress fun = mir->zeroFields()
20657 ? wasm::SymbolicAddress::ArrayNew_true
20658 : wasm::SymbolicAddress::ArrayNew_false;
20659
20660 if (lir->numElements()->isConstant()) {
20661 // numElements is constant, so we can do optimized code generation.
20662 uint32_t numElements = lir->numElements()->toConstant()->toInt32();
20663 CheckedUint32 storageBytes =
20664 WasmArrayObject::calcStorageBytesChecked(mir->elemSize(), numElements);
20665 if (!storageBytes.isValid() ||
20666 storageBytes.value() > WasmArrayObject_MaxInlineBytes) {
20667 // Too much array data to store inline. Immediately perform an instance
20668 // call to handle the out-of-line storage (or the trap).
20669 masm.move32(Imm32(numElements), temp1);
20670 callWasmArrayAllocFun(lir, fun, temp1, typeDefData, output,
20671 mir->trapSiteDesc());
20672 } else {
20673 // storageBytes is small enough to be stored inline in WasmArrayObject.
20674 // Attempt a nursery allocation and fall back to an instance call if it
20675 // fails.
20676 Register instance = ToRegister(lir->instance());
20677 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"
, 20677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg"
")"); do { *((volatile int*)__null) = 20677; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20678
20679 auto ool = new (alloc())
20680 OutOfLineWasmNewArray(lir, fun, temp1, mozilla::Some(numElements),
20681 typeDefData, output, mir->trapSiteDesc());
20682 addOutOfLineCode(ool, lir->mir());
20683
20684 masm.wasmNewArrayObjectFixed(instance, output, typeDefData, temp1, temp2,
20685 ool->entry(), numElements,
20686 storageBytes.value(), mir->zeroFields());
20687
20688 masm.bind(ool->rejoin());
20689 }
20690 } else {
20691 // numElements is dynamic. Attempt a dynamic inline-storage nursery
20692 // allocation and fall back to an instance call if it fails.
20693 Register instance = ToRegister(lir->instance());
20694 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"
, 20694); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg"
")"); do { *((volatile int*)__null) = 20694; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20695 Register numElements = ToRegister(lir->numElements());
20696
20697 auto ool = new (alloc())
20698 OutOfLineWasmNewArray(lir, fun, numElements, mozilla::Nothing(),
20699 typeDefData, output, mir->trapSiteDesc());
20700 addOutOfLineCode(ool, lir->mir());
20701
20702 masm.wasmNewArrayObject(instance, output, numElements, typeDefData, temp1,
20703 ool->entry(), mir->elemSize(), mir->zeroFields());
20704
20705 masm.bind(ool->rejoin());
20706 }
20707}
20708
20709void CodeGenerator::visitWasmHeapReg(LWasmHeapReg* ins) {
20710#ifdef WASM_HAS_HEAPREG1
20711 masm.movePtr(HeapReg, ToRegister(ins->output()));
20712#else
20713 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 20713); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 20713; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
20714#endif
20715}
20716
20717void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) {
20718 const MWasmBoundsCheck* mir = ins->mir();
20719 Register ptr = ToRegister(ins->ptr());
20720 Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit());
20721 // When there are no spectre mitigations in place, branching out-of-line to
20722 // the trap is a big performance win, but with mitigations it's trickier. See
20723 // bug 1680243.
20724 if (JitOptions.spectreIndexMasking) {
20725 Label ok;
20726 masm.wasmBoundsCheck32(Assembler::Below, ptr, boundsCheckLimit, &ok);
20727 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
20728 masm.bind(&ok);
20729 } else {
20730 OutOfLineAbortingWasmTrap* ool = new (alloc())
20731 OutOfLineAbortingWasmTrap(mir->trapSiteDesc(), wasm::Trap::OutOfBounds);
20732 addOutOfLineCode(ool, mir);
20733 masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
20734 ool->entry());
20735 }
20736}
20737
20738void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) {
20739 const MWasmBoundsCheck* mir = ins->mir();
20740 Register64 ptr = ToRegister64(ins->ptr());
20741 Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit());
20742 // See above.
20743 if (JitOptions.spectreIndexMasking) {
20744 Label ok;
20745 masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok);
20746 masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc());
20747 masm.bind(&ok);
20748 } else {
20749 OutOfLineAbortingWasmTrap* ool = new (alloc())
20750 OutOfLineAbortingWasmTrap(mir->trapSiteDesc(), wasm::Trap::OutOfBounds);
20751 addOutOfLineCode(ool, mir);
20752 masm.wasmBoundsCheck64(Assembler::AboveOrEqual, ptr, boundsCheckLimit,
20753 ool->entry());
20754 }
20755}
20756
20757void CodeGenerator::visitWasmBoundsCheckRange32(LWasmBoundsCheckRange32* ins) {
20758 const MWasmBoundsCheckRange32* mir = ins->mir();
20759 Register index = ToRegister(ins->index());
20760 Register length = ToRegister(ins->length());
20761 Register limit = ToRegister(ins->limit());
20762 Register tmp = ToRegister(ins->temp0());
20763
20764 masm.wasmBoundsCheckRange32(index, length, limit, tmp, mir->trapSiteDesc());
20765}
20766
20767void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) {
20768 const MWasmAlignmentCheck* mir = ins->mir();
20769 Register ptr = ToRegister(ins->ptr());
20770 OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap(
20771 mir->trapSiteDesc(), wasm::Trap::UnalignedAccess);
20772 addOutOfLineCode(ool, mir);
20773 masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1),
20774 ool->entry());
20775}
20776
20777void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) {
20778 const MWasmAlignmentCheck* mir = ins->mir();
20779 Register64 ptr = ToRegister64(ins->ptr());
20780#ifdef JS_64BIT1
20781 Register r = ptr.reg;
20782#else
20783 Register r = ptr.low;
20784#endif
20785 OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap(
20786 mir->trapSiteDesc(), wasm::Trap::UnalignedAccess);
20787 addOutOfLineCode(ool, mir);
20788 masm.branchTestPtr(Assembler::NonZero, r, Imm32(mir->byteSize() - 1),
20789 ool->entry());
20790}
20791
20792void CodeGenerator::visitWasmLoadInstance(LWasmLoadInstance* ins) {
20793 switch (ins->mir()->type()) {
20794 case MIRType::WasmAnyRef:
20795 case MIRType::Pointer:
20796 masm.loadPtr(Address(ToRegister(ins->instance()), ins->mir()->offset()),
20797 ToRegister(ins->output()));
20798 break;
20799 case MIRType::Int32:
20800 masm.load32(Address(ToRegister(ins->instance()), ins->mir()->offset()),
20801 ToRegister(ins->output()));
20802 break;
20803 default:
20804 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"
, 20804); AnnotateMozCrashReason("MOZ_CRASH(" "MIRType not supported in WasmLoadInstance"
")"); do { *((volatile int*)__null) = 20804; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
20805 }
20806}
20807
20808void CodeGenerator::visitWasmLoadInstance64(LWasmLoadInstance64* ins) {
20809 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"
, 20809); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 20809; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
20810 masm.load64(Address(ToRegister(ins->instance()), ins->mir()->offset()),
20811 ToOutRegister64(ins));
20812}
20813
20814void CodeGenerator::incrementWarmUpCounter(AbsoluteAddress warmUpCount,
20815 JSScript* script, Register tmp) {
20816 // The code depends on the JitScript* not being discarded without also
20817 // invalidating Ion code. Assert this.
20818#ifdef DEBUG1
20819 Label ok;
20820 masm.movePtr(ImmGCPtr(script), tmp);
20821 masm.loadJitScript(tmp, tmp);
20822 masm.branchPtr(Assembler::Equal, tmp, ImmPtr(script->jitScript()), &ok);
20823 masm.assumeUnreachable("Didn't find JitScript?");
20824 masm.bind(&ok);
20825#endif
20826
20827 masm.load32(warmUpCount, tmp);
20828 masm.add32(Imm32(1), tmp);
20829 masm.store32(tmp, warmUpCount);
20830}
20831
20832void CodeGenerator::visitIncrementWarmUpCounter(LIncrementWarmUpCounter* ins) {
20833 Register tmp = ToRegister(ins->temp0());
20834
20835 AbsoluteAddress warmUpCount =
20836 AbsoluteAddress(ins->mir()->script()->jitScript())
20837 .offset(JitScript::offsetOfWarmUpCount());
20838 incrementWarmUpCounter(warmUpCount, ins->mir()->script(), tmp);
20839}
20840
20841void CodeGenerator::visitLexicalCheck(LLexicalCheck* ins) {
20842 ValueOperand inputValue = ToValue(ins, LLexicalCheck::InputIndex);
20843 Label bail;
20844 masm.branchTestMagicValue(Assembler::Equal, inputValue,
20845 JS_UNINITIALIZED_LEXICAL, &bail);
20846 bailoutFrom(&bail, ins->snapshot());
20847}
20848
20849void CodeGenerator::visitThrowRuntimeLexicalError(
20850 LThrowRuntimeLexicalError* ins) {
20851 pushArg(Imm32(ins->mir()->errorNumber()));
20852
20853 using Fn = bool (*)(JSContext*, unsigned);
20854 callVM<Fn, jit::ThrowRuntimeLexicalError>(ins);
20855}
20856
20857void CodeGenerator::visitThrowMsg(LThrowMsg* ins) {
20858 pushArg(Imm32(static_cast<int32_t>(ins->mir()->throwMsgKind())));
20859
20860 using Fn = bool (*)(JSContext*, unsigned);
20861 callVM<Fn, js::ThrowMsgOperation>(ins);
20862}
20863
20864void CodeGenerator::visitGlobalDeclInstantiation(
20865 LGlobalDeclInstantiation* ins) {
20866 pushArg(ImmPtr(ins->mir()->resumePoint()->pc()));
20867 pushArg(ImmGCPtr(ins->mir()->block()->info().script()));
20868
20869 using Fn = bool (*)(JSContext*, HandleScript, const jsbytecode*);
20870 callVM<Fn, GlobalDeclInstantiationFromIon>(ins);
20871}
20872
20873void CodeGenerator::visitDebugger(LDebugger* ins) {
20874 Register cx = ToRegister(ins->temp0());
20875
20876 masm.loadJSContext(cx);
20877 using Fn = bool (*)(JSContext* cx);
20878 masm.setupAlignedABICall();
20879 masm.passABIArg(cx);
20880 masm.callWithABI<Fn, GlobalHasLiveOnDebuggerStatement>();
20881
20882 Label bail;
20883 masm.branchIfTrueBool(ReturnReg, &bail);
20884 bailoutFrom(&bail, ins->snapshot());
20885}
20886
20887void CodeGenerator::visitNewTarget(LNewTarget* ins) {
20888 ValueOperand output = ToOutValue(ins);
20889
20890 // if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)]
20891 Label notConstructing, done;
20892 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
20893 masm.branchTestPtr(Assembler::Zero, calleeToken,
20894 Imm32(CalleeToken_FunctionConstructing), &notConstructing);
20895
20896 Register argvLen = output.scratchReg();
20897 masm.loadNumActualArgs(FramePointer, argvLen);
20898
20899 Label useNFormals;
20900
20901 size_t numFormalArgs = ins->mirRaw()->block()->info().nargs();
20902 masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs), &useNFormals);
20903
20904 size_t argsOffset = JitFrameLayout::offsetOfActualArgs();
20905 {
20906 BaseValueIndex newTarget(FramePointer, argvLen, argsOffset);
20907 masm.loadValue(newTarget, output);
20908 masm.jump(&done);
20909 }
20910
20911 masm.bind(&useNFormals);
20912
20913 {
20914 Address newTarget(FramePointer,
20915 argsOffset + (numFormalArgs * sizeof(Value)));
20916 masm.loadValue(newTarget, output);
20917 masm.jump(&done);
20918 }
20919
20920 // else output = undefined
20921 masm.bind(&notConstructing);
20922 masm.moveValue(UndefinedValue(), output);
20923 masm.bind(&done);
20924}
20925
20926void CodeGenerator::visitCheckReturn(LCheckReturn* ins) {
20927 ValueOperand returnValue = ToValue(ins, LCheckReturn::ReturnValueIndex);
20928 ValueOperand thisValue = ToValue(ins, LCheckReturn::ThisValueIndex);
20929 ValueOperand output = ToOutValue(ins);
20930
20931 using Fn = bool (*)(JSContext*, HandleValue);
20932 OutOfLineCode* ool = oolCallVM<Fn, ThrowBadDerivedReturnOrUninitializedThis>(
20933 ins, ArgList(returnValue), StoreNothing());
20934
20935 Label noChecks;
20936 masm.branchTestObject(Assembler::Equal, returnValue, &noChecks);
20937 masm.branchTestUndefined(Assembler::NotEqual, returnValue, ool->entry());
20938 masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry());
20939 masm.moveValue(thisValue, output);
20940 masm.jump(ool->rejoin());
20941 masm.bind(&noChecks);
20942 masm.moveValue(returnValue, output);
20943 masm.bind(ool->rejoin());
20944}
20945
20946void CodeGenerator::visitCheckIsObj(LCheckIsObj* ins) {
20947 ValueOperand value = ToValue(ins, LCheckIsObj::ValueIndex);
20948 Register output = ToRegister(ins->output());
20949
20950 using Fn = bool (*)(JSContext*, CheckIsObjectKind);
20951 OutOfLineCode* ool = oolCallVM<Fn, ThrowCheckIsObject>(
20952 ins, ArgList(Imm32(ins->mir()->checkKind())), StoreNothing());
20953
20954 masm.fallibleUnboxObject(value, output, ool->entry());
20955 masm.bind(ool->rejoin());
20956}
20957
20958void CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) {
20959 ValueOperand checkValue = ToValue(ins, LCheckObjCoercible::ValueIndex);
20960
20961 using Fn = bool (*)(JSContext*, HandleValue);
20962 OutOfLineCode* ool = oolCallVM<Fn, ThrowObjectCoercible>(
20963 ins, ArgList(checkValue), StoreNothing());
20964 masm.branchTestNull(Assembler::Equal, checkValue, ool->entry());
20965 masm.branchTestUndefined(Assembler::Equal, checkValue, ool->entry());
20966 masm.bind(ool->rejoin());
20967}
20968
20969void CodeGenerator::visitCheckClassHeritage(LCheckClassHeritage* ins) {
20970 ValueOperand heritage = ToValue(ins, LCheckClassHeritage::HeritageIndex);
20971 Register temp0 = ToRegister(ins->temp0());
20972 Register temp1 = ToRegister(ins->temp1());
20973
20974 using Fn = bool (*)(JSContext*, HandleValue);
20975 OutOfLineCode* ool = oolCallVM<Fn, CheckClassHeritageOperation>(
20976 ins, ArgList(heritage), StoreNothing());
20977
20978 masm.branchTestNull(Assembler::Equal, heritage, ool->rejoin());
20979 masm.fallibleUnboxObject(heritage, temp0, ool->entry());
20980
20981 masm.isConstructor(temp0, temp1, ool->entry());
20982 masm.branchTest32(Assembler::Zero, temp1, temp1, ool->entry());
20983
20984 masm.bind(ool->rejoin());
20985}
20986
20987void CodeGenerator::visitCheckThis(LCheckThis* ins) {
20988 ValueOperand thisValue = ToValue(ins, LCheckThis::ValueIndex);
20989
20990 using Fn = bool (*)(JSContext*);
20991 OutOfLineCode* ool =
20992 oolCallVM<Fn, ThrowUninitializedThis>(ins, ArgList(), StoreNothing());
20993 masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry());
20994 masm.bind(ool->rejoin());
20995}
20996
20997void CodeGenerator::visitCheckThisReinit(LCheckThisReinit* ins) {
20998 ValueOperand thisValue = ToValue(ins, LCheckThisReinit::ThisValueIndex);
20999
21000 using Fn = bool (*)(JSContext*);
21001 OutOfLineCode* ool =
21002 oolCallVM<Fn, ThrowInitializedThis>(ins, ArgList(), StoreNothing());
21003 masm.branchTestMagic(Assembler::NotEqual, thisValue, ool->entry());
21004 masm.bind(ool->rejoin());
21005}
21006
21007void CodeGenerator::visitGenerator(LGenerator* lir) {
21008 Register callee = ToRegister(lir->callee());
21009 Register environmentChain = ToRegister(lir->environmentChain());
21010 Register argsObject = ToRegister(lir->argsObject());
21011
21012 pushArg(argsObject);
21013 pushArg(environmentChain);
21014 pushArg(ImmGCPtr(current->mir()->info().script()));
21015 pushArg(callee);
21016
21017 using Fn = JSObject* (*)(JSContext* cx, HandleFunction, HandleScript,
21018 HandleObject, HandleObject);
21019 callVM<Fn, CreateGenerator>(lir);
21020}
21021
21022void CodeGenerator::visitAsyncResolve(LAsyncResolve* lir) {
21023 Register generator = ToRegister(lir->generator());
21024 ValueOperand value = ToValue(lir, LAsyncResolve::ValueIndex);
21025
21026 pushArg(value);
21027 pushArg(generator);
21028
21029 using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>,
21030 HandleValue);
21031 callVM<Fn, js::AsyncFunctionResolve>(lir);
21032}
21033
21034void CodeGenerator::visitAsyncReject(LAsyncReject* lir) {
21035 Register generator = ToRegister(lir->generator());
21036 ValueOperand reason = ToValue(lir, LAsyncReject::ReasonIndex);
21037 ValueOperand stack = ToValue(lir, LAsyncReject::StackIndex);
21038
21039 pushArg(stack);
21040 pushArg(reason);
21041 pushArg(generator);
21042
21043 using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>,
21044 HandleValue, HandleValue);
21045 callVM<Fn, js::AsyncFunctionReject>(lir);
21046}
21047
21048void CodeGenerator::visitAsyncAwait(LAsyncAwait* lir) {
21049 ValueOperand value = ToValue(lir, LAsyncAwait::ValueIndex);
21050 Register generator = ToRegister(lir->generator());
21051
21052 pushArg(value);
21053 pushArg(generator);
21054
21055 using Fn =
21056 JSObject* (*)(JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj,
21057 HandleValue value);
21058 callVM<Fn, js::AsyncFunctionAwait>(lir);
21059}
21060
21061void CodeGenerator::visitCanSkipAwait(LCanSkipAwait* lir) {
21062 ValueOperand value = ToValue(lir, LCanSkipAwait::ValueIndex);
21063
21064 pushArg(value);
21065
21066 using Fn = bool (*)(JSContext*, HandleValue, bool* canSkip);
21067 callVM<Fn, js::CanSkipAwait>(lir);
21068}
21069
21070void CodeGenerator::visitMaybeExtractAwaitValue(LMaybeExtractAwaitValue* lir) {
21071 ValueOperand value = ToValue(lir, LMaybeExtractAwaitValue::ValueIndex);
21072 ValueOperand output = ToOutValue(lir);
21073 Register canSkip = ToRegister(lir->canSkip());
21074
21075 Label cantExtract, finished;
21076 masm.branchIfFalseBool(canSkip, &cantExtract);
21077
21078 pushArg(value);
21079
21080 using Fn = bool (*)(JSContext*, HandleValue, MutableHandleValue);
21081 callVM<Fn, js::ExtractAwaitValue>(lir);
21082 masm.jump(&finished);
21083 masm.bind(&cantExtract);
21084
21085 masm.moveValue(value, output);
21086
21087 masm.bind(&finished);
21088}
21089
21090void CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) {
21091 ValueOperand checkValue = ToValue(ins, LDebugCheckSelfHosted::ValueIndex);
21092 pushArg(checkValue);
21093 using Fn = bool (*)(JSContext*, HandleValue);
21094 callVM<Fn, js::Debug_CheckSelfHosted>(ins);
21095}
21096
21097void CodeGenerator::visitRandom(LRandom* ins) {
21098 using mozilla::non_crypto::XorShift128PlusRNG;
21099
21100 FloatRegister output = ToFloatRegister(ins->output());
21101 Register rngReg = ToRegister(ins->temp0());
21102
21103 Register64 temp1 = ToRegister64(ins->temp1());
21104 Register64 temp2 = ToRegister64(ins->temp2());
21105
21106 const XorShift128PlusRNG* rng = gen->realm->addressOfRandomNumberGenerator();
21107 masm.movePtr(ImmPtr(rng), rngReg);
21108
21109 masm.randomDouble(rngReg, output, temp1, temp2);
21110 if (js::SupportDifferentialTesting()) {
21111 masm.loadConstantDouble(0.0, output);
21112 }
21113}
21114
21115void CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins) {
21116 Register input = ToRegister(ins->input());
21117 Register output = ToRegister(ins->output());
21118
21119 switch (ins->mir()->mode()) {
21120 case MSignExtendInt32::Byte:
21121 masm.move8SignExtend(input, output);
21122 break;
21123 case MSignExtendInt32::Half:
21124 masm.move16SignExtend(input, output);
21125 break;
21126 }
21127}
21128
21129void CodeGenerator::visitSignExtendIntPtr(LSignExtendIntPtr* ins) {
21130 Register input = ToRegister(ins->input());
21131 Register output = ToRegister(ins->output());
21132
21133 switch (ins->mir()->mode()) {
21134 case MSignExtendIntPtr::Byte:
21135 masm.move8SignExtendToPtr(input, output);
21136 break;
21137 case MSignExtendIntPtr::Half:
21138 masm.move16SignExtendToPtr(input, output);
21139 break;
21140 case MSignExtendIntPtr::Word:
21141 masm.move32SignExtendToPtr(input, output);
21142 break;
21143 }
21144}
21145
21146void CodeGenerator::visitRotate(LRotate* ins) {
21147 MRotate* mir = ins->mir();
21148 Register input = ToRegister(ins->input());
21149 Register dest = ToRegister(ins->output());
21150
21151 const LAllocation* count = ins->count();
21152 if (count->isConstant()) {
21153 int32_t c = ToInt32(count) & 0x1F;
21154 if (mir->isLeftRotate()) {
21155 masm.rotateLeft(Imm32(c), input, dest);
21156 } else {
21157 masm.rotateRight(Imm32(c), input, dest);
21158 }
21159 } else {
21160 Register creg = ToRegister(count);
21161 if (mir->isLeftRotate()) {
21162 masm.rotateLeft(creg, input, dest);
21163 } else {
21164 masm.rotateRight(creg, input, dest);
21165 }
21166 }
21167}
21168
21169void CodeGenerator::visitReinterpretCast(LReinterpretCast* lir) {
21170 MReinterpretCast* ins = lir->mir();
21171
21172 MIRType to = ins->type();
21173 mozilla::DebugOnly<MIRType> from = ins->input()->type();
21174
21175 switch (to) {
21176 case MIRType::Int32:
21177 MOZ_ASSERT(from == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(from == MIRType::Float32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(from == MIRType::Float32))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("from == MIRType::Float32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21177); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from == MIRType::Float32"
")"); do { *((volatile int*)__null) = 21177; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21178 masm.moveFloat32ToGPR(ToFloatRegister(lir->input()),
21179 ToRegister(lir->output()));
21180 break;
21181 case MIRType::Float32:
21182 MOZ_ASSERT(from == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(from == MIRType::Int32)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(from == MIRType::Int32))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("from == MIRType::Int32"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from == MIRType::Int32"
")"); do { *((volatile int*)__null) = 21182; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21183 masm.moveGPRToFloat32(ToRegister(lir->input()),
21184 ToFloatRegister(lir->output()));
21185 break;
21186 case MIRType::Double:
21187 case MIRType::Int64:
21188 MOZ_CRASH("not handled by this LIR opcode")do { do { } while (false); MOZ_ReportCrash("" "not handled by this LIR opcode"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21188); AnnotateMozCrashReason("MOZ_CRASH(" "not handled by this LIR opcode"
")"); do { *((volatile int*)__null) = 21188; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
21189 default:
21190 MOZ_CRASH("unexpected ReinterpretCast")do { do { } while (false); MOZ_ReportCrash("" "unexpected ReinterpretCast"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21190); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected ReinterpretCast"
")"); do { *((volatile int*)__null) = 21190; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
21191 }
21192}
21193
21194void CodeGenerator::visitReinterpretCastFromI64(LReinterpretCastFromI64* lir) {
21195 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"
, 21195); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double"
")"); do { *((volatile int*)__null) = 21195; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21196 MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->input()->type() == MIRType::Int64
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->input()->type() == MIRType::Int64
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->input()->type() == MIRType::Int64", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21196); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 21196; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21197 masm.moveGPR64ToDouble(ToRegister64(lir->input()),
21198 ToFloatRegister(lir->output()));
21199}
21200
21201void CodeGenerator::visitReinterpretCastToI64(LReinterpretCastToI64* lir) {
21202 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"
, 21202); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 21202; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21203 MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mir()->input()->type() == MIRType::Double
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(lir->mir()->input()->type() == MIRType::Double
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"lir->mir()->input()->type() == MIRType::Double", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 21203); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Double"
")"); do { *((volatile int*)__null) = 21203; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21204 masm.moveDoubleToGPR64(ToFloatRegister(lir->input()), ToOutRegister64(lir));
21205}
21206
21207class OutOfLineNaNToZero : public OutOfLineCodeBase<CodeGenerator> {
21208 LNaNToZero* lir_;
21209
21210 public:
21211 explicit OutOfLineNaNToZero(LNaNToZero* lir) : lir_(lir) {}
21212
21213 void accept(CodeGenerator* codegen) override {
21214 codegen->visitOutOfLineNaNToZero(this);
21215 }
21216 LNaNToZero* lir() const { return lir_; }
21217};
21218
21219void CodeGenerator::visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool) {
21220 FloatRegister output = ToFloatRegister(ool->lir()->output());
21221 masm.loadConstantDouble(0.0, output);
21222 masm.jump(ool->rejoin());
21223}
21224
21225void CodeGenerator::visitNaNToZero(LNaNToZero* lir) {
21226 FloatRegister input = ToFloatRegister(lir->input());
21227
21228 OutOfLineNaNToZero* ool = new (alloc()) OutOfLineNaNToZero(lir);
21229 addOutOfLineCode(ool, lir->mir());
21230
21231 if (lir->mir()->operandIsNeverNegativeZero()) {
21232 masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry());
21233 } else {
21234 FloatRegister scratch = ToFloatRegister(lir->temp0());
21235 masm.loadConstantDouble(0.0, scratch);
21236 masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch,
21237 ool->entry());
21238 }
21239 masm.bind(ool->rejoin());
21240}
21241
21242void CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) {
21243 Register obj = ToRegister(lir->object());
21244 Register output = ToRegister(lir->output());
21245 Register temp = ToRegister(lir->temp0());
21246
21247 masm.setIsPackedArray(obj, output, temp);
21248}
21249
21250void CodeGenerator::visitGuardArrayIsPacked(LGuardArrayIsPacked* lir) {
21251 Register array = ToRegister(lir->array());
21252 Register temp0 = ToRegister(lir->temp0());
21253 Register temp1 = ToRegister(lir->temp1());
21254
21255 Label bail;
21256 masm.branchArrayIsNotPacked(array, temp0, temp1, &bail);
21257 bailoutFrom(&bail, lir->snapshot());
21258}
21259
21260void CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) {
21261 Register target = ToRegister(lir->target());
21262 ValueOperand out = ToOutValue(lir);
21263 Register scratch = out.scratchReg();
21264
21265 using Fn = bool (*)(JSContext*, HandleObject, MutableHandleValue);
21266 OutOfLineCode* ool = oolCallVM<Fn, jit::GetPrototypeOf>(lir, ArgList(target),
21267 StoreValueTo(out));
21268
21269 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"
, 21269); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 21269; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21270
21271 masm.loadObjProto(target, scratch);
21272
21273 Label hasProto;
21274 masm.branchPtr(Assembler::Above, scratch, ImmWord(1), &hasProto);
21275
21276 // Call into the VM for lazy prototypes.
21277 masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), ool->entry());
21278
21279 masm.moveValue(NullValue(), out);
21280 masm.jump(ool->rejoin());
21281
21282 masm.bind(&hasProto);
21283 masm.tagValue(JSVAL_TYPE_OBJECT, scratch, out);
21284
21285 masm.bind(ool->rejoin());
21286}
21287
21288void CodeGenerator::visitObjectWithProto(LObjectWithProto* lir) {
21289 pushArg(ToValue(lir, LObjectWithProto::PrototypeIndex));
21290
21291 using Fn = PlainObject* (*)(JSContext*, HandleValue);
21292 callVM<Fn, js::ObjectWithProtoOperation>(lir);
21293}
21294
21295void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) {
21296 Register obj = ToRegister(lir->input());
21297 Register output = ToRegister(lir->output());
21298
21299 masm.loadObjProto(obj, output);
21300
21301#ifdef DEBUG1
21302 // We shouldn't encounter a null or lazy proto.
21303 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"
, 21303); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 21303; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21304
21305 Label done;
21306 masm.branchPtr(Assembler::Above, output, ImmWord(1), &done);
21307 masm.assumeUnreachable("Unexpected null or lazy proto in MObjectStaticProto");
21308 masm.bind(&done);
21309#endif
21310}
21311
21312void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) {
21313 pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind())));
21314
21315 using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind);
21316 callVM<Fn, js::BuiltinObjectOperation>(lir);
21317}
21318
21319void CodeGenerator::visitSuperFunction(LSuperFunction* lir) {
21320 Register callee = ToRegister(lir->callee());
21321 ValueOperand out = ToOutValue(lir);
21322 Register temp = ToRegister(lir->temp0());
21323
21324#ifdef DEBUG1
21325 Label classCheckDone;
21326 masm.branchTestObjIsFunction(Assembler::Equal, callee, temp, callee,
21327 &classCheckDone);
21328 masm.assumeUnreachable("Unexpected non-JSFunction callee in JSOp::SuperFun");
21329 masm.bind(&classCheckDone);
21330#endif
21331
21332 // Load prototype of callee
21333 masm.loadObjProto(callee, temp);
21334
21335#ifdef DEBUG1
21336 // We won't encounter a lazy proto, because |callee| is guaranteed to be a
21337 // JSFunction and only proxy objects can have a lazy proto.
21338 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"
, 21338); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 21338; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21339
21340 Label proxyCheckDone;
21341 masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone);
21342 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperFun");
21343 masm.bind(&proxyCheckDone);
21344#endif
21345
21346 Label nullProto, done;
21347 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
21348
21349 // Box prototype and return
21350 masm.tagValue(JSVAL_TYPE_OBJECT, temp, out);
21351 masm.jump(&done);
21352
21353 masm.bind(&nullProto);
21354 masm.moveValue(NullValue(), out);
21355
21356 masm.bind(&done);
21357}
21358
21359void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) {
21360 Register func = ToRegister(lir->function());
21361 ValueOperand homeObject = ToValue(lir, LInitHomeObject::HomeObjectIndex);
21362
21363 masm.assertFunctionIsExtended(func);
21364
21365 Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
21366
21367 emitPreBarrier(addr);
21368 masm.storeValue(homeObject, addr);
21369}
21370
21371void CodeGenerator::visitIsTypedArrayConstructor(
21372 LIsTypedArrayConstructor* lir) {
21373 Register object = ToRegister(lir->object());
21374 Register output = ToRegister(lir->output());
21375
21376 masm.setIsDefinitelyTypedArrayConstructor(object, output);
21377}
21378
21379void CodeGenerator::visitLoadValueTag(LLoadValueTag* lir) {
21380 ValueOperand value = ToValue(lir, LLoadValueTag::ValueIndex);
21381 Register output = ToRegister(lir->output());
21382
21383 Register tag = masm.extractTag(value, output);
21384 if (tag != output) {
21385 masm.mov(tag, output);
21386 }
21387}
21388
21389void CodeGenerator::visitGuardTagNotEqual(LGuardTagNotEqual* lir) {
21390 Register lhs = ToRegister(lir->lhs());
21391 Register rhs = ToRegister(lir->rhs());
21392
21393 bailoutCmp32(Assembler::Equal, lhs, rhs, lir->snapshot());
21394
21395 // If both lhs and rhs are numbers, can't use tag comparison to do inequality
21396 // comparison
21397 Label done;
21398 masm.branchTestNumber(Assembler::NotEqual, lhs, &done);
21399 masm.branchTestNumber(Assembler::NotEqual, rhs, &done);
21400 bailout(lir->snapshot());
21401
21402 masm.bind(&done);
21403}
21404
21405void CodeGenerator::visitLoadWrapperTarget(LLoadWrapperTarget* lir) {
21406 Register object = ToRegister(lir->object());
21407 Register output = ToRegister(lir->output());
21408
21409 masm.loadPtr(Address(object, ProxyObject::offsetOfReservedSlots()), output);
21410
21411 // Bail for revoked proxies.
21412 Label bail;
21413 Address targetAddr(output,
21414 js::detail::ProxyReservedSlots::offsetOfPrivateSlot());
21415 if (lir->mir()->fallible()) {
21416 masm.fallibleUnboxObject(targetAddr, output, &bail);
21417 bailoutFrom(&bail, lir->snapshot());
21418 } else {
21419 masm.unboxObject(targetAddr, output);
21420 }
21421}
21422
21423void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) {
21424 Register object = ToRegister(lir->object());
21425 Register temp0 = ToRegister(lir->temp0());
21426 Register temp1 = ToRegister(lir->temp1());
21427 Register temp2 = ToRegister(lir->temp2());
21428
21429 masm.movePropertyKey(lir->mir()->propId(), temp1);
21430 masm.movePtr(ImmGCPtr(lir->mir()->getterSetter()), temp2);
21431
21432 using Fn = bool (*)(JSContext* cx, JSObject* obj, jsid id,
21433 GetterSetter* getterSetter);
21434 masm.setupAlignedABICall();
21435 masm.loadJSContext(temp0);
21436 masm.passABIArg(temp0);
21437 masm.passABIArg(object);
21438 masm.passABIArg(temp1);
21439 masm.passABIArg(temp2);
21440 masm.callWithABI<Fn, ObjectHasGetterSetterPure>();
21441
21442 bailoutIfFalseBool(ReturnReg, lir->snapshot());
21443}
21444
21445void CodeGenerator::visitGuardIsExtensible(LGuardIsExtensible* lir) {
21446 Register object = ToRegister(lir->object());
21447 Register temp = ToRegister(lir->temp0());
21448
21449 Label bail;
21450 masm.branchIfObjectNotExtensible(object, temp, &bail);
21451 bailoutFrom(&bail, lir->snapshot());
21452}
21453
21454void CodeGenerator::visitGuardInt32IsNonNegative(
21455 LGuardInt32IsNonNegative* lir) {
21456 Register index = ToRegister(lir->index());
21457
21458 bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot());
21459}
21460
21461void CodeGenerator::visitGuardInt32Range(LGuardInt32Range* lir) {
21462 Register input = ToRegister(lir->input());
21463
21464 bailoutCmp32(Assembler::LessThan, input, Imm32(lir->mir()->minimum()),
21465 lir->snapshot());
21466 bailoutCmp32(Assembler::GreaterThan, input, Imm32(lir->mir()->maximum()),
21467 lir->snapshot());
21468}
21469
21470void CodeGenerator::visitGuardIndexIsNotDenseElement(
21471 LGuardIndexIsNotDenseElement* lir) {
21472 Register object = ToRegister(lir->object());
21473 Register index = ToRegister(lir->index());
21474 Register temp = ToRegister(lir->temp0());
21475 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
21476
21477 // Load obj->elements.
21478 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
21479
21480 // Ensure index >= initLength or the element is a hole.
21481 Label notDense;
21482 Address capacity(temp, ObjectElements::offsetOfInitializedLength());
21483 masm.spectreBoundsCheck32(index, capacity, spectreTemp, &notDense);
21484
21485 BaseValueIndex element(temp, index);
21486 masm.branchTestMagic(Assembler::Equal, element, &notDense);
21487
21488 bailout(lir->snapshot());
21489
21490 masm.bind(&notDense);
21491}
21492
21493void CodeGenerator::visitGuardIndexIsValidUpdateOrAdd(
21494 LGuardIndexIsValidUpdateOrAdd* lir) {
21495 Register object = ToRegister(lir->object());
21496 Register index = ToRegister(lir->index());
21497 Register temp = ToRegister(lir->temp0());
21498 Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1());
21499
21500 // Load obj->elements.
21501 masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
21502
21503 Label success;
21504
21505 // If length is writable, branch to &success. All indices are writable.
21506 Address flags(temp, ObjectElements::offsetOfFlags());
21507 masm.branchTest32(Assembler::Zero, flags,
21508 Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH),
21509 &success);
21510
21511 // Otherwise, ensure index is in bounds.
21512 Label bail;
21513 Address length(temp, ObjectElements::offsetOfLength());
21514 masm.spectreBoundsCheck32(index, length, spectreTemp, &bail);
21515 masm.bind(&success);
21516
21517 bailoutFrom(&bail, lir->snapshot());
21518}
21519
21520void CodeGenerator::visitCallAddOrUpdateSparseElement(
21521 LCallAddOrUpdateSparseElement* lir) {
21522 Register object = ToRegister(lir->object());
21523 Register index = ToRegister(lir->index());
21524 ValueOperand value = ToValue(lir, LCallAddOrUpdateSparseElement::ValueIndex);
21525
21526 pushArg(Imm32(lir->mir()->strict()));
21527 pushArg(value);
21528 pushArg(index);
21529 pushArg(object);
21530
21531 using Fn =
21532 bool (*)(JSContext*, Handle<NativeObject*>, int32_t, HandleValue, bool);
21533 callVM<Fn, js::AddOrUpdateSparseElementHelper>(lir);
21534}
21535
21536void CodeGenerator::visitCallGetSparseElement(LCallGetSparseElement* lir) {
21537 Register object = ToRegister(lir->object());
21538 Register index = ToRegister(lir->index());
21539
21540 pushArg(index);
21541 pushArg(object);
21542
21543 using Fn =
21544 bool (*)(JSContext*, Handle<NativeObject*>, int32_t, MutableHandleValue);
21545 callVM<Fn, js::GetSparseElementHelper>(lir);
21546}
21547
21548void CodeGenerator::visitCallNativeGetElement(LCallNativeGetElement* lir) {
21549 Register object = ToRegister(lir->object());
21550 Register index = ToRegister(lir->index());
21551
21552 pushArg(index);
21553 pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(object)));
21554 pushArg(object);
21555
21556 using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t,
21557 MutableHandleValue);
21558 callVM<Fn, js::NativeGetElement>(lir);
21559}
21560
21561void CodeGenerator::visitCallNativeGetElementSuper(
21562 LCallNativeGetElementSuper* lir) {
21563 Register object = ToRegister(lir->object());
21564 Register index = ToRegister(lir->index());
21565 ValueOperand receiver =
21566 ToValue(lir, LCallNativeGetElementSuper::ReceiverIndex);
21567
21568 pushArg(index);
21569 pushArg(receiver);
21570 pushArg(object);
21571
21572 using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t,
21573 MutableHandleValue);
21574 callVM<Fn, js::NativeGetElement>(lir);
21575}
21576
21577void CodeGenerator::visitCallObjectHasSparseElement(
21578 LCallObjectHasSparseElement* lir) {
21579 Register object = ToRegister(lir->object());
21580 Register index = ToRegister(lir->index());
21581 Register temp0 = ToRegister(lir->temp0());
21582 Register temp1 = ToRegister(lir->temp1());
21583 Register output = ToRegister(lir->output());
21584
21585 masm.reserveStack(sizeof(Value));
21586 masm.moveStackPtrTo(temp1);
21587
21588 using Fn = bool (*)(JSContext*, NativeObject*, int32_t, Value*);
21589 masm.setupAlignedABICall();
21590 masm.loadJSContext(temp0);
21591 masm.passABIArg(temp0);
21592 masm.passABIArg(object);
21593 masm.passABIArg(index);
21594 masm.passABIArg(temp1);
21595 masm.callWithABI<Fn, HasNativeElementPure>();
21596 masm.storeCallPointerResult(temp0);
21597
21598 Label bail, ok;
21599 uint32_t framePushed = masm.framePushed();
21600 masm.branchIfTrueBool(temp0, &ok);
21601 masm.adjustStack(sizeof(Value));
21602 masm.jump(&bail);
21603
21604 masm.bind(&ok);
21605 masm.setFramePushed(framePushed);
21606 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
21607 masm.adjustStack(sizeof(Value));
21608
21609 bailoutFrom(&bail, lir->snapshot());
21610}
21611
21612void CodeGenerator::visitBigIntAsIntN(LBigIntAsIntN* ins) {
21613 Register bits = ToRegister(ins->bits());
21614 Register input = ToRegister(ins->input());
21615
21616 pushArg(bits);
21617 pushArg(input);
21618
21619 using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t);
21620 callVM<Fn, jit::BigIntAsIntN>(ins);
21621}
21622
21623void CodeGenerator::visitBigIntAsUintN(LBigIntAsUintN* ins) {
21624 Register bits = ToRegister(ins->bits());
21625 Register input = ToRegister(ins->input());
21626
21627 pushArg(bits);
21628 pushArg(input);
21629
21630 using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t);
21631 callVM<Fn, jit::BigIntAsUintN>(ins);
21632}
21633
21634void CodeGenerator::visitGuardNonGCThing(LGuardNonGCThing* ins) {
21635 ValueOperand input = ToValue(ins, LGuardNonGCThing::InputIndex);
21636
21637 Label bail;
21638 masm.branchTestGCThing(Assembler::Equal, input, &bail);
21639 bailoutFrom(&bail, ins->snapshot());
21640}
21641
21642void CodeGenerator::visitToHashableNonGCThing(LToHashableNonGCThing* ins) {
21643 ValueOperand input = ToValue(ins, LToHashableNonGCThing::InputIndex);
21644 FloatRegister tempFloat = ToFloatRegister(ins->temp0());
21645 ValueOperand output = ToOutValue(ins);
21646
21647 masm.toHashableNonGCThing(input, output, tempFloat);
21648}
21649
21650void CodeGenerator::visitToHashableString(LToHashableString* ins) {
21651 Register input = ToRegister(ins->input());
21652 Register output = ToRegister(ins->output());
21653
21654 using Fn = JSAtom* (*)(JSContext*, JSString*);
21655 auto* ool = oolCallVM<Fn, js::AtomizeString>(ins, ArgList(input),
21656 StoreRegisterTo(output));
21657
21658 Label isAtom;
21659 masm.branchTest32(Assembler::NonZero,
21660 Address(input, JSString::offsetOfFlags()),
21661 Imm32(JSString::ATOM_BIT), &isAtom);
21662
21663 masm.tryFastAtomize(input, output, output, ool->entry());
21664 masm.jump(ool->rejoin());
21665 masm.bind(&isAtom);
21666 masm.movePtr(input, output);
21667 masm.bind(ool->rejoin());
21668}
21669
21670void CodeGenerator::visitToHashableValue(LToHashableValue* ins) {
21671 ValueOperand input = ToValue(ins, LToHashableValue::InputIndex);
21672 FloatRegister tempFloat = ToFloatRegister(ins->temp0());
21673 ValueOperand output = ToOutValue(ins);
21674
21675 Register str = output.scratchReg();
21676
21677 using Fn = JSAtom* (*)(JSContext*, JSString*);
21678 auto* ool =
21679 oolCallVM<Fn, js::AtomizeString>(ins, ArgList(str), StoreRegisterTo(str));
21680
21681 masm.toHashableValue(input, output, tempFloat, ool->entry(), ool->rejoin());
21682}
21683
21684void CodeGenerator::visitHashNonGCThing(LHashNonGCThing* ins) {
21685 ValueOperand input = ToValue(ins, LHashNonGCThing::InputIndex);
21686 Register temp = ToRegister(ins->temp0());
21687 Register output = ToRegister(ins->output());
21688
21689 masm.prepareHashNonGCThing(input, output, temp);
21690}
21691
21692void CodeGenerator::visitHashString(LHashString* ins) {
21693 Register input = ToRegister(ins->input());
21694 Register temp = ToRegister(ins->temp0());
21695 Register output = ToRegister(ins->output());
21696
21697 masm.prepareHashString(input, output, temp);
21698}
21699
21700void CodeGenerator::visitHashSymbol(LHashSymbol* ins) {
21701 Register input = ToRegister(ins->input());
21702 Register output = ToRegister(ins->output());
21703
21704 masm.prepareHashSymbol(input, output);
21705}
21706
21707void CodeGenerator::visitHashBigInt(LHashBigInt* ins) {
21708 Register input = ToRegister(ins->input());
21709 Register temp0 = ToRegister(ins->temp0());
21710 Register temp1 = ToRegister(ins->temp1());
21711 Register temp2 = ToRegister(ins->temp2());
21712 Register output = ToRegister(ins->output());
21713
21714 masm.prepareHashBigInt(input, output, temp0, temp1, temp2);
21715}
21716
21717void CodeGenerator::visitHashObject(LHashObject* ins) {
21718 Register setObj = ToRegister(ins->setObject());
21719 ValueOperand input = ToValue(ins, LHashObject::InputIndex);
21720 Register temp0 = ToRegister(ins->temp0());
21721 Register temp1 = ToRegister(ins->temp1());
21722 Register temp2 = ToRegister(ins->temp2());
21723 Register temp3 = ToRegister(ins->temp3());
21724 Register output = ToRegister(ins->output());
21725
21726 masm.prepareHashObject(setObj, input, output, temp0, temp1, temp2, temp3);
21727}
21728
21729void CodeGenerator::visitHashValue(LHashValue* ins) {
21730 Register setObj = ToRegister(ins->setObject());
21731 ValueOperand input = ToValue(ins, LHashValue::InputIndex);
21732 Register temp0 = ToRegister(ins->temp0());
21733 Register temp1 = ToRegister(ins->temp1());
21734 Register temp2 = ToRegister(ins->temp2());
21735 Register temp3 = ToRegister(ins->temp3());
21736 Register output = ToRegister(ins->output());
21737
21738 masm.prepareHashValue(setObj, input, output, temp0, temp1, temp2, temp3);
21739}
21740
21741void CodeGenerator::visitSetObjectHasNonBigInt(LSetObjectHasNonBigInt* ins) {
21742 Register setObj = ToRegister(ins->setObject());
21743 ValueOperand input = ToValue(ins, LSetObjectHasNonBigInt::InputIndex);
21744 Register hash = ToRegister(ins->hash());
21745 Register temp0 = ToRegister(ins->temp0());
21746 Register temp1 = ToRegister(ins->temp1());
21747 Register output = ToRegister(ins->output());
21748
21749 masm.setObjectHasNonBigInt(setObj, input, hash, output, temp0, temp1);
21750}
21751
21752void CodeGenerator::visitSetObjectHasBigInt(LSetObjectHasBigInt* ins) {
21753 Register setObj = ToRegister(ins->setObject());
21754 ValueOperand input = ToValue(ins, LSetObjectHasBigInt::InputIndex);
21755 Register hash = ToRegister(ins->hash());
21756 Register temp0 = ToRegister(ins->temp0());
21757 Register temp1 = ToRegister(ins->temp1());
21758 Register temp2 = ToRegister(ins->temp2());
21759 Register temp3 = ToRegister(ins->temp3());
21760 Register output = ToRegister(ins->output());
21761
21762 masm.setObjectHasBigInt(setObj, input, hash, output, temp0, temp1, temp2,
21763 temp3);
21764}
21765
21766void CodeGenerator::visitSetObjectHasValue(LSetObjectHasValue* ins) {
21767 Register setObj = ToRegister(ins->setObject());
21768 ValueOperand input = ToValue(ins, LSetObjectHasValue::InputIndex);
21769 Register hash = ToRegister(ins->hash());
21770 Register temp0 = ToRegister(ins->temp0());
21771 Register temp1 = ToRegister(ins->temp1());
21772 Register temp2 = ToRegister(ins->temp2());
21773 Register temp3 = ToRegister(ins->temp3());
21774 Register output = ToRegister(ins->output());
21775
21776 masm.setObjectHasValue(setObj, input, hash, output, temp0, temp1, temp2,
21777 temp3);
21778}
21779
21780void CodeGenerator::visitSetObjectHasValueVMCall(
21781 LSetObjectHasValueVMCall* ins) {
21782 pushArg(ToValue(ins, LSetObjectHasValueVMCall::InputIndex));
21783 pushArg(ToRegister(ins->setObject()));
21784
21785 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*);
21786 callVM<Fn, jit::SetObjectHas>(ins);
21787}
21788
21789void CodeGenerator::visitSetObjectDelete(LSetObjectDelete* ins) {
21790 pushArg(ToValue(ins, LSetObjectDelete::KeyIndex));
21791 pushArg(ToRegister(ins->setObject()));
21792 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*);
21793 callVM<Fn, jit::SetObjectDelete>(ins);
21794}
21795
21796void CodeGenerator::visitSetObjectAdd(LSetObjectAdd* ins) {
21797 pushArg(ToValue(ins, LSetObjectAdd::KeyIndex));
21798 pushArg(ToRegister(ins->setObject()));
21799 using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue);
21800 callVM<Fn, jit::SetObjectAdd>(ins);
21801}
21802
21803void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) {
21804 Register setObj = ToRegister(ins->setObject());
21805 Register output = ToRegister(ins->output());
21806
21807 masm.loadSetObjectSize(setObj, output);
21808}
21809
21810void CodeGenerator::visitMapObjectHasNonBigInt(LMapObjectHasNonBigInt* ins) {
21811 Register mapObj = ToRegister(ins->mapObject());
21812 ValueOperand input = ToValue(ins, LMapObjectHasNonBigInt::InputIndex);
21813 Register hash = ToRegister(ins->hash());
21814 Register temp0 = ToRegister(ins->temp0());
21815 Register temp1 = ToRegister(ins->temp1());
21816 Register output = ToRegister(ins->output());
21817
21818 masm.mapObjectHasNonBigInt(mapObj, input, hash, output, temp0, temp1);
21819}
21820
21821void CodeGenerator::visitMapObjectHasBigInt(LMapObjectHasBigInt* ins) {
21822 Register mapObj = ToRegister(ins->mapObject());
21823 ValueOperand input = ToValue(ins, LMapObjectHasBigInt::InputIndex);
21824 Register hash = ToRegister(ins->hash());
21825 Register temp0 = ToRegister(ins->temp0());
21826 Register temp1 = ToRegister(ins->temp1());
21827 Register temp2 = ToRegister(ins->temp2());
21828 Register temp3 = ToRegister(ins->temp3());
21829 Register output = ToRegister(ins->output());
21830
21831 masm.mapObjectHasBigInt(mapObj, input, hash, output, temp0, temp1, temp2,
21832 temp3);
21833}
21834
21835void CodeGenerator::visitMapObjectHasValue(LMapObjectHasValue* ins) {
21836 Register mapObj = ToRegister(ins->mapObject());
21837 ValueOperand input = ToValue(ins, LMapObjectHasValue::InputIndex);
21838 Register hash = ToRegister(ins->hash());
21839 Register temp0 = ToRegister(ins->temp0());
21840 Register temp1 = ToRegister(ins->temp1());
21841 Register temp2 = ToRegister(ins->temp2());
21842 Register temp3 = ToRegister(ins->temp3());
21843 Register output = ToRegister(ins->output());
21844
21845 masm.mapObjectHasValue(mapObj, input, hash, output, temp0, temp1, temp2,
21846 temp3);
21847}
21848
21849void CodeGenerator::visitMapObjectHasValueVMCall(
21850 LMapObjectHasValueVMCall* ins) {
21851 pushArg(ToValue(ins, LMapObjectHasValueVMCall::InputIndex));
21852 pushArg(ToRegister(ins->mapObject()));
21853
21854 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*);
21855 callVM<Fn, jit::MapObjectHas>(ins);
21856}
21857
21858void CodeGenerator::visitMapObjectGetNonBigInt(LMapObjectGetNonBigInt* ins) {
21859 Register mapObj = ToRegister(ins->mapObject());
21860 ValueOperand input = ToValue(ins, LMapObjectGetNonBigInt::InputIndex);
21861 Register hash = ToRegister(ins->hash());
21862 Register temp0 = ToRegister(ins->temp0());
21863 Register temp1 = ToRegister(ins->temp1());
21864 ValueOperand output = ToOutValue(ins);
21865
21866 masm.mapObjectGetNonBigInt(mapObj, input, hash, output, temp0, temp1,
21867 output.scratchReg());
21868}
21869
21870void CodeGenerator::visitMapObjectGetBigInt(LMapObjectGetBigInt* ins) {
21871 Register mapObj = ToRegister(ins->mapObject());
21872 ValueOperand input = ToValue(ins, LMapObjectGetBigInt::InputIndex);
21873 Register hash = ToRegister(ins->hash());
21874 Register temp0 = ToRegister(ins->temp0());
21875 Register temp1 = ToRegister(ins->temp1());
21876 Register temp2 = ToRegister(ins->temp2());
21877 Register temp3 = ToRegister(ins->temp3());
21878 ValueOperand output = ToOutValue(ins);
21879
21880 masm.mapObjectGetBigInt(mapObj, input, hash, output, temp0, temp1, temp2,
21881 temp3, output.scratchReg());
21882}
21883
21884void CodeGenerator::visitMapObjectGetValue(LMapObjectGetValue* ins) {
21885 Register mapObj = ToRegister(ins->map());
21886 ValueOperand input = ToValue(ins, LMapObjectGetValue::ValueIndex);
21887 Register hash = ToRegister(ins->hash());
21888 Register temp0 = ToRegister(ins->temp0());
21889 Register temp1 = ToRegister(ins->temp1());
21890 Register temp2 = ToRegister(ins->temp2());
21891 Register temp3 = ToRegister(ins->temp3());
21892 ValueOperand output = ToOutValue(ins);
21893
21894 masm.mapObjectGetValue(mapObj, input, hash, output, temp0, temp1, temp2,
21895 temp3, output.scratchReg());
21896}
21897
21898void CodeGenerator::visitMapObjectGetValueVMCall(
21899 LMapObjectGetValueVMCall* ins) {
21900 pushArg(ToValue(ins, LMapObjectGetValueVMCall::InputIndex));
21901 pushArg(ToRegister(ins->mapObject()));
21902
21903 using Fn =
21904 bool (*)(JSContext*, Handle<MapObject*>, HandleValue, MutableHandleValue);
21905 callVM<Fn, jit::MapObjectGet>(ins);
21906}
21907
21908void CodeGenerator::visitMapObjectDelete(LMapObjectDelete* ins) {
21909 pushArg(ToValue(ins, LMapObjectDelete::KeyIndex));
21910 pushArg(ToRegister(ins->mapObject()));
21911 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*);
21912 callVM<Fn, jit::MapObjectDelete>(ins);
21913}
21914
21915void CodeGenerator::visitMapObjectSet(LMapObjectSet* ins) {
21916 pushArg(ToValue(ins, LMapObjectSet::ValueIndex));
21917 pushArg(ToValue(ins, LMapObjectSet::KeyIndex));
21918 pushArg(ToRegister(ins->mapObject()));
21919 using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, HandleValue);
21920 callVM<Fn, jit::MapObjectSet>(ins);
21921}
21922
21923void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) {
21924 Register mapObj = ToRegister(ins->mapObject());
21925 Register output = ToRegister(ins->output());
21926
21927 masm.loadMapObjectSize(mapObj, output);
21928}
21929
21930void CodeGenerator::visitDateFillLocalTimeSlots(LDateFillLocalTimeSlots* ins) {
21931 Register date = ToRegister(ins->date());
21932 Register temp = ToRegister(ins->temp0());
21933
21934 masm.dateFillLocalTimeSlots(date, temp, liveVolatileRegs(ins));
21935}
21936
21937void CodeGenerator::visitDateHoursFromSecondsIntoYear(
21938 LDateHoursFromSecondsIntoYear* ins) {
21939 auto secondsIntoYear =
21940 ToValue(ins, LDateHoursFromSecondsIntoYear::SecondsIntoYearIndex);
21941 auto output = ToOutValue(ins);
21942 Register temp0 = ToRegister(ins->temp0());
21943 Register temp1 = ToRegister(ins->temp1());
21944
21945 masm.dateHoursFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
21946}
21947
21948void CodeGenerator::visitDateMinutesFromSecondsIntoYear(
21949 LDateMinutesFromSecondsIntoYear* ins) {
21950 auto secondsIntoYear =
21951 ToValue(ins, LDateMinutesFromSecondsIntoYear::SecondsIntoYearIndex);
21952 auto output = ToOutValue(ins);
21953 Register temp0 = ToRegister(ins->temp0());
21954 Register temp1 = ToRegister(ins->temp1());
21955
21956 masm.dateMinutesFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
21957}
21958
21959void CodeGenerator::visitDateSecondsFromSecondsIntoYear(
21960 LDateSecondsFromSecondsIntoYear* ins) {
21961 auto secondsIntoYear =
21962 ToValue(ins, LDateSecondsFromSecondsIntoYear::SecondsIntoYearIndex);
21963 auto output = ToOutValue(ins);
21964 Register temp0 = ToRegister(ins->temp0());
21965 Register temp1 = ToRegister(ins->temp1());
21966
21967 masm.dateSecondsFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1);
21968}
21969
21970template <size_t NumDefs>
21971void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) {
21972 wasm::JitCallStackArgVector stackArgs;
21973 masm.propagateOOM(stackArgs.reserve(lir->numOperands()));
21974 if (masm.oom()) {
21975 return;
21976 }
21977
21978 MIonToWasmCall* mir = lir->mir();
21979 const wasm::FuncExport& funcExport = mir->funcExport();
21980 const wasm::FuncType& sig =
21981 mir->instance()->code().codeMeta().getFuncType(funcExport.funcIndex());
21982
21983 WasmABIArgGenerator abi;
21984 for (size_t i = 0; i < lir->numOperands(); i++) {
21985 MIRType argMir;
21986 switch (sig.args()[i].kind()) {
21987 case wasm::ValType::I32:
21988 case wasm::ValType::I64:
21989 case wasm::ValType::F32:
21990 case wasm::ValType::F64:
21991 argMir = sig.args()[i].toMIRType();
21992 break;
21993 case wasm::ValType::V128:
21994 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"
, 21994); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected argument type when calling from ion to wasm"
")"); do { *((volatile int*)__null) = 21994; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
21995 case wasm::ValType::Ref:
21996 // temporarilyUnsupportedReftypeForEntry() restricts args to externref
21997 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"
, 21997); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "sig.args()[i].refType().isExtern()"
")"); do { *((volatile int*)__null) = 21997; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
21998 // Argument is boxed on the JS side to an anyref, so passed as a
21999 // pointer here.
22000 argMir = sig.args()[i].toMIRType();
22001 break;
22002 }
22003
22004 ABIArg arg = abi.next(argMir);
22005 switch (arg.kind()) {
22006 case ABIArg::GPR:
22007 case ABIArg::FPU: {
22008 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"
, 22008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToAnyRegister(lir->getOperand(i)) == arg.reg()"
")"); do { *((volatile int*)__null) = 22008; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22009 stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg());
22010 break;
22011 }
22012 case ABIArg::Stack: {
22013 const LAllocation* larg = lir->getOperand(i);
22014 if (larg->isConstant()) {
22015 stackArgs.infallibleEmplaceBack(ToInt32(larg));
22016 } else if (larg->isGeneralReg()) {
22017 stackArgs.infallibleEmplaceBack(ToRegister(larg));
22018 } else if (larg->isFloatReg()) {
22019 stackArgs.infallibleEmplaceBack(ToFloatRegister(larg));
22020 } else {
22021 // Always use the stack pointer here because GenerateDirectCallFromJit
22022 // depends on this.
22023 Address addr = ToAddress<BaseRegForAddress::SP>(larg);
22024 stackArgs.infallibleEmplaceBack(addr);
22025 }
22026 break;
22027 }
22028#ifdef JS_CODEGEN_REGISTER_PAIR
22029 case ABIArg::GPR_PAIR: {
22030 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"
, 22031); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls"
")"); do { *((volatile int*)__null) = 22031; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
22031 "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"
, 22031); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls"
")"); do { *((volatile int*)__null) = 22031; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
22032 }
22033#endif
22034 case ABIArg::Uninitialized: {
22035 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"
, 22035); AnnotateMozCrashReason("MOZ_CRASH(" "Uninitialized ABIArg kind"
")"); do { *((volatile int*)__null) = 22035; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
22036 }
22037 }
22038 }
22039
22040 const wasm::ValTypeVector& results = sig.results();
22041 if (results.length() == 0) {
22042 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"
, 22042); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value"
")"); do { *((volatile int*)__null) = 22042; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22043 } else {
22044 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"
, 22044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 1"
") (" "multi-value return unimplemented" ")"); do { *((volatile
int*)__null) = 22044; __attribute__((nomerge)) ::abort(); } while
(false); } } while (false)
;
22045 switch (results[0].kind()) {
22046 case wasm::ValType::I32:
22047 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"
, 22047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int32"
")"); do { *((volatile int*)__null) = 22047; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22048 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"
, 22048); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { *((volatile int*)__null) = 22048; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22049 break;
22050 case wasm::ValType::I64:
22051 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"
, 22051); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64"
")"); do { *((volatile int*)__null) = 22051; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22052 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"
, 22052); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutRegister64(lir) == ReturnReg64"
")"); do { *((volatile int*)__null) = 22052; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22053 break;
22054 case wasm::ValType::F32:
22055 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"
, 22055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32"
")"); do { *((volatile int*)__null) = 22055; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22056 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"
, 22056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg"
")"); do { *((volatile int*)__null) = 22056; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22057 break;
22058 case wasm::ValType::F64:
22059 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"
, 22059); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double"
")"); do { *((volatile int*)__null) = 22059; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22060 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"
, 22060); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg"
")"); do { *((volatile int*)__null) = 22060; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22061 break;
22062 case wasm::ValType::V128:
22063 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"
, 22063); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected return type when calling from ion to wasm"
")"); do { *((volatile int*)__null) = 22063; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
22064 case wasm::ValType::Ref:
22065 // The wasm stubs layer unboxes anything that needs to be unboxed
22066 // and leaves it in a Value. A FuncRef/EqRef we could in principle
22067 // leave it as a raw object pointer but for now it complicates the
22068 // API to do so.
22069 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"
, 22069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value"
")"); do { *((volatile int*)__null) = 22069; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22070 break;
22071 }
22072 }
22073
22074 WasmInstanceObject* instObj = lir->mir()->instanceObject();
22075
22076 Register scratch = ToRegister(lir->temp());
22077
22078 uint32_t callOffset;
22079 ensureOsiSpace();
22080 GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs,
22081 scratch, &callOffset);
22082
22083 // Add the instance object to the constant pool, so it is transferred to
22084 // the owning IonScript and so that it gets traced as long as the IonScript
22085 // lives.
22086
22087 uint32_t unused;
22088 masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused));
22089
22090 markSafepointAt(callOffset, lir);
22091}
22092
22093void CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) {
22094 emitIonToWasmCallBase(lir);
22095}
22096void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) {
22097 emitIonToWasmCallBase(lir);
22098}
22099void CodeGenerator::visitIonToWasmCallI64(LIonToWasmCallI64* lir) {
22100 emitIonToWasmCallBase(lir);
22101}
22102
22103void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) {
22104 masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output()));
22105}
22106
22107void CodeGenerator::visitWasmFence(LWasmFence* lir) {
22108 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"
, 22108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()"
")"); do { *((volatile int*)__null) = 22108; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22109 masm.memoryBarrier(MembarFull);
22110}
22111
22112void CodeGenerator::visitWasmAnyRefFromJSValue(LWasmAnyRefFromJSValue* lir) {
22113 ValueOperand input = ToValue(lir, LWasmAnyRefFromJSValue::InputIndex);
22114 Register output = ToRegister(lir->output());
22115 FloatRegister tempFloat = ToFloatRegister(lir->temp0());
22116
22117 using Fn = JSObject* (*)(JSContext* cx, HandleValue value);
22118 OutOfLineCode* oolBoxValue = oolCallVM<Fn, wasm::AnyRef::boxValue>(
22119 lir, ArgList(input), StoreRegisterTo(output));
22120 masm.convertValueToWasmAnyRef(input, output, tempFloat, oolBoxValue->entry());
22121 masm.bind(oolBoxValue->rejoin());
22122}
22123
22124void CodeGenerator::visitWasmAnyRefFromJSObject(LWasmAnyRefFromJSObject* lir) {
22125 Register input = ToRegister(lir->input());
22126 Register output = ToRegister(lir->output());
22127 masm.convertObjectToWasmAnyRef(input, output);
22128}
22129
22130void CodeGenerator::visitWasmAnyRefFromJSString(LWasmAnyRefFromJSString* lir) {
22131 Register input = ToRegister(lir->input());
22132 Register output = ToRegister(lir->output());
22133 masm.convertStringToWasmAnyRef(input, output);
22134}
22135
22136void CodeGenerator::visitWasmAnyRefIsJSString(LWasmAnyRefIsJSString* lir) {
22137 Register input = ToRegister(lir->input());
22138 Register output = ToRegister(lir->output());
22139 Register temp = ToRegister(lir->temp0());
22140 Label fallthrough;
22141 Label isJSString;
22142 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
22143 masm.move32(Imm32(0), output);
22144 masm.jump(&fallthrough);
22145 masm.bind(&isJSString);
22146 masm.move32(Imm32(1), output);
22147 masm.bind(&fallthrough);
22148}
22149
22150void CodeGenerator::visitWasmTrapIfAnyRefIsNotJSString(
22151 LWasmTrapIfAnyRefIsNotJSString* lir) {
22152 Register input = ToRegister(lir->input());
22153 Register temp = ToRegister(lir->temp0());
22154 Label isJSString;
22155 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
22156 masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc());
22157 masm.bind(&isJSString);
22158}
22159
22160void CodeGenerator::visitWasmAnyRefJSStringLength(
22161 LWasmAnyRefJSStringLength* lir) {
22162 Register input = ToRegister(lir->input());
22163 Register output = ToRegister(lir->output());
22164 Register temp = ToRegister(lir->temp0());
22165 Label isJSString;
22166 masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString);
22167 masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc());
22168 masm.bind(&isJSString);
22169 masm.untagWasmAnyRef(input, temp, wasm::AnyRefTag::String);
22170 masm.loadStringLength(temp, output);
22171}
22172
22173void CodeGenerator::visitWasmNewI31Ref(LWasmNewI31Ref* lir) {
22174 if (lir->value()->isConstant()) {
22175 // i31ref are often created with constants. If that's the case we will
22176 // do the operation statically here. This is similar to what is done
22177 // in masm.truncate32ToWasmI31Ref.
22178 Register output = ToRegister(lir->output());
22179 uint32_t value =
22180 static_cast<uint32_t>(lir->value()->toConstant()->toInt32());
22181 uintptr_t ptr = wasm::AnyRef::fromUint32Truncate(value).rawValue();
22182 masm.movePtr(ImmWord(ptr), output);
22183 } else {
22184 Register value = ToRegister(lir->value());
22185 Register output = ToRegister(lir->output());
22186 masm.truncate32ToWasmI31Ref(value, output);
22187 }
22188}
22189
22190void CodeGenerator::visitWasmI31RefGet(LWasmI31RefGet* lir) {
22191 Register value = ToRegister(lir->value());
22192 Register output = ToRegister(lir->output());
22193 if (lir->mir()->wideningOp() == wasm::FieldWideningOp::Signed) {
22194 masm.convertWasmI31RefTo32Signed(value, output);
22195 } else {
22196 masm.convertWasmI31RefTo32Unsigned(value, output);
22197 }
22198}
22199
22200#ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT1
22201void CodeGenerator::visitAddDisposableResource(LAddDisposableResource* lir) {
22202 Register environment = ToRegister(lir->environment());
22203 ValueOperand resource = ToValue(lir, LAddDisposableResource::ResourceIndex);
22204 ValueOperand method = ToValue(lir, LAddDisposableResource::MethodIndex);
22205 Register needsClosure = ToRegister(lir->needsClosure());
22206 uint8_t hint = lir->hint();
22207
22208 pushArg(Imm32(hint));
22209 pushArg(needsClosure);
22210 pushArg(method);
22211 pushArg(resource);
22212 pushArg(environment);
22213
22214 using Fn = bool (*)(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>,
22215 JS::Handle<JS::Value>, bool, UsingHint);
22216 callVM<Fn, js::AddDisposableResourceToCapability>(lir);
22217}
22218
22219void CodeGenerator::visitTakeDisposeCapability(LTakeDisposeCapability* lir) {
22220 Register environment = ToRegister(lir->environment());
22221 ValueOperand output = ToOutValue(lir);
22222
22223 Address capabilityAddr(
22224 environment, DisposableEnvironmentObject::offsetOfDisposeCapability());
22225 emitPreBarrier(capabilityAddr);
22226 masm.loadValue(capabilityAddr, output);
22227 masm.storeValue(JS::UndefinedValue(), capabilityAddr);
22228}
22229
22230void CodeGenerator::visitCreateSuppressedError(LCreateSuppressedError* lir) {
22231 ValueOperand error = ToValue(lir, LCreateSuppressedError::ErrorIndex);
22232 ValueOperand suppressed =
22233 ToValue(lir, LCreateSuppressedError::SuppressedIndex);
22234
22235 pushArg(suppressed);
22236 pushArg(error);
22237
22238 using Fn = ErrorObject* (*)(JSContext*, JS::Handle<JS::Value>,
22239 JS::Handle<JS::Value>);
22240 callVM<Fn, js::CreateSuppressedError>(lir);
22241}
22242#endif
22243
22244#ifdef FUZZING_JS_FUZZILLI
22245void CodeGenerator::emitFuzzilliHashObject(LInstruction* lir, Register obj,
22246 Register output) {
22247 using Fn = void (*)(JSContext* cx, JSObject* obj, uint32_t* out);
22248 OutOfLineCode* ool = oolCallVM<Fn, FuzzilliHashObjectInl>(
22249 lir, ArgList(obj), StoreRegisterTo(output));
22250
22251 masm.jump(ool->entry());
22252 masm.bind(ool->rejoin());
22253}
22254
22255void CodeGenerator::emitFuzzilliHashBigInt(LInstruction* lir, Register bigInt,
22256 Register output) {
22257 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
22258 volatileRegs.takeUnchecked(output);
22259
22260 masm.PushRegsInMask(volatileRegs);
22261
22262 using Fn = uint32_t (*)(BigInt* bigInt);
22263 masm.setupUnalignedABICall(output);
22264 masm.passABIArg(bigInt);
22265 masm.callWithABI<Fn, js::FuzzilliHashBigInt>();
22266 masm.storeCallInt32Result(output);
22267
22268 masm.PopRegsInMask(volatileRegs);
22269}
22270
22271void CodeGenerator::visitFuzzilliHashV(LFuzzilliHashV* ins) {
22272 ValueOperand value = ToValue(ins, 0);
22273
22274 FloatRegister scratchFloat = ToFloatRegister(ins->getTemp(1));
22275 Register scratch = ToRegister(ins->getTemp(0));
22276 Register output = ToRegister(ins->output());
22277 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"
, 22277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output"
")"); do { *((volatile int*)__null) = 22277; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22278
22279 Label hashDouble, done;
22280
22281 Label isInt32, isDouble, isNull, isUndefined, isBoolean, isBigInt, isObject;
22282 {
22283 ScratchTagScope tag(masm, value);
22284 masm.splitTagForTest(value, tag);
22285
22286 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
22287 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
22288 masm.branchTestNull(Assembler::Equal, tag, &isNull);
22289 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
22290 masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean);
22291 masm.branchTestBigInt(Assembler::Equal, tag, &isBigInt);
22292 masm.branchTestObject(Assembler::Equal, tag, &isObject);
22293
22294 // Symbol or String.
22295 masm.move32(Imm32(0), output);
22296 masm.jump(&done);
22297 }
22298
22299 masm.bind(&isInt32);
22300 {
22301 masm.unboxInt32(value, scratch);
22302 masm.convertInt32ToDouble(scratch, scratchFloat);
22303 masm.jump(&hashDouble);
22304 }
22305
22306 masm.bind(&isDouble);
22307 {
22308 masm.unboxDouble(value, scratchFloat);
22309 masm.jump(&hashDouble);
22310 }
22311
22312 masm.bind(&isNull);
22313 {
22314 masm.loadConstantDouble(1.0, scratchFloat);
22315 masm.jump(&hashDouble);
22316 }
22317
22318 masm.bind(&isUndefined);
22319 {
22320 masm.loadConstantDouble(2.0, scratchFloat);
22321 masm.jump(&hashDouble);
22322 }
22323
22324 masm.bind(&isBoolean);
22325 {
22326 masm.unboxBoolean(value, scratch);
22327 masm.add32(Imm32(3), scratch);
22328 masm.convertInt32ToDouble(scratch, scratchFloat);
22329 masm.jump(&hashDouble);
22330 }
22331
22332 masm.bind(&isBigInt);
22333 {
22334 masm.unboxBigInt(value, scratch);
22335 emitFuzzilliHashBigInt(ins, scratch, output);
22336 masm.jump(&done);
22337 }
22338
22339 masm.bind(&isObject);
22340 {
22341 masm.unboxObject(value, scratch);
22342 emitFuzzilliHashObject(ins, scratch, output);
22343 masm.jump(&done);
22344 }
22345
22346 masm.bind(&hashDouble);
22347 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
22348
22349 masm.bind(&done);
22350}
22351
22352void CodeGenerator::visitFuzzilliHashT(LFuzzilliHashT* ins) {
22353 const LAllocation* value = ins->value();
22354 MIRType mirType = ins->mir()->getOperand(0)->type();
22355
22356 Register scratch = ToTempRegisterOrInvalid(ins->getTemp(0));
22357 FloatRegister scratchFloat = ToTempFloatRegisterOrInvalid(ins->getTemp(1));
22358
22359 Register output = ToRegister(ins->output());
22360 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"
, 22360); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output"
")"); do { *((volatile int*)__null) = 22360; __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
22361
22362 switch (mirType) {
22363 case MIRType::Undefined: {
22364 masm.loadConstantDouble(2.0, scratchFloat);
22365 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
22366 break;
22367 }
22368
22369 case MIRType::Null: {
22370 masm.loadConstantDouble(1.0, scratchFloat);
22371 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
22372 break;
22373 }
22374
22375 case MIRType::Int32: {
22376 masm.move32(ToRegister(value), scratch);
22377 masm.convertInt32ToDouble(scratch, scratchFloat);
22378 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
22379 break;
22380 }
22381
22382 case MIRType::Double: {
22383 masm.moveDouble(ToFloatRegister(value), scratchFloat);
22384 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
22385 break;
22386 }
22387
22388 case MIRType::Float32: {
22389 masm.convertFloat32ToDouble(ToFloatRegister(value), scratchFloat);
22390 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
22391 break;
22392 }
22393
22394 case MIRType::Boolean: {
22395 masm.move32(ToRegister(value), scratch);
22396 masm.add32(Imm32(3), scratch);
22397 masm.convertInt32ToDouble(scratch, scratchFloat);
22398 masm.fuzzilliHashDouble(scratchFloat, output, scratch);
22399 break;
22400 }
22401
22402 case MIRType::BigInt: {
22403 emitFuzzilliHashBigInt(ins, ToRegister(value), output);
22404 break;
22405 }
22406
22407 case MIRType::Object: {
22408 emitFuzzilliHashObject(ins, ToRegister(value), output);
22409 break;
22410 }
22411
22412 default:
22413 MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 22413); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type"
")"); do { *((volatile int*)__null) = 22413; __attribute__((
nomerge)) ::abort(); } while (false); } while (false)
;
22414 }
22415}
22416
22417void CodeGenerator::visitFuzzilliHashStore(LFuzzilliHashStore* ins) {
22418 Register value = ToRegister(ins->value());
22419 Register temp0 = ToRegister(ins->getTemp(0));
22420 Register temp1 = ToRegister(ins->getTemp(1));
22421
22422 masm.fuzzilliStoreHash(value, temp0, temp1);
22423}
22424#endif
22425
22426static_assert(!std::is_polymorphic_v<CodeGenerator>,
22427 "CodeGenerator should not have any virtual methods");
22428
22429} // namespace jit
22430} // namespace js