Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp
Warning:line 12403, column 8
Value stored to 'extractObject' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_js_src_jit3.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/jit -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/jit -resource-dir /usr/lib/llvm-18/lib/clang/18 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/js/src/js-confdefs.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -I /var/lib/jenkins/workspace/firefox-scan-build/js/src/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/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-05-16-034744-15991-1 -x c++ Unified_cpp_js_src_jit3.cpp
1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7#include "jit/CodeGenerator.h"
8
9#include "mozilla/Assertions.h"
10#include "mozilla/Casting.h"
11#include "mozilla/DebugOnly.h"
12#include "mozilla/EndianUtils.h"
13#include "mozilla/EnumeratedArray.h"
14#include "mozilla/EnumeratedRange.h"
15#include "mozilla/EnumSet.h"
16#include "mozilla/IntegerTypeTraits.h"
17#include "mozilla/Latin1.h"
18#include "mozilla/MathAlgorithms.h"
19#include "mozilla/ScopeExit.h"
20#include "mozilla/SIMD.h"
21
22#include <limits>
23#include <type_traits>
24#include <utility>
25
26#include "jslibmath.h"
27#include "jsmath.h"
28#include "jsnum.h"
29
30#include "builtin/MapObject.h"
31#include "builtin/RegExp.h"
32#include "builtin/String.h"
33#include "irregexp/RegExpTypes.h"
34#include "jit/ABIArgGenerator.h"
35#include "jit/CompileInfo.h"
36#include "jit/InlineScriptTree.h"
37#include "jit/Invalidation.h"
38#include "jit/IonGenericCallStub.h"
39#include "jit/IonIC.h"
40#include "jit/IonScript.h"
41#include "jit/JitcodeMap.h"
42#include "jit/JitFrames.h"
43#include "jit/JitRuntime.h"
44#include "jit/JitSpewer.h"
45#include "jit/JitZone.h"
46#include "jit/Linker.h"
47#include "jit/MIRGenerator.h"
48#include "jit/MoveEmitter.h"
49#include "jit/RangeAnalysis.h"
50#include "jit/RegExpStubConstants.h"
51#include "jit/SafepointIndex.h"
52#include "jit/SharedICHelpers.h"
53#include "jit/SharedICRegisters.h"
54#include "jit/VMFunctions.h"
55#include "jit/WarpSnapshot.h"
56#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin
57#include "js/experimental/JitInfo.h" // JSJit{Getter,Setter}CallArgs, JSJitMethodCallArgsTraits, JSJitInfo
58#include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration
59#include "js/RegExpFlags.h" // JS::RegExpFlag
60#include "js/ScalarType.h" // js::Scalar::Type
61#include "proxy/DOMProxy.h"
62#include "proxy/ScriptedProxyHandler.h"
63#include "util/CheckedArithmetic.h"
64#include "util/Unicode.h"
65#include "vm/ArrayBufferViewObject.h"
66#include "vm/AsyncFunction.h"
67#include "vm/AsyncIteration.h"
68#include "vm/BuiltinObjectKind.h"
69#include "vm/FunctionFlags.h" // js::FunctionFlags
70#include "vm/Interpreter.h"
71#include "vm/JSAtomUtils.h" // AtomizeString
72#include "vm/MatchPairs.h"
73#include "vm/RegExpObject.h"
74#include "vm/RegExpStatics.h"
75#include "vm/StaticStrings.h"
76#include "vm/StringObject.h"
77#include "vm/StringType.h"
78#include "vm/TypedArrayObject.h"
79#include "wasm/WasmCodegenConstants.h"
80#include "wasm/WasmPI.h"
81#include "wasm/WasmValType.h"
82#ifdef MOZ_VTUNE1
83# include "vtune/VTuneWrapper.h"
84#endif
85#include "wasm/WasmBinary.h"
86#include "wasm/WasmGC.h"
87#include "wasm/WasmGcObject.h"
88#include "wasm/WasmStubs.h"
89
90#include "builtin/Boolean-inl.h"
91#include "jit/MacroAssembler-inl.h"
92#include "jit/shared/CodeGenerator-shared-inl.h"
93#include "jit/TemplateObject-inl.h"
94#include "jit/VMFunctionList-inl.h"
95#include "vm/JSScript-inl.h"
96#include "wasm/WasmInstance-inl.h"
97
98using namespace js;
99using namespace js::jit;
100
101using JS::GenericNaN;
102using mozilla::AssertedCast;
103using mozilla::CheckedUint32;
104using mozilla::DebugOnly;
105using mozilla::FloatingPoint;
106using mozilla::Maybe;
107using mozilla::NegativeInfinity;
108using mozilla::PositiveInfinity;
109
110using JS::ExpandoAndGeneration;
111
112namespace js {
113namespace jit {
114
115#ifdef CHECK_OSIPOINT_REGISTERS1
116template <class Op>
117static void HandleRegisterDump(Op op, MacroAssembler& masm,
118 LiveRegisterSet liveRegs, Register activation,
119 Register scratch) {
120 const size_t baseOffset = JitActivation::offsetOfRegs();
121
122 // Handle live GPRs.
123 for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) {
124 Register reg = *iter;
125 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
126
127 if (reg == activation) {
128 // To use the original value of the activation register (that's
129 // now on top of the stack), we need the scratch register.
130 masm.push(scratch);
131 masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch);
132 op(scratch, dump);
133 masm.pop(scratch);
134 } else {
135 op(reg, dump);
136 }
137 }
138
139 // Handle live FPRs.
140 for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) {
141 FloatRegister reg = *iter;
142 Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg));
143 op(reg, dump);
144 }
145}
146
147class StoreOp {
148 MacroAssembler& masm;
149
150 public:
151 explicit StoreOp(MacroAssembler& masm) : masm(masm) {}
152
153 void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); }
154 void operator()(FloatRegister reg, Address dump) {
155 if (reg.isDouble()) {
156 masm.storeDouble(reg, dump);
157 } else if (reg.isSingle()) {
158 masm.storeFloat32(reg, dump);
159 } else if (reg.isSimd128()) {
160 MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 160); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD"
")"); do { *((volatile int*)__null) = 160; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
161 } else {
162 MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 162); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type."
")"); do { *((volatile int*)__null) = 162; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
163 }
164 }
165};
166
167class VerifyOp {
168 MacroAssembler& masm;
169 Label* failure_;
170
171 public:
172 VerifyOp(MacroAssembler& masm, Label* failure)
173 : masm(masm), failure_(failure) {}
174
175 void operator()(Register reg, Address dump) {
176 masm.branchPtr(Assembler::NotEqual, dump, reg, failure_);
177 }
178 void operator()(FloatRegister reg, Address dump) {
179 if (reg.isDouble()) {
180 ScratchDoubleScope scratch(masm);
181 masm.loadDouble(dump, scratch);
182 masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_);
183 } else if (reg.isSingle()) {
184 ScratchFloat32Scope scratch(masm);
185 masm.loadFloat32(dump, scratch);
186 masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_);
187 } else if (reg.isSimd128()) {
188 MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 188); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD"
")"); do { *((volatile int*)__null) = 188; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
189 } else {
190 MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type."
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 190); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type."
")"); do { *((volatile int*)__null) = 190; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
191 }
192 }
193};
194
195void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) {
196 // Ensure the live registers stored by callVM did not change between
197 // the call and this OsiPoint. Try-catch relies on this invariant.
198
199 // Load pointer to the JitActivation in a scratch register.
200 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
201 Register scratch = allRegs.takeAny();
202 masm.push(scratch);
203 masm.loadJitActivation(scratch);
204
205 // If we should not check registers (because the instruction did not call
206 // into the VM, or a GC happened), we're done.
207 Label failure, done;
208 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
209 masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done);
210
211 // Having more than one VM function call made in one visit function at
212 // runtime is a sec-ciritcal error, because if we conservatively assume that
213 // one of the function call can re-enter Ion, then the invalidation process
214 // will potentially add a call at a random location, by patching the code
215 // before the return address.
216 masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure);
217
218 // Set checkRegs to 0, so that we don't try to verify registers after we
219 // return from this script to the caller.
220 masm.store32(Imm32(0), checkRegs);
221
222 // Ignore clobbered registers. Some instructions (like LValueToInt32) modify
223 // temps after calling into the VM. This is fine because no other
224 // instructions (including this OsiPoint) will depend on them. Also
225 // backtracking can also use the same register for an input and an output.
226 // These are marked as clobbered and shouldn't get checked.
227 LiveRegisterSet liveRegs;
228 liveRegs.set() = RegisterSet::Intersect(
229 safepoint->liveRegs().set(),
230 RegisterSet::Not(safepoint->clobberedRegs().set()));
231
232 VerifyOp op(masm, &failure);
233 HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny());
234
235 masm.jump(&done);
236
237 // Do not profile the callWithABI that occurs below. This is to avoid a
238 // rare corner case that occurs when profiling interacts with itself:
239 //
240 // When slow profiling assertions are turned on, FunctionBoundary ops
241 // (which update the profiler pseudo-stack) may emit a callVM, which
242 // forces them to have an osi point associated with them. The
243 // FunctionBoundary for inline function entry is added to the caller's
244 // graph with a PC from the caller's code, but during codegen it modifies
245 // Gecko Profiler instrumentation to add the callee as the current top-most
246 // script. When codegen gets to the OSIPoint, and the callWithABI below is
247 // emitted, the codegen thinks that the current frame is the callee, but
248 // the PC it's using from the OSIPoint refers to the caller. This causes
249 // the profiler instrumentation of the callWithABI below to ASSERT, since
250 // the script and pc are mismatched. To avoid this, we simply omit
251 // instrumentation for these callWithABIs.
252
253 // Any live register captured by a safepoint (other than temp registers)
254 // must remain unchanged between the call and the OsiPoint instruction.
255 masm.bind(&failure);
256 masm.assumeUnreachable("Modified registers between VM call and OsiPoint");
257
258 masm.bind(&done);
259 masm.pop(scratch);
260}
261
262bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) {
263 if (!checkOsiPointRegisters) {
264 return false;
265 }
266
267 if (safepoint->liveRegs().emptyGeneral() &&
268 safepoint->liveRegs().emptyFloat()) {
269 return false; // No registers to check.
270 }
271
272 return true;
273}
274
275void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) {
276 if (!shouldVerifyOsiPointRegs(safepoint)) {
277 return;
278 }
279
280 // Set checkRegs to 0. If we perform a VM call, the instruction
281 // will set it to 1.
282 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
283 Register scratch = allRegs.takeAny();
284 masm.push(scratch);
285 masm.loadJitActivation(scratch);
286 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
287 masm.store32(Imm32(0), checkRegs);
288 masm.pop(scratch);
289}
290
291static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) {
292 // Store a copy of all live registers before performing the call.
293 // When we reach the OsiPoint, we can use this to check nothing
294 // modified them in the meantime.
295
296 // Load pointer to the JitActivation in a scratch register.
297 AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All());
298 Register scratch = allRegs.takeAny();
299 masm.push(scratch);
300 masm.loadJitActivation(scratch);
301
302 Address checkRegs(scratch, JitActivation::offsetOfCheckRegs());
303 masm.add32(Imm32(1), checkRegs);
304
305 StoreOp op(masm);
306 HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny());
307
308 masm.pop(scratch);
309}
310#endif // CHECK_OSIPOINT_REGISTERS
311
312// Before doing any call to Cpp, you should ensure that volatile
313// registers are evicted by the register allocator.
314void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) {
315 TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id);
316 const VMFunctionData& fun = GetVMFunction(id);
317
318 // Stack is:
319 // ... frame ...
320 // [args]
321#ifdef DEBUG1
322 MOZ_ASSERT(pushedArgs_ == fun.explicitArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(pushedArgs_ == fun.explicitArgs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(pushedArgs_ == fun.explicitArgs
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"pushedArgs_ == fun.explicitArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 322); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs"
")"); do { *((volatile int*)__null) = 322; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
323 pushedArgs_ = 0;
324#endif
325
326#ifdef CHECK_OSIPOINT_REGISTERS1
327 if (shouldVerifyOsiPointRegs(ins->safepoint())) {
328 StoreAllLiveRegs(masm, ins->safepoint()->liveRegs());
329 }
330#endif
331
332#ifdef DEBUG1
333 if (ins->mirRaw()) {
334 MOZ_ASSERT(ins->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ins->mirRaw()->isInstruction())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ins->mirRaw()->isInstruction
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ins->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()"
")"); do { *((volatile int*)__null) = 334; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
335 MInstruction* mir = ins->mirRaw()->toInstruction();
336 MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint())do { if (mir->needsResumePoint()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(mir->resumePoint
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(mir->resumePoint()))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("mir->resumePoint()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 336); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()"
")"); do { *((volatile int*)__null) = 336; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
337
338 // If this MIR instruction has an overridden AliasSet, set the JitRuntime's
339 // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call
340 // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because
341 // interrupt callbacks can call JS (chrome JS or shell testing functions).
342 bool isWhitelisted = mir->isInterruptCheck() || mir->isCheckOverRecursed();
343 if (!mir->hasDefaultAliasSet() && !isWhitelisted) {
344 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
345 masm.move32(Imm32(1), ReturnReg);
346 masm.store32(ReturnReg, AbsoluteAddress(addr));
347 }
348 }
349#endif
350
351 // Push an exit frame descriptor.
352 masm.PushFrameDescriptor(FrameType::IonJS);
353
354 // Call the wrapper function. The wrapper is in charge to unwind the stack
355 // when returning from the call. Failures are handled with exceptions based
356 // on the return value of the C functions. To guard the outcome of the
357 // returned value, use another LIR instruction.
358 ensureOsiSpace();
359 uint32_t callOffset = masm.callJit(code);
360 markSafepointAt(callOffset, ins);
361
362#ifdef DEBUG1
363 // Reset the disallowArbitraryCode flag after the call.
364 {
365 const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode();
366 masm.push(ReturnReg);
367 masm.move32(Imm32(0), ReturnReg);
368 masm.store32(ReturnReg, AbsoluteAddress(addr));
369 masm.pop(ReturnReg);
370 }
371#endif
372
373 // Pop rest of the exit frame and the arguments left on the stack.
374 int framePop =
375 sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall();
376 masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop);
377
378 // Stack is:
379 // ... frame ...
380}
381
382template <typename Fn, Fn fn>
383void CodeGenerator::callVM(LInstruction* ins) {
384 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
385 callVMInternal(id, ins);
386}
387
388// ArgSeq store arguments for OutOfLineCallVM.
389//
390// OutOfLineCallVM are created with "oolCallVM" function. The third argument of
391// this function is an instance of a class which provides a "generate" in charge
392// of pushing the argument, with "pushArg", for a VMFunction.
393//
394// Such list of arguments can be created by using the "ArgList" function which
395// creates one instance of "ArgSeq", where the type of the arguments are
396// inferred from the type of the arguments.
397//
398// The list of arguments must be written in the same order as if you were
399// calling the function in C++.
400//
401// Example:
402// ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs()))
403
404template <typename... ArgTypes>
405class ArgSeq {
406 std::tuple<std::remove_reference_t<ArgTypes>...> args_;
407
408 template <std::size_t... ISeq>
409 inline void generate(CodeGenerator* codegen,
410 std::index_sequence<ISeq...>) const {
411 // Arguments are pushed in reverse order, from last argument to first
412 // argument.
413 (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...);
414 }
415
416 public:
417 explicit ArgSeq(ArgTypes&&... args)
418 : args_(std::forward<ArgTypes>(args)...) {}
419
420 inline void generate(CodeGenerator* codegen) const {
421 generate(codegen, std::index_sequence_for<ArgTypes...>{});
422 }
423
424#ifdef DEBUG1
425 static constexpr size_t numArgs = sizeof...(ArgTypes);
426#endif
427};
428
429template <typename... ArgTypes>
430inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) {
431 return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...);
432}
433
434// Store wrappers, to generate the right move of data after the VM call.
435
436struct StoreNothing {
437 inline void generate(CodeGenerator* codegen) const {}
438 inline LiveRegisterSet clobbered() const {
439 return LiveRegisterSet(); // No register gets clobbered
440 }
441};
442
443class StoreRegisterTo {
444 private:
445 Register out_;
446
447 public:
448 explicit StoreRegisterTo(Register out) : out_(out) {}
449
450 inline void generate(CodeGenerator* codegen) const {
451 // It's okay to use storePointerResultTo here - the VMFunction wrapper
452 // ensures the upper bytes are zero for bool/int32 return values.
453 codegen->storePointerResultTo(out_);
454 }
455 inline LiveRegisterSet clobbered() const {
456 LiveRegisterSet set;
457 set.add(out_);
458 return set;
459 }
460};
461
462class StoreFloatRegisterTo {
463 private:
464 FloatRegister out_;
465
466 public:
467 explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {}
468
469 inline void generate(CodeGenerator* codegen) const {
470 codegen->storeFloatResultTo(out_);
471 }
472 inline LiveRegisterSet clobbered() const {
473 LiveRegisterSet set;
474 set.add(out_);
475 return set;
476 }
477};
478
479template <typename Output>
480class StoreValueTo_ {
481 private:
482 Output out_;
483
484 public:
485 explicit StoreValueTo_(const Output& out) : out_(out) {}
486
487 inline void generate(CodeGenerator* codegen) const {
488 codegen->storeResultValueTo(out_);
489 }
490 inline LiveRegisterSet clobbered() const {
491 LiveRegisterSet set;
492 set.add(out_);
493 return set;
494 }
495};
496
497template <typename Output>
498StoreValueTo_<Output> StoreValueTo(const Output& out) {
499 return StoreValueTo_<Output>(out);
500}
501
502template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
503class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> {
504 private:
505 LInstruction* lir_;
506 ArgSeq args_;
507 StoreOutputTo out_;
508
509 public:
510 OutOfLineCallVM(LInstruction* lir, const ArgSeq& args,
511 const StoreOutputTo& out)
512 : lir_(lir), args_(args), out_(out) {}
513
514 void accept(CodeGenerator* codegen) override {
515 codegen->visitOutOfLineCallVM(this);
516 }
517
518 LInstruction* lir() const { return lir_; }
519 const ArgSeq& args() const { return args_; }
520 const StoreOutputTo& out() const { return out_; }
521};
522
523template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
524OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args,
525 const StoreOutputTo& out) {
526 MOZ_ASSERT(lir->mirRaw())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mirRaw())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mirRaw()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("lir->mirRaw()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 526); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()"
")"); do { *((volatile int*)__null) = 526; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
527 MOZ_ASSERT(lir->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mirRaw()->isInstruction())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(lir->mirRaw()->isInstruction
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("lir->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 527); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()"
")"); do { *((volatile int*)__null) = 527; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
528
529#ifdef DEBUG1
530 VMFunctionId id = VMFunctionToId<Fn, fn>::id;
531 const VMFunctionData& fun = GetVMFunction(id);
532 MOZ_ASSERT(fun.explicitArgs == args.numArgs)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun.explicitArgs == args.numArgs)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun.explicitArgs == args.numArgs
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"fun.explicitArgs == args.numArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs"
")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
533 MOZ_ASSERT(fun.returnsData() !=do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo
, StoreNothing>))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v
<StoreOutputTo, StoreNothing>)))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { *((volatile int*)__null) = 534; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
534 (std::is_same_v<StoreOutputTo, StoreNothing>))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo
, StoreNothing>))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v
<StoreOutputTo, StoreNothing>)))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)"
")"); do { *((volatile int*)__null) = 534; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
535#endif
536
537 OutOfLineCode* ool = new (alloc())
538 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out);
539 addOutOfLineCode(ool, lir->mirRaw()->toInstruction());
540 return ool;
541}
542
543template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo>
544void CodeGenerator::visitOutOfLineCallVM(
545 OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) {
546 LInstruction* lir = ool->lir();
547
548#ifdef JS_JITSPEW1
549 JitSpewStart(JitSpew_Codegen, " # LIR=%s",
550 lir->opName());
551 if (const char* extra = lir->getExtraName()) {
552 JitSpewCont(JitSpew_Codegen, ":%s", extra);
553 }
554 JitSpewFin(JitSpew_Codegen);
555#endif
556 perfSpewer_.recordInstruction(masm, lir);
557 saveLive(lir);
558 ool->args().generate(this);
559 callVM<Fn, fn>(lir);
560 ool->out().generate(this);
561 restoreLiveIgnore(lir, ool->out().clobbered());
562 masm.jump(ool->rejoin());
563}
564
565class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> {
566 private:
567 LInstruction* lir_;
568 size_t cacheIndex_;
569 size_t cacheInfoIndex_;
570
571 public:
572 OutOfLineICFallback(LInstruction* lir, size_t cacheIndex,
573 size_t cacheInfoIndex)
574 : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {}
575
576 void bind(MacroAssembler* masm) override {
577 // The binding of the initial jump is done in
578 // CodeGenerator::visitOutOfLineICFallback.
579 }
580
581 size_t cacheIndex() const { return cacheIndex_; }
582 size_t cacheInfoIndex() const { return cacheInfoIndex_; }
583 LInstruction* lir() const { return lir_; }
584
585 void accept(CodeGenerator* codegen) override {
586 codegen->visitOutOfLineICFallback(this);
587 }
588};
589
590void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) {
591 if (cacheIndex == SIZE_MAX(18446744073709551615UL)) {
592 masm.setOOM();
593 return;
594 }
595
596 DataPtr<IonIC> cache(this, cacheIndex);
597 MInstruction* mir = lir->mirRaw()->toInstruction();
598 cache->setScriptedLocation(mir->block()->info().script(),
599 mir->resumePoint()->pc());
600
601 Register temp = cache->scratchRegisterForEntryJump();
602 icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp);
603 masm.jump(Address(temp, 0));
604
605 MOZ_ASSERT(!icInfo_.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!icInfo_.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!icInfo_.empty()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!icInfo_.empty()"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!icInfo_.empty()"
")"); do { *((volatile int*)__null) = 605; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
606
607 OutOfLineICFallback* ool =
608 new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1);
609 addOutOfLineCode(ool, mir);
610
611 masm.bind(ool->rejoin());
612 cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset()));
613}
614
615void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) {
616 LInstruction* lir = ool->lir();
617 size_t cacheIndex = ool->cacheIndex();
618 size_t cacheInfoIndex = ool->cacheInfoIndex();
619
620 DataPtr<IonIC> ic(this, cacheIndex);
621
622 // Register the location of the OOL path in the IC.
623 ic->setFallbackOffset(CodeOffset(masm.currentOffset()));
624
625 switch (ic->kind()) {
626 case CacheKind::GetProp:
627 case CacheKind::GetElem: {
628 IonGetPropertyIC* getPropIC = ic->asGetPropertyIC();
629
630 saveLive(lir);
631
632 pushArg(getPropIC->id());
633 pushArg(getPropIC->value());
634 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
635 pushArg(ImmGCPtr(gen->outerInfo().script()));
636
637 using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*,
638 HandleValue, HandleValue, MutableHandleValue);
639 callVM<Fn, IonGetPropertyIC::update>(lir);
640
641 StoreValueTo(getPropIC->output()).generate(this);
642 restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered());
643
644 masm.jump(ool->rejoin());
645 return;
646 }
647 case CacheKind::GetPropSuper:
648 case CacheKind::GetElemSuper: {
649 IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC();
650
651 saveLive(lir);
652
653 pushArg(getPropSuperIC->id());
654 pushArg(getPropSuperIC->receiver());
655 pushArg(getPropSuperIC->object());
656 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
657 pushArg(ImmGCPtr(gen->outerInfo().script()));
658
659 using Fn =
660 bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject,
661 HandleValue, HandleValue, MutableHandleValue);
662 callVM<Fn, IonGetPropSuperIC::update>(lir);
663
664 StoreValueTo(getPropSuperIC->output()).generate(this);
665 restoreLiveIgnore(lir,
666 StoreValueTo(getPropSuperIC->output()).clobbered());
667
668 masm.jump(ool->rejoin());
669 return;
670 }
671 case CacheKind::SetProp:
672 case CacheKind::SetElem: {
673 IonSetPropertyIC* setPropIC = ic->asSetPropertyIC();
674
675 saveLive(lir);
676
677 pushArg(setPropIC->rhs());
678 pushArg(setPropIC->id());
679 pushArg(setPropIC->object());
680 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
681 pushArg(ImmGCPtr(gen->outerInfo().script()));
682
683 using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*,
684 HandleObject, HandleValue, HandleValue);
685 callVM<Fn, IonSetPropertyIC::update>(lir);
686
687 restoreLive(lir);
688
689 masm.jump(ool->rejoin());
690 return;
691 }
692 case CacheKind::GetName: {
693 IonGetNameIC* getNameIC = ic->asGetNameIC();
694
695 saveLive(lir);
696
697 pushArg(getNameIC->environment());
698 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
699 pushArg(ImmGCPtr(gen->outerInfo().script()));
700
701 using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject,
702 MutableHandleValue);
703 callVM<Fn, IonGetNameIC::update>(lir);
704
705 StoreValueTo(getNameIC->output()).generate(this);
706 restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered());
707
708 masm.jump(ool->rejoin());
709 return;
710 }
711 case CacheKind::BindName: {
712 IonBindNameIC* bindNameIC = ic->asBindNameIC();
713
714 saveLive(lir);
715
716 pushArg(bindNameIC->environment());
717 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
718 pushArg(ImmGCPtr(gen->outerInfo().script()));
719
720 using Fn =
721 JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject);
722 callVM<Fn, IonBindNameIC::update>(lir);
723
724 StoreRegisterTo(bindNameIC->output()).generate(this);
725 restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered());
726
727 masm.jump(ool->rejoin());
728 return;
729 }
730 case CacheKind::GetIterator: {
731 IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC();
732
733 saveLive(lir);
734
735 pushArg(getIteratorIC->value());
736 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
737 pushArg(ImmGCPtr(gen->outerInfo().script()));
738
739 using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*,
740 HandleValue);
741 callVM<Fn, IonGetIteratorIC::update>(lir);
742
743 StoreRegisterTo(getIteratorIC->output()).generate(this);
744 restoreLiveIgnore(lir,
745 StoreRegisterTo(getIteratorIC->output()).clobbered());
746
747 masm.jump(ool->rejoin());
748 return;
749 }
750 case CacheKind::OptimizeSpreadCall: {
751 auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC();
752
753 saveLive(lir);
754
755 pushArg(optimizeSpreadCallIC->value());
756 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
757 pushArg(ImmGCPtr(gen->outerInfo().script()));
758
759 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*,
760 HandleValue, MutableHandleValue);
761 callVM<Fn, IonOptimizeSpreadCallIC::update>(lir);
762
763 StoreValueTo(optimizeSpreadCallIC->output()).generate(this);
764 restoreLiveIgnore(
765 lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered());
766
767 masm.jump(ool->rejoin());
768 return;
769 }
770 case CacheKind::In: {
771 IonInIC* inIC = ic->asInIC();
772
773 saveLive(lir);
774
775 pushArg(inIC->object());
776 pushArg(inIC->key());
777 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
778 pushArg(ImmGCPtr(gen->outerInfo().script()));
779
780 using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue,
781 HandleObject, bool*);
782 callVM<Fn, IonInIC::update>(lir);
783
784 StoreRegisterTo(inIC->output()).generate(this);
785 restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered());
786
787 masm.jump(ool->rejoin());
788 return;
789 }
790 case CacheKind::HasOwn: {
791 IonHasOwnIC* hasOwnIC = ic->asHasOwnIC();
792
793 saveLive(lir);
794
795 pushArg(hasOwnIC->id());
796 pushArg(hasOwnIC->value());
797 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
798 pushArg(ImmGCPtr(gen->outerInfo().script()));
799
800 using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue,
801 HandleValue, int32_t*);
802 callVM<Fn, IonHasOwnIC::update>(lir);
803
804 StoreRegisterTo(hasOwnIC->output()).generate(this);
805 restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered());
806
807 masm.jump(ool->rejoin());
808 return;
809 }
810 case CacheKind::CheckPrivateField: {
811 IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC();
812
813 saveLive(lir);
814
815 pushArg(checkPrivateFieldIC->id());
816 pushArg(checkPrivateFieldIC->value());
817
818 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
819 pushArg(ImmGCPtr(gen->outerInfo().script()));
820
821 using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*,
822 HandleValue, HandleValue, bool*);
823 callVM<Fn, IonCheckPrivateFieldIC::update>(lir);
824
825 StoreRegisterTo(checkPrivateFieldIC->output()).generate(this);
826 restoreLiveIgnore(
827 lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered());
828
829 masm.jump(ool->rejoin());
830 return;
831 }
832 case CacheKind::InstanceOf: {
833 IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC();
834
835 saveLive(lir);
836
837 pushArg(hasInstanceOfIC->rhs());
838 pushArg(hasInstanceOfIC->lhs());
839 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
840 pushArg(ImmGCPtr(gen->outerInfo().script()));
841
842 using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*,
843 HandleValue lhs, HandleObject rhs, bool* res);
844 callVM<Fn, IonInstanceOfIC::update>(lir);
845
846 StoreRegisterTo(hasInstanceOfIC->output()).generate(this);
847 restoreLiveIgnore(lir,
848 StoreRegisterTo(hasInstanceOfIC->output()).clobbered());
849
850 masm.jump(ool->rejoin());
851 return;
852 }
853 case CacheKind::UnaryArith: {
854 IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC();
855
856 saveLive(lir);
857
858 pushArg(unaryArithIC->input());
859 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
860 pushArg(ImmGCPtr(gen->outerInfo().script()));
861
862 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
863 IonUnaryArithIC* stub, HandleValue val,
864 MutableHandleValue res);
865 callVM<Fn, IonUnaryArithIC::update>(lir);
866
867 StoreValueTo(unaryArithIC->output()).generate(this);
868 restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered());
869
870 masm.jump(ool->rejoin());
871 return;
872 }
873 case CacheKind::ToPropertyKey: {
874 IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC();
875
876 saveLive(lir);
877
878 pushArg(toPropertyKeyIC->input());
879 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
880 pushArg(ImmGCPtr(gen->outerInfo().script()));
881
882 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
883 IonToPropertyKeyIC* ic, HandleValue val,
884 MutableHandleValue res);
885 callVM<Fn, IonToPropertyKeyIC::update>(lir);
886
887 StoreValueTo(toPropertyKeyIC->output()).generate(this);
888 restoreLiveIgnore(lir,
889 StoreValueTo(toPropertyKeyIC->output()).clobbered());
890
891 masm.jump(ool->rejoin());
892 return;
893 }
894 case CacheKind::BinaryArith: {
895 IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC();
896
897 saveLive(lir);
898
899 pushArg(binaryArithIC->rhs());
900 pushArg(binaryArithIC->lhs());
901 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
902 pushArg(ImmGCPtr(gen->outerInfo().script()));
903
904 using Fn = bool (*)(JSContext* cx, HandleScript outerScript,
905 IonBinaryArithIC* stub, HandleValue lhs,
906 HandleValue rhs, MutableHandleValue res);
907 callVM<Fn, IonBinaryArithIC::update>(lir);
908
909 StoreValueTo(binaryArithIC->output()).generate(this);
910 restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered());
911
912 masm.jump(ool->rejoin());
913 return;
914 }
915 case CacheKind::Compare: {
916 IonCompareIC* compareIC = ic->asCompareIC();
917
918 saveLive(lir);
919
920 pushArg(compareIC->rhs());
921 pushArg(compareIC->lhs());
922 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
923 pushArg(ImmGCPtr(gen->outerInfo().script()));
924
925 using Fn =
926 bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub,
927 HandleValue lhs, HandleValue rhs, bool* res);
928 callVM<Fn, IonCompareIC::update>(lir);
929
930 StoreRegisterTo(compareIC->output()).generate(this);
931 restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered());
932
933 masm.jump(ool->rejoin());
934 return;
935 }
936 case CacheKind::CloseIter: {
937 IonCloseIterIC* closeIterIC = ic->asCloseIterIC();
938
939 saveLive(lir);
940
941 pushArg(closeIterIC->iter());
942 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
943 pushArg(ImmGCPtr(gen->outerInfo().script()));
944
945 using Fn =
946 bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject);
947 callVM<Fn, IonCloseIterIC::update>(lir);
948
949 restoreLive(lir);
950
951 masm.jump(ool->rejoin());
952 return;
953 }
954 case CacheKind::OptimizeGetIterator: {
955 auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC();
956
957 saveLive(lir);
958
959 pushArg(optimizeGetIteratorIC->value());
960 icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1));
961 pushArg(ImmGCPtr(gen->outerInfo().script()));
962
963 using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*,
964 HandleValue, bool* res);
965 callVM<Fn, IonOptimizeGetIteratorIC::update>(lir);
966
967 StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this);
968 restoreLiveIgnore(
969 lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered());
970
971 masm.jump(ool->rejoin());
972 return;
973 }
974 case CacheKind::Call:
975 case CacheKind::TypeOf:
976 case CacheKind::TypeOfEq:
977 case CacheKind::ToBool:
978 case CacheKind::GetIntrinsic:
979 case CacheKind::NewArray:
980 case CacheKind::NewObject:
981 MOZ_CRASH("Unsupported IC")do { do { } while (false); MOZ_ReportCrash("" "Unsupported IC"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 981); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported IC" ")"
); do { *((volatile int*)__null) = 981; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
982 }
983 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 983); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 983; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
;
984}
985
986StringObject* MNewStringObject::templateObj() const {
987 return &templateObj_->as<StringObject>();
988}
989
990CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph,
991 MacroAssembler* masm)
992 : CodeGeneratorSpecific(gen, graph, masm),
993 ionScriptLabels_(gen->alloc()),
994 ionNurseryObjectLabels_(gen->alloc()),
995 scriptCounts_(nullptr),
996 zoneStubsToReadBarrier_(0) {}
997
998CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); }
999
1000void CodeGenerator::visitValueToInt32(LValueToInt32* lir) {
1001 ValueOperand operand = ToValue(lir, LValueToInt32::Input);
1002 Register output = ToRegister(lir->output());
1003 FloatRegister temp = ToFloatRegister(lir->tempFloat());
1004
1005 Label fails;
1006 if (lir->mode() == LValueToInt32::TRUNCATE) {
1007 OutOfLineCode* oolDouble = oolTruncateDouble(temp, output, lir->mir());
1008
1009 // We can only handle strings in truncation contexts, like bitwise
1010 // operations.
1011 Register stringReg = ToRegister(lir->temp());
1012 using Fn = bool (*)(JSContext*, JSString*, double*);
1013 auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg),
1014 StoreFloatRegisterTo(temp));
1015 Label* stringEntry = oolString->entry();
1016 Label* stringRejoin = oolString->rejoin();
1017
1018 masm.truncateValueToInt32(operand, stringEntry, stringRejoin,
1019 oolDouble->entry(), stringReg, temp, output,
1020 &fails);
1021 masm.bind(oolDouble->rejoin());
1022 } else {
1023 MOZ_ASSERT(lir->mode() == LValueToInt32::NORMAL)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(lir->mode() == LValueToInt32::NORMAL)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(lir->mode() == LValueToInt32::NORMAL))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("lir->mode() == LValueToInt32::NORMAL"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp"
, 1023); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mode() == LValueToInt32::NORMAL"
")"); do { *((volatile int*)__null) = 1023; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1024 masm.convertValueToInt32(operand, temp, output, &fails,
1025 lir->mirNormal()->needsNegativeZeroCheck(),
1026 lir->mirNormal()->conversion());
1027 }
1028
1029 bailoutFrom(&fails, lir->snapshot());
1030}
1031
1032void CodeGenerator::visitValueToDouble(LValueToDouble* lir) {
1033 ValueOperand operand = ToValue(lir, LValueToDouble::InputIndex);
1034 FloatRegister output = ToFloatRegister(lir->output());
1035
1036 // Set if we can handle other primitives beside strings, as long as they're
1037 // guaranteed to never throw. This rules out symbols and BigInts, but allows
1038 // booleans, undefined, and null.
1039 bool hasNonStringPrimitives =
1040 lir->mir()->conversion() == MToFPInstruction::NonStringPrimitives;
1041
1042 Label isDouble, isInt32, isBool, isNull, isUndefined, done;
1043
1044 {
1045 ScratchTagScope tag(masm, operand);
1046 masm.splitTagForTest(operand, tag);
1047
1048 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
1049 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
1050
1051 if (hasNonStringPrimitives) {
1052 masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
1053 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
1054 masm.branchTestNull(Assembler::Equal, tag, &isNull);
1055 }
1056 }
1057
1058 bailout(lir->snapshot());
1059
1060 if (hasNonStringPrimitives) {
1061 masm.bind(&isNull);
1062 masm.loadConstantDouble(0.0, output);
1063 masm.jump(&done);
1064 }
1065
1066 if (hasNonStringPrimitives) {
1067 masm.bind(&isUndefined);
1068 masm.loadConstantDouble(GenericNaN(), output);
1069 masm.jump(&done);
1070 }
1071
1072 if (hasNonStringPrimitives) {
1073 masm.bind(&isBool);
1074 masm.boolValueToDouble(operand, output);
1075 masm.jump(&done);
1076 }
1077
1078 masm.bind(&isInt32);
1079 masm.int32ValueToDouble(operand, output);
1080 masm.jump(&done);
1081
1082 masm.bind(&isDouble);
1083 masm.unboxDouble(operand, output);
1084 masm.bind(&done);
1085}
1086
1087void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) {
1088 ValueOperand operand = ToValue(lir, LValueToFloat32::InputIndex);
1089 FloatRegister output = ToFloatRegister(lir->output());
1090
1091 // Set if we can handle other primitives beside strings, as long as they're
1092 // guaranteed to never throw. This rules out symbols and BigInts, but allows
1093 // booleans, undefined, and null.
1094 bool hasNonStringPrimitives =
1095 lir->mir()->conversion() == MToFPInstruction::NonStringPrimitives;
1096
1097 Label isDouble, isInt32, isBool, isNull, isUndefined, done;
1098
1099 {
1100 ScratchTagScope tag(masm, operand);
1101 masm.splitTagForTest(operand, tag);
1102
1103 masm.branchTestDouble(Assembler::Equal, tag, &isDouble);
1104 masm.branchTestInt32(Assembler::Equal, tag, &isInt32);
1105
1106 if (hasNonStringPrimitives) {
1107 masm.branchTestBoolean(Assembler::Equal, tag, &isBool);
1108 masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined);
1109 masm.branchTestNull(Assembler::Equal, tag, &isNull);
1110 }
1111 }
1112
1113 bailout(lir->snapshot());
1114
1115 if (hasNonStringPrimitives) {
1116 masm.bind(&isNull);
1117 masm.loadConstantFloat32(0.0f, output);
1118 masm.jump(&done);
1119 }
1120
1121 if (hasNonStringPrimitives) {
1122 masm.bind(&isUndefined);
1123 masm.loadConstantFloat32(float(GenericNaN()), output);
1124 masm.jump(&done);
1125 }
1126
1127 if (hasNonStringPrimitives) {
1128 masm.bind(&isBool);
1129 masm.boolValueToFloat32(operand, output);
1130 masm.jump(&done);
1131 }
1132
1133 masm.bind(&isInt32);
1134 masm.int32ValueToFloat32(operand, output);
1135 masm.jump(&done);
1136
1137 masm.bind(&isDouble);
1138 // ARM and MIPS may not have a double register available if we've
1139 // allocated output as a float32.
1140#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
1141 ScratchDoubleScope fpscratch(masm);
1142 masm.unboxDouble(operand, fpscratch);
1143 masm.convertDoubleToFloat32(fpscratch, output);
1144#else
1145 masm.unboxDouble(operand, output);
1146 masm.convertDoubleToFloat32(output, output);
1147#endif
1148 masm.bind(&done);
1149}
1150
1151void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) {
1152 ValueOperand operand = ToValue(lir, LValueToBigInt::InputIndex);
1153 Register output = ToRegister(lir->output());
1154
1155 using Fn = BigInt* (*)(JSContext*, HandleValue);
1156 auto* ool =
1157 oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output));
1158
1159 Register tag = masm.extractTag(operand, output);
1160
1161 Label notBigInt, done;
1162 masm.branchTestBigInt(Assembler::NotEqual, tag, &notBigInt);
1163 masm.unboxBigInt(operand, output);
1164 masm.jump(&done);
1165 masm.bind(&notBigInt);
1166
1167 masm.branchTestBoolean(Assembler::Equal, tag, ool->entry());
1168 masm.branchTestString(Assembler::Equal, tag, ool->entry());
1169
1170 // ToBigInt(object) can have side-effects; all other types throw a TypeError.
1171 bailout(lir->snapshot());
1172
1173 masm.bind(ool->rejoin());
1174 masm.bind(&done);
1175}
1176
1177void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) {
1178 masm.convertInt32ToDouble(ToRegister(lir->input()),
1179 ToFloatRegister(lir->output()));
1180}
1181
1182void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) {
1183 masm.convertFloat32ToDouble(ToFloatRegister(lir->input()),
1184 ToFloatRegister(lir->output()));
1185}
1186
1187void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) {
1188 masm.convertDoubleToFloat32(ToFloatRegister(lir->input()),
1189 ToFloatRegister(lir->output()));
1190}
1191
1192void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) {
1193 masm.convertInt32ToFloat32(ToRegister(lir->input()),
1194 ToFloatRegister(lir->output()));
1195}
1196
1197void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) {
1198 Label fail;
1199 FloatRegister input = ToFloatRegister(lir->input());
1200 Register output = ToRegister(lir->output());
1201 masm.convertDoubleToInt32(input, output, &fail,
1202 lir->mir()->needsNegativeZeroCheck());
1203 bailoutFrom(&fail, lir->snapshot());
1204}
1205
1206void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) {
1207 Label fail;
1208 FloatRegister input = ToFloatRegister(lir->input());
1209 Register output = ToRegister(lir->output());
1210 masm.convertFloat32ToInt32(input, output, &fail,
1211 lir->mir()->needsNegativeZeroCheck());
1212 bailoutFrom(&fail, lir->snapshot());
1213}
1214
1215void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) {
1216#ifdef JS_64BIT1
1217 // This LIR instruction is only used if the input can be negative.
1218 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"
, 1218); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->canBeNegative()"
")"); do { *((volatile int*)__null) = 1218; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1219
1220 Register output = ToRegister(lir->output());
1221 const LAllocation* input = lir->input();
1222 if (input->isRegister()) {
1223 masm.move32SignExtendToPtr(ToRegister(input), output);
1224 } else {
1225 masm.load32SignExtendToPtr(ToAddress(input), output);
1226 }
1227#else
1228 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"
, 1228); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms"
")"); do { *((volatile int*)__null) = 1228; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1229#endif
1230}
1231
1232void CodeGenerator::visitNonNegativeIntPtrToInt32(
1233 LNonNegativeIntPtrToInt32* lir) {
1234#ifdef JS_64BIT1
1235 Register output = ToRegister(lir->output());
1236 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"
, 1236); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output"
")"); do { *((volatile int*)__null) = 1236; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1237
1238 Label bail;
1239 masm.guardNonNegativeIntPtrToInt32(output, &bail);
1240 bailoutFrom(&bail, lir->snapshot());
1241#else
1242 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"
, 1242); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms"
")"); do { *((volatile int*)__null) = 1242; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1243#endif
1244}
1245
1246void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) {
1247 Register input = ToRegister(lir->input());
1248 FloatRegister output = ToFloatRegister(lir->output());
1249 masm.convertIntPtrToDouble(input, output);
1250}
1251
1252void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) {
1253 Register output = ToRegister(lir->output());
1254 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"
, 1254); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output"
")"); do { *((volatile int*)__null) = 1254; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1255
1256 uint32_t byteSize = lir->mir()->byteSize();
1257
1258#ifdef DEBUG1
1259 Label ok;
1260 masm.branchTestPtr(Assembler::NotSigned, output, output, &ok);
1261 masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength");
1262 masm.bind(&ok);
1263#endif
1264
1265 Label bail;
1266 masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail);
1267 bailoutFrom(&bail, lir->snapshot());
1268}
1269
1270void CodeGenerator::emitOOLTestObject(Register objreg,
1271 Label* ifEmulatesUndefined,
1272 Label* ifDoesntEmulateUndefined,
1273 Register scratch) {
1274 saveVolatile(scratch);
1275#if defined(DEBUG1) || defined(FUZZING)
1276 masm.loadPtr(AbsoluteAddress(
1277 gen->runtime->addressOfHasSeenObjectEmulateUndefinedFuse()),
1278 scratch);
1279 using Fn = bool (*)(JSObject* obj, size_t fuseValue);
1280 masm.setupAlignedABICall();
1281 masm.passABIArg(objreg);
1282 masm.passABIArg(scratch);
1283 masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>();
1284#else
1285 using Fn = bool (*)(JSObject* obj);
1286 masm.setupAlignedABICall();
1287 masm.passABIArg(objreg);
1288 masm.callWithABI<Fn, js::EmulatesUndefined>();
1289#endif
1290 masm.storeCallPointerResult(scratch);
1291 restoreVolatile(scratch);
1292
1293 masm.branchIfTrueBool(scratch, ifEmulatesUndefined);
1294 masm.jump(ifDoesntEmulateUndefined);
1295}
1296
1297// Base out-of-line code generator for all tests of the truthiness of an
1298// object, where the object might not be truthy. (Recall that per spec all
1299// objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class
1300// flag to permit objects to look like |undefined| in certain contexts,
1301// including in object truthiness testing.) We check truthiness inline except
1302// when we're testing it on a proxy, in which case out-of-line code will call
1303// EmulatesUndefined for a conclusive answer.
1304class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> {
1305 Register objreg_;
1306 Register scratch_;
1307
1308 Label* ifEmulatesUndefined_;
1309 Label* ifDoesntEmulateUndefined_;
1310
1311#ifdef DEBUG1
1312 bool initialized() { return ifEmulatesUndefined_ != nullptr; }
1313#endif
1314
1315 public:
1316 OutOfLineTestObject()
1317 : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {}
1318
1319 void accept(CodeGenerator* codegen) final {
1320 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"
, 1320); AnnotateMozCrashReason("MOZ_ASSERT" "(" "initialized()"
")"); do { *((volatile int*)__null) = 1320; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1321 codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_,
1322 ifDoesntEmulateUndefined_, scratch_);
1323 }
1324
1325 // Specify the register where the object to be tested is found, labels to
1326 // jump to if the object is truthy or falsy, and a scratch register for
1327 // use in the out-of-line path.
1328 void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined,
1329 Label* ifDoesntEmulateUndefined, Register scratch) {
1330 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"
, 1330); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!initialized()"
")"); do { *((volatile int*)__null) = 1330; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1331 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"
, 1331); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ifEmulatesUndefined"
")"); do { *((volatile int*)__null) = 1331; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1332 objreg_ = objreg;
1333 scratch_ = scratch;
1334 ifEmulatesUndefined_ = ifEmulatesUndefined;
1335 ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined;
1336 }
1337};
1338
1339// A subclass of OutOfLineTestObject containing two extra labels, for use when
1340// the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line
1341// code. The user should bind these labels in inline code, and specify them as
1342// targets via setInputAndTargets, as appropriate.
1343class OutOfLineTestObjectWithLabels : public OutOfLineTestObject {
1344 Label label1_;
1345 Label label2_;
1346
1347 public:
1348 OutOfLineTestObjectWithLabels() = default;
1349
1350 Label* label1() { return &label1_; }
1351 Label* label2() { return &label2_; }
1352};
1353
1354void CodeGenerator::testObjectEmulatesUndefinedKernel(
1355 Register objreg, Label* ifEmulatesUndefined,
1356 Label* ifDoesntEmulateUndefined, Register scratch,
1357 OutOfLineTestObject* ool) {
1358 ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined,
1359 scratch);
1360
1361 // Perform a fast-path check of the object's class flags if the object's
1362 // not a proxy. Let out-of-line code handle the slow cases that require
1363 // saving registers, making a function call, and restoring registers.
1364 masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(),
1365 ifEmulatesUndefined);
1366}
1367
1368void CodeGenerator::branchTestObjectEmulatesUndefined(
1369 Register objreg, Label* ifEmulatesUndefined,
1370 Label* ifDoesntEmulateUndefined, Register scratch,
1371 OutOfLineTestObject* ool) {
1372 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"
, 1373); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()"
") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { *((volatile int*)__null) = 1373; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1373 "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"
, 1373); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()"
") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path"
")"); do { *((volatile int*)__null) = 1373; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1374
1375 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1376 ifDoesntEmulateUndefined, scratch, ool);
1377 masm.bind(ifDoesntEmulateUndefined);
1378}
1379
1380void CodeGenerator::testObjectEmulatesUndefined(Register objreg,
1381 Label* ifEmulatesUndefined,
1382 Label* ifDoesntEmulateUndefined,
1383 Register scratch,
1384 OutOfLineTestObject* ool) {
1385 testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined,
1386 ifDoesntEmulateUndefined, scratch, ool);
1387 masm.jump(ifDoesntEmulateUndefined);
1388}
1389
1390void CodeGenerator::testValueTruthyForType(
1391 JSValueType type, ScratchTagScope& tag, const ValueOperand& value,
1392 Register tempToUnbox, Register temp, FloatRegister floatTemp,
1393 Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool,
1394 bool skipTypeTest) {
1395#ifdef DEBUG1
1396 if (skipTypeTest) {
1397 Label expected;
1398 masm.branchTestType(Assembler::Equal, tag, type, &expected);
1399 masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType");
1400 masm.bind(&expected);
1401 }
1402#endif
1403
1404 // Handle irregular types first.
1405 switch (type) {
1406 case JSVAL_TYPE_UNDEFINED:
1407 case JSVAL_TYPE_NULL:
1408 // Undefined and null are falsy.
1409 if (!skipTypeTest) {
1410 masm.branchTestType(Assembler::Equal, tag, type, ifFalsy);
1411 } else {
1412 masm.jump(ifFalsy);
1413 }
1414 return;
1415 case JSVAL_TYPE_SYMBOL:
1416 // Symbols are truthy.
1417 if (!skipTypeTest) {
1418 masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy);
1419 } else {
1420 masm.jump(ifTruthy);
1421 }
1422 return;
1423 case JSVAL_TYPE_OBJECT: {
1424 Label notObject;
1425 if (!skipTypeTest) {
1426 masm.branchTestObject(Assembler::NotEqual, tag, &notObject);
1427 }
1428 ScratchTagScopeRelease _(&tag);
1429 Register objreg = masm.extractObject(value, tempToUnbox);
1430 testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool);
1431 masm.bind(&notObject);
1432 return;
1433 }
1434 default:
1435 break;
1436 }
1437
1438 // Check the type of the value (unless this is the last possible type).
1439 Label differentType;
1440 if (!skipTypeTest) {
1441 masm.branchTestType(Assembler::NotEqual, tag, type, &differentType);
1442 }
1443
1444 // Branch if the value is falsy.
1445 ScratchTagScopeRelease _(&tag);
1446 switch (type) {
1447 case JSVAL_TYPE_BOOLEAN: {
1448 masm.branchTestBooleanTruthy(false, value, ifFalsy);
1449 break;
1450 }
1451 case JSVAL_TYPE_INT32: {
1452 masm.branchTestInt32Truthy(false, value, ifFalsy);
1453 break;
1454 }
1455 case JSVAL_TYPE_STRING: {
1456 masm.branchTestStringTruthy(false, value, ifFalsy);
1457 break;
1458 }
1459 case JSVAL_TYPE_BIGINT: {
1460 masm.branchTestBigIntTruthy(false, value, ifFalsy);
1461 break;
1462 }
1463 case JSVAL_TYPE_DOUBLE: {
1464 masm.unboxDouble(value, floatTemp);
1465 masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy);
1466 break;
1467 }
1468 default:
1469 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"
, 1469); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected value type"
")"); do { *((volatile int*)__null) = 1469; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1470 }
1471
1472 // If we reach this point, the value is truthy. We fall through for
1473 // truthy on the last test; otherwise, branch.
1474 if (!skipTypeTest) {
1475 masm.jump(ifTruthy);
1476 }
1477
1478 masm.bind(&differentType);
1479}
1480
1481void CodeGenerator::testValueTruthy(const ValueOperand& value,
1482 Register tempToUnbox, Register temp,
1483 FloatRegister floatTemp,
1484 const TypeDataList& observedTypes,
1485 Label* ifTruthy, Label* ifFalsy,
1486 OutOfLineTestObject* ool) {
1487 ScratchTagScope tag(masm, value);
1488 masm.splitTagForTest(value, tag);
1489
1490 const std::initializer_list<JSValueType> defaultOrder = {
1491 JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN,
1492 JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING,
1493 JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT};
1494
1495 mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder);
1496
1497 // Generate tests for previously observed types first.
1498 // The TypeDataList is sorted by descending frequency.
1499 for (auto& observed : observedTypes) {
1500 JSValueType type = observed.type();
1501 remaining -= type;
1502
1503 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1504 ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false);
1505 }
1506
1507 // Generate tests for remaining types.
1508 for (auto type : defaultOrder) {
1509 if (!remaining.contains(type)) {
1510 continue;
1511 }
1512 remaining -= type;
1513
1514 // We don't need a type test for the last possible type.
1515 bool skipTypeTest = remaining.isEmpty();
1516 testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp,
1517 ifTruthy, ifFalsy, ool, skipTypeTest);
1518 }
1519 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"
, 1519); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()"
")"); do { *((volatile int*)__null) = 1519; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1520
1521 // We fall through if the final test is truthy.
1522}
1523
1524void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) {
1525 Label* ifTrueLabel = getJumpLabelForBranch(lir->ifTrue());
1526 Label* ifFalseLabel = getJumpLabelForBranch(lir->ifFalse());
1527 Register input = ToRegister(lir->input());
1528
1529 if (isNextBlock(lir->ifFalse()->lir())) {
1530 masm.branchIfBigIntIsNonZero(input, ifTrueLabel);
1531 } else if (isNextBlock(lir->ifTrue()->lir())) {
1532 masm.branchIfBigIntIsZero(input, ifFalseLabel);
1533 } else {
1534 masm.branchIfBigIntIsZero(input, ifFalseLabel);
1535 jumpToBlock(lir->ifTrue());
1536 }
1537}
1538
1539void CodeGenerator::assertObjectDoesNotEmulateUndefined(
1540 Register input, Register temp, const MInstruction* mir) {
1541#if defined(DEBUG1) || defined(FUZZING)
1542 // Validate that the object indeed doesn't have the emulates undefined flag.
1543 auto* ool = new (alloc()) OutOfLineTestObjectWithLabels();
1544 addOutOfLineCode(ool, mir);
1545
1546 Label* doesNotEmulateUndefined = ool->label1();
1547 Label* emulatesUndefined = ool->label2();
1548
1549 testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined,
1550 temp, ool);
1551 masm.bind(emulatesUndefined);
1552 masm.assumeUnreachable(
1553 "Found an object emulating undefined while the fuse is intact");
1554 masm.bind(doesNotEmulateUndefined);
1555#endif
1556}
1557
1558void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) {
1559 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1560 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1561 Register input = ToRegister(lir->input());
1562
1563 bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted();
1564 if (intact) {
1565 assertObjectDoesNotEmulateUndefined(input, ToRegister(lir->temp()),
1566 lir->mir());
1567 // Bug 1874905: It would be fantastic if this could be optimized out
1568 masm.jump(truthy);
1569 } else {
1570 auto* ool = new (alloc()) OutOfLineTestObject();
1571 addOutOfLineCode(ool, lir->mir());
1572
1573 testObjectEmulatesUndefined(input, falsy, truthy, ToRegister(lir->temp()),
1574 ool);
1575 }
1576}
1577
1578void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) {
1579 auto* ool = new (alloc()) OutOfLineTestObject();
1580 addOutOfLineCode(ool, lir->mir());
1581
1582 Label* truthy = getJumpLabelForBranch(lir->ifTruthy());
1583 Label* falsy = getJumpLabelForBranch(lir->ifFalsy());
1584
1585 ValueOperand input = ToValue(lir, LTestVAndBranch::Input);
1586 Register tempToUnbox = ToTempUnboxRegister(lir->temp1());
1587 Register temp = ToRegister(lir->temp2());
1588 FloatRegister floatTemp = ToFloatRegister(lir->tempFloat());
1589 const TypeDataList& observedTypes = lir->mir()->observedTypes();
1590
1591 testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy,
1592 falsy, ool);
1593 masm.jump(truthy);
1594}
1595
1596void CodeGenerator::visitBooleanToString(LBooleanToString* lir) {
1597 Register input = ToRegister(lir->input());
1598 Register output = ToRegister(lir->output());
1599 const JSAtomState& names = gen->runtime->names();
1600 Label true_, done;
1601
1602 masm.branchTest32(Assembler::NonZero, input, input, &true_);
1603 masm.movePtr(ImmGCPtr(names.false_), output);
1604 masm.jump(&done);
1605
1606 masm.bind(&true_);
1607 masm.movePtr(ImmGCPtr(names.true_), output);
1608
1609 masm.bind(&done);
1610}
1611
1612void CodeGenerator::visitIntToString(LIntToString* lir) {
1613 Register input = ToRegister(lir->input());
1614 Register output = ToRegister(lir->output());
1615
1616 using Fn = JSLinearString* (*)(JSContext*, int);
1617 OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>(
1618 lir, ArgList(input), StoreRegisterTo(output));
1619
1620 masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(),
1621 ool->entry());
1622
1623 masm.bind(ool->rejoin());
1624}
1625
1626void CodeGenerator::visitDoubleToString(LDoubleToString* lir) {
1627 FloatRegister input = ToFloatRegister(lir->input());
1628 Register temp = ToRegister(lir->temp0());
1629 Register output = ToRegister(lir->output());
1630
1631 using Fn = JSString* (*)(JSContext*, double);
1632 OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>(
1633 lir, ArgList(input), StoreRegisterTo(output));
1634
1635 // Try double to integer conversion and run integer to string code.
1636 masm.convertDoubleToInt32(input, temp, ool->entry(), false);
1637 masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(),
1638 ool->entry());
1639
1640 masm.bind(ool->rejoin());
1641}
1642
1643void CodeGenerator::visitValueToString(LValueToString* lir) {
1644 ValueOperand input = ToValue(lir, LValueToString::InputIndex);
1645 Register output = ToRegister(lir->output());
1646
1647 using Fn = JSString* (*)(JSContext*, HandleValue);
1648 OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>(
1649 lir, ArgList(input), StoreRegisterTo(output));
1650
1651 Label done;
1652 Register tag = masm.extractTag(input, output);
1653 const JSAtomState& names = gen->runtime->names();
1654
1655 // String
1656 {
1657 Label notString;
1658 masm.branchTestString(Assembler::NotEqual, tag, &notString);
1659 masm.unboxString(input, output);
1660 masm.jump(&done);
1661 masm.bind(&notString);
1662 }
1663
1664 // Integer
1665 {
1666 Label notInteger;
1667 masm.branchTestInt32(Assembler::NotEqual, tag, &notInteger);
1668 Register unboxed = ToTempUnboxRegister(lir->temp0());
1669 unboxed = masm.extractInt32(input, unboxed);
1670 masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(),
1671 ool->entry());
1672 masm.jump(&done);
1673 masm.bind(&notInteger);
1674 }
1675
1676 // Double
1677 {
1678 // Note: no fastpath. Need two extra registers and can only convert doubles
1679 // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT.
1680 masm.branchTestDouble(Assembler::Equal, tag, ool->entry());
1681 }
1682
1683 // Undefined
1684 {
1685 Label notUndefined;
1686 masm.branchTestUndefined(Assembler::NotEqual, tag, &notUndefined);
1687 masm.movePtr(ImmGCPtr(names.undefined), output);
1688 masm.jump(&done);
1689 masm.bind(&notUndefined);
1690 }
1691
1692 // Null
1693 {
1694 Label notNull;
1695 masm.branchTestNull(Assembler::NotEqual, tag, &notNull);
1696 masm.movePtr(ImmGCPtr(names.null), output);
1697 masm.jump(&done);
1698 masm.bind(&notNull);
1699 }
1700
1701 // Boolean
1702 {
1703 Label notBoolean, true_;
1704 masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
1705 masm.branchTestBooleanTruthy(true, input, &true_);
1706 masm.movePtr(ImmGCPtr(names.false_), output);
1707 masm.jump(&done);
1708 masm.bind(&true_);
1709 masm.movePtr(ImmGCPtr(names.true_), output);
1710 masm.jump(&done);
1711 masm.bind(&notBoolean);
1712 }
1713
1714 // Objects/symbols are only possible when |mir->mightHaveSideEffects()|.
1715 if (lir->mir()->mightHaveSideEffects()) {
1716 // Object
1717 if (lir->mir()->supportSideEffects()) {
1718 masm.branchTestObject(Assembler::Equal, tag, ool->entry());
1719 } else {
1720 // Bail.
1721 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"
, 1721); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { *((volatile int*)__null) = 1721; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1722 Label bail;
1723 masm.branchTestObject(Assembler::Equal, tag, &bail);
1724 bailoutFrom(&bail, lir->snapshot());
1725 }
1726
1727 // Symbol
1728 if (lir->mir()->supportSideEffects()) {
1729 masm.branchTestSymbol(Assembler::Equal, tag, ool->entry());
1730 } else {
1731 // Bail.
1732 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"
, 1732); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()"
")"); do { *((volatile int*)__null) = 1732; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1733 Label bail;
1734 masm.branchTestSymbol(Assembler::Equal, tag, &bail);
1735 bailoutFrom(&bail, lir->snapshot());
1736 }
1737 }
1738
1739 // BigInt
1740 {
1741 // No fastpath currently implemented.
1742 masm.branchTestBigInt(Assembler::Equal, tag, ool->entry());
1743 }
1744
1745 masm.assumeUnreachable("Unexpected type for LValueToString.");
1746
1747 masm.bind(&done);
1748 masm.bind(ool->rejoin());
1749}
1750
1751using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**);
1752
1753static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder,
1754 size_t offset, Register buffer,
1755 LiveGeneralRegisterSet& liveVolatiles,
1756 StoreBufferMutationFn fun) {
1757 Label callVM;
1758 Label exit;
1759
1760 // Call into the VM to barrier the write. The only registers that need to
1761 // be preserved are those in liveVolatiles, so once they are saved on the
1762 // stack all volatile registers are available for use.
1763 masm.bind(&callVM);
1764 masm.PushRegsInMask(liveVolatiles);
1765
1766 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
1767 regs.takeUnchecked(buffer);
1768 regs.takeUnchecked(holder);
1769 Register addrReg = regs.takeAny();
1770
1771 masm.computeEffectiveAddress(Address(holder, offset), addrReg);
1772
1773 bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>();
1774 if (needExtraReg) {
1775 masm.push(holder);
1776 masm.setupUnalignedABICall(holder);
1777 } else {
1778 masm.setupUnalignedABICall(regs.takeAny());
1779 }
1780 masm.passABIArg(buffer);
1781 masm.passABIArg(addrReg);
1782 masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun),
1783 ABIType::General, CheckUnsafeCallWithABI::DontCheckOther);
1784
1785 if (needExtraReg) {
1786 masm.pop(holder);
1787 }
1788 masm.PopRegsInMask(liveVolatiles);
1789 masm.bind(&exit);
1790}
1791
1792// Warning: this function modifies prev and next.
1793static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder,
1794 size_t offset, Register prev, Register next,
1795 LiveGeneralRegisterSet& liveVolatiles) {
1796 Label exit;
1797 Label checkRemove, putCell;
1798
1799 // if (next && (buffer = next->storeBuffer()))
1800 // but we never pass in nullptr for next.
1801 Register storebuffer = next;
1802 masm.loadStoreBuffer(next, storebuffer);
1803 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove);
1804
1805 // if (prev && prev->storeBuffer())
1806 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell);
1807 masm.loadStoreBuffer(prev, prev);
1808 masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit);
1809
1810 // buffer->putCell(cellp)
1811 masm.bind(&putCell);
1812 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
1813 JSString::addCellAddressToStoreBuffer);
1814 masm.jump(&exit);
1815
1816 // if (prev && (buffer = prev->storeBuffer()))
1817 masm.bind(&checkRemove);
1818 masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit);
1819 masm.loadStoreBuffer(prev, storebuffer);
1820 masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit);
1821 EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles,
1822 JSString::removeCellAddressFromStoreBuffer);
1823
1824 masm.bind(&exit);
1825}
1826
1827void CodeGenerator::visitRegExp(LRegExp* lir) {
1828 Register output = ToRegister(lir->output());
1829 Register temp = ToRegister(lir->temp0());
1830 JSObject* source = lir->mir()->source();
1831
1832 using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>);
1833 OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>(
1834 lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output));
1835 if (lir->mir()->hasShared()) {
1836 TemplateObject templateObject(source);
1837 masm.createGCObject(output, temp, templateObject, gc::Heap::Default,
1838 ool->entry());
1839 } else {
1840 masm.jump(ool->entry());
1841 }
1842 masm.bind(ool->rejoin());
1843}
1844
1845static constexpr int32_t RegExpPairsVectorStartOffset(
1846 int32_t inputOutputDataStartOffset) {
1847 return inputOutputDataStartOffset + int32_t(InputOutputDataSize) +
1848 int32_t(sizeof(MatchPairs));
1849}
1850
1851static Address RegExpPairCountAddress(MacroAssembler& masm,
1852 int32_t inputOutputDataStartOffset) {
1853 return Address(FramePointer, inputOutputDataStartOffset +
1854 int32_t(InputOutputDataSize) +
1855 MatchPairs::offsetOfPairCount());
1856}
1857
1858static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp,
1859 Register input, Register lastIndex,
1860 Register staticsReg, Register temp1,
1861 Register temp2, gc::Heap initialStringHeap,
1862 LiveGeneralRegisterSet& volatileRegs) {
1863 Address pendingInputAddress(staticsReg,
1864 RegExpStatics::offsetOfPendingInput());
1865 Address matchesInputAddress(staticsReg,
1866 RegExpStatics::offsetOfMatchesInput());
1867 Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource());
1868 Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex());
1869
1870 masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String);
1871 masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String);
1872 masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String);
1873
1874 if (initialStringHeap == gc::Heap::Default) {
1875 // Writing into RegExpStatics tenured memory; must post-barrier.
1876 if (staticsReg.volatile_()) {
1877 volatileRegs.add(staticsReg);
1878 }
1879
1880 masm.loadPtr(pendingInputAddress, temp1);
1881 masm.storePtr(input, pendingInputAddress);
1882 masm.movePtr(input, temp2);
1883 EmitPostWriteBarrierS(masm, staticsReg,
1884 RegExpStatics::offsetOfPendingInput(),
1885 temp1 /* prev */, temp2 /* next */, volatileRegs);
1886
1887 masm.loadPtr(matchesInputAddress, temp1);
1888 masm.storePtr(input, matchesInputAddress);
1889 masm.movePtr(input, temp2);
1890 EmitPostWriteBarrierS(masm, staticsReg,
1891 RegExpStatics::offsetOfMatchesInput(),
1892 temp1 /* prev */, temp2 /* next */, volatileRegs);
1893 } else {
1894 masm.debugAssertGCThingIsTenured(input, temp1);
1895 masm.storePtr(input, pendingInputAddress);
1896 masm.storePtr(input, matchesInputAddress);
1897 }
1898
1899 masm.storePtr(lastIndex,
1900 Address(staticsReg, RegExpStatics::offsetOfLazyIndex()));
1901 masm.store32(
1902 Imm32(1),
1903 Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation()));
1904
1905 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset(
1906 RegExpObject::SHARED_SLOT)),
1907 temp1, JSVAL_TYPE_PRIVATE_GCTHING);
1908 masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2);
1909 masm.storePtr(temp2, lazySourceAddress);
1910 static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size");
1911 masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2);
1912 masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags()));
1913}
1914
1915// Prepare an InputOutputData and optional MatchPairs which space has been
1916// allocated for on the stack, and try to execute a RegExp on a string input.
1917// If the RegExp was successfully executed and matched the input, fallthrough.
1918// Otherwise, jump to notFound or failure.
1919//
1920// inputOutputDataStartOffset is the offset relative to the frame pointer
1921// register. This offset is negative for the RegExpExecTest stub.
1922static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp,
1923 Register input, Register lastIndex,
1924 Register temp1, Register temp2,
1925 Register temp3,
1926 int32_t inputOutputDataStartOffset,
1927 gc::Heap initialStringHeap, Label* notFound,
1928 Label* failure) {
1929 JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp");
1930
1931 using irregexp::InputOutputData;
1932
1933 /*
1934 * [SMDOC] Stack layout for PrepareAndExecuteRegExp
1935 *
1936 * Before this function is called, the caller is responsible for
1937 * allocating enough stack space for the following data:
1938 *
1939 * inputOutputDataStartOffset +-----> +---------------+
1940 * |InputOutputData|
1941 * inputStartAddress +----------> inputStart|
1942 * inputEndAddress +----------> inputEnd|
1943 * startIndexAddress +----------> startIndex|
1944 * matchesAddress +----------> matches|-----+
1945 * +---------------+ |
1946 * matchPairs(Address|Offset) +-----> +---------------+ <--+
1947 * | MatchPairs |
1948 * pairCountAddress +----------> count |
1949 * pairsPointerAddress +----------> pairs |-----+
1950 * +---------------+ |
1951 * pairsArray(Address|Offset) +-----> +---------------+ <--+
1952 * | MatchPair |
1953 * firstMatchStartAddress +----------> start | <--+
1954 * | limit | |
1955 * +---------------+ |
1956 * . |
1957 * . Reserved space for
1958 * . RegExpObject::MaxPairCount
1959 * . MatchPair objects
1960 * . |
1961 * +---------------+ |
1962 * | MatchPair | |
1963 * | start | |
1964 * | limit | <--+
1965 * +---------------+
1966 */
1967
1968 int32_t ioOffset = inputOutputDataStartOffset;
1969 int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData));
1970 int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs));
1971
1972 Address inputStartAddress(FramePointer,
1973 ioOffset + InputOutputData::offsetOfInputStart());
1974 Address inputEndAddress(FramePointer,
1975 ioOffset + InputOutputData::offsetOfInputEnd());
1976 Address startIndexAddress(FramePointer,
1977 ioOffset + InputOutputData::offsetOfStartIndex());
1978 Address matchesAddress(FramePointer,
1979 ioOffset + InputOutputData::offsetOfMatches());
1980
1981 Address matchPairsAddress(FramePointer, matchPairsOffset);
1982 Address pairCountAddress(FramePointer,
1983 matchPairsOffset + MatchPairs::offsetOfPairCount());
1984 Address pairsPointerAddress(FramePointer,
1985 matchPairsOffset + MatchPairs::offsetOfPairs());
1986
1987 Address pairsArrayAddress(FramePointer, pairsArrayOffset);
1988 Address firstMatchStartAddress(FramePointer,
1989 pairsArrayOffset + MatchPair::offsetOfStart());
1990
1991 // First, fill in a skeletal MatchPairs instance on the stack. This will be
1992 // passed to the OOL stub in the caller if we aren't able to execute the
1993 // RegExp inline, and that stub needs to be able to determine whether the
1994 // execution finished successfully.
1995
1996 // Initialize MatchPairs::pairCount to 1. The correct value can only
1997 // be determined after loading the RegExpShared. If the RegExpShared
1998 // has Kind::Atom, this is the correct pairCount.
1999 masm.store32(Imm32(1), pairCountAddress);
2000
2001 // Initialize MatchPairs::pairs pointer
2002 masm.computeEffectiveAddress(pairsArrayAddress, temp1);
2003 masm.storePtr(temp1, pairsPointerAddress);
2004
2005 // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch
2006 masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress);
2007
2008 // Determine the set of volatile inputs to save when calling into C++ or
2009 // regexp code.
2010 LiveGeneralRegisterSet volatileRegs;
2011 if (lastIndex.volatile_()) {
2012 volatileRegs.add(lastIndex);
2013 }
2014 if (input.volatile_()) {
2015 volatileRegs.add(input);
2016 }
2017 if (regexp.volatile_()) {
2018 volatileRegs.add(regexp);
2019 }
2020
2021 // Ensure the input string is not a rope.
2022 Label isLinear;
2023 masm.branchIfNotRope(input, &isLinear);
2024 {
2025 masm.PushRegsInMask(volatileRegs);
2026
2027 using Fn = JSLinearString* (*)(JSString*);
2028 masm.setupUnalignedABICall(temp1);
2029 masm.passABIArg(input);
2030 masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>();
2031
2032 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"
, 2032); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)"
")"); do { *((volatile int*)__null) = 2032; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2033 masm.storeCallPointerResult(temp1);
2034 masm.PopRegsInMask(volatileRegs);
2035
2036 masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure);
2037 }
2038 masm.bind(&isLinear);
2039
2040 // Load the RegExpShared.
2041 Register regexpReg = temp1;
2042 Address sharedSlot = Address(
2043 regexp, NativeObject::getFixedSlotOffset(RegExpObject::SHARED_SLOT));
2044 masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure);
2045 masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING);
2046
2047 // Handle Atom matches
2048 Label notAtom, checkSuccess;
2049 masm.branchPtr(Assembler::Equal,
2050 Address(regexpReg, RegExpShared::offsetOfPatternAtom()),
2051 ImmWord(0), &notAtom);
2052 {
2053 masm.computeEffectiveAddress(matchPairsAddress, temp3);
2054
2055 masm.PushRegsInMask(volatileRegs);
2056 using Fn = RegExpRunStatus (*)(RegExpShared* re, JSLinearString* input,
2057 size_t start, MatchPairs* matchPairs);
2058 masm.setupUnalignedABICall(temp2);
2059 masm.passABIArg(regexpReg);
2060 masm.passABIArg(input);
2061 masm.passABIArg(lastIndex);
2062 masm.passABIArg(temp3);
2063 masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>();
2064
2065 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"
, 2065); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)"
")"); do { *((volatile int*)__null) = 2065; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2066 masm.storeCallInt32Result(temp1);
2067 masm.PopRegsInMask(volatileRegs);
2068
2069 masm.jump(&checkSuccess);
2070 }
2071 masm.bind(&notAtom);
2072
2073 // Don't handle regexps with too many capture pairs.
2074 masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2);
2075 masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount),
2076 failure);
2077
2078 // Fill in the pair count in the MatchPairs on the stack.
2079 masm.store32(temp2, pairCountAddress);
2080
2081 // Load code pointer and length of input (in bytes).
2082 // Store the input start in the InputOutputData.
2083 Register codePointer = temp1; // Note: temp1 was previously regexpReg.
2084 Register byteLength = temp3;
2085 {
2086 Label isLatin1, done;
2087 masm.loadStringLength(input, byteLength);
2088
2089 masm.branchLatin1String(input, &isLatin1);
2090
2091 // Two-byte input
2092 masm.loadStringChars(input, temp2, CharEncoding::TwoByte);
2093 masm.storePtr(temp2, inputStartAddress);
2094 masm.loadPtr(
2095 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)),
2096 codePointer);
2097 masm.lshiftPtr(Imm32(1), byteLength);
2098 masm.jump(&done);
2099
2100 // Latin1 input
2101 masm.bind(&isLatin1);
2102 masm.loadStringChars(input, temp2, CharEncoding::Latin1);
2103 masm.storePtr(temp2, inputStartAddress);
2104 masm.loadPtr(
2105 Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)),
2106 codePointer);
2107
2108 masm.bind(&done);
2109
2110 // Store end pointer
2111 masm.addPtr(byteLength, temp2);
2112 masm.storePtr(temp2, inputEndAddress);
2113 }
2114
2115 // Guard that the RegExpShared has been compiled for this type of input.
2116 // If it has not been compiled, we fall back to the OOL case, which will
2117 // do a VM call into the interpreter.
2118 // TODO: add an interpreter trampoline?
2119 masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure);
2120 masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
2121
2122 // Finish filling in the InputOutputData instance on the stack
2123 masm.computeEffectiveAddress(matchPairsAddress, temp2);
2124 masm.storePtr(temp2, matchesAddress);
2125 masm.storePtr(lastIndex, startIndexAddress);
2126
2127 // Execute the RegExp.
2128 masm.computeEffectiveAddress(
2129 Address(FramePointer, inputOutputDataStartOffset), temp2);
2130 masm.PushRegsInMask(volatileRegs);
2131 masm.setupUnalignedABICall(temp3);
2132 masm.passABIArg(temp2);
2133 masm.callWithABI(codePointer);
2134 masm.storeCallInt32Result(temp1);
2135 masm.PopRegsInMask(volatileRegs);
2136
2137 masm.bind(&checkSuccess);
2138 masm.branch32(Assembler::Equal, temp1,
2139 Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound);
2140 masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)),
2141 failure);
2142
2143 // Lazily update the RegExpStatics.
2144 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2145 RegExpRealm::offsetOfRegExpStatics();
2146 masm.loadGlobalObjectData(temp1);
2147 masm.loadPtr(Address(temp1, offset), temp1);
2148 UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3,
2149 initialStringHeap, volatileRegs);
2150
2151 return true;
2152}
2153
2154static void CopyStringChars(MacroAssembler& masm, Register to, Register from,
2155 Register len, Register byteOpScratch,
2156 CharEncoding encoding,
2157 size_t maximumLength = SIZE_MAX(18446744073709551615UL));
2158
2159class CreateDependentString {
2160 CharEncoding encoding_;
2161 Register string_;
2162 Register temp1_;
2163 Register temp2_;
2164 Label* failure_;
2165
2166 enum class FallbackKind : uint8_t {
2167 InlineString,
2168 FatInlineString,
2169 NotInlineString,
2170 Count
2171 };
2172 mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)>
2173 fallbacks_, joins_;
2174
2175 public:
2176 CreateDependentString(CharEncoding encoding, Register string, Register temp1,
2177 Register temp2, Label* failure)
2178 : encoding_(encoding),
2179 string_(string),
2180 temp1_(temp1),
2181 temp2_(temp2),
2182 failure_(failure) {}
2183
2184 Register string() const { return string_; }
2185 CharEncoding encoding() const { return encoding_; }
2186
2187 // Generate code that creates DependentString.
2188 // Caller should call generateFallback after masm.ret(), to generate
2189 // fallback path.
2190 void generate(MacroAssembler& masm, const JSAtomState& names,
2191 CompileRuntime* runtime, Register base,
2192 BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
2193 gc::Heap initialStringHeap);
2194
2195 // Generate fallback path for creating DependentString.
2196 void generateFallback(MacroAssembler& masm);
2197};
2198
2199void CreateDependentString::generate(MacroAssembler& masm,
2200 const JSAtomState& names,
2201 CompileRuntime* runtime, Register base,
2202 BaseIndex startIndexAddress,
2203 BaseIndex limitIndexAddress,
2204 gc::Heap initialStringHeap) {
2205 JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)",
2206 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2207
2208 auto newGCString = [&](FallbackKind kind) {
2209 uint32_t flags = kind == FallbackKind::InlineString
2210 ? JSString::INIT_THIN_INLINE_FLAGS
2211 : kind == FallbackKind::FatInlineString
2212 ? JSString::INIT_FAT_INLINE_FLAGS
2213 : JSString::INIT_DEPENDENT_FLAGS;
2214 if (encoding_ == CharEncoding::Latin1) {
2215 flags |= JSString::LATIN1_CHARS_BIT;
2216 }
2217
2218 if (kind != FallbackKind::FatInlineString) {
2219 masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]);
2220 } else {
2221 masm.newGCFatInlineString(string_, temp2_, initialStringHeap,
2222 &fallbacks_[kind]);
2223 }
2224 masm.bind(&joins_[kind]);
2225 masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags()));
2226 };
2227
2228 // Compute the string length.
2229 masm.load32(startIndexAddress, temp2_);
2230 masm.load32(limitIndexAddress, temp1_);
2231 masm.sub32(temp2_, temp1_);
2232
2233 Label done, nonEmpty;
2234
2235 // Zero length matches use the empty string.
2236 masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty);
2237 masm.movePtr(ImmGCPtr(names.empty_), string_);
2238 masm.jump(&done);
2239
2240 masm.bind(&nonEmpty);
2241
2242 // Complete matches use the base string.
2243 Label nonBaseStringMatch;
2244 masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch);
2245 masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()),
2246 temp1_, &nonBaseStringMatch);
2247 masm.movePtr(base, string_);
2248 masm.jump(&done);
2249
2250 masm.bind(&nonBaseStringMatch);
2251
2252 Label notInline;
2253
2254 int32_t maxInlineLength = encoding_ == CharEncoding::Latin1
2255 ? JSFatInlineString::MAX_LENGTH_LATIN1
2256 : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
2257 masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), &notInline);
2258 {
2259 // Make a thin or fat inline string.
2260 Label stringAllocated, fatInline;
2261
2262 int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1
2263 ? JSThinInlineString::MAX_LENGTH_LATIN1
2264 : JSThinInlineString::MAX_LENGTH_TWO_BYTE;
2265 masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength),
2266 &fatInline);
2267 if (encoding_ == CharEncoding::Latin1) {
2268 // One character Latin-1 strings can be loaded directly from the
2269 // static strings table.
2270 Label thinInline;
2271 masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline);
2272 {
2273 static_assert(
2274 StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR,
2275 "Latin-1 strings can be loaded from static strings");
2276
2277 masm.loadStringChars(base, temp1_, encoding_);
2278 masm.loadChar(temp1_, temp2_, temp1_, encoding_);
2279
2280 masm.lookupStaticString(temp1_, string_, runtime->staticStrings());
2281
2282 masm.jump(&done);
2283 }
2284 masm.bind(&thinInline);
2285 }
2286 {
2287 newGCString(FallbackKind::InlineString);
2288 masm.jump(&stringAllocated);
2289 }
2290 masm.bind(&fatInline);
2291 { newGCString(FallbackKind::FatInlineString); }
2292 masm.bind(&stringAllocated);
2293
2294 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2295
2296 masm.push(string_);
2297 masm.push(base);
2298
2299 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"
, 2300); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer"
") (" "startIndexAddress is still valid after stack pushes" ")"
); do { *((volatile int*)__null) = 2300; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2300 "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"
, 2300); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer"
") (" "startIndexAddress is still valid after stack pushes" ")"
); do { *((volatile int*)__null) = 2300; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2301
2302 // Load chars pointer for the new string.
2303 masm.loadInlineStringCharsForStore(string_, string_);
2304
2305 // Load the source characters pointer.
2306 masm.loadStringChars(base, temp2_, encoding_);
2307 masm.load32(startIndexAddress, base);
2308 masm.addToCharPtr(temp2_, base, encoding_);
2309
2310 CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_);
2311
2312 masm.pop(base);
2313 masm.pop(string_);
2314
2315 masm.jump(&done);
2316 }
2317
2318 masm.bind(&notInline);
2319
2320 {
2321 // Make a dependent string.
2322 // Warning: string may be tenured (if the fallback case is hit), so
2323 // stores into it must be post barriered.
2324 newGCString(FallbackKind::NotInlineString);
2325
2326 masm.store32(temp1_, Address(string_, JSString::offsetOfLength()));
2327
2328 masm.loadNonInlineStringChars(base, temp1_, encoding_);
2329 masm.load32(startIndexAddress, temp2_);
2330 masm.addToCharPtr(temp1_, temp2_, encoding_);
2331 masm.storeNonInlineStringChars(temp1_, string_);
2332 masm.storeDependentStringBase(base, string_);
2333
2334 // Ensure that the depended-on string is flagged as such, so we don't
2335 // convert it into a forwarded atom
2336 masm.load32(Address(base, JSString::offsetOfFlags()), temp2_);
2337 Label skipDependedOn;
2338 masm.branchTest32(Assembler::NonZero, temp2_, Imm32(JSString::ATOM_BIT),
2339 &skipDependedOn);
2340 masm.or32(Imm32(JSString::DEPENDED_ON_BIT), temp2_);
2341 masm.store32(temp2_, Address(base, JSString::offsetOfFlags()));
2342 masm.bind(&skipDependedOn);
2343
2344 // Follow any base pointer if the input is itself a dependent string.
2345 // Watch for undepended strings, which have a base pointer but don't
2346 // actually share their characters with it.
2347 Label noBase;
2348 masm.movePtr(base, temp1_);
2349 masm.and32(Imm32(JSString::TYPE_FLAGS_MASK), temp2_);
2350 masm.branchTest32(Assembler::Zero, temp2_, Imm32(JSString::DEPENDENT_BIT),
2351 &noBase);
2352 masm.loadDependentStringBase(base, temp1_);
2353 masm.storeDependentStringBase(temp1_, string_);
2354#ifdef DEBUG1
2355 Label isAppropriatelyMarked;
2356 masm.branchTest32(Assembler::NonZero,
2357 Address(temp1_, JSString::offsetOfFlags()),
2358 Imm32(JSString::ATOM_BIT | JSString::DEPENDED_ON_BIT),
2359 &isAppropriatelyMarked);
2360 masm.assumeUnreachable("Base chain missing DEPENDED_ON_BIT");
2361 masm.bind(&isAppropriatelyMarked);
2362#endif
2363
2364 masm.bind(&noBase);
2365
2366 // Post-barrier the base store, whether it was the direct or indirect
2367 // base (both will end up in temp1 here).
2368 masm.branchPtrInNurseryChunk(Assembler::Equal, string_, temp2_, &done);
2369 masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp1_, temp2_, &done);
2370
2371 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2372 regsToSave.takeUnchecked(temp1_);
2373 regsToSave.takeUnchecked(temp2_);
2374
2375 masm.PushRegsInMask(regsToSave);
2376
2377 masm.mov(ImmPtr(runtime), temp1_);
2378
2379 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell);
2380 masm.setupUnalignedABICall(temp2_);
2381 masm.passABIArg(temp1_);
2382 masm.passABIArg(string_);
2383 masm.callWithABI<Fn, PostWriteBarrier>();
2384
2385 masm.PopRegsInMask(regsToSave);
2386 }
2387
2388 masm.bind(&done);
2389}
2390
2391void CreateDependentString::generateFallback(MacroAssembler& masm) {
2392 JitSpew(JitSpew_Codegen,
2393 "# Emitting CreateDependentString fallback (encoding=%s)",
2394 (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte"));
2395
2396 LiveRegisterSet regsToSave(RegisterSet::Volatile());
2397 regsToSave.takeUnchecked(string_);
2398 regsToSave.takeUnchecked(temp2_);
2399
2400 for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) {
2401 masm.bind(&fallbacks_[kind]);
2402
2403 masm.PushRegsInMask(regsToSave);
2404
2405 using Fn = void* (*)(JSContext* cx);
2406 masm.setupUnalignedABICall(string_);
2407 masm.loadJSContext(string_);
2408 masm.passABIArg(string_);
2409 if (kind == FallbackKind::FatInlineString) {
2410 masm.callWithABI<Fn, AllocateFatInlineString>();
2411 } else {
2412 masm.callWithABI<Fn, AllocateDependentString>();
2413 }
2414 masm.storeCallPointerResult(string_);
2415
2416 masm.PopRegsInMask(regsToSave);
2417
2418 masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_);
2419
2420 masm.jump(&joins_[kind]);
2421 }
2422}
2423
2424// Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar,
2425// but RegExpExecMatch also has to load and update .lastIndex for global/sticky
2426// regular expressions.
2427static JitCode* GenerateRegExpMatchStubShared(JSContext* cx,
2428 gc::Heap initialStringHeap,
2429 bool isExecMatch) {
2430 if (isExecMatch) {
2431 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub");
2432 } else {
2433 JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub");
2434 }
2435
2436 // |initialStringHeap| could be stale after a GC.
2437 JS::AutoCheckCannotGC nogc(cx);
2438
2439 Register regexp = RegExpMatcherRegExpReg;
2440 Register input = RegExpMatcherStringReg;
2441 Register lastIndex = RegExpMatcherLastIndexReg;
2442 ValueOperand result = JSReturnOperand;
2443
2444 // We are free to clobber all registers, as LRegExpMatcher is a call
2445 // instruction.
2446 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2447 regs.take(input);
2448 regs.take(regexp);
2449 regs.take(lastIndex);
2450
2451 Register temp1 = regs.takeAny();
2452 Register temp2 = regs.takeAny();
2453 Register temp3 = regs.takeAny();
2454 Register maybeTemp4 = InvalidReg;
2455 if (!regs.empty()) {
2456 // There are not enough registers on x86.
2457 maybeTemp4 = regs.takeAny();
2458 }
2459 Register maybeTemp5 = InvalidReg;
2460 if (!regs.empty()) {
2461 // There are not enough registers on x86.
2462 maybeTemp5 = regs.takeAny();
2463 }
2464
2465 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
2466 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
2467
2468 TempAllocator temp(&cx->tempLifoAlloc());
2469 JitContext jcx(cx);
2470 StackMacroAssembler masm(cx, temp);
2471 AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared");
2472
2473#ifdef JS_USE_LINK_REGISTER
2474 masm.pushReturnAddress();
2475#endif
2476 masm.push(FramePointer);
2477 masm.moveStackPtrTo(FramePointer);
2478
2479 Label notFoundZeroLastIndex;
2480 if (isExecMatch) {
2481 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
2482 }
2483
2484 // The InputOutputData is placed above the frame pointer and return address on
2485 // the stack.
2486 int32_t inputOutputDataStartOffset = 2 * sizeof(void*);
2487
2488 Label notFound, oolEntry;
2489 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
2490 temp3, inputOutputDataStartOffset,
2491 initialStringHeap, &notFound, &oolEntry)) {
2492 return nullptr;
2493 }
2494
2495 // If a regexp has named captures, fall back to the OOL stub, which
2496 // will end up calling CreateRegExpMatchResults.
2497 Register shared = temp2;
2498 masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset(
2499 RegExpObject::SHARED_SLOT)),
2500 shared, JSVAL_TYPE_PRIVATE_GCTHING);
2501 masm.branchPtr(Assembler::NotEqual,
2502 Address(shared, RegExpShared::offsetOfGroupsTemplate()),
2503 ImmWord(0), &oolEntry);
2504
2505 // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub.
2506 masm.branchTest32(Assembler::NonZero,
2507 Address(shared, RegExpShared::offsetOfFlags()),
2508 Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry);
2509
2510 Address pairCountAddress =
2511 RegExpPairCountAddress(masm, inputOutputDataStartOffset);
2512
2513 // Construct the result.
2514 Register object = temp1;
2515 {
2516 // In most cases, the array will have just 1-2 elements, so we optimize for
2517 // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots
2518 // because two slots are used for the elements header).
2519
2520 // Load the array length in temp2 and the shape in temp3.
2521 Label allocated;
2522 masm.load32(pairCountAddress, temp2);
2523 size_t offset = GlobalObjectData::offsetOfRegExpRealm() +
2524 RegExpRealm::offsetOfNormalMatchResultShape();
2525 masm.loadGlobalObjectData(temp3);
2526 masm.loadPtr(Address(temp3, offset), temp3);
2527
2528 auto emitAllocObject = [&](size_t elementCapacity) {
2529 gc::AllocKind kind = GuessArrayGCKind(elementCapacity);
2530 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"
, 2530); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)"
")"); do { *((volatile int*)__null) = 2530; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2531 kind = ForegroundToBackgroundAllocKind(kind);
2532
2533#ifdef DEBUG1
2534 // Assert all of the available slots are used for |elementCapacity|
2535 // elements.
2536 size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity;
2537 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"
, 2537); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)"
")"); do { *((volatile int*)__null) = 2537; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2538#endif
2539
2540 constexpr size_t numUsedDynamicSlots =
2541 RegExpRealm::MatchResultObjectSlotSpan;
2542 constexpr size_t numDynamicSlots =
2543 RegExpRealm::MatchResultObjectNumDynamicSlots;
2544 constexpr size_t arrayLength = 1;
2545 masm.createArrayWithFixedElements(object, temp3, temp2, temp3,
2546 arrayLength, elementCapacity,
2547 numUsedDynamicSlots, numDynamicSlots,
2548 kind, gc::Heap::Default, &oolEntry);
2549 };
2550
2551 Label moreThan2;
2552 masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2);
2553 emitAllocObject(2);
2554 masm.jump(&allocated);
2555
2556 Label moreThan6;
2557 masm.bind(&moreThan2);
2558 masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6);
2559 emitAllocObject(6);
2560 masm.jump(&allocated);
2561
2562 masm.bind(&moreThan6);
2563 static_assert(RegExpObject::MaxPairCount == 14);
2564 emitAllocObject(RegExpObject::MaxPairCount);
2565
2566 masm.bind(&allocated);
2567 }
2568
2569 // clang-format off
2570 /*
2571 * [SMDOC] Stack layout for the RegExpMatcher stub
2572 *
2573 * +---------------+
2574 * FramePointer +-----> |Caller-FramePtr|
2575 * +---------------+
2576 * |Return-Address |
2577 * +---------------+
2578 * inputOutputDataStartOffset +-----> +---------------+
2579 * |InputOutputData|
2580 * +---------------+
2581 * +---------------+
2582 * | MatchPairs |
2583 * pairsCountAddress +-----------> count |
2584 * | pairs |
2585 * | |
2586 * +---------------+
2587 * pairsVectorStartOffset +-----> +---------------+
2588 * | MatchPair |
2589 * matchPairStart +------------> start | <-------+
2590 * matchPairLimit +------------> limit | | Reserved space for
2591 * +---------------+ | `RegExpObject::MaxPairCount`
2592 * . | MatchPair objects.
2593 * . |
2594 * . | `count` objects will be
2595 * +---------------+ | initialized and can be
2596 * | MatchPair | | accessed below.
2597 * | start | <-------+
2598 * | limit |
2599 * +---------------+
2600 */
2601 // clang-format on
2602
2603 static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t),
2604 "MatchPair consists of two int32 values representing the start"
2605 "and the end offset of the match");
2606
2607 int32_t pairsVectorStartOffset =
2608 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
2609
2610 // Incremented by one below for each match pair.
2611 Register matchIndex = temp2;
2612 masm.move32(Imm32(0), matchIndex);
2613
2614 // The element in which to store the result of the current match.
2615 size_t elementsOffset = NativeObject::offsetOfFixedElements();
2616 BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset);
2617
2618 // The current match pair's "start" and "limit" member.
2619 BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight,
2620 pairsVectorStartOffset + MatchPair::offsetOfStart());
2621 BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight,
2622 pairsVectorStartOffset + MatchPair::offsetOfLimit());
2623
2624 Label* depStrFailure = &oolEntry;
2625 Label restoreRegExpAndLastIndex;
2626
2627 Register temp4;
2628 if (maybeTemp4 == InvalidReg) {
2629 depStrFailure = &restoreRegExpAndLastIndex;
2630
2631 // We don't have enough registers for a fourth temporary. Reuse |regexp|
2632 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
2633 masm.push(regexp);
2634 temp4 = regexp;
2635 } else {
2636 temp4 = maybeTemp4;
2637 }
2638
2639 Register temp5;
2640 if (maybeTemp5 == InvalidReg) {
2641 depStrFailure = &restoreRegExpAndLastIndex;
2642
2643 // We don't have enough registers for a fifth temporary. Reuse |lastIndex|
2644 // as a temporary. We restore its value at |restoreRegExpAndLastIndex|.
2645 masm.push(lastIndex);
2646 temp5 = lastIndex;
2647 } else {
2648 temp5 = maybeTemp5;
2649 }
2650
2651 auto maybeRestoreRegExpAndLastIndex = [&]() {
2652 if (maybeTemp5 == InvalidReg) {
2653 masm.pop(lastIndex);
2654 }
2655 if (maybeTemp4 == InvalidReg) {
2656 masm.pop(regexp);
2657 }
2658 };
2659
2660 // Loop to construct the match strings. There are two different loops,
2661 // depending on whether the input is a Two-Byte or a Latin-1 string.
2662 CreateDependentString depStrs[]{
2663 {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure},
2664 {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure},
2665 };
2666
2667 {
2668 Label isLatin1, done;
2669 masm.branchLatin1String(input, &isLatin1);
2670
2671 for (auto& depStr : depStrs) {
2672 if (depStr.encoding() == CharEncoding::Latin1) {
2673 masm.bind(&isLatin1);
2674 }
2675
2676 Label matchLoop;
2677 masm.bind(&matchLoop);
2678
2679 static_assert(MatchPair::NoMatch == -1,
2680 "MatchPair::start is negative if no match was found");
2681
2682 Label isUndefined, storeDone;
2683 masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0),
2684 &isUndefined);
2685 {
2686 depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()),
2687 input, matchPairStart, matchPairLimit,
2688 initialStringHeap);
2689
2690 // Storing into nursery-allocated results object's elements; no post
2691 // barrier.
2692 masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement);
2693 masm.jump(&storeDone);
2694 }
2695 masm.bind(&isUndefined);
2696 { masm.storeValue(UndefinedValue(), objectMatchElement); }
2697 masm.bind(&storeDone);
2698
2699 masm.add32(Imm32(1), matchIndex);
2700 masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex,
2701 &done);
2702 masm.jump(&matchLoop);
2703 }
2704
2705#ifdef DEBUG1
2706 masm.assumeUnreachable("The match string loop doesn't fall through.");
2707#endif
2708
2709 masm.bind(&done);
2710 }
2711
2712 maybeRestoreRegExpAndLastIndex();
2713
2714 // Fill in the rest of the output object.
2715 masm.store32(
2716 matchIndex,
2717 Address(object,
2718 elementsOffset + ObjectElements::offsetOfInitializedLength()));
2719 masm.store32(
2720 matchIndex,
2721 Address(object, elementsOffset + ObjectElements::offsetOfLength()));
2722
2723 Address firstMatchPairStartAddress(
2724 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart());
2725 Address firstMatchPairLimitAddress(
2726 FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit());
2727
2728 static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0,
2729 "First slot holds the 'index' property");
2730 static_assert(RegExpRealm::MatchResultObjectInputSlot == 1,
2731 "Second slot holds the 'input' property");
2732
2733 masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
2734
2735 masm.load32(firstMatchPairStartAddress, temp3);
2736 masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
2737
2738 // No post barrier needed (address is within nursery object.)
2739 masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
2740
2741 // For the ExecMatch stub, if the regular expression is global or sticky, we
2742 // have to update its .lastIndex slot.
2743 if (isExecMatch) {
2744 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"
, 2744); AnnotateMozCrashReason("MOZ_ASSERT" "(" "object != lastIndex"
")"); do { *((volatile int*)__null) = 2744; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2745 Label notGlobalOrSticky;
2746 masm.branchTest32(Assembler::Zero, flagsSlot,
2747 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
2748 &notGlobalOrSticky);
2749 masm.load32(firstMatchPairLimitAddress, lastIndex);
2750 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
2751 masm.bind(&notGlobalOrSticky);
2752 }
2753
2754 // All done!
2755 masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
2756 masm.pop(FramePointer);
2757 masm.ret();
2758
2759 masm.bind(&notFound);
2760 if (isExecMatch) {
2761 Label notGlobalOrSticky;
2762 masm.branchTest32(Assembler::Zero, flagsSlot,
2763 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
2764 &notGlobalOrSticky);
2765 masm.bind(&notFoundZeroLastIndex);
2766 masm.storeValue(Int32Value(0), lastIndexSlot);
2767 masm.bind(&notGlobalOrSticky);
2768 }
2769 masm.moveValue(NullValue(), result);
2770 masm.pop(FramePointer);
2771 masm.ret();
2772
2773 // Fallback paths for CreateDependentString.
2774 for (auto& depStr : depStrs) {
2775 depStr.generateFallback(masm);
2776 }
2777
2778 // Fall-through to the ool entry after restoring the registers.
2779 masm.bind(&restoreRegExpAndLastIndex);
2780 maybeRestoreRegExpAndLastIndex();
2781
2782 // Use an undefined value to signal to the caller that the OOL stub needs to
2783 // be called.
2784 masm.bind(&oolEntry);
2785 masm.moveValue(UndefinedValue(), result);
2786 masm.pop(FramePointer);
2787 masm.ret();
2788
2789 Linker linker(masm);
2790 JitCode* code = linker.newCode(cx, CodeKind::Other);
2791 if (!code) {
2792 return nullptr;
2793 }
2794
2795 const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub";
2796 CollectPerfSpewerJitCodeProfile(code, name);
2797#ifdef MOZ_VTUNE1
2798 vtune::MarkStub(code, name);
2799#endif
2800
2801 return code;
2802}
2803
2804JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) {
2805 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
2806 /* isExecMatch = */ false);
2807}
2808
2809JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) {
2810 return GenerateRegExpMatchStubShared(cx, initialStringHeap,
2811 /* isExecMatch = */ true);
2812}
2813
2814class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator> {
2815 LRegExpMatcher* lir_;
2816
2817 public:
2818 explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir) : lir_(lir) {}
2819
2820 void accept(CodeGenerator* codegen) override {
2821 codegen->visitOutOfLineRegExpMatcher(this);
2822 }
2823
2824 LRegExpMatcher* lir() const { return lir_; }
2825};
2826
2827void CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool) {
2828 LRegExpMatcher* lir = ool->lir();
2829 Register lastIndex = ToRegister(lir->lastIndex());
2830 Register input = ToRegister(lir->string());
2831 Register regexp = ToRegister(lir->regexp());
2832
2833 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2834 regs.take(lastIndex);
2835 regs.take(input);
2836 regs.take(regexp);
2837 Register temp = regs.takeAny();
2838
2839 masm.computeEffectiveAddress(
2840 Address(masm.getStackPointer(), InputOutputDataSize), temp);
2841
2842 pushArg(temp);
2843 pushArg(lastIndex);
2844 pushArg(input);
2845 pushArg(regexp);
2846
2847 // We are not using oolCallVM because we are in a Call, and that live
2848 // registers are already saved by the the register allocator.
2849 using Fn =
2850 bool (*)(JSContext*, HandleObject regexp, HandleString input,
2851 int32_t lastIndex, MatchPairs* pairs, MutableHandleValue output);
2852 callVM<Fn, RegExpMatcherRaw>(lir);
2853
2854 masm.jump(ool->rejoin());
2855}
2856
2857void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) {
2858 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"
, 2858); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { *((volatile int*)__null) = 2858; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2859 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"
, 2859); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { *((volatile int*)__null) = 2859; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2860 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"
, 2860); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg"
")"); do { *((volatile int*)__null) = 2860; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2861 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"
, 2861); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { *((volatile int*)__null) = 2861; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2862
2863#if defined(JS_NUNBOX32)
2864 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
2865 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
2866 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
2867 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
2868 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type);
2869 static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data);
2870#elif defined(JS_PUNBOX641)
2871 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
2872 static_assert(RegExpMatcherStringReg != JSReturnReg);
2873 static_assert(RegExpMatcherLastIndexReg != JSReturnReg);
2874#endif
2875
2876 masm.reserveStack(RegExpReservedStack);
2877
2878 OutOfLineRegExpMatcher* ool = new (alloc()) OutOfLineRegExpMatcher(lir);
2879 addOutOfLineCode(ool, lir->mir());
2880
2881 const JitZone* jitZone = gen->realm->zone()->jitZone();
2882 JitCode* regExpMatcherStub =
2883 jitZone->regExpMatcherStubNoBarrier(&zoneStubsToReadBarrier_);
2884 masm.call(regExpMatcherStub);
2885 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
2886 masm.bind(ool->rejoin());
2887
2888 masm.freeStack(RegExpReservedStack);
2889}
2890
2891class OutOfLineRegExpExecMatch : public OutOfLineCodeBase<CodeGenerator> {
2892 LRegExpExecMatch* lir_;
2893
2894 public:
2895 explicit OutOfLineRegExpExecMatch(LRegExpExecMatch* lir) : lir_(lir) {}
2896
2897 void accept(CodeGenerator* codegen) override {
2898 codegen->visitOutOfLineRegExpExecMatch(this);
2899 }
2900
2901 LRegExpExecMatch* lir() const { return lir_; }
2902};
2903
2904void CodeGenerator::visitOutOfLineRegExpExecMatch(
2905 OutOfLineRegExpExecMatch* ool) {
2906 LRegExpExecMatch* lir = ool->lir();
2907 Register input = ToRegister(lir->string());
2908 Register regexp = ToRegister(lir->regexp());
2909
2910 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2911 regs.take(input);
2912 regs.take(regexp);
2913 Register temp = regs.takeAny();
2914
2915 masm.computeEffectiveAddress(
2916 Address(masm.getStackPointer(), InputOutputDataSize), temp);
2917
2918 pushArg(temp);
2919 pushArg(input);
2920 pushArg(regexp);
2921
2922 // We are not using oolCallVM because we are in a Call and live registers have
2923 // already been saved by the register allocator.
2924 using Fn =
2925 bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input,
2926 MatchPairs* pairs, MutableHandleValue output);
2927 callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir);
2928 masm.jump(ool->rejoin());
2929}
2930
2931void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) {
2932 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"
, 2932); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg"
")"); do { *((volatile int*)__null) = 2932; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2933 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"
, 2933); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg"
")"); do { *((volatile int*)__null) = 2933; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2934 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"
, 2934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand"
")"); do { *((volatile int*)__null) = 2934; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2935
2936#if defined(JS_NUNBOX32)
2937 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type);
2938 static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data);
2939 static_assert(RegExpMatcherStringReg != JSReturnReg_Type);
2940 static_assert(RegExpMatcherStringReg != JSReturnReg_Data);
2941#elif defined(JS_PUNBOX641)
2942 static_assert(RegExpMatcherRegExpReg != JSReturnReg);
2943 static_assert(RegExpMatcherStringReg != JSReturnReg);
2944#endif
2945
2946 masm.reserveStack(RegExpReservedStack);
2947
2948 auto* ool = new (alloc()) OutOfLineRegExpExecMatch(lir);
2949 addOutOfLineCode(ool, lir->mir());
2950
2951 const JitZone* jitZone = gen->realm->zone()->jitZone();
2952 JitCode* regExpExecMatchStub =
2953 jitZone->regExpExecMatchStubNoBarrier(&zoneStubsToReadBarrier_);
2954 masm.call(regExpExecMatchStub);
2955 masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
2956
2957 masm.bind(ool->rejoin());
2958 masm.freeStack(RegExpReservedStack);
2959}
2960
2961JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) {
2962 JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub");
2963
2964 Register regexp = RegExpSearcherRegExpReg;
2965 Register input = RegExpSearcherStringReg;
2966 Register lastIndex = RegExpSearcherLastIndexReg;
2967 Register result = ReturnReg;
2968
2969 // We are free to clobber all registers, as LRegExpSearcher is a call
2970 // instruction.
2971 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
2972 regs.take(input);
2973 regs.take(regexp);
2974 regs.take(lastIndex);
2975
2976 Register temp1 = regs.takeAny();
2977 Register temp2 = regs.takeAny();
2978 Register temp3 = regs.takeAny();
2979
2980 TempAllocator temp(&cx->tempLifoAlloc());
2981 JitContext jcx(cx);
2982 StackMacroAssembler masm(cx, temp);
2983 AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub");
2984
2985#ifdef JS_USE_LINK_REGISTER
2986 masm.pushReturnAddress();
2987#endif
2988 masm.push(FramePointer);
2989 masm.moveStackPtrTo(FramePointer);
2990
2991#ifdef DEBUG1
2992 // Store sentinel value to cx->regExpSearcherLastLimit.
2993 // See comment in RegExpSearcherImpl.
2994 masm.loadJSContext(temp1);
2995 masm.store32(Imm32(RegExpSearcherLastLimitSentinel),
2996 Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit()));
2997#endif
2998
2999 // The InputOutputData is placed above the frame pointer and return address on
3000 // the stack.
3001 int32_t inputOutputDataStartOffset = 2 * sizeof(void*);
3002
3003 Label notFound, oolEntry;
3004 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3005 temp3, inputOutputDataStartOffset,
3006 initialStringHeap, &notFound, &oolEntry)) {
3007 return nullptr;
3008 }
3009
3010 // clang-format off
3011 /*
3012 * [SMDOC] Stack layout for the RegExpSearcher stub
3013 *
3014 * +---------------+
3015 * FramePointer +-----> |Caller-FramePtr|
3016 * +---------------+
3017 * |Return-Address |
3018 * +---------------+
3019 * inputOutputDataStartOffset +-----> +---------------+
3020 * |InputOutputData|
3021 * +---------------+
3022 * +---------------+
3023 * | MatchPairs |
3024 * | count |
3025 * | pairs |
3026 * | |
3027 * +---------------+
3028 * pairsVectorStartOffset +-----> +---------------+
3029 * | MatchPair |
3030 * matchPairStart +------------> start | <-------+
3031 * matchPairLimit +------------> limit | | Reserved space for
3032 * +---------------+ | `RegExpObject::MaxPairCount`
3033 * . | MatchPair objects.
3034 * . |
3035 * . | Only a single object will
3036 * +---------------+ | be initialized and can be
3037 * | MatchPair | | accessed below.
3038 * | start | <-------+
3039 * | limit |
3040 * +---------------+
3041 */
3042 // clang-format on
3043
3044 int32_t pairsVectorStartOffset =
3045 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
3046 Address matchPairStart(FramePointer,
3047 pairsVectorStartOffset + MatchPair::offsetOfStart());
3048 Address matchPairLimit(FramePointer,
3049 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3050
3051 // Store match limit to cx->regExpSearcherLastLimit and return the index.
3052 masm.load32(matchPairLimit, result);
3053 masm.loadJSContext(input);
3054 masm.store32(result,
3055 Address(input, JSContext::offsetOfRegExpSearcherLastLimit()));
3056 masm.load32(matchPairStart, result);
3057 masm.pop(FramePointer);
3058 masm.ret();
3059
3060 masm.bind(&notFound);
3061 masm.move32(Imm32(RegExpSearcherResultNotFound), result);
3062 masm.pop(FramePointer);
3063 masm.ret();
3064
3065 masm.bind(&oolEntry);
3066 masm.move32(Imm32(RegExpSearcherResultFailed), result);
3067 masm.pop(FramePointer);
3068 masm.ret();
3069
3070 Linker linker(masm);
3071 JitCode* code = linker.newCode(cx, CodeKind::Other);
3072 if (!code) {
3073 return nullptr;
3074 }
3075
3076 CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub");
3077#ifdef MOZ_VTUNE1
3078 vtune::MarkStub(code, "RegExpSearcherStub");
3079#endif
3080
3081 return code;
3082}
3083
3084class OutOfLineRegExpSearcher : public OutOfLineCodeBase<CodeGenerator> {
3085 LRegExpSearcher* lir_;
3086
3087 public:
3088 explicit OutOfLineRegExpSearcher(LRegExpSearcher* lir) : lir_(lir) {}
3089
3090 void accept(CodeGenerator* codegen) override {
3091 codegen->visitOutOfLineRegExpSearcher(this);
3092 }
3093
3094 LRegExpSearcher* lir() const { return lir_; }
3095};
3096
3097void CodeGenerator::visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool) {
3098 LRegExpSearcher* lir = ool->lir();
3099 Register lastIndex = ToRegister(lir->lastIndex());
3100 Register input = ToRegister(lir->string());
3101 Register regexp = ToRegister(lir->regexp());
3102
3103 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3104 regs.take(lastIndex);
3105 regs.take(input);
3106 regs.take(regexp);
3107 Register temp = regs.takeAny();
3108
3109 masm.computeEffectiveAddress(
3110 Address(masm.getStackPointer(), InputOutputDataSize), temp);
3111
3112 pushArg(temp);
3113 pushArg(lastIndex);
3114 pushArg(input);
3115 pushArg(regexp);
3116
3117 // We are not using oolCallVM because we are in a Call, and that live
3118 // registers are already saved by the the register allocator.
3119 using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input,
3120 int32_t lastIndex, MatchPairs* pairs, int32_t* result);
3121 callVM<Fn, RegExpSearcherRaw>(lir);
3122
3123 masm.jump(ool->rejoin());
3124}
3125
3126void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) {
3127 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"
, 3127); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg"
")"); do { *((volatile int*)__null) = 3127; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3128 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"
, 3128); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg"
")"); do { *((volatile int*)__null) = 3128; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3129 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"
, 3129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg"
")"); do { *((volatile int*)__null) = 3129; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3130 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"
, 3130); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { *((volatile int*)__null) = 3130; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3131
3132 static_assert(RegExpSearcherRegExpReg != ReturnReg);
3133 static_assert(RegExpSearcherStringReg != ReturnReg);
3134 static_assert(RegExpSearcherLastIndexReg != ReturnReg);
3135
3136 masm.reserveStack(RegExpReservedStack);
3137
3138 OutOfLineRegExpSearcher* ool = new (alloc()) OutOfLineRegExpSearcher(lir);
3139 addOutOfLineCode(ool, lir->mir());
3140
3141 const JitZone* jitZone = gen->realm->zone()->jitZone();
3142 JitCode* regExpSearcherStub =
3143 jitZone->regExpSearcherStubNoBarrier(&zoneStubsToReadBarrier_);
3144 masm.call(regExpSearcherStub);
3145 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed),
3146 ool->entry());
3147 masm.bind(ool->rejoin());
3148
3149 masm.freeStack(RegExpReservedStack);
3150}
3151
3152void CodeGenerator::visitRegExpSearcherLastLimit(
3153 LRegExpSearcherLastLimit* lir) {
3154 Register result = ToRegister(lir->output());
3155 Register scratch = ToRegister(lir->temp0());
3156
3157 masm.loadAndClearRegExpSearcherLastLimit(result, scratch);
3158}
3159
3160JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) {
3161 JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub");
3162
3163 Register regexp = RegExpExecTestRegExpReg;
3164 Register input = RegExpExecTestStringReg;
3165 Register result = ReturnReg;
3166
3167 TempAllocator temp(&cx->tempLifoAlloc());
3168 JitContext jcx(cx);
3169 StackMacroAssembler masm(cx, temp);
3170 AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub");
3171
3172#ifdef JS_USE_LINK_REGISTER
3173 masm.pushReturnAddress();
3174#endif
3175 masm.push(FramePointer);
3176 masm.moveStackPtrTo(FramePointer);
3177
3178 // We are free to clobber all registers, as LRegExpExecTest is a call
3179 // instruction.
3180 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
3181 regs.take(input);
3182 regs.take(regexp);
3183
3184 // Ensure lastIndex != result.
3185 regs.take(result);
3186 Register lastIndex = regs.takeAny();
3187 regs.add(result);
3188 Register temp1 = regs.takeAny();
3189 Register temp2 = regs.takeAny();
3190 Register temp3 = regs.takeAny();
3191
3192 Address flagsSlot(regexp, RegExpObject::offsetOfFlags());
3193 Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex());
3194
3195 masm.reserveStack(RegExpReservedStack);
3196
3197 // Load lastIndex and skip RegExp execution if needed.
3198 Label notFoundZeroLastIndex;
3199 masm.loadRegExpLastIndex(regexp, input, lastIndex, &notFoundZeroLastIndex);
3200
3201 // In visitRegExpMatcher and visitRegExpSearcher, we reserve stack space
3202 // before calling the stub. For RegExpExecTest we call the stub before
3203 // reserving stack space, so the offset of the InputOutputData relative to the
3204 // frame pointer is negative.
3205 constexpr int32_t inputOutputDataStartOffset = -int32_t(RegExpReservedStack);
3206
3207 // On ARM64, load/store instructions can encode an immediate offset in the
3208 // range [-256, 4095]. If we ever fail this assertion, it would be more
3209 // efficient to store the data above the frame pointer similar to
3210 // RegExpMatcher and RegExpSearcher.
3211 static_assert(inputOutputDataStartOffset >= -256);
3212
3213 Label notFound, oolEntry;
3214 if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2,
3215 temp3, inputOutputDataStartOffset,
3216 initialStringHeap, &notFound, &oolEntry)) {
3217 return nullptr;
3218 }
3219
3220 // Set `result` to true/false to indicate found/not-found, or to
3221 // RegExpExecTestResultFailed if we have to retry in C++. If the regular
3222 // expression is global or sticky, we also have to update its .lastIndex slot.
3223
3224 Label done;
3225 int32_t pairsVectorStartOffset =
3226 RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
3227 Address matchPairLimit(FramePointer,
3228 pairsVectorStartOffset + MatchPair::offsetOfLimit());
3229
3230 masm.move32(Imm32(1), result);
3231 masm.branchTest32(Assembler::Zero, flagsSlot,
3232 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3233 &done);
3234 masm.load32(matchPairLimit, lastIndex);
3235 masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot);
3236 masm.jump(&done);
3237
3238 masm.bind(&notFound);
3239 masm.move32(Imm32(0), result);
3240 masm.branchTest32(Assembler::Zero, flagsSlot,
3241 Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky),
3242 &done);
3243 masm.storeValue(Int32Value(0), lastIndexSlot);
3244 masm.jump(&done);
3245
3246 masm.bind(&notFoundZeroLastIndex);
3247 masm.move32(Imm32(0), result);
3248 masm.storeValue(Int32Value(0), lastIndexSlot);
3249 masm.jump(&done);
3250
3251 masm.bind(&oolEntry);
3252 masm.move32(Imm32(RegExpExecTestResultFailed), result);
3253
3254 masm.bind(&done);
3255 masm.freeStack(RegExpReservedStack);
3256 masm.pop(FramePointer);
3257 masm.ret();
3258
3259 Linker linker(masm);
3260 JitCode* code = linker.newCode(cx, CodeKind::Other);
3261 if (!code) {
3262 return nullptr;
3263 }
3264
3265 CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub");
3266#ifdef MOZ_VTUNE1
3267 vtune::MarkStub(code, "RegExpExecTestStub");
3268#endif
3269
3270 return code;
3271}
3272
3273class OutOfLineRegExpExecTest : public OutOfLineCodeBase<CodeGenerator> {
3274 LRegExpExecTest* lir_;
3275
3276 public:
3277 explicit OutOfLineRegExpExecTest(LRegExpExecTest* lir) : lir_(lir) {}
3278
3279 void accept(CodeGenerator* codegen) override {
3280 codegen->visitOutOfLineRegExpExecTest(this);
3281 }
3282
3283 LRegExpExecTest* lir() const { return lir_; }
3284};
3285
3286void CodeGenerator::visitOutOfLineRegExpExecTest(OutOfLineRegExpExecTest* ool) {
3287 LRegExpExecTest* lir = ool->lir();
3288 Register input = ToRegister(lir->string());
3289 Register regexp = ToRegister(lir->regexp());
3290
3291 pushArg(input);
3292 pushArg(regexp);
3293
3294 // We are not using oolCallVM because we are in a Call and live registers have
3295 // already been saved by the register allocator.
3296 using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp,
3297 HandleString input, bool* result);
3298 callVM<Fn, RegExpBuiltinExecTestFromJit>(lir);
3299
3300 masm.jump(ool->rejoin());
3301}
3302
3303void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) {
3304 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"
, 3304); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg"
")"); do { *((volatile int*)__null) = 3304; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3305 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"
, 3305); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg"
")"); do { *((volatile int*)__null) = 3305; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3306 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"
, 3306); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg"
")"); do { *((volatile int*)__null) = 3306; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3307
3308 static_assert(RegExpExecTestRegExpReg != ReturnReg);
3309 static_assert(RegExpExecTestStringReg != ReturnReg);
3310
3311 auto* ool = new (alloc()) OutOfLineRegExpExecTest(lir);
3312 addOutOfLineCode(ool, lir->mir());
3313
3314 const JitZone* jitZone = gen->realm->zone()->jitZone();
3315 JitCode* regExpExecTestStub =
3316 jitZone->regExpExecTestStubNoBarrier(&zoneStubsToReadBarrier_);
3317 masm.call(regExpExecTestStub);
3318
3319 masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed),
3320 ool->entry());
3321
3322 masm.bind(ool->rejoin());
3323}
3324
3325void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) {
3326 Register regexp = ToRegister(ins->regexp());
3327 Register input = ToRegister(ins->input());
3328 Register output = ToRegister(ins->output());
3329
3330 using Fn =
3331 bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*);
3332 auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>(
3333 ins, ArgList(regexp, input), StoreRegisterTo(output));
3334
3335 // Load RegExpShared in |output|.
3336 Label vmCall;
3337 masm.loadParsedRegExpShared(regexp, output, ool->entry());
3338
3339 // Return true iff pairCount > 1.
3340 Label returnTrue;
3341 masm.branch32(Assembler::Above,
3342 Address(output, RegExpShared::offsetOfPairCount()), Imm32(1),
3343 &returnTrue);
3344 masm.move32(Imm32(0), output);
3345 masm.jump(ool->rejoin());
3346
3347 masm.bind(&returnTrue);
3348 masm.move32(Imm32(1), output);
3349
3350 masm.bind(ool->rejoin());
3351}
3352
3353class OutOfLineRegExpPrototypeOptimizable
3354 : public OutOfLineCodeBase<CodeGenerator> {
3355 LRegExpPrototypeOptimizable* ins_;
3356
3357 public:
3358 explicit OutOfLineRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins)
3359 : ins_(ins) {}
3360
3361 void accept(CodeGenerator* codegen) override {
3362 codegen->visitOutOfLineRegExpPrototypeOptimizable(this);
3363 }
3364 LRegExpPrototypeOptimizable* ins() const { return ins_; }
3365};
3366
3367void CodeGenerator::visitRegExpPrototypeOptimizable(
3368 LRegExpPrototypeOptimizable* ins) {
3369 Register object = ToRegister(ins->object());
3370 Register output = ToRegister(ins->output());
3371 Register temp = ToRegister(ins->temp0());
3372
3373 OutOfLineRegExpPrototypeOptimizable* ool =
3374 new (alloc()) OutOfLineRegExpPrototypeOptimizable(ins);
3375 addOutOfLineCode(ool, ins->mir());
3376
3377 const GlobalObject* global = gen->realm->maybeGlobal();
3378 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"
, 3378); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")"
); do { *((volatile int*)__null) = 3378; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3379 masm.branchIfNotRegExpPrototypeOptimizable(object, temp, global,
3380 ool->entry());
3381 masm.move32(Imm32(0x1), output);
3382
3383 masm.bind(ool->rejoin());
3384}
3385
3386void CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable(
3387 OutOfLineRegExpPrototypeOptimizable* ool) {
3388 LRegExpPrototypeOptimizable* ins = ool->ins();
3389 Register object = ToRegister(ins->object());
3390 Register output = ToRegister(ins->output());
3391
3392 saveVolatile(output);
3393
3394 using Fn = bool (*)(JSContext* cx, JSObject* proto);
3395 masm.setupAlignedABICall();
3396 masm.loadJSContext(output);
3397 masm.passABIArg(output);
3398 masm.passABIArg(object);
3399 masm.callWithABI<Fn, RegExpPrototypeOptimizableRaw>();
3400 masm.storeCallBoolResult(output);
3401
3402 restoreVolatile(output);
3403
3404 masm.jump(ool->rejoin());
3405}
3406
3407class OutOfLineRegExpInstanceOptimizable
3408 : public OutOfLineCodeBase<CodeGenerator> {
3409 LRegExpInstanceOptimizable* ins_;
3410
3411 public:
3412 explicit OutOfLineRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins)
3413 : ins_(ins) {}
3414
3415 void accept(CodeGenerator* codegen) override {
3416 codegen->visitOutOfLineRegExpInstanceOptimizable(this);
3417 }
3418 LRegExpInstanceOptimizable* ins() const { return ins_; }
3419};
3420
3421void CodeGenerator::visitRegExpInstanceOptimizable(
3422 LRegExpInstanceOptimizable* ins) {
3423 Register object = ToRegister(ins->object());
3424 Register output = ToRegister(ins->output());
3425 Register temp = ToRegister(ins->temp0());
3426
3427 OutOfLineRegExpInstanceOptimizable* ool =
3428 new (alloc()) OutOfLineRegExpInstanceOptimizable(ins);
3429 addOutOfLineCode(ool, ins->mir());
3430
3431 const GlobalObject* global = gen->realm->maybeGlobal();
3432 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"
, 3432); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")"
); do { *((volatile int*)__null) = 3432; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3433 masm.branchIfNotRegExpInstanceOptimizable(object, temp, global, ool->entry());
3434 masm.move32(Imm32(0x1), output);
3435
3436 masm.bind(ool->rejoin());
3437}
3438
3439void CodeGenerator::visitOutOfLineRegExpInstanceOptimizable(
3440 OutOfLineRegExpInstanceOptimizable* ool) {
3441 LRegExpInstanceOptimizable* ins = ool->ins();
3442 Register object = ToRegister(ins->object());
3443 Register proto = ToRegister(ins->proto());
3444 Register output = ToRegister(ins->output());
3445
3446 saveVolatile(output);
3447
3448 using Fn = bool (*)(JSContext* cx, JSObject* obj, JSObject* proto);
3449 masm.setupAlignedABICall();
3450 masm.loadJSContext(output);
3451 masm.passABIArg(output);
3452 masm.passABIArg(object);
3453 masm.passABIArg(proto);
3454 masm.callWithABI<Fn, RegExpInstanceOptimizableRaw>();
3455 masm.storeCallBoolResult(output);
3456
3457 restoreVolatile(output);
3458
3459 masm.jump(ool->rejoin());
3460}
3461
3462static void FindFirstDollarIndex(MacroAssembler& masm, Register str,
3463 Register len, Register temp0, Register temp1,
3464 Register output, CharEncoding encoding) {
3465#ifdef DEBUG1
3466 Label ok;
3467 masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok);
3468 masm.assumeUnreachable("Length should be greater than 0.");
3469 masm.bind(&ok);
3470#endif
3471
3472 Register chars = temp0;
3473 masm.loadStringChars(str, chars, encoding);
3474
3475 masm.move32(Imm32(0), output);
3476
3477 Label start, done;
3478 masm.bind(&start);
3479
3480 Register currentChar = temp1;
3481 masm.loadChar(chars, output, currentChar, encoding);
3482 masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done);
3483
3484 masm.add32(Imm32(1), output);
3485 masm.branch32(Assembler::NotEqual, output, len, &start);
3486
3487 masm.move32(Imm32(-1), output);
3488
3489 masm.bind(&done);
3490}
3491
3492void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) {
3493 Register str = ToRegister(ins->str());
3494 Register output = ToRegister(ins->output());
3495 Register temp0 = ToRegister(ins->temp0());
3496 Register temp1 = ToRegister(ins->temp1());
3497 Register len = ToRegister(ins->temp2());
3498
3499 using Fn = bool (*)(JSContext*, JSString*, int32_t*);
3500 OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>(
3501 ins, ArgList(str), StoreRegisterTo(output));
3502
3503 masm.branchIfRope(str, ool->entry());
3504 masm.loadStringLength(str, len);
3505
3506 Label isLatin1, done;
3507 masm.branchLatin1String(str, &isLatin1);
3508 {
3509 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3510 CharEncoding::TwoByte);
3511 masm.jump(&done);
3512 }
3513 masm.bind(&isLatin1);
3514 {
3515 FindFirstDollarIndex(masm, str, len, temp0, temp1, output,
3516 CharEncoding::Latin1);
3517 }
3518 masm.bind(&done);
3519 masm.bind(ool->rejoin());
3520}
3521
3522void CodeGenerator::visitStringReplace(LStringReplace* lir) {
3523 if (lir->replacement()->isConstant()) {
3524 pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString()));
3525 } else {
3526 pushArg(ToRegister(lir->replacement()));
3527 }
3528
3529 if (lir->pattern()->isConstant()) {
3530 pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString()));
3531 } else {
3532 pushArg(ToRegister(lir->pattern()));
3533 }
3534
3535 if (lir->string()->isConstant()) {
3536 pushArg(ImmGCPtr(lir->string()->toConstant()->toString()));
3537 } else {
3538 pushArg(ToRegister(lir->string()));
3539 }
3540
3541 using Fn =
3542 JSString* (*)(JSContext*, HandleString, HandleString, HandleString);
3543 if (lir->mir()->isFlatReplacement()) {
3544 callVM<Fn, StringFlatReplaceString>(lir);
3545 } else {
3546 callVM<Fn, StringReplace>(lir);
3547 }
3548}
3549
3550void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) {
3551 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3552 TypedOrValueRegister lhs =
3553 TypedOrValueRegister(ToValue(lir, LBinaryValueCache::LhsIndex));
3554 TypedOrValueRegister rhs =
3555 TypedOrValueRegister(ToValue(lir, LBinaryValueCache::RhsIndex));
3556 ValueOperand output = ToOutValue(lir);
3557
3558 JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
3559
3560 switch (jsop) {
3561 case JSOp::Add:
3562 case JSOp::Sub:
3563 case JSOp::Mul:
3564 case JSOp::Div:
3565 case JSOp::Mod:
3566 case JSOp::Pow:
3567 case JSOp::BitAnd:
3568 case JSOp::BitOr:
3569 case JSOp::BitXor:
3570 case JSOp::Lsh:
3571 case JSOp::Rsh:
3572 case JSOp::Ursh: {
3573 IonBinaryArithIC ic(liveRegs, lhs, rhs, output);
3574 addIC(lir, allocateIC(ic));
3575 return;
3576 }
3577 default:
3578 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"
, 3578); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache"
")"); do { *((volatile int*)__null) = 3578; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3579 }
3580}
3581
3582void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) {
3583 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3584 TypedOrValueRegister lhs =
3585 TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::LhsIndex));
3586 TypedOrValueRegister rhs =
3587 TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::RhsIndex));
3588 Register output = ToRegister(lir->output());
3589
3590 JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc());
3591
3592 switch (jsop) {
3593 case JSOp::Lt:
3594 case JSOp::Le:
3595 case JSOp::Gt:
3596 case JSOp::Ge:
3597 case JSOp::Eq:
3598 case JSOp::Ne:
3599 case JSOp::StrictEq:
3600 case JSOp::StrictNe: {
3601 IonCompareIC ic(liveRegs, lhs, rhs, output);
3602 addIC(lir, allocateIC(ic));
3603 return;
3604 }
3605 default:
3606 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"
, 3606); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache"
")"); do { *((volatile int*)__null) = 3606; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3607 }
3608}
3609
3610void CodeGenerator::visitUnaryCache(LUnaryCache* lir) {
3611 LiveRegisterSet liveRegs = lir->safepoint()->liveRegs();
3612 TypedOrValueRegister input =
3613 TypedOrValueRegister(ToValue(lir, LUnaryCache::InputIndex));
3614 ValueOperand output = ToOutValue(lir);
3615
3616 IonUnaryArithIC ic(liveRegs, input, output);
3617 addIC(lir, allocateIC(ic));
3618}
3619
3620void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) {
3621 pushArg(ImmPtr(lir->mir()->module()));
3622
3623 using Fn = JSObject* (*)(JSContext*, HandleObject);
3624 callVM<Fn, js::GetOrCreateModuleMetaObject>(lir);
3625}
3626
3627void CodeGenerator::visitDynamicImport(LDynamicImport* lir) {
3628 pushArg(ToValue(lir, LDynamicImport::OptionsIndex));
3629 pushArg(ToValue(lir, LDynamicImport::SpecifierIndex));
3630 pushArg(ImmGCPtr(current->mir()->info().script()));
3631
3632 using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue);
3633 callVM<Fn, js::StartDynamicModuleImport>(lir);
3634}
3635
3636void CodeGenerator::visitLambda(LLambda* lir) {
3637 Register envChain = ToRegister(lir->environmentChain());
3638 Register output = ToRegister(lir->output());
3639 Register tempReg = ToRegister(lir->temp0());
3640
3641 JSFunction* fun = lir->mir()->templateFunction();
3642
3643 using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject);
3644 OutOfLineCode* ool = oolCallVM<Fn, js::Lambda>(
3645 lir, ArgList(ImmGCPtr(fun), envChain), StoreRegisterTo(output));
3646
3647 TemplateObject templateObject(fun);
3648 masm.createGCObject(output, tempReg, templateObject, gc::Heap::Default,
3649 ool->entry());
3650
3651 masm.storeValue(JSVAL_TYPE_OBJECT, envChain,
3652 Address(output, JSFunction::offsetOfEnvironment()));
3653 // No post barrier needed because output is guaranteed to be allocated in
3654 // the nursery.
3655
3656 masm.bind(ool->rejoin());
3657}
3658
3659void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) {
3660 Register envChain = ToRegister(lir->envChain());
3661 Register prototype = ToRegister(lir->prototype());
3662
3663 pushArg(prototype);
3664 pushArg(envChain);
3665 pushArg(ImmGCPtr(lir->mir()->function()));
3666
3667 using Fn =
3668 JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject);
3669 callVM<Fn, js::FunWithProtoOperation>(lir);
3670}
3671
3672void CodeGenerator::visitSetFunName(LSetFunName* lir) {
3673 pushArg(Imm32(lir->mir()->prefixKind()));
3674 pushArg(ToValue(lir, LSetFunName::NameIndex));
3675 pushArg(ToRegister(lir->fun()));
3676
3677 using Fn =
3678 bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind);
3679 callVM<Fn, js::SetFunctionName>(lir);
3680}
3681
3682void CodeGenerator::visitOsiPoint(LOsiPoint* lir) {
3683 // Note: markOsiPoint ensures enough space exists between the last
3684 // LOsiPoint and this one to patch adjacent call instructions.
3685
3686 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"
, 3686); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 3686; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3687
3688 uint32_t osiCallPointOffset = markOsiPoint(lir);
3689
3690 LSafepoint* safepoint = lir->associatedSafepoint();
3691 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"
, 3691); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()"
")"); do { *((volatile int*)__null) = 3691; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3692 safepoint->setOsiCallPointOffset(osiCallPointOffset);
3693
3694#ifdef DEBUG1
3695 // There should be no movegroups or other instructions between
3696 // an instruction and its OsiPoint. This is necessary because
3697 // we use the OsiPoint's snapshot from within VM calls.
3698 for (LInstructionReverseIterator iter(current->rbegin(lir));
3699 iter != current->rend(); iter++) {
3700 if (*iter == lir) {
3701 continue;
3702 }
3703 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"
, 3703); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!iter->isMoveGroup()"
")"); do { *((volatile int*)__null) = 3703; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3704 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"
, 3704); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint"
")"); do { *((volatile int*)__null) = 3704; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3705 break;
3706 }
3707#endif
3708
3709#ifdef CHECK_OSIPOINT_REGISTERS1
3710 if (shouldVerifyOsiPointRegs(safepoint)) {
3711 verifyOsiPointRegs(safepoint);
3712 }
3713#endif
3714}
3715
3716void CodeGenerator::visitPhi(LPhi* lir) {
3717 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"
, 3717); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator"
")"); do { *((volatile int*)__null) = 3717; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3718}
3719
3720void CodeGenerator::visitGoto(LGoto* lir) { jumpToBlock(lir->target()); }
3721
3722void CodeGenerator::visitTableSwitch(LTableSwitch* ins) {
3723 MTableSwitch* mir = ins->mir();
3724 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
3725 const LAllocation* temp;
3726
3727 if (mir->getOperand(0)->type() != MIRType::Int32) {
3728 temp = ins->tempInt()->output();
3729
3730 // The input is a double, so try and convert it to an integer.
3731 // If it does not fit in an integer, take the default case.
3732 masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp),
3733 defaultcase, false);
3734 } else {
3735 temp = ins->index();
3736 }
3737
3738 emitTableSwitchDispatch(mir, ToRegister(temp),
3739 ToRegisterOrInvalid(ins->tempPointer()));
3740}
3741
3742void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) {
3743 MTableSwitch* mir = ins->mir();
3744 Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label();
3745
3746 Register index = ToRegister(ins->tempInt());
3747 ValueOperand value = ToValue(ins, LTableSwitchV::InputValue);
3748 Register tag = masm.extractTag(value, index);
3749 masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase);
3750
3751 Label unboxInt, isInt;
3752 masm.branchTestInt32(Assembler::Equal, tag, &unboxInt);
3753 {
3754 FloatRegister floatIndex = ToFloatRegister(ins->tempFloat());
3755 masm.unboxDouble(value, floatIndex);
3756 masm.convertDoubleToInt32(floatIndex, index, defaultcase, false);
3757 masm.jump(&isInt);
3758 }
3759
3760 masm.bind(&unboxInt);
3761 masm.unboxInt32(value, index);
3762
3763 masm.bind(&isInt);
3764
3765 emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer()));
3766}
3767
3768void CodeGenerator::visitParameter(LParameter* lir) {}
3769
3770void CodeGenerator::visitCallee(LCallee* lir) {
3771 Register callee = ToRegister(lir->output());
3772 Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken());
3773
3774 masm.loadFunctionFromCalleeToken(ptr, callee);
3775}
3776
3777void CodeGenerator::visitIsConstructing(LIsConstructing* lir) {
3778 Register output = ToRegister(lir->output());
3779 Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken());
3780 masm.loadPtr(calleeToken, output);
3781
3782 // We must be inside a function.
3783 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"
, 3783); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()"
")"); do { *((volatile int*)__null) = 3783; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3784
3785 // The low bit indicates whether this call is constructing, just clear the
3786 // other bits.
3787 static_assert(CalleeToken_Function == 0x0,
3788 "CalleeTokenTag value should match");
3789 static_assert(CalleeToken_FunctionConstructing == 0x1,
3790 "CalleeTokenTag value should match");
3791 masm.andPtr(Imm32(0x1), output);
3792}
3793
3794void CodeGenerator::visitReturn(LReturn* lir) {
3795#if defined(JS_NUNBOX32)
3796 DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX);
3797 DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX);
3798 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"
, 3798); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type"
")"); do { *((volatile int*)__null) = 3798; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3799 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"
, 3799); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data"
")"); do { *((volatile int*)__null) = 3799; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3800#elif defined(JS_PUNBOX641)
3801 DebugOnly<LAllocation*> result = lir->getOperand(0);
3802 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"
, 3802); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg"
")"); do { *((volatile int*)__null) = 3802; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3803#endif
3804 // Don't emit a jump to the return label if this is the last block, as
3805 // it'll fall through to the epilogue.
3806 //
3807 // This is -not- true however for a Generator-return, which may appear in the
3808 // middle of the last block, so we should always emit the jump there.
3809 if (current->mir() != *gen->graph().poBegin() || lir->isGenerator()) {
3810 masm.jump(&returnLabel_);
3811 }
3812}
3813
3814void CodeGenerator::visitOsrEntry(LOsrEntry* lir) {
3815 Register temp = ToRegister(lir->temp());
3816
3817 // Remember the OSR entry offset into the code buffer.
3818 masm.flushBuffer();
3819 setOsrEntryOffset(masm.size());
3820
3821 // Allocate the full frame for this function
3822 // Note we have a new entry here. So we reset MacroAssembler::framePushed()
3823 // to 0, before reserving the stack.
3824 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"
, 3824); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 3824; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3825 masm.setFramePushed(0);
3826
3827 // The Baseline code ensured both the frame pointer and stack pointer point to
3828 // the JitFrameLayout on the stack.
3829
3830 // If profiling, save the current frame pointer to a per-thread global field.
3831 if (isProfilerInstrumentationEnabled()) {
3832 masm.profilerEnterFrame(FramePointer, temp);
3833 }
3834
3835 masm.reserveStack(frameSize());
3836 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"
, 3836); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()"
")"); do { *((volatile int*)__null) = 3836; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3837
3838 // Ensure that the Ion frames is properly aligned.
3839 masm.assertStackAlignment(JitStackAlignment, 0);
3840}
3841
3842void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) {
3843 const LAllocation* frame = lir->getOperand(0);
3844 const LDefinition* object = lir->getDef(0);
3845
3846 const ptrdiff_t frameOffset =
3847 BaselineFrame::reverseOffsetOfEnvironmentChain();
3848
3849 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
3850}
3851
3852void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) {
3853 const LAllocation* frame = lir->getOperand(0);
3854 const LDefinition* object = lir->getDef(0);
3855
3856 const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj();
3857
3858 masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object));
3859}
3860
3861void CodeGenerator::visitOsrValue(LOsrValue* value) {
3862 const LAllocation* frame = value->getOperand(0);
3863 const ValueOperand out = ToOutValue(value);
3864
3865 const ptrdiff_t frameOffset = value->mir()->frameOffset();
3866
3867 masm.loadValue(Address(ToRegister(frame), frameOffset), out);
3868}
3869
3870void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) {
3871 const LAllocation* frame = lir->getOperand(0);
3872 const ValueOperand out = ToOutValue(lir);
3873
3874 Address flags =
3875 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags());
3876 Address retval =
3877 Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue());
3878
3879 masm.moveValue(UndefinedValue(), out);
3880
3881 Label done;
3882 masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL),
3883 &done);
3884 masm.loadValue(retval, out);
3885 masm.bind(&done);
3886}
3887
3888void CodeGenerator::visitStackArgT(LStackArgT* lir) {
3889 const LAllocation* arg = lir->arg();
3890 MIRType argType = lir->type();
3891 uint32_t argslot = lir->argslot();
3892 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"
, 3892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { *((volatile int*)__null) = 3892; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3893
3894 Address dest = AddressOfPassedArg(argslot);
3895
3896 if (arg->isFloatReg()) {
3897 masm.boxDouble(ToFloatRegister(arg), dest);
3898 } else if (arg->isRegister()) {
3899 masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest);
3900 } else {
3901 masm.storeValue(arg->toConstant()->toJSValue(), dest);
3902 }
3903}
3904
3905void CodeGenerator::visitStackArgV(LStackArgV* lir) {
3906 ValueOperand val = ToValue(lir, 0);
3907 uint32_t argslot = lir->argslot();
3908 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"
, 3908); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()"
")"); do { *((volatile int*)__null) = 3908; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3909
3910 masm.storeValue(val, AddressOfPassedArg(argslot));
3911}
3912
3913void CodeGenerator::visitMoveGroup(LMoveGroup* group) {
3914 if (!group->numMoves()) {
3915 return;
3916 }
3917
3918 MoveResolver& resolver = masm.moveResolver();
3919
3920 for (size_t i = 0; i < group->numMoves(); i++) {
3921 const LMove& move = group->getMove(i);
3922
3923 LAllocation from = move.from();
3924 LAllocation to = move.to();
3925 LDefinition::Type type = move.type();
3926
3927 // No bogus moves.
3928 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"
, 3928); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to"
")"); do { *((volatile int*)__null) = 3928; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3929 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"
, 3929); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!from.isConstant()"
")"); do { *((volatile int*)__null) = 3929; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3930 MoveOp::Type moveType;
3931 switch (type) {
3932 case LDefinition::OBJECT:
3933 case LDefinition::SLOTS:
3934 case LDefinition::WASM_ANYREF:
3935#ifdef JS_NUNBOX32
3936 case LDefinition::TYPE:
3937 case LDefinition::PAYLOAD:
3938#else
3939 case LDefinition::BOX:
3940#endif
3941 case LDefinition::GENERAL:
3942 case LDefinition::STACKRESULTS:
3943 moveType = MoveOp::GENERAL;
3944 break;
3945 case LDefinition::INT32:
3946 moveType = MoveOp::INT32;
3947 break;
3948 case LDefinition::FLOAT32:
3949 moveType = MoveOp::FLOAT32;
3950 break;
3951 case LDefinition::DOUBLE:
3952 moveType = MoveOp::DOUBLE;
3953 break;
3954 case LDefinition::SIMD128:
3955 moveType = MoveOp::SIMD128;
3956 break;
3957 default:
3958 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"
, 3958); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected move type"
")"); do { *((volatile int*)__null) = 3958; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3959 }
3960
3961 masm.propagateOOM(
3962 resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType));
3963 }
3964
3965 masm.propagateOOM(resolver.resolve());
3966 if (masm.oom()) {
3967 return;
3968 }
3969
3970 MoveEmitter emitter(masm);
3971
3972#ifdef JS_CODEGEN_X86
3973 if (group->maybeScratchRegister().isGeneralReg()) {
3974 emitter.setScratchRegister(
3975 group->maybeScratchRegister().toGeneralReg()->reg());
3976 } else {
3977 resolver.sortMemoryToMemoryMoves();
3978 }
3979#endif
3980
3981 emitter.emit(resolver);
3982 emitter.finish();
3983}
3984
3985void CodeGenerator::visitInteger(LInteger* lir) {
3986 masm.move32(Imm32(lir->i32()), ToRegister(lir->output()));
3987}
3988
3989void CodeGenerator::visitInteger64(LInteger64* lir) {
3990 masm.move64(Imm64(lir->i64()), ToOutRegister64(lir));
3991}
3992
3993void CodeGenerator::visitPointer(LPointer* lir) {
3994 masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output()));
3995}
3996
3997void CodeGenerator::visitNurseryObject(LNurseryObject* lir) {
3998 Register output = ToRegister(lir->output());
3999 uint32_t nurseryIndex = lir->mir()->nurseryIndex();
4000
4001 // Load a pointer to the entry in IonScript's nursery objects list.
4002 CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output);
4003 masm.propagateOOM(ionNurseryObjectLabels_.emplaceBack(label, nurseryIndex));
4004
4005 // Load the JSObject*.
4006 masm.loadPtr(Address(output, 0), output);
4007}
4008
4009void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) {
4010 // No-op.
4011}
4012
4013void CodeGenerator::visitDebugEnterGCUnsafeRegion(
4014 LDebugEnterGCUnsafeRegion* lir) {
4015 Register temp = ToRegister(lir->temp0());
4016
4017 masm.loadJSContext(temp);
4018
4019 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4020 masm.add32(Imm32(1), inUnsafeRegion);
4021
4022 Label ok;
4023 masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok);
4024 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4025 masm.bind(&ok);
4026}
4027
4028void CodeGenerator::visitDebugLeaveGCUnsafeRegion(
4029 LDebugLeaveGCUnsafeRegion* lir) {
4030 Register temp = ToRegister(lir->temp0());
4031
4032 masm.loadJSContext(temp);
4033
4034 Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion());
4035 masm.add32(Imm32(-1), inUnsafeRegion);
4036
4037 Label ok;
4038 masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok);
4039 masm.assumeUnreachable("unbalanced enter/leave GC unsafe region");
4040 masm.bind(&ok);
4041}
4042
4043void CodeGenerator::visitSlots(LSlots* lir) {
4044 Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots());
4045 masm.loadPtr(slots, ToRegister(lir->output()));
4046}
4047
4048void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) {
4049 ValueOperand dest = ToOutValue(lir);
4050 Register base = ToRegister(lir->input());
4051 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4052
4053 masm.loadValue(Address(base, offset), dest);
4054}
4055
4056static ConstantOrRegister ToConstantOrRegister(const LAllocation* value,
4057 MIRType valueType) {
4058 if (value->isConstant()) {
4059 return ConstantOrRegister(value->toConstant()->toJSValue());
4060 }
4061 return TypedOrValueRegister(valueType, ToAnyRegister(value));
4062}
4063
4064void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) {
4065 Register base = ToRegister(lir->slots());
4066 int32_t offset = lir->mir()->slot() * sizeof(js::Value);
4067 Address dest(base, offset);
4068
4069 if (lir->mir()->needsBarrier()) {
4070 emitPreBarrier(dest);
4071 }
4072
4073 MIRType valueType = lir->mir()->value()->type();
4074 ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType);
4075 masm.storeUnboxedValue(value, valueType, dest);
4076}
4077
4078void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) {
4079 Register base = ToRegister(lir->slots());
4080 int32_t offset = lir->mir()->slot() * sizeof(Value);
4081
4082 const ValueOperand value = ToValue(lir, LStoreDynamicSlotV::ValueIndex);
4083
4084 if (lir->mir()->needsBarrier()) {
4085 emitPreBarrier(Address(base, offset));
4086 }
4087
4088 masm.storeValue(value, Address(base, offset));
4089}
4090
4091void CodeGenerator::visitElements(LElements* lir) {
4092 Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements());
4093 masm.loadPtr(elements, ToRegister(lir->output()));
4094}
4095
4096void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) {
4097 Address environment(ToRegister(lir->function()),
4098 JSFunction::offsetOfEnvironment());
4099 masm.unboxObject(environment, ToRegister(lir->output()));
4100}
4101
4102void CodeGenerator::visitHomeObject(LHomeObject* lir) {
4103 Register func = ToRegister(lir->function());
4104 Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot());
4105
4106 masm.assertFunctionIsExtended(func);
4107#ifdef DEBUG1
4108 Label isObject;
4109 masm.branchTestObject(Assembler::Equal, homeObject, &isObject);
4110 masm.assumeUnreachable("[[HomeObject]] must be Object");
4111 masm.bind(&isObject);
4112#endif
4113
4114 masm.unboxObject(homeObject, ToRegister(lir->output()));
4115}
4116
4117void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) {
4118 Register homeObject = ToRegister(lir->homeObject());
4119 ValueOperand output = ToOutValue(lir);
4120 Register temp = output.scratchReg();
4121
4122 masm.loadObjProto(homeObject, temp);
4123
4124#ifdef DEBUG1
4125 // We won't encounter a lazy proto, because the prototype is guaranteed to
4126 // either be a JSFunction or a PlainObject, and only proxy objects can have a
4127 // lazy proto.
4128 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"
, 4128); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1"
")"); do { *((volatile int*)__null) = 4128; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4129
4130 Label proxyCheckDone;
4131 masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone);
4132 masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase");
4133 masm.bind(&proxyCheckDone);
4134#endif
4135
4136 Label nullProto, done;
4137 masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto);
4138
4139 // Box prototype and return
4140 masm.tagValue(JSVAL_TYPE_OBJECT, temp, output);
4141 masm.jump(&done);
4142
4143 masm.bind(&nullProto);
4144 masm.moveValue(NullValue(), output);
4145
4146 masm.bind(&done);
4147}
4148
4149template <class T>
4150static T* ToConstantObject(MDefinition* def) {
4151 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"
, 4151); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->isConstant()"
")"); do { *((volatile int*)__null) = 4151; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4152 return &def->toConstant()->toObject().as<T>();
4153}
4154
4155void CodeGenerator::visitNewLexicalEnvironmentObject(
4156 LNewLexicalEnvironmentObject* lir) {
4157 Register output = ToRegister(lir->output());
4158 Register temp = ToRegister(lir->temp0());
4159
4160 auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>(
4161 lir->mir()->templateObj());
4162 auto* scope = &templateObj->scope();
4163 gc::Heap initialHeap = gc::Heap::Default;
4164
4165 using Fn =
4166 BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>);
4167 auto* ool =
4168 oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>(
4169 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4170
4171 TemplateObject templateObject(templateObj);
4172 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4173
4174 masm.bind(ool->rejoin());
4175}
4176
4177void CodeGenerator::visitNewClassBodyEnvironmentObject(
4178 LNewClassBodyEnvironmentObject* lir) {
4179 Register output = ToRegister(lir->output());
4180 Register temp = ToRegister(lir->temp0());
4181
4182 auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>(
4183 lir->mir()->templateObj());
4184 auto* scope = &templateObj->scope();
4185 gc::Heap initialHeap = gc::Heap::Default;
4186
4187 using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*,
4188 Handle<ClassBodyScope*>);
4189 auto* ool =
4190 oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>(
4191 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4192
4193 TemplateObject templateObject(templateObj);
4194 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4195
4196 masm.bind(ool->rejoin());
4197}
4198
4199void CodeGenerator::visitNewVarEnvironmentObject(
4200 LNewVarEnvironmentObject* lir) {
4201 Register output = ToRegister(lir->output());
4202 Register temp = ToRegister(lir->temp0());
4203
4204 auto* templateObj =
4205 ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj());
4206 auto* scope = &templateObj->scope().as<VarScope>();
4207 gc::Heap initialHeap = gc::Heap::Default;
4208
4209 using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>);
4210 auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>(
4211 lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output));
4212
4213 TemplateObject templateObject(templateObj);
4214 masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry());
4215
4216 masm.bind(ool->rejoin());
4217}
4218
4219void CodeGenerator::visitGuardShape(LGuardShape* guard) {
4220 Register obj = ToRegister(guard->input());
4221 Register temp = ToTempRegisterOrInvalid(guard->temp0());
4222 Label bail;
4223 masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp,
4224 obj, &bail);
4225 bailoutFrom(&bail, guard->snapshot());
4226}
4227
4228void CodeGenerator::visitGuardFuse(LGuardFuse* guard) {
4229 auto fuseIndex = guard->mir()->fuseIndex();
4230 switch (fuseIndex) {
4231 case RealmFuses::FuseIndex::OptimizeGetIteratorFuse:
4232 addOptimizeGetIteratorFuseDependency();
4233 return;
4234 default:
4235 // validateAndRegisterFuseDependencies doesn't have
4236 // handling for this yet, actively check fuse instead.
4237 break;
4238 }
4239
4240 Register temp = ToRegister(guard->temp0());
4241 Label bail;
4242
4243 // Bake specific fuse address for Ion code, because we won't share this code
4244 // across realms.
4245 GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex);
4246 masm.loadPtr(AbsoluteAddress(fuse->fuseRef()), temp);
4247 masm.branchPtr(Assembler::NotEqual, temp, ImmPtr(nullptr), &bail);
4248
4249 bailoutFrom(&bail, guard->snapshot());
4250}
4251
4252void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) {
4253 Register obj = ToRegister(guard->object());
4254 Register shapeList = ToRegister(guard->shapeList());
4255 Register temp = ToRegister(guard->temp0());
4256 Register temp2 = ToRegister(guard->temp1());
4257 Register temp3 = ToRegister(guard->temp2());
4258 Register spectre = ToTempRegisterOrInvalid(guard->temp3());
4259
4260 Label bail;
4261 masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp);
4262 masm.branchTestObjShapeList(Assembler::NotEqual, obj, temp, temp2, temp3,
4263 spectre, &bail);
4264 bailoutFrom(&bail, guard->snapshot());
4265}
4266
4267void CodeGenerator::visitGuardProto(LGuardProto* guard) {
4268 Register obj = ToRegister(guard->object());
4269 Register expected = ToRegister(guard->expected());
4270 Register temp = ToRegister(guard->temp0());
4271
4272 masm.loadObjProto(obj, temp);
4273
4274 Label bail;
4275 masm.branchPtr(Assembler::NotEqual, temp, expected, &bail);
4276 bailoutFrom(&bail, guard->snapshot());
4277}
4278
4279void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) {
4280 Register obj = ToRegister(guard->input());
4281 Register temp = ToRegister(guard->temp0());
4282
4283 masm.loadObjProto(obj, temp);
4284
4285 Label bail;
4286 masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail);
4287 bailoutFrom(&bail, guard->snapshot());
4288}
4289
4290void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) {
4291 Register obj = ToRegister(guard->input());
4292 Register temp = ToRegister(guard->temp0());
4293
4294 Label bail;
4295 masm.branchIfNonNativeObj(obj, temp, &bail);
4296 bailoutFrom(&bail, guard->snapshot());
4297}
4298
4299void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) {
4300 Register temp = ToRegister(guard->temp0());
4301 Label bail;
4302
4303 masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp);
4304 masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()),
4305 &bail);
4306 bailoutFrom(&bail, guard->snapshot());
4307}
4308
4309void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) {
4310 Register obj = ToRegister(guard->input());
4311 Register temp = ToRegister(guard->temp0());
4312
4313 Label bail;
4314 masm.branchTestObjectIsProxy(false, obj, temp, &bail);
4315 bailoutFrom(&bail, guard->snapshot());
4316}
4317
4318void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) {
4319 Register obj = ToRegister(guard->input());
4320 Register temp = ToRegister(guard->temp0());
4321
4322 Label bail;
4323 masm.branchTestObjectIsProxy(true, obj, temp, &bail);
4324 bailoutFrom(&bail, guard->snapshot());
4325}
4326
4327void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) {
4328 Register proxy = ToRegister(guard->proxy());
4329 Register temp = ToRegister(guard->temp0());
4330
4331 Label bail;
4332 masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp,
4333 GetDOMProxyHandlerFamily(), &bail);
4334 bailoutFrom(&bail, guard->snapshot());
4335}
4336
4337void CodeGenerator::visitProxyGet(LProxyGet* lir) {
4338 Register proxy = ToRegister(lir->proxy());
4339 Register temp = ToRegister(lir->temp0());
4340
4341 pushArg(lir->mir()->id(), temp);
4342 pushArg(proxy);
4343
4344 using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue);
4345 callVM<Fn, ProxyGetProperty>(lir);
4346}
4347
4348void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) {
4349 Register proxy = ToRegister(lir->proxy());
4350 ValueOperand idVal = ToValue(lir, LProxyGetByValue::IdIndex);
4351
4352 pushArg(idVal);
4353 pushArg(proxy);
4354
4355 using Fn =
4356 bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue);
4357 callVM<Fn, ProxyGetPropertyByValue>(lir);
4358}
4359
4360void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) {
4361 Register proxy = ToRegister(lir->proxy());
4362 ValueOperand idVal = ToValue(lir, LProxyHasProp::IdIndex);
4363
4364 pushArg(idVal);
4365 pushArg(proxy);
4366
4367 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*);
4368 if (lir->mir()->hasOwn()) {
4369 callVM<Fn, ProxyHasOwn>(lir);
4370 } else {
4371 callVM<Fn, ProxyHas>(lir);
4372 }
4373}
4374
4375void CodeGenerator::visitProxySet(LProxySet* lir) {
4376 Register proxy = ToRegister(lir->proxy());
4377 ValueOperand rhs = ToValue(lir, LProxySet::RhsIndex);
4378 Register temp = ToRegister(lir->temp0());
4379
4380 pushArg(Imm32(lir->mir()->strict()));
4381 pushArg(rhs);
4382 pushArg(lir->mir()->id(), temp);
4383 pushArg(proxy);
4384
4385 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
4386 callVM<Fn, ProxySetProperty>(lir);
4387}
4388
4389void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) {
4390 Register proxy = ToRegister(lir->proxy());
4391 ValueOperand idVal = ToValue(lir, LProxySetByValue::IdIndex);
4392 ValueOperand rhs = ToValue(lir, LProxySetByValue::RhsIndex);
4393
4394 pushArg(Imm32(lir->mir()->strict()));
4395 pushArg(rhs);
4396 pushArg(idVal);
4397 pushArg(proxy);
4398
4399 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool);
4400 callVM<Fn, ProxySetPropertyByValue>(lir);
4401}
4402
4403void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) {
4404 Register obj = ToRegister(lir->obj());
4405 ValueOperand rhs = ToValue(lir, LCallSetArrayLength::RhsIndex);
4406
4407 pushArg(Imm32(lir->mir()->strict()));
4408 pushArg(rhs);
4409 pushArg(obj);
4410
4411 using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool);
4412 callVM<Fn, jit::SetArrayLength>(lir);
4413}
4414
4415void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) {
4416 Register obj = ToRegister(lir->object());
4417 Register temp0 = ToRegister(lir->temp0());
4418 Register temp1 = ToRegister(lir->temp1());
4419 Register temp2 = ToRegister(lir->temp2());
4420 Register temp3 = ToRegister(lir->temp3());
4421 ValueOperand output = ToOutValue(lir);
4422
4423 Label bail, cacheHit;
4424 masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2,
4425 output, &cacheHit);
4426
4427 masm.branchIfNonNativeObj(obj, temp0, &bail);
4428
4429 masm.Push(UndefinedValue());
4430 masm.moveStackPtrTo(temp3);
4431
4432 using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id,
4433 MegamorphicCache::Entry* cacheEntry, Value* vp);
4434 masm.setupAlignedABICall();
4435 masm.loadJSContext(temp0);
4436 masm.passABIArg(temp0);
4437 masm.passABIArg(obj);
4438 masm.movePropertyKey(lir->mir()->name(), temp1);
4439 masm.passABIArg(temp1);
4440 masm.passABIArg(temp2);
4441 masm.passABIArg(temp3);
4442
4443 masm.callWithABI<Fn, GetNativeDataPropertyPure>();
4444
4445 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"
, 4445); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)"
")"); do { *((volatile int*)__null) = 4445; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4446 masm.Pop(output);
4447
4448 masm.branchIfFalseBool(ReturnReg, &bail);
4449
4450 masm.bind(&cacheHit);
4451 bailoutFrom(&bail, lir->snapshot());
4452}
4453
4454void CodeGenerator::visitMegamorphicLoadSlotByValue(
4455 LMegamorphicLoadSlotByValue* lir) {
4456 Register obj = ToRegister(lir->object());
4457 ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex);
4458 Register temp0 = ToRegister(lir->temp0());
4459 Register temp1 = ToRegister(lir->temp1());
4460 Register temp2 = ToRegister(lir->temp2());
4461 ValueOperand output = ToOutValue(lir);
4462
4463 Label bail, cacheHit;
4464 masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2,
4465 output, &cacheHit);
4466
4467 masm.branchIfNonNativeObj(obj, temp0, &bail);
4468
4469 // idVal will be in vp[0], result will be stored in vp[1].
4470 masm.reserveStack(sizeof(Value));
4471 masm.Push(idVal);
4472 masm.moveStackPtrTo(temp0);
4473
4474 using Fn = bool (*)(JSContext* cx, JSObject* obj,
4475 MegamorphicCache::Entry* cacheEntry, Value* vp);
4476 masm.setupAlignedABICall();
4477 masm.loadJSContext(temp1);
4478 masm.passABIArg(temp1);
4479 masm.passABIArg(obj);
4480 masm.passABIArg(temp2);
4481 masm.passABIArg(temp0);
4482 masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>();
4483
4484 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"
, 4484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)"
")"); do { *((volatile int*)__null) = 4484; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4485 masm.storeCallPointerResult(temp0);
4486 masm.Pop(idVal);
4487
4488 uint32_t framePushed = masm.framePushed();
4489 Label ok;
4490 masm.branchIfTrueBool(temp0, &ok);
4491 masm.freeStack(sizeof(Value)); // Discard result Value.
4492 masm.jump(&bail);
4493
4494 masm.bind(&ok);
4495 masm.setFramePushed(framePushed);
4496 masm.Pop(output);
4497
4498 masm.bind(&cacheHit);
4499 bailoutFrom(&bail, lir->snapshot());
4500}
4501
4502void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) {
4503 Register obj = ToRegister(lir->object());
4504 ValueOperand value = ToValue(lir, LMegamorphicStoreSlot::RhsIndex);
4505
4506 Register temp0 = ToRegister(lir->temp0());
4507#ifndef JS_CODEGEN_X86
4508 Register temp1 = ToRegister(lir->temp1());
4509 Register temp2 = ToRegister(lir->temp2());
4510#endif
4511
4512 Label cacheHit, done;
4513#ifdef JS_CODEGEN_X86
4514 masm.emitMegamorphicCachedSetSlot(
4515 lir->mir()->name(), obj, temp0, value, &cacheHit,
4516 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
4517 EmitPreBarrier(masm, addr, mirType);
4518 });
4519#else
4520 masm.emitMegamorphicCachedSetSlot(
4521 lir->mir()->name(), obj, temp0, temp1, temp2, value, &cacheHit,
4522 [](MacroAssembler& masm, const Address& addr, MIRType mirType) {
4523 EmitPreBarrier(masm, addr, mirType);
4524 });
4525#endif
4526
4527 pushArg(Imm32(lir->mir()->strict()));
4528 pushArg(value);
4529 pushArg(lir->mir()->name(), temp0);
4530 pushArg(obj);
4531
4532 using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool);
4533 callVM<Fn, SetPropertyMegamorphic<true>>(lir);
4534
4535 masm.jump(&done);
4536 masm.bind(&cacheHit);
4537
4538 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done);
4539 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done);
4540
4541 saveVolatile(temp0);
4542 emitPostWriteBarrier(obj);
4543 restoreVolatile(temp0);
4544
4545 masm.bind(&done);
4546}
4547
4548void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) {
4549 Register obj = ToRegister(lir->object());
4550 ValueOperand idVal = ToValue(lir, LMegamorphicHasProp::IdIndex);
4551 Register temp0 = ToRegister(lir->temp0());
4552 Register temp1 = ToRegister(lir->temp1());
4553 Register temp2 = ToRegister(lir->temp2());
4554 Register output = ToRegister(lir->output());
4555
4556 Label bail, cacheHit;
4557 masm.emitMegamorphicCacheLookupExists(idVal, obj, temp0, temp1, temp2, output,
4558 &cacheHit, lir->mir()->hasOwn());
4559
4560 masm.branchIfNonNativeObj(obj, temp0, &bail);
4561
4562 // idVal will be in vp[0], result will be stored in vp[1].
4563 masm.reserveStack(sizeof(Value));
4564 masm.Push(idVal);
4565 masm.moveStackPtrTo(temp0);
4566
4567 using Fn = bool (*)(JSContext* cx, JSObject* obj,
4568 MegamorphicCache::Entry* cacheEntry, Value* vp);
4569 masm.setupAlignedABICall();
4570 masm.loadJSContext(temp1);
4571 masm.passABIArg(temp1);
4572 masm.passABIArg(obj);
4573 masm.passABIArg(temp2);
4574 masm.passABIArg(temp0);
4575 if (lir->mir()->hasOwn()) {
4576 masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>();
4577 } else {
4578 masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>();
4579 }
4580
4581 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"
, 4581); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)"
")"); do { *((volatile int*)__null) = 4581; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4582 masm.storeCallPointerResult(temp0);
4583 masm.Pop(idVal);
4584
4585 uint32_t framePushed = masm.framePushed();
4586 Label ok;
4587 masm.branchIfTrueBool(temp0, &ok);
4588 masm.freeStack(sizeof(Value)); // Discard result Value.
4589 masm.jump(&bail);
4590
4591 masm.bind(&ok);
4592 masm.setFramePushed(framePushed);
4593 masm.unboxBoolean(Address(masm.getStackPointer(), 0), output);
4594 masm.freeStack(sizeof(Value));
4595 masm.bind(&cacheHit);
4596
4597 bailoutFrom(&bail, lir->snapshot());
4598}
4599
4600void CodeGenerator::visitSmallObjectVariableKeyHasProp(
4601 LSmallObjectVariableKeyHasProp* lir) {
4602 Register id = ToRegister(lir->id());
4603 Register output = ToRegister(lir->output());
4604
4605#ifdef DEBUG1
4606 Label isAtom;
4607 masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()),
4608 Imm32(JSString::ATOM_BIT), &isAtom);
4609 masm.assumeUnreachable("Expected atom input");
4610 masm.bind(&isAtom);
4611#endif
4612
4613 SharedShape* shape = &lir->mir()->shape()->asShared();
4614
4615 Label done, success;
4616 for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) {
4617 masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()),
4618 &success);
4619 }
4620 masm.move32(Imm32(0), output);
4621 masm.jump(&done);
4622 masm.bind(&success);
4623 masm.move32(Imm32(1), output);
4624 masm.bind(&done);
4625}
4626
4627void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared(
4628 LGuardIsNotArrayBufferMaybeShared* guard) {
4629 Register obj = ToRegister(guard->input());
4630 Register temp = ToRegister(guard->temp0());
4631
4632 Label bail;
4633 masm.loadObjClassUnsafe(obj, temp);
4634 masm.branchPtr(Assembler::Equal, temp,
4635 ImmPtr(&FixedLengthArrayBufferObject::class_), &bail);
4636 masm.branchPtr(Assembler::Equal, temp,
4637 ImmPtr(&FixedLengthSharedArrayBufferObject::class_), &bail);
4638 masm.branchPtr(Assembler::Equal, temp,
4639 ImmPtr(&ResizableArrayBufferObject::class_), &bail);
4640 masm.branchPtr(Assembler::Equal, temp,
4641 ImmPtr(&GrowableSharedArrayBufferObject::class_), &bail);
4642 bailoutFrom(&bail, guard->snapshot());
4643}
4644
4645void CodeGenerator::visitGuardIsTypedArray(LGuardIsTypedArray* guard) {
4646 Register obj = ToRegister(guard->input());
4647 Register temp = ToRegister(guard->temp0());
4648
4649 Label bail;
4650 masm.loadObjClassUnsafe(obj, temp);
4651 masm.branchIfClassIsNotTypedArray(temp, &bail);
4652 bailoutFrom(&bail, guard->snapshot());
4653}
4654
4655void CodeGenerator::visitGuardIsFixedLengthTypedArray(
4656 LGuardIsFixedLengthTypedArray* guard) {
4657 Register obj = ToRegister(guard->input());
4658 Register temp = ToRegister(guard->temp0());
4659
4660 Label bail;
4661 masm.loadObjClassUnsafe(obj, temp);
4662 masm.branchIfClassIsNotFixedLengthTypedArray(temp, &bail);
4663 bailoutFrom(&bail, guard->snapshot());
4664}
4665
4666void CodeGenerator::visitGuardIsResizableTypedArray(
4667 LGuardIsResizableTypedArray* guard) {
4668 Register obj = ToRegister(guard->input());
4669 Register temp = ToRegister(guard->temp0());
4670
4671 Label bail;
4672 masm.loadObjClassUnsafe(obj, temp);
4673 masm.branchIfClassIsNotResizableTypedArray(temp, &bail);
4674 bailoutFrom(&bail, guard->snapshot());
4675}
4676
4677void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) {
4678 Register obj = ToRegister(guard->input());
4679
4680 Label bail;
4681
4682 Address handlerAddr(obj, ProxyObject::offsetOfHandler());
4683 masm.branchPtr(Assembler::NotEqual, handlerAddr,
4684 ImmPtr(guard->mir()->handler()), &bail);
4685
4686 bailoutFrom(&bail, guard->snapshot());
4687}
4688
4689void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) {
4690 Register input = ToRegister(guard->input());
4691 Register expected = ToRegister(guard->expected());
4692
4693 Assembler::Condition cond =
4694 guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
4695 bailoutCmpPtr(cond, input, expected, guard->snapshot());
4696}
4697
4698void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) {
4699 Register input = ToRegister(guard->input());
4700 Register expected = ToRegister(guard->expected());
4701
4702 bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot());
4703}
4704
4705void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) {
4706 Register str = ToRegister(guard->str());
4707 Register scratch = ToRegister(guard->temp0());
4708
4709 LiveRegisterSet volatileRegs = liveVolatileRegs(guard);
4710 volatileRegs.takeUnchecked(scratch);
4711
4712 Label bail;
4713 masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs,
4714 &bail);
4715 bailoutFrom(&bail, guard->snapshot());
4716}
4717
4718void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) {
4719 Register symbol = ToRegister(guard->symbol());
4720
4721 bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()),
4722 guard->snapshot());
4723}
4724
4725void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) {
4726 Register num = ToRegister(guard->num());
4727
4728 bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()),
4729 guard->snapshot());
4730}
4731
4732void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) {
4733 Register str = ToRegister(lir->string());
4734 Register output = ToRegister(lir->output());
4735
4736 Label vmCall, done;
4737 masm.loadStringIndexValue(str, output, &vmCall);
4738 masm.jump(&done);
4739
4740 {
4741 masm.bind(&vmCall);
4742
4743 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
4744 volatileRegs.takeUnchecked(output);
4745 masm.PushRegsInMask(volatileRegs);
4746
4747 using Fn = int32_t (*)(JSString* str);
4748 masm.setupAlignedABICall();
4749 masm.passABIArg(str);
4750 masm.callWithABI<Fn, GetIndexFromString>();
4751 masm.storeCallInt32Result(output);
4752
4753 masm.PopRegsInMask(volatileRegs);
4754
4755 // GetIndexFromString returns a negative value on failure.
4756 bailoutTest32(Assembler::Signed, output, output, lir->snapshot());
4757 }
4758
4759 masm.bind(&done);
4760}
4761
4762void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) {
4763 Register str = ToRegister(lir->string());
4764 Register output = ToRegister(lir->output());
4765 Register temp = ToRegister(lir->temp0());
4766
4767 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
4768
4769 Label bail;
4770 masm.guardStringToInt32(str, output, temp, volatileRegs, &bail);
4771 bailoutFrom(&bail, lir->snapshot());
4772}
4773
4774void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) {
4775 Register str = ToRegister(lir->string());
4776 FloatRegister output = ToFloatRegister(lir->output());
4777 Register temp0 = ToRegister(lir->temp0());
4778 Register temp1 = ToRegister(lir->temp1());
4779
4780 Label vmCall, done;
4781 // Use indexed value as fast path if possible.
4782 masm.loadStringIndexValue(str, temp0, &vmCall);
4783 masm.convertInt32ToDouble(temp0, output);
4784 masm.jump(&done);
4785 {
4786 masm.bind(&vmCall);
4787
4788 // Reserve stack for holding the result value of the call.
4789 masm.reserveStack(sizeof(double));
4790 masm.moveStackPtrTo(temp0);
4791
4792 LiveRegisterSet volatileRegs = liveVolatileRegs(lir);
4793 volatileRegs.takeUnchecked(temp0);
4794 volatileRegs.takeUnchecked(temp1);
4795 masm.PushRegsInMask(volatileRegs);
4796
4797 using Fn = bool (*)(JSContext* cx, JSString* str, double* result);
4798 masm.setupAlignedABICall();
4799 masm.loadJSContext(temp1);
4800 masm.passABIArg(temp1);
4801 masm.passABIArg(str);
4802 masm.passABIArg(temp0);
4803 masm.callWithABI<Fn, StringToNumberPure>();
4804 masm.storeCallPointerResult(temp0);
4805
4806 masm.PopRegsInMask(volatileRegs);
4807
4808 Label ok;
4809 masm.branchIfTrueBool(temp0, &ok);
4810 {
4811 // OOM path, recovered by StringToNumberPure.
4812 //
4813 // Use addToStackPtr instead of freeStack as freeStack tracks stack height
4814 // flow-insensitively, and using it here would confuse the stack height
4815 // tracking.
4816 masm.addToStackPtr(Imm32(sizeof(double)));
4817 bailout(lir->snapshot());
4818 }
4819 masm.bind(&ok);
4820 masm.Pop(output);
4821 }
4822 masm.bind(&done);
4823}
4824
4825void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) {
4826 Register obj = ToRegister(guard->input());
4827 Register temp = ToRegister(guard->temp0());
4828
4829 // Load obj->elements.
4830 masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp);
4831
4832 // Make sure there are no dense elements.
4833 Address initLength(temp, ObjectElements::offsetOfInitializedLength());
4834 bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot());
4835}
4836
4837void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) {
4838 Register input = ToRegister(lir->input());
4839 Register64 output = ToOutRegister64(lir);
4840
4841 masm.move32To64ZeroExtend(input, output);
4842}
4843
4844void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input,
4845 Register64 output) {
4846 Register temp = output.scratchReg();
4847
4848 saveLive(lir);
4849
4850 masm.reserveStack(sizeof(uint64_t));
4851 masm.moveStackPtrTo(temp);
4852 pushArg(temp);
4853 pushArg(input);
4854
4855 using Fn = bool (*)(JSContext*, HandleString, uint64_t*);
4856 callVM<Fn, DoStringToInt64>(lir);
4857
4858 masm.load64(Address(masm.getStackPointer(), 0), output);
4859 masm.freeStack(sizeof(uint64_t));
4860
4861 restoreLiveIgnore(lir, StoreValueTo(output).clobbered());
4862}
4863
4864void CodeGenerator::visitStringToInt64(LStringToInt64* lir) {
4865 Register input = ToRegister(lir->input());
4866 Register64 output = ToOutRegister64(lir);
4867
4868 emitStringToInt64(lir, input, output);
4869}
4870
4871void CodeGenerator::visitValueToInt64(LValueToInt64* lir) {
4872 ValueOperand input = ToValue(lir, LValueToInt64::InputIndex);
4873 Register temp = ToRegister(lir->temp0());
4874 Register64 output = ToOutRegister64(lir);
4875
4876 int checks = 3;
4877
4878 Label fail, done;
4879 // Jump to fail if this is the last check and we fail it,
4880 // otherwise to the next test.
4881 auto emitTestAndUnbox = [&](auto testAndUnbox) {
4882 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"
, 4882); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks > 0"
")"); do { *((volatile int*)__null) = 4882; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4883
4884 checks--;
4885 Label notType;
4886 Label* target = checks ? &notType : &fail;
4887
4888 testAndUnbox(target);
4889
4890 if (checks) {
4891 masm.jump(&done);
4892 masm.bind(&notType);
4893 }
4894 };
4895
4896 Register tag = masm.extractTag(input, temp);
4897
4898 // BigInt.
4899 emitTestAndUnbox([&](Label* target) {
4900 masm.branchTestBigInt(Assembler::NotEqual, tag, target);
4901 masm.unboxBigInt(input, temp);
4902 masm.loadBigInt64(temp, output);
4903 });
4904
4905 // Boolean
4906 emitTestAndUnbox([&](Label* target) {
4907 masm.branchTestBoolean(Assembler::NotEqual, tag, target);
4908 masm.unboxBoolean(input, temp);
4909 masm.move32To64ZeroExtend(temp, output);
4910 });
4911
4912 // String
4913 emitTestAndUnbox([&](Label* target) {
4914 masm.branchTestString(Assembler::NotEqual, tag, target);
4915 masm.unboxString(input, temp);
4916 emitStringToInt64(lir, temp, output);
4917 });
4918
4919 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"
, 4919); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0"
")"); do { *((volatile int*)__null) = 4919; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4920
4921 bailoutFrom(&fail, lir->snapshot());
4922 masm.bind(&done);
4923}
4924
4925void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) {
4926 Register operand = ToRegister(lir->input());
4927 Register64 output = ToOutRegister64(lir);
4928
4929 masm.loadBigInt64(operand, output);
4930}
4931
4932OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir,
4933 Scalar::Type type,
4934 Register64 input,
4935 Register output) {
4936#if JS_BITS_PER_WORD64 == 32
4937 using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t);
4938 auto args = ArgList(input.low, input.high);
4939#else
4940 using Fn = BigInt* (*)(JSContext*, uint64_t);
4941 auto args = ArgList(input);
4942#endif
4943
4944 if (type == Scalar::BigInt64) {
4945 return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args,
4946 StoreRegisterTo(output));
4947 }
4948 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"
, 4948); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == Scalar::BigUint64"
")"); do { *((volatile int*)__null) = 4948; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4949 return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args,
4950 StoreRegisterTo(output));
4951}
4952
4953void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type,
4954 Register64 input, Register output,
4955 Register maybeTemp) {
4956 OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output);
4957
4958 if (maybeTemp != InvalidReg) {
4959 masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry());
4960 } else {
4961 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
4962 regs.take(input);
4963 regs.take(output);
4964
4965 Register temp = regs.takeAny();
4966
4967 masm.push(temp);
4968
4969 Label fail, ok;
4970 masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail);
4971 masm.pop(temp);
4972 masm.jump(&ok);
4973 masm.bind(&fail);
4974 masm.pop(temp);
4975 masm.jump(ool->entry());
4976 masm.bind(&ok);
4977 }
4978 masm.initializeBigInt64(type, output, input);
4979 masm.bind(ool->rejoin());
4980}
4981
4982void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) {
4983 Register64 input = ToRegister64(lir->input());
4984 Register temp = ToRegister(lir->temp0());
4985 Register output = ToRegister(lir->output());
4986
4987 emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp);
4988}
4989
4990void CodeGenerator::visitGuardValue(LGuardValue* lir) {
4991 ValueOperand input = ToValue(lir, LGuardValue::InputIndex);
4992 Value expected = lir->mir()->expected();
4993 Label bail;
4994 masm.branchTestValue(Assembler::NotEqual, input, expected, &bail);
4995 bailoutFrom(&bail, lir->snapshot());
4996}
4997
4998void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) {
4999 ValueOperand input = ToValue(lir, LGuardNullOrUndefined::InputIndex);
5000
5001 ScratchTagScope tag(masm, input);
5002 masm.splitTagForTest(input, tag);
5003
5004 Label done;
5005 masm.branchTestNull(Assembler::Equal, tag, &done);
5006
5007 Label bail;
5008 masm.branchTestUndefined(Assembler::NotEqual, tag, &bail);
5009 bailoutFrom(&bail, lir->snapshot());
5010
5011 masm.bind(&done);
5012}
5013
5014void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) {
5015 ValueOperand input = ToValue(lir, LGuardIsNotObject::InputIndex);
5016
5017 Label bail;
5018 masm.branchTestObject(Assembler::Equal, input, &bail);
5019 bailoutFrom(&bail, lir->snapshot());
5020}
5021
5022void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) {
5023 Register function = ToRegister(lir->function());
5024
5025 Label bail;
5026 if (uint16_t flags = lir->mir()->expectedFlags()) {
5027 masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail);
5028 }
5029 if (uint16_t flags = lir->mir()->unexpectedFlags()) {
5030 masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail);
5031 }
5032 bailoutFrom(&bail, lir->snapshot());
5033}
5034
5035void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor(
5036 LGuardFunctionIsNonBuiltinCtor* lir) {
5037 Register function = ToRegister(lir->function());
5038 Register temp = ToRegister(lir->temp0());
5039
5040 Label bail;
5041 masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail);
5042 bailoutFrom(&bail, lir->snapshot());
5043}
5044
5045void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) {
5046 Register function = ToRegister(lir->function());
5047 Register temp = ToRegister(lir->temp0());
5048
5049 Assembler::Condition cond =
5050 lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
5051
5052 Label bail;
5053 masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail);
5054 bailoutFrom(&bail, lir->snapshot());
5055}
5056
5057void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) {
5058 Register function = ToRegister(lir->function());
5059
5060 Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript());
5061 bailoutCmpPtr(Assembler::NotEqual, scriptAddr,
5062 ImmGCPtr(lir->mir()->expected()), lir->snapshot());
5063}
5064
5065// Out-of-line path to update the store buffer.
5066class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> {
5067 LInstruction* lir_;
5068 const LAllocation* object_;
5069
5070 public:
5071 OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object)
5072 : lir_(lir), object_(object) {}
5073
5074 void accept(CodeGenerator* codegen) override {
5075 codegen->visitOutOfLineCallPostWriteBarrier(this);
5076 }
5077
5078 LInstruction* lir() const { return lir_; }
5079 const LAllocation* object() const { return object_; }
5080};
5081
5082static void EmitStoreBufferCheckForConstant(MacroAssembler& masm,
5083 const gc::TenuredCell* cell,
5084 AllocatableGeneralRegisterSet& regs,
5085 Label* exit, Label* callVM) {
5086 Register temp = regs.takeAny();
5087
5088 gc::Arena* arena = cell->arena();
5089
5090 Register cells = temp;
5091 masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells);
5092
5093 size_t index = gc::ArenaCellSet::getCellIndex(cell);
5094 size_t word;
5095 uint32_t mask;
5096 gc::ArenaCellSet::getWordIndexAndMask(index, &word, &mask);
5097 size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t);
5098
5099 masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask),
5100 exit);
5101
5102 // Check whether this is the sentinel set and if so call the VM to allocate
5103 // one for this arena.
5104 masm.branchPtr(Assembler::Equal,
5105 Address(cells, gc::ArenaCellSet::offsetOfArena()),
5106 ImmPtr(nullptr), callVM);
5107
5108 // Add the cell to the set.
5109 masm.or32(Imm32(mask), Address(cells, offset));
5110 masm.jump(exit);
5111
5112 regs.add(temp);
5113}
5114
5115static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime,
5116 Register objreg, JSObject* maybeConstant,
5117 bool isGlobal,
5118 AllocatableGeneralRegisterSet& regs) {
5119 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"
, 5119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeConstant"
")"); do { *((volatile int*)__null) = 5119; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5120
5121 Label callVM;
5122 Label exit;
5123
5124 Register temp = regs.takeAny();
5125
5126 // We already have a fast path to check whether a global is in the store
5127 // buffer.
5128 if (!isGlobal) {
5129 if (maybeConstant) {
5130 // Check store buffer bitmap directly for known object.
5131 EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs,
5132 &exit, &callVM);
5133 } else {
5134 // Check one element cache to avoid VM call.
5135 masm.branchPtr(Assembler::Equal,
5136 AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()),
5137 objreg, &exit);
5138 }
5139 }
5140
5141 // Call into the VM to barrier the write.
5142 masm.bind(&callVM);
5143
5144 Register runtimereg = temp;
5145 masm.mov(ImmPtr(runtime), runtimereg);
5146
5147 masm.setupAlignedABICall();
5148 masm.passABIArg(runtimereg);
5149 masm.passABIArg(objreg);
5150 if (isGlobal) {
5151 using Fn = void (*)(JSRuntime* rt, GlobalObject* obj);
5152 masm.callWithABI<Fn, PostGlobalWriteBarrier>();
5153 } else {
5154 using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj);
5155 masm.callWithABI<Fn, PostWriteBarrier>();
5156 }
5157
5158 masm.bind(&exit);
5159}
5160
5161void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) {
5162 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5163
5164 Register objreg;
5165 JSObject* object = nullptr;
5166 bool isGlobal = false;
5167 if (obj->isConstant()) {
5168 object = &obj->toConstant()->toObject();
5169 isGlobal = isGlobalObject(object);
5170 objreg = regs.takeAny();
5171 masm.movePtr(ImmGCPtr(object), objreg);
5172 } else {
5173 objreg = ToRegister(obj);
5174 regs.takeUnchecked(objreg);
5175 }
5176
5177 EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs);
5178}
5179
5180// Returns true if `def` might be allocated in the nursery.
5181static bool ValueNeedsPostBarrier(MDefinition* def) {
5182 if (def->isBox()) {
5183 def = def->toBox()->input();
5184 }
5185 if (def->type() == MIRType::Value) {
5186 return true;
5187 }
5188 return NeedsPostBarrier(def->type());
5189}
5190
5191class OutOfLineElementPostWriteBarrier
5192 : public OutOfLineCodeBase<CodeGenerator> {
5193 LiveRegisterSet liveVolatileRegs_;
5194 const LAllocation* index_;
5195 int32_t indexDiff_;
5196 Register obj_;
5197 Register scratch_;
5198
5199 public:
5200 OutOfLineElementPostWriteBarrier(const LiveRegisterSet& liveVolatileRegs,
5201 Register obj, const LAllocation* index,
5202 Register scratch, int32_t indexDiff)
5203 : liveVolatileRegs_(liveVolatileRegs),
5204 index_(index),
5205 indexDiff_(indexDiff),
5206 obj_(obj),
5207 scratch_(scratch) {}
5208
5209 void accept(CodeGenerator* codegen) override {
5210 codegen->visitOutOfLineElementPostWriteBarrier(this);
5211 }
5212
5213 const LiveRegisterSet& liveVolatileRegs() const { return liveVolatileRegs_; }
5214 const LAllocation* index() const { return index_; }
5215 int32_t indexDiff() const { return indexDiff_; }
5216
5217 Register object() const { return obj_; }
5218 Register scratch() const { return scratch_; }
5219};
5220
5221void CodeGenerator::emitElementPostWriteBarrier(
5222 MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj,
5223 const LAllocation* index, Register scratch, const ConstantOrRegister& val,
5224 int32_t indexDiff) {
5225 if (val.constant()) {
5226 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"
, 5227); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { *((volatile int*)__null) = 5227; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
5227 !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"
, 5227); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())"
")"); do { *((volatile int*)__null) = 5227; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5228 return;
5229 }
5230
5231 TypedOrValueRegister reg = val.reg();
5232 if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) {
5233 return;
5234 }
5235
5236 auto* ool = new (alloc()) OutOfLineElementPostWriteBarrier(
5237 liveVolatileRegs, obj, index, scratch, indexDiff);
5238 addOutOfLineCode(ool, mir);
5239
5240 masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, ool->rejoin());
5241
5242 if (reg.hasValue()) {
5243 masm.branchValueIsNurseryCell(Assembler::Equal, reg.valueReg(), scratch,
5244 ool->entry());
5245 } else {
5246 masm.branchPtrInNurseryChunk(Assembler::Equal, reg.typedReg().gpr(),
5247 scratch, ool->entry());
5248 }
5249
5250 masm.bind(ool->rejoin());
5251}
5252
5253void CodeGenerator::visitOutOfLineElementPostWriteBarrier(
5254 OutOfLineElementPostWriteBarrier* ool) {
5255 Register obj = ool->object();
5256 Register scratch = ool->scratch();
5257 const LAllocation* index = ool->index();
5258 int32_t indexDiff = ool->indexDiff();
5259
5260 masm.PushRegsInMask(ool->liveVolatileRegs());
5261
5262 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5263 regs.takeUnchecked(obj);
5264 regs.takeUnchecked(scratch);
5265
5266 Register indexReg;
5267 if (index->isConstant()) {
5268 indexReg = regs.takeAny();
5269 masm.move32(Imm32(ToInt32(index) + indexDiff), indexReg);
5270 } else {
5271 indexReg = ToRegister(index);
5272 regs.takeUnchecked(indexReg);
5273 if (indexDiff != 0) {
5274 masm.add32(Imm32(indexDiff), indexReg);
5275 }
5276 }
5277
5278 masm.setupUnalignedABICall(scratch);
5279 masm.movePtr(ImmPtr(gen->runtime), scratch);
5280 masm.passABIArg(scratch);
5281 masm.passABIArg(obj);
5282 masm.passABIArg(indexReg);
5283 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
5284 masm.callWithABI<Fn, PostWriteElementBarrier>();
5285
5286 // We don't need a sub32 here because indexReg must be in liveVolatileRegs
5287 // if indexDiff is not zero, so it will be restored below.
5288 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"
, 5288); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ool->liveVolatileRegs().has(indexReg)"
")"); do { *((volatile int*)__null) = 5288; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5289
5290 masm.PopRegsInMask(ool->liveVolatileRegs());
5291
5292 masm.jump(ool->rejoin());
5293}
5294
5295void CodeGenerator::emitPostWriteBarrier(Register objreg) {
5296 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5297 regs.takeUnchecked(objreg);
5298 EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs);
5299}
5300
5301void CodeGenerator::visitOutOfLineCallPostWriteBarrier(
5302 OutOfLineCallPostWriteBarrier* ool) {
5303 saveLiveVolatile(ool->lir());
5304 const LAllocation* obj = ool->object();
5305 emitPostWriteBarrier(obj);
5306 restoreLiveVolatile(ool->lir());
5307
5308 masm.jump(ool->rejoin());
5309}
5310
5311void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal,
5312 OutOfLineCode* ool) {
5313 // Check whether an object is a global that we have already barriered before
5314 // calling into the VM.
5315 //
5316 // We only check for the script's global, not other globals within the same
5317 // compartment, because we bake in a pointer to realm->globalWriteBarriered
5318 // and doing that would be invalid for other realms because they could be
5319 // collected before the Ion code is discarded.
5320
5321 if (!maybeGlobal->isConstant()) {
5322 return;
5323 }
5324
5325 JSObject* obj = &maybeGlobal->toConstant()->toObject();
5326 if (gen->realm->maybeGlobal() != obj) {
5327 return;
5328 }
5329
5330 const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered();
5331 masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0),
5332 ool->rejoin());
5333}
5334
5335template <class LPostBarrierType, MIRType nurseryType>
5336void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir,
5337 OutOfLineCode* ool) {
5338 static_assert(NeedsPostBarrier(nurseryType));
5339
5340 addOutOfLineCode(ool, lir->mir());
5341
5342 Register temp = ToTempRegisterOrInvalid(lir->temp0());
5343
5344 if (lir->object()->isConstant()) {
5345 // Constant nursery objects cannot appear here, see
5346 // LIRGenerator::visitPostWriteElementBarrier.
5347 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"
, 5347); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { *((volatile int*)__null) = 5347; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5348 } else {
5349 masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()),
5350 temp, ool->rejoin());
5351 }
5352
5353 maybeEmitGlobalBarrierCheck(lir->object(), ool);
5354
5355 Register value = ToRegister(lir->value());
5356 if constexpr (nurseryType == MIRType::Object) {
5357 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"
, 5357); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object"
")"); do { *((volatile int*)__null) = 5357; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5358 } else if constexpr (nurseryType == MIRType::String) {
5359 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"
, 5359); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String"
")"); do { *((volatile int*)__null) = 5359; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5360 } else {
5361 static_assert(nurseryType == MIRType::BigInt);
5362 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"
, 5362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt"
")"); do { *((volatile int*)__null) = 5362; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5363 }
5364 masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry());
5365
5366 masm.bind(ool->rejoin());
5367}
5368
5369template <class LPostBarrierType>
5370void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir,
5371 OutOfLineCode* ool) {
5372 addOutOfLineCode(ool, lir->mir());
5373
5374 Register temp = ToTempRegisterOrInvalid(lir->temp0());
5375
5376 if (lir->object()->isConstant()) {
5377 // Constant nursery objects cannot appear here, see
5378 // LIRGenerator::visitPostWriteElementBarrier.
5379 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"
, 5379); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())"
")"); do { *((volatile int*)__null) = 5379; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5380 } else {
5381 masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()),
5382 temp, ool->rejoin());
5383 }
5384
5385 maybeEmitGlobalBarrierCheck(lir->object(), ool);
5386
5387 ValueOperand value = ToValue(lir, LPostBarrierType::ValueIndex);
5388 masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry());
5389
5390 masm.bind(ool->rejoin());
5391}
5392
5393void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) {
5394 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5395 visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool);
5396}
5397
5398void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) {
5399 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5400 visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool);
5401}
5402
5403void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) {
5404 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5405 visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool);
5406}
5407
5408void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) {
5409 auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object());
5410 visitPostWriteBarrierCommonV(lir, ool);
5411}
5412
5413// Out-of-line path to update the store buffer.
5414class OutOfLineCallPostWriteElementBarrier
5415 : public OutOfLineCodeBase<CodeGenerator> {
5416 LInstruction* lir_;
5417 const LAllocation* object_;
5418 const LAllocation* index_;
5419
5420 public:
5421 OutOfLineCallPostWriteElementBarrier(LInstruction* lir,
5422 const LAllocation* object,
5423 const LAllocation* index)
5424 : lir_(lir), object_(object), index_(index) {}
5425
5426 void accept(CodeGenerator* codegen) override {
5427 codegen->visitOutOfLineCallPostWriteElementBarrier(this);
5428 }
5429
5430 LInstruction* lir() const { return lir_; }
5431
5432 const LAllocation* object() const { return object_; }
5433
5434 const LAllocation* index() const { return index_; }
5435};
5436
5437void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier(
5438 OutOfLineCallPostWriteElementBarrier* ool) {
5439 saveLiveVolatile(ool->lir());
5440
5441 const LAllocation* obj = ool->object();
5442 const LAllocation* index = ool->index();
5443
5444 Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj);
5445 Register indexreg = ToRegister(index);
5446
5447 AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
5448 regs.takeUnchecked(indexreg);
5449
5450 if (obj->isConstant()) {
5451 objreg = regs.takeAny();
5452 masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg);
5453 } else {
5454 regs.takeUnchecked(objreg);
5455 }
5456
5457 Register runtimereg = regs.takeAny();
5458 using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index);
5459 masm.setupAlignedABICall();
5460 masm.mov(ImmPtr(gen->runtime), runtimereg);
5461 masm.passABIArg(runtimereg);
5462 masm.passABIArg(objreg);
5463 masm.passABIArg(indexreg);
5464 masm.callWithABI<Fn, PostWriteElementBarrier>();
5465
5466 restoreLiveVolatile(ool->lir());
5467
5468 masm.jump(ool->rejoin());
5469}
5470
5471void CodeGenerator::visitPostWriteElementBarrierO(
5472 LPostWriteElementBarrierO* lir) {
5473 auto ool = new (alloc())
5474 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5475 visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir,
5476 ool);
5477}
5478
5479void CodeGenerator::visitPostWriteElementBarrierS(
5480 LPostWriteElementBarrierS* lir) {
5481 auto ool = new (alloc())
5482 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5483 visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir,
5484 ool);
5485}
5486
5487void CodeGenerator::visitPostWriteElementBarrierBI(
5488 LPostWriteElementBarrierBI* lir) {
5489 auto ool = new (alloc())
5490 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5491 visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir,
5492 ool);
5493}
5494
5495void CodeGenerator::visitPostWriteElementBarrierV(
5496 LPostWriteElementBarrierV* lir) {
5497 auto ool = new (alloc())
5498 OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index());
5499 visitPostWriteBarrierCommonV(lir, ool);
5500}
5501
5502void CodeGenerator::visitAssertCanElidePostWriteBarrier(
5503 LAssertCanElidePostWriteBarrier* lir) {
5504 Register object = ToRegister(lir->object());
5505 ValueOperand value =
5506 ToValue(lir, LAssertCanElidePostWriteBarrier::ValueIndex);
5507 Register temp = ToRegister(lir->temp0());
5508
5509 Label ok;
5510 masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok);
5511 masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok);
5512
5513 masm.assumeUnreachable("Unexpected missing post write barrier");
5514
5515 masm.bind(&ok);
5516}
5517
5518template <typename LCallIns>
5519void CodeGenerator::emitCallNative(LCallIns* call, JSNative native,
5520 Register argContextReg, Register argUintNReg,
5521 Register argVpReg, Register tempReg,
5522 uint32_t unusedStack) {
5523 masm.checkStackAlignment();
5524
5525 // Native functions have the signature:
5526 // bool (*)(JSContext*, unsigned, Value* vp)
5527 // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward
5528 // are the function arguments.
5529
5530 // Allocate space for the outparam, moving the StackPointer to what will be
5531 // &vp[1].
5532 masm.adjustStack(unusedStack);
5533
5534 // Push a Value containing the callee object: natives are allowed to access
5535 // their callee before setting the return value. The StackPointer is moved
5536 // to &vp[0].
5537 //
5538 // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|.
5539 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
5540 Register calleeReg = ToRegister(call->getCallee());
5541 masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg)));
5542
5543 // Enter the callee realm.
5544 if (call->mir()->maybeCrossRealm()) {
5545 masm.switchToObjectRealm(calleeReg, tempReg);
5546 }
5547 } else {
5548 WrappedFunction* target = call->mir()->getSingleTarget();
5549 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
5550
5551 // Enter the callee realm.
5552 if (call->mir()->maybeCrossRealm()) {
5553 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg);
5554 masm.switchToObjectRealm(tempReg, tempReg);
5555 }
5556 }
5557
5558 // Preload arguments into registers.
5559 masm.loadJSContext(argContextReg);
5560 masm.moveStackPtrTo(argVpReg);
5561
5562 // Initialize |NativeExitFrameLayout::argc_|.
5563 masm.Push(argUintNReg);
5564
5565 // Construct native exit frame.
5566 //
5567 // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and
5568 // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|.
5569 //
5570 // The NativeExitFrameLayout is now fully initialized.
5571 uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg);
5572 masm.enterFakeExitFrameForNative(argContextReg, tempReg,
5573 call->mir()->isConstructing());
5574
5575 markSafepointAt(safepointOffset, call);
5576
5577 // Construct and execute call.
5578 masm.setupAlignedABICall();
5579 masm.passABIArg(argContextReg);
5580 masm.passABIArg(argUintNReg);
5581 masm.passABIArg(argVpReg);
5582
5583 ensureOsiSpace();
5584 // If we're using a simulator build, `native` will already point to the
5585 // simulator's call-redirection code for LCallClassHook. Load the address in
5586 // a register first so that we don't try to redirect it a second time.
5587 bool emittedCall = false;
5588#ifdef JS_SIMULATOR
5589 if constexpr (std::is_same_v<LCallIns, LCallClassHook>) {
5590 masm.movePtr(ImmPtr(native), tempReg);
5591 masm.callWithABI(tempReg);
5592 emittedCall = true;
5593 }
5594#endif
5595 if (!emittedCall) {
5596 masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General,
5597 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
5598 }
5599
5600 // Test for failure.
5601 masm.branchIfFalseBool(ReturnReg, masm.failureLabel());
5602
5603 // Exit the callee realm.
5604 if (call->mir()->maybeCrossRealm()) {
5605 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5606 }
5607
5608 // Load the outparam vp[0] into output register(s).
5609 masm.loadValue(
5610 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
5611 JSReturnOperand);
5612
5613 // Until C++ code is instrumented against Spectre, prevent speculative
5614 // execution from returning any private data.
5615 if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() &&
5616 call->mir()->hasLiveDefUses()) {
5617 masm.speculationBarrier();
5618 }
5619
5620#ifdef DEBUG1
5621 // Native constructors are guaranteed to return an Object value.
5622 if (call->mir()->isConstructing()) {
5623 Label notPrimitive;
5624 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
5625 &notPrimitive);
5626 masm.assumeUnreachable("native constructors don't return primitives");
5627 masm.bind(&notPrimitive);
5628 }
5629#endif
5630}
5631
5632template <typename LCallIns>
5633void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) {
5634 uint32_t unusedStack =
5635 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
5636
5637 // Registers used for callWithABI() argument-passing.
5638 const Register argContextReg = ToRegister(call->getArgContextReg());
5639 const Register argUintNReg = ToRegister(call->getArgUintNReg());
5640 const Register argVpReg = ToRegister(call->getArgVpReg());
5641
5642 // Misc. temporary registers.
5643 const Register tempReg = ToRegister(call->getTempReg());
5644
5645 DebugOnly<uint32_t> initialStack = masm.framePushed();
5646
5647 // Initialize the argc register.
5648 masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg);
5649
5650 // Create the exit frame and call the native.
5651 emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg,
5652 unusedStack);
5653
5654 // The next instruction is removing the footer of the exit frame, so there
5655 // is no need for leaveFakeExitFrame.
5656
5657 // Move the StackPointer back to its original location, unwinding the native
5658 // exit frame.
5659 masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack);
5660 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"
, 5660); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 5660; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5661}
5662
5663void CodeGenerator::visitCallNative(LCallNative* call) {
5664 WrappedFunction* target = call->getSingleTarget();
5665 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"
, 5665); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { *((volatile int*)__null) = 5665; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5666 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"
, 5666); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 5666; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5667
5668 JSNative native = target->native();
5669 if (call->ignoresReturnValue() && target->hasJitInfo()) {
5670 const JSJitInfo* jitInfo = target->jitInfo();
5671 if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) {
5672 native = jitInfo->ignoresReturnValueMethod;
5673 }
5674 }
5675 emitCallNative(call, native);
5676}
5677
5678void CodeGenerator::visitCallClassHook(LCallClassHook* call) {
5679 emitCallNative(call, call->mir()->target());
5680}
5681
5682static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv,
5683 DOMObjectKind kind) {
5684 // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This
5685 // will be in the first slot but may be fixed or non-fixed.
5686 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"
, 5686); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv"
")"); do { *((volatile int*)__null) = 5686; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5687
5688 switch (kind) {
5689 case DOMObjectKind::Native:
5690 // If it's a native object, the value must be in a fixed slot.
5691 // See CanAttachDOMCall in CacheIR.cpp.
5692 masm.debugAssertObjHasFixedSlots(obj, priv);
5693 masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv);
5694 break;
5695 case DOMObjectKind::Proxy: {
5696#ifdef DEBUG1
5697 // Sanity check: it must be a DOM proxy.
5698 Label isDOMProxy;
5699 masm.branchTestProxyHandlerFamily(
5700 Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy);
5701 masm.assumeUnreachable("Expected a DOM proxy");
5702 masm.bind(&isDOMProxy);
5703#endif
5704 masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), priv);
5705 masm.loadPrivate(
5706 Address(priv, js::detail::ProxyReservedSlots::offsetOfSlot(0)), priv);
5707 break;
5708 }
5709 }
5710}
5711
5712void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) {
5713 WrappedFunction* target = call->getSingleTarget();
5714 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"
, 5714); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { *((volatile int*)__null) = 5714; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5715 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"
, 5715); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()"
")"); do { *((volatile int*)__null) = 5715; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5716 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"
, 5716); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitInfo()"
")"); do { *((volatile int*)__null) = 5716; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5717 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"
, 5717); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()"
")"); do { *((volatile int*)__null) = 5717; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5718
5719 int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
5720
5721 // Registers used for callWithABI() argument-passing.
5722 const Register argJSContext = ToRegister(call->getArgJSContext());
5723 const Register argObj = ToRegister(call->getArgObj());
5724 const Register argPrivate = ToRegister(call->getArgPrivate());
5725 const Register argArgs = ToRegister(call->getArgArgs());
5726
5727 DebugOnly<uint32_t> initialStack = masm.framePushed();
5728
5729 masm.checkStackAlignment();
5730
5731 // DOM methods have the signature:
5732 // bool (*)(JSContext*, HandleObject, void* private, const
5733 // JSJitMethodCallArgs& args)
5734 // Where args is initialized from an argc and a vp, vp[0] is space for an
5735 // outparam and the callee, vp[1] is |this|, and vp[2] onward are the
5736 // function arguments. Note that args stores the argv, not the vp, and
5737 // argv == vp + 2.
5738
5739 // Nestle the stack up against the pushed arguments, leaving StackPointer at
5740 // &vp[1]
5741 masm.adjustStack(unusedStack);
5742 // argObj is filled with the extracted object, then returned.
5743 Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj);
5744 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"
, 5744); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj == argObj"
")"); do { *((volatile int*)__null) = 5744; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5745
5746 // Push a Value containing the callee object: natives are allowed to access
5747 // their callee before setting the return value. After this the StackPointer
5748 // points to &vp[0].
5749 masm.Push(ObjectValue(*target->rawNativeJSFunction()));
5750
5751 // Now compute the argv value. Since StackPointer is pointing to &vp[0] and
5752 // argv is &vp[2] we just need to add 2*sizeof(Value) to the current
5753 // StackPointer.
5754 static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0);
5755 static_assert(JSJitMethodCallArgsTraits::offsetOfArgc ==
5756 IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv);
5757 masm.computeEffectiveAddress(
5758 Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs);
5759
5760 LoadDOMPrivate(masm, obj, argPrivate,
5761 static_cast<MCallDOMNative*>(call->mir())->objectKind());
5762
5763 // Push argc from the call instruction into what will become the IonExitFrame
5764 masm.Push(Imm32(call->numActualArgs()));
5765
5766 // Push our argv onto the stack
5767 masm.Push(argArgs);
5768 // And store our JSJitMethodCallArgs* in argArgs.
5769 masm.moveStackPtrTo(argArgs);
5770
5771 // Push |this| object for passing HandleObject. We push after argc to
5772 // maintain the same sp-relative location of the object pointer with other
5773 // DOMExitFrames.
5774 masm.Push(argObj);
5775 masm.moveStackPtrTo(argObj);
5776
5777 if (call->mir()->maybeCrossRealm()) {
5778 // We use argJSContext as scratch register here.
5779 masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext);
5780 masm.switchToObjectRealm(argJSContext, argJSContext);
5781 }
5782
5783 // Construct native exit frame.
5784 uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext);
5785 masm.loadJSContext(argJSContext);
5786 masm.enterFakeExitFrame(argJSContext, argJSContext,
5787 ExitFrameType::IonDOMMethod);
5788
5789 markSafepointAt(safepointOffset, call);
5790
5791 // Construct and execute call.
5792 masm.setupAlignedABICall();
5793 masm.loadJSContext(argJSContext);
5794 masm.passABIArg(argJSContext);
5795 masm.passABIArg(argObj);
5796 masm.passABIArg(argPrivate);
5797 masm.passABIArg(argArgs);
5798 ensureOsiSpace();
5799 masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method),
5800 ABIType::General,
5801 CheckUnsafeCallWithABI::DontCheckHasExitFrame);
5802
5803 if (target->jitInfo()->isInfallible) {
5804 masm.loadValue(Address(masm.getStackPointer(),
5805 IonDOMMethodExitFrameLayout::offsetOfResult()),
5806 JSReturnOperand);
5807 } else {
5808 // Test for failure.
5809 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
5810
5811 // Load the outparam vp[0] into output register(s).
5812 masm.loadValue(Address(masm.getStackPointer(),
5813 IonDOMMethodExitFrameLayout::offsetOfResult()),
5814 JSReturnOperand);
5815 }
5816
5817 // Switch back to the current realm if needed. Note: if the DOM method threw
5818 // an exception, the exception handler will do this.
5819 if (call->mir()->maybeCrossRealm()) {
5820 static_assert(!JSReturnOperand.aliases(ReturnReg),
5821 "Clobbering ReturnReg should not affect the return value");
5822 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5823 }
5824
5825 // Until C++ code is instrumented against Spectre, prevent speculative
5826 // execution from returning any private data.
5827 if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) {
5828 masm.speculationBarrier();
5829 }
5830
5831 // The next instruction is removing the footer of the exit frame, so there
5832 // is no need for leaveFakeExitFrame.
5833
5834 // Move the StackPointer back to its original location, unwinding the native
5835 // exit frame.
5836 masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack);
5837 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"
, 5837); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack"
")"); do { *((volatile int*)__null) = 5837; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5838}
5839
5840void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) {
5841 pushArg(ImmGCPtr(lir->mir()->name()));
5842
5843 using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue);
5844 callVM<Fn, GetIntrinsicValue>(lir);
5845}
5846
5847void CodeGenerator::emitCallInvokeFunction(
5848 LInstruction* call, Register calleereg, bool constructing,
5849 bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) {
5850 // Nestle %esp up to the argument vector.
5851 // Each path must account for framePushed_ separately, for callVM to be valid.
5852 masm.freeStack(unusedStack);
5853
5854 pushArg(masm.getStackPointer()); // argv.
5855 pushArg(Imm32(argc)); // argc.
5856 pushArg(Imm32(ignoresReturnValue));
5857 pushArg(Imm32(constructing)); // constructing.
5858 pushArg(calleereg); // JSFunction*.
5859
5860 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
5861 MutableHandleValue);
5862 callVM<Fn, jit::InvokeFunction>(call);
5863
5864 // Un-nestle %esp from the argument vector. No prefix was pushed.
5865 masm.reserveStack(unusedStack);
5866}
5867
5868void CodeGenerator::visitCallGeneric(LCallGeneric* call) {
5869 // The callee is passed straight through to the trampoline.
5870 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"
, 5870); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg"
")"); do { *((volatile int*)__null) = 5870; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5871
5872 Register argcReg = ToRegister(call->getArgc());
5873 uint32_t unusedStack =
5874 UnusedStackBytesForCall(call->mir()->paddedNumStackArgs());
5875
5876 // Known-target case is handled by LCallKnown.
5877 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"
, 5877); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->hasSingleTarget()"
")"); do { *((volatile int*)__null) = 5877; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5878
5879 masm.checkStackAlignment();
5880
5881 masm.move32(Imm32(call->numActualArgs()), argcReg);
5882
5883 // Nestle the StackPointer up to the argument vector.
5884 masm.freeStack(unusedStack);
5885 ensureOsiSpace();
5886
5887 auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct
5888 : IonGenericCallKind::Call;
5889
5890 TrampolinePtr genericCallStub =
5891 gen->jitRuntime()->getIonGenericCallStub(kind);
5892 uint32_t callOffset = masm.callJit(genericCallStub);
5893 markSafepointAt(callOffset, call);
5894
5895 if (call->mir()->maybeCrossRealm()) {
5896 static_assert(!JSReturnOperand.aliases(ReturnReg),
5897 "ReturnReg available as scratch after scripted calls");
5898 masm.switchToRealm(gen->realm->realmPtr(), ReturnReg);
5899 }
5900
5901 // Restore stack pointer.
5902 masm.setFramePushed(frameSize());
5903 emitRestoreStackPointerFromFP();
5904
5905 // If the return value of the constructing function is Primitive,
5906 // replace the return value with the Object from CreateThis.
5907 if (call->mir()->isConstructing()) {
5908 Label notPrimitive;
5909 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
5910 &notPrimitive);
5911 masm.loadValue(Address(masm.getStackPointer(), unusedStack),
5912 JSReturnOperand);
5913#ifdef DEBUG1
5914 masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand,
5915 &notPrimitive);
5916 masm.assumeUnreachable("CreateThis creates an object");
5917#endif
5918 masm.bind(&notPrimitive);
5919 }
5920}
5921
5922void JitRuntime::generateIonGenericCallArgumentsShift(
5923 MacroAssembler& masm, Register argc, Register curr, Register end,
5924 Register scratch, Label* done) {
5925 static_assert(sizeof(Value) == 8);
5926 // There are |argc| Values on the stack. Shift them all down by 8 bytes,
5927 // overwriting the first value.
5928
5929 // Initialize `curr` to the destination of the first copy, and `end` to the
5930 // final value of curr.
5931 masm.moveStackPtrTo(curr);
5932 masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end);
5933
5934 Label loop;
5935 masm.bind(&loop);
5936 masm.branchPtr(Assembler::Equal, curr, end, done);
5937 masm.loadPtr(Address(curr, 8), scratch);
5938 masm.storePtr(scratch, Address(curr, 0));
5939 masm.addPtr(Imm32(sizeof(uintptr_t)), curr);
5940 masm.jump(&loop);
5941}
5942
5943void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm,
5944 IonGenericCallKind kind) {
5945 AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub");
5946 ionGenericCallStubOffset_[kind] = startTrampolineCode(masm);
5947
5948 // This code is tightly coupled with visitCallGeneric.
5949 //
5950 // Upon entry:
5951 // IonGenericCallCalleeReg contains a pointer to the callee object.
5952 // IonGenericCallArgcReg contains the number of actual args.
5953 // The arguments have been pushed onto the stack:
5954 // [newTarget] (iff isConstructing)
5955 // [argN]
5956 // ...
5957 // [arg1]
5958 // [arg0]
5959 // [this]
5960 // <return address> (if not JS_USE_LINK_REGISTER)
5961 //
5962 // This trampoline is responsible for entering the callee's realm,
5963 // massaging the stack into the right shape, and then performing a
5964 // tail call. We will return directly to the Ion code from the
5965 // callee.
5966 //
5967 // To do a tail call, we keep the return address in a register, even
5968 // on platforms that don't normally use a link register, and push it
5969 // just before jumping to the callee, after we are done setting up
5970 // the stack.
5971 //
5972 // The caller is responsible for switching back to the caller's
5973 // realm and cleaning up the stack.
5974
5975 Register calleeReg = IonGenericCallCalleeReg;
5976 Register argcReg = IonGenericCallArgcReg;
5977 Register scratch = IonGenericCallScratch;
5978 Register scratch2 = IonGenericCallScratch2;
5979
5980#ifndef JS_USE_LINK_REGISTER
5981 Register returnAddrReg = IonGenericCallReturnAddrReg;
5982 masm.pop(returnAddrReg);
5983#endif
5984
5985#ifdef JS_CODEGEN_ARM
5986 // The default second scratch register on arm is lr, which we need
5987 // preserved for tail calls.
5988 AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg);
5989#endif
5990
5991 bool isConstructing = kind == IonGenericCallKind::Construct;
5992
5993 Label entry, notFunction, noJitEntry, vmCall;
5994 masm.bind(&entry);
5995
5996 // Guard that the callee is actually a function.
5997 masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch,
5998 calleeReg, &notFunction);
5999
6000 // Guard that the callee supports the [[Call]] or [[Construct]] operation.
6001 // If these tests fail, we will call into the VM to throw an exception.
6002 if (isConstructing) {
6003 masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR,
6004 Assembler::Zero, &vmCall);
6005 } else {
6006 masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor,
6007 calleeReg, scratch, &vmCall);
6008 }
6009
6010 if (isConstructing) {
6011 // Use the slow path if CreateThis was unable to create the |this| object.
6012 Address thisAddr(masm.getStackPointer(), 0);
6013 masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall);
6014 }
6015
6016 masm.switchToObjectRealm(calleeReg, scratch);
6017
6018 // Load jitCodeRaw for callee if it exists.
6019 masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry);
6020
6021 // ****************************
6022 // * Functions with jit entry *
6023 // ****************************
6024 masm.loadJitCodeRaw(calleeReg, scratch2);
6025
6026 // Construct the JitFrameLayout.
6027 masm.PushCalleeToken(calleeReg, isConstructing);
6028 masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch);
6029#ifndef JS_USE_LINK_REGISTER
6030 masm.push(returnAddrReg);
6031#endif
6032
6033 // Check whether we need a rectifier frame.
6034 Label noRectifier;
6035 masm.loadFunctionArgCount(calleeReg, scratch);
6036 masm.branch32(Assembler::BelowOrEqual, scratch, argcReg, &noRectifier);
6037 {
6038 // Tail-call the arguments rectifier.
6039 // Because all trampolines are created at the same time,
6040 // we can't create a TrampolinePtr for the arguments rectifier,
6041 // because it hasn't been linked yet. We can, however, directly
6042 // encode its offset.
6043 Label rectifier;
6044 bindLabelToOffset(&rectifier, argumentsRectifierOffset_);
6045
6046 masm.jump(&rectifier);
6047 }
6048
6049 // Tail call the jit entry.
6050 masm.bind(&noRectifier);
6051 masm.jump(scratch2);
6052
6053 // ********************
6054 // * Native functions *
6055 // ********************
6056 masm.bind(&noJitEntry);
6057 if (!isConstructing) {
6058 generateIonGenericCallFunCall(masm, &entry, &vmCall);
6059 }
6060 generateIonGenericCallNativeFunction(masm, isConstructing);
6061
6062 // *******************
6063 // * Bound functions *
6064 // *******************
6065 // TODO: support class hooks?
6066 masm.bind(&notFunction);
6067 if (!isConstructing) {
6068 // TODO: support generic bound constructors?
6069 generateIonGenericCallBoundFunction(masm, &entry, &vmCall);
6070 }
6071
6072 // ********************
6073 // * Fallback VM call *
6074 // ********************
6075 masm.bind(&vmCall);
6076
6077 masm.push(masm.getStackPointer()); // argv
6078 masm.push(argcReg); // argc
6079 masm.push(Imm32(false)); // ignores return value
6080 masm.push(Imm32(isConstructing)); // constructing
6081 masm.push(calleeReg); // callee
6082
6083 using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*,
6084 MutableHandleValue);
6085 VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id;
6086 uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)];
6087 Label invokeFunctionVMEntry;
6088 bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset);
6089
6090 masm.pushFrameDescriptor(FrameType::IonJS);
6091#ifndef JS_USE_LINK_REGISTER
6092 masm.push(returnAddrReg);
6093#endif
6094 masm.jump(&invokeFunctionVMEntry);
6095}
6096
6097void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm,
6098 bool isConstructing) {
6099 Register calleeReg = IonGenericCallCalleeReg;
6100 Register argcReg = IonGenericCallArgcReg;
6101 Register scratch = IonGenericCallScratch;
6102 Register scratch2 = IonGenericCallScratch2;
6103 Register contextReg = IonGenericCallScratch3;
6104#ifndef JS_USE_LINK_REGISTER
6105 Register returnAddrReg = IonGenericCallReturnAddrReg;
6106#endif
6107
6108 // Push a value containing the callee, which will become argv[0].
6109 masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg);
6110
6111 // Load the callee address into calleeReg.
6112#ifdef JS_SIMULATOR
6113 masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg);
6114#else
6115 masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
6116 calleeReg);
6117#endif
6118
6119 // Load argv into scratch2.
6120 masm.moveStackPtrTo(scratch2);
6121
6122 // Push argc.
6123 masm.push(argcReg);
6124
6125 masm.loadJSContext(contextReg);
6126
6127 // Construct native exit frame. Note that unlike other cases in this
6128 // trampoline, this code does not use a tail call.
6129 masm.pushFrameDescriptor(FrameType::IonJS);
6130#ifdef JS_USE_LINK_REGISTER
6131 masm.pushReturnAddress();
6132#else
6133 masm.push(returnAddrReg);
6134#endif
6135
6136 masm.push(FramePointer);
6137 masm.moveStackPtrTo(FramePointer);
6138 masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing);
6139
6140 masm.setupUnalignedABICall(scratch);
6141 masm.passABIArg(contextReg); // cx
6142 masm.passABIArg(argcReg); // argc
6143 masm.passABIArg(scratch2); // argv
6144
6145 masm.callWithABI(calleeReg);
6146
6147 // Test for failure.
6148 masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
6149
6150 masm.loadValue(
6151 Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()),
6152 JSReturnOperand);
6153
6154 // Leave the exit frame.
6155 masm.moveToStackPtr(FramePointer);
6156 masm.pop(FramePointer);
6157
6158 // Return.
6159 masm.ret();
6160}
6161
6162void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm,
6163 Label* entry, Label* vmCall) {
6164 Register calleeReg = IonGenericCallCalleeReg;
6165 Register argcReg = IonGenericCallArgcReg;
6166 Register scratch = IonGenericCallScratch;
6167 Register scratch2 = IonGenericCallScratch2;
6168 Register scratch3 = IonGenericCallScratch3;
6169
6170 Label notFunCall;
6171 masm.branchPtr(Assembler::NotEqual,
6172 Address(calleeReg, JSFunction::offsetOfNativeOrEnv()),
6173 ImmPtr(js::fun_call), &notFunCall);
6174
6175 // In general, we can implement fun_call by replacing calleeReg with
6176 // |this|, sliding all the other arguments down, and decrementing argc.
6177 //
6178 // *BEFORE* *AFTER*
6179 // [argN] argc = N+1 <padding>
6180 // ... [argN] argc = N
6181 // [arg1] ...
6182 // [arg0] [arg1] <- now arg0
6183 // [this] <- top of stack (aligned) [arg0] <- now this
6184 //
6185 // The only exception is when argc is already 0, in which case instead
6186 // of shifting arguments down we replace [this] with UndefinedValue():
6187 //
6188 // *BEFORE* *AFTER*
6189 // [this] argc = 0 [undef] argc = 0
6190 //
6191 // After making this transformation, we can jump back to the beginning
6192 // of this trampoline to handle the inner call.
6193
6194 // Guard that |this| is an object. If it is, replace calleeReg.
6195 masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall);
6196 masm.movePtr(scratch, calleeReg);
6197
6198 Label hasArgs;
6199 masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs);
6200
6201 // No arguments. Replace |this| with |undefined| and start from the top.
6202 masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0));
6203 masm.jump(entry);
6204
6205 masm.bind(&hasArgs);
6206
6207 Label doneSliding;
6208 generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2,
6209 scratch3, &doneSliding);
6210 masm.bind(&doneSliding);
6211 masm.sub32(Imm32(1), argcReg);
6212
6213 masm.jump(entry);
6214
6215 masm.bind(&notFunCall);
6216}
6217
6218void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm,
6219 Label* entry,
6220 Label* vmCall) {
6221 Register calleeReg = IonGenericCallCalleeReg;
6222 Register argcReg = IonGenericCallArgcReg;
6223 Register scratch = IonGenericCallScratch;
6224 Register scratch2 = IonGenericCallScratch2;
6225 Register scratch3 = IonGenericCallScratch3;
6226
6227 masm.branchTestObjClass(Assembler::NotEqual, calleeReg,
6228 &BoundFunctionObject::class_, scratch, calleeReg,
6229 vmCall);
6230
6231 Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot());
6232 Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot());
6233 Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot());
6234 Address firstInlineArgSlot(
6235 calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg());
6236
6237 // Check that we won't be pushing too many arguments.
6238 masm.load32(flagsSlot, scratch);
6239 masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch);
6240 masm.add32(argcReg, scratch);
6241 masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall);
6242
6243 // The stack is currently correctly aligned for a jit call. We will
6244 // be updating the `this` value and potentially adding additional
6245 // arguments. On platforms with 16-byte alignment, if the number of
6246 // bound arguments is odd, we have to m