File: | var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp |
Warning: | line 12828, column 8 Value stored to 'extractObject' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | * vim: set ts=8 sts=2 et sw=2 tw=80: |
3 | * This Source Code Form is subject to the terms of the Mozilla Public |
4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | |
7 | #include "jit/CodeGenerator.h" |
8 | |
9 | #include "mozilla/Assertions.h" |
10 | #include "mozilla/Casting.h" |
11 | #include "mozilla/DebugOnly.h" |
12 | #include "mozilla/EndianUtils.h" |
13 | #include "mozilla/EnumeratedArray.h" |
14 | #include "mozilla/EnumeratedRange.h" |
15 | #include "mozilla/EnumSet.h" |
16 | #include "mozilla/IntegerTypeTraits.h" |
17 | #include "mozilla/Latin1.h" |
18 | #include "mozilla/MathAlgorithms.h" |
19 | #include "mozilla/ScopeExit.h" |
20 | #include "mozilla/SIMD.h" |
21 | |
22 | #include <limits> |
23 | #include <type_traits> |
24 | #include <utility> |
25 | |
26 | #include "jslibmath.h" |
27 | #include "jsmath.h" |
28 | #include "jsnum.h" |
29 | |
30 | #include "builtin/MapObject.h" |
31 | #include "builtin/RegExp.h" |
32 | #include "builtin/String.h" |
33 | #include "irregexp/RegExpTypes.h" |
34 | #include "jit/ABIArgGenerator.h" |
35 | #include "jit/CompileInfo.h" |
36 | #include "jit/InlineScriptTree.h" |
37 | #include "jit/Invalidation.h" |
38 | #include "jit/IonGenericCallStub.h" |
39 | #include "jit/IonIC.h" |
40 | #include "jit/IonScript.h" |
41 | #include "jit/JitcodeMap.h" |
42 | #include "jit/JitFrames.h" |
43 | #include "jit/JitRuntime.h" |
44 | #include "jit/JitSpewer.h" |
45 | #include "jit/JitZone.h" |
46 | #include "jit/Linker.h" |
47 | #include "jit/MIRGenerator.h" |
48 | #include "jit/MoveEmitter.h" |
49 | #include "jit/RangeAnalysis.h" |
50 | #include "jit/RegExpStubConstants.h" |
51 | #include "jit/SafepointIndex.h" |
52 | #include "jit/SharedICHelpers.h" |
53 | #include "jit/SharedICRegisters.h" |
54 | #include "jit/VMFunctions.h" |
55 | #include "jit/WarpSnapshot.h" |
56 | #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin |
57 | #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter}CallArgs, JSJitMethodCallArgsTraits, JSJitInfo |
58 | #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration |
59 | #include "js/RegExpFlags.h" // JS::RegExpFlag |
60 | #include "js/ScalarType.h" // js::Scalar::Type |
61 | #include "proxy/DOMProxy.h" |
62 | #include "proxy/ScriptedProxyHandler.h" |
63 | #include "util/CheckedArithmetic.h" |
64 | #include "util/Unicode.h" |
65 | #include "vm/ArrayBufferViewObject.h" |
66 | #include "vm/AsyncFunction.h" |
67 | #include "vm/AsyncIteration.h" |
68 | #include "vm/BuiltinObjectKind.h" |
69 | #include "vm/FunctionFlags.h" // js::FunctionFlags |
70 | #include "vm/Interpreter.h" |
71 | #include "vm/JSAtomUtils.h" // AtomizeString |
72 | #include "vm/MatchPairs.h" |
73 | #include "vm/RegExpObject.h" |
74 | #include "vm/RegExpStatics.h" |
75 | #include "vm/StaticStrings.h" |
76 | #include "vm/StringObject.h" |
77 | #include "vm/StringType.h" |
78 | #include "vm/TypedArrayObject.h" |
79 | #include "wasm/WasmCodegenConstants.h" |
80 | #include "wasm/WasmPI.h" |
81 | #include "wasm/WasmValType.h" |
82 | #ifdef MOZ_VTUNE1 |
83 | # include "vtune/VTuneWrapper.h" |
84 | #endif |
85 | #include "wasm/WasmBinary.h" |
86 | #include "wasm/WasmGC.h" |
87 | #include "wasm/WasmGcObject.h" |
88 | #include "wasm/WasmStubs.h" |
89 | |
90 | #include "builtin/Boolean-inl.h" |
91 | #include "jit/MacroAssembler-inl.h" |
92 | #include "jit/shared/CodeGenerator-shared-inl.h" |
93 | #include "jit/TemplateObject-inl.h" |
94 | #include "jit/VMFunctionList-inl.h" |
95 | #include "vm/BytecodeUtil-inl.h" |
96 | #include "vm/JSScript-inl.h" |
97 | #include "wasm/WasmInstance-inl.h" |
98 | |
99 | using namespace js; |
100 | using namespace js::jit; |
101 | |
102 | using mozilla::CheckedUint32; |
103 | using mozilla::DebugOnly; |
104 | using mozilla::FloatingPoint; |
105 | using mozilla::NegativeInfinity; |
106 | using mozilla::PositiveInfinity; |
107 | |
108 | using JS::ExpandoAndGeneration; |
109 | |
110 | namespace js { |
111 | namespace jit { |
112 | |
113 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
114 | template <class Op> |
115 | static void HandleRegisterDump(Op op, MacroAssembler& masm, |
116 | LiveRegisterSet liveRegs, Register activation, |
117 | Register scratch) { |
118 | const size_t baseOffset = JitActivation::offsetOfRegs(); |
119 | |
120 | // Handle live GPRs. |
121 | for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) { |
122 | Register reg = *iter; |
123 | Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
124 | |
125 | if (reg == activation) { |
126 | // To use the original value of the activation register (that's |
127 | // now on top of the stack), we need the scratch register. |
128 | masm.push(scratch); |
129 | masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch); |
130 | op(scratch, dump); |
131 | masm.pop(scratch); |
132 | } else { |
133 | op(reg, dump); |
134 | } |
135 | } |
136 | |
137 | // Handle live FPRs. |
138 | for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) { |
139 | FloatRegister reg = *iter; |
140 | Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
141 | op(reg, dump); |
142 | } |
143 | } |
144 | |
145 | class StoreOp { |
146 | MacroAssembler& masm; |
147 | |
148 | public: |
149 | explicit StoreOp(MacroAssembler& masm) : masm(masm) {} |
150 | |
151 | void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); } |
152 | void operator()(FloatRegister reg, Address dump) { |
153 | if (reg.isDouble()) { |
154 | masm.storeDouble(reg, dump); |
155 | } else if (reg.isSingle()) { |
156 | masm.storeFloat32(reg, dump); |
157 | } else if (reg.isSimd128()) { |
158 | MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 158); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { *((volatile int*)__null) = 158; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
159 | } else { |
160 | MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 160); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type." ")"); do { *((volatile int*)__null) = 160; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
161 | } |
162 | } |
163 | }; |
164 | |
165 | class VerifyOp { |
166 | MacroAssembler& masm; |
167 | Label* failure_; |
168 | |
169 | public: |
170 | VerifyOp(MacroAssembler& masm, Label* failure) |
171 | : masm(masm), failure_(failure) {} |
172 | |
173 | void operator()(Register reg, Address dump) { |
174 | masm.branchPtr(Assembler::NotEqual, dump, reg, failure_); |
175 | } |
176 | void operator()(FloatRegister reg, Address dump) { |
177 | if (reg.isDouble()) { |
178 | ScratchDoubleScope scratch(masm); |
179 | masm.loadDouble(dump, scratch); |
180 | masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_); |
181 | } else if (reg.isSingle()) { |
182 | ScratchFloat32Scope scratch(masm); |
183 | masm.loadFloat32(dump, scratch); |
184 | masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_); |
185 | } else if (reg.isSimd128()) { |
186 | MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 186); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { *((volatile int*)__null) = 186; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
187 | } else { |
188 | MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 188); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type." ")"); do { *((volatile int*)__null) = 188; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
189 | } |
190 | } |
191 | }; |
192 | |
193 | void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) { |
194 | // Ensure the live registers stored by callVM did not change between |
195 | // the call and this OsiPoint. Try-catch relies on this invariant. |
196 | |
197 | // Load pointer to the JitActivation in a scratch register. |
198 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
199 | Register scratch = allRegs.takeAny(); |
200 | masm.push(scratch); |
201 | masm.loadJitActivation(scratch); |
202 | |
203 | // If we should not check registers (because the instruction did not call |
204 | // into the VM, or a GC happened), we're done. |
205 | Label failure, done; |
206 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
207 | masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done); |
208 | |
209 | // Having more than one VM function call made in one visit function at |
210 | // runtime is a sec-ciritcal error, because if we conservatively assume that |
211 | // one of the function call can re-enter Ion, then the invalidation process |
212 | // will potentially add a call at a random location, by patching the code |
213 | // before the return address. |
214 | masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure); |
215 | |
216 | // Set checkRegs to 0, so that we don't try to verify registers after we |
217 | // return from this script to the caller. |
218 | masm.store32(Imm32(0), checkRegs); |
219 | |
220 | // Ignore clobbered registers. Some instructions (like LValueToInt32) modify |
221 | // temps after calling into the VM. This is fine because no other |
222 | // instructions (including this OsiPoint) will depend on them. Also |
223 | // backtracking can also use the same register for an input and an output. |
224 | // These are marked as clobbered and shouldn't get checked. |
225 | LiveRegisterSet liveRegs; |
226 | liveRegs.set() = RegisterSet::Intersect( |
227 | safepoint->liveRegs().set(), |
228 | RegisterSet::Not(safepoint->clobberedRegs().set())); |
229 | |
230 | VerifyOp op(masm, &failure); |
231 | HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
232 | |
233 | masm.jump(&done); |
234 | |
235 | // Do not profile the callWithABI that occurs below. This is to avoid a |
236 | // rare corner case that occurs when profiling interacts with itself: |
237 | // |
238 | // When slow profiling assertions are turned on, FunctionBoundary ops |
239 | // (which update the profiler pseudo-stack) may emit a callVM, which |
240 | // forces them to have an osi point associated with them. The |
241 | // FunctionBoundary for inline function entry is added to the caller's |
242 | // graph with a PC from the caller's code, but during codegen it modifies |
243 | // Gecko Profiler instrumentation to add the callee as the current top-most |
244 | // script. When codegen gets to the OSIPoint, and the callWithABI below is |
245 | // emitted, the codegen thinks that the current frame is the callee, but |
246 | // the PC it's using from the OSIPoint refers to the caller. This causes |
247 | // the profiler instrumentation of the callWithABI below to ASSERT, since |
248 | // the script and pc are mismatched. To avoid this, we simply omit |
249 | // instrumentation for these callWithABIs. |
250 | |
251 | // Any live register captured by a safepoint (other than temp registers) |
252 | // must remain unchanged between the call and the OsiPoint instruction. |
253 | masm.bind(&failure); |
254 | masm.assumeUnreachable("Modified registers between VM call and OsiPoint"); |
255 | |
256 | masm.bind(&done); |
257 | masm.pop(scratch); |
258 | } |
259 | |
260 | bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) { |
261 | if (!checkOsiPointRegisters) { |
262 | return false; |
263 | } |
264 | |
265 | if (safepoint->liveRegs().emptyGeneral() && |
266 | safepoint->liveRegs().emptyFloat()) { |
267 | return false; // No registers to check. |
268 | } |
269 | |
270 | return true; |
271 | } |
272 | |
273 | void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) { |
274 | if (!shouldVerifyOsiPointRegs(safepoint)) { |
275 | return; |
276 | } |
277 | |
278 | // Set checkRegs to 0. If we perform a VM call, the instruction |
279 | // will set it to 1. |
280 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
281 | Register scratch = allRegs.takeAny(); |
282 | masm.push(scratch); |
283 | masm.loadJitActivation(scratch); |
284 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
285 | masm.store32(Imm32(0), checkRegs); |
286 | masm.pop(scratch); |
287 | } |
288 | |
289 | static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) { |
290 | // Store a copy of all live registers before performing the call. |
291 | // When we reach the OsiPoint, we can use this to check nothing |
292 | // modified them in the meantime. |
293 | |
294 | // Load pointer to the JitActivation in a scratch register. |
295 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
296 | Register scratch = allRegs.takeAny(); |
297 | masm.push(scratch); |
298 | masm.loadJitActivation(scratch); |
299 | |
300 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
301 | masm.add32(Imm32(1), checkRegs); |
302 | |
303 | StoreOp op(masm); |
304 | HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
305 | |
306 | masm.pop(scratch); |
307 | } |
308 | #endif // CHECK_OSIPOINT_REGISTERS |
309 | |
310 | // Before doing any call to Cpp, you should ensure that volatile |
311 | // registers are evicted by the register allocator. |
312 | void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) { |
313 | TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id); |
314 | const VMFunctionData& fun = GetVMFunction(id); |
315 | |
316 | // Stack is: |
317 | // ... frame ... |
318 | // [args] |
319 | #ifdef DEBUG1 |
320 | MOZ_ASSERT(pushedArgs_ == fun.explicitArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(pushedArgs_ == fun.explicitArgs)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pushedArgs_ == fun.explicitArgs ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pushedArgs_ == fun.explicitArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 320); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs" ")"); do { *((volatile int*)__null) = 320; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
321 | pushedArgs_ = 0; |
322 | #endif |
323 | |
324 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
325 | if (shouldVerifyOsiPointRegs(ins->safepoint())) { |
326 | StoreAllLiveRegs(masm, ins->safepoint()->liveRegs()); |
327 | } |
328 | #endif |
329 | |
330 | #ifdef DEBUG1 |
331 | if (ins->mirRaw()) { |
332 | MOZ_ASSERT(ins->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mirRaw()->isInstruction())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mirRaw()->isInstruction ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ins->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 332); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()" ")"); do { *((volatile int*)__null) = 332; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
333 | MInstruction* mir = ins->mirRaw()->toInstruction(); |
334 | MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint())do { if (mir->needsResumePoint()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(mir->resumePoint ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mir->resumePoint()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("mir->resumePoint()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()" ")"); do { *((volatile int*)__null) = 334; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
335 | |
336 | // If this MIR instruction has an overridden AliasSet, set the JitRuntime's |
337 | // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call |
338 | // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because |
339 | // interrupt callbacks can call JS (chrome JS or shell testing functions). |
340 | bool isWhitelisted = mir->isInterruptCheck() || mir->isCheckOverRecursed(); |
341 | if (!mir->hasDefaultAliasSet() && !isWhitelisted) { |
342 | const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode(); |
343 | masm.move32(Imm32(1), ReturnReg); |
344 | masm.store32(ReturnReg, AbsoluteAddress(addr)); |
345 | } |
346 | } |
347 | #endif |
348 | |
349 | // Push an exit frame descriptor. |
350 | masm.PushFrameDescriptor(FrameType::IonJS); |
351 | |
352 | // Call the wrapper function. The wrapper is in charge to unwind the stack |
353 | // when returning from the call. Failures are handled with exceptions based |
354 | // on the return value of the C functions. To guard the outcome of the |
355 | // returned value, use another LIR instruction. |
356 | ensureOsiSpace(); |
357 | uint32_t callOffset = masm.callJit(code); |
358 | markSafepointAt(callOffset, ins); |
359 | |
360 | #ifdef DEBUG1 |
361 | // Reset the disallowArbitraryCode flag after the call. |
362 | { |
363 | const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode(); |
364 | masm.push(ReturnReg); |
365 | masm.move32(Imm32(0), ReturnReg); |
366 | masm.store32(ReturnReg, AbsoluteAddress(addr)); |
367 | masm.pop(ReturnReg); |
368 | } |
369 | #endif |
370 | |
371 | // Pop rest of the exit frame and the arguments left on the stack. |
372 | int framePop = |
373 | sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall(); |
374 | masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop); |
375 | |
376 | // Stack is: |
377 | // ... frame ... |
378 | } |
379 | |
380 | template <typename Fn, Fn fn> |
381 | void CodeGenerator::callVM(LInstruction* ins) { |
382 | VMFunctionId id = VMFunctionToId<Fn, fn>::id; |
383 | callVMInternal(id, ins); |
384 | } |
385 | |
386 | // ArgSeq store arguments for OutOfLineCallVM. |
387 | // |
388 | // OutOfLineCallVM are created with "oolCallVM" function. The third argument of |
389 | // this function is an instance of a class which provides a "generate" in charge |
390 | // of pushing the argument, with "pushArg", for a VMFunction. |
391 | // |
392 | // Such list of arguments can be created by using the "ArgList" function which |
393 | // creates one instance of "ArgSeq", where the type of the arguments are |
394 | // inferred from the type of the arguments. |
395 | // |
396 | // The list of arguments must be written in the same order as if you were |
397 | // calling the function in C++. |
398 | // |
399 | // Example: |
400 | // ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs())) |
401 | |
402 | template <typename... ArgTypes> |
403 | class ArgSeq { |
404 | std::tuple<std::remove_reference_t<ArgTypes>...> args_; |
405 | |
406 | template <std::size_t... ISeq> |
407 | inline void generate(CodeGenerator* codegen, |
408 | std::index_sequence<ISeq...>) const { |
409 | // Arguments are pushed in reverse order, from last argument to first |
410 | // argument. |
411 | (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...); |
412 | } |
413 | |
414 | public: |
415 | explicit ArgSeq(ArgTypes&&... args) |
416 | : args_(std::forward<ArgTypes>(args)...) {} |
417 | |
418 | inline void generate(CodeGenerator* codegen) const { |
419 | generate(codegen, std::index_sequence_for<ArgTypes...>{}); |
420 | } |
421 | |
422 | #ifdef DEBUG1 |
423 | static constexpr size_t numArgs = sizeof...(ArgTypes); |
424 | #endif |
425 | }; |
426 | |
427 | template <typename... ArgTypes> |
428 | inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) { |
429 | return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...); |
430 | } |
431 | |
432 | // Store wrappers, to generate the right move of data after the VM call. |
433 | |
434 | struct StoreNothing { |
435 | inline void generate(CodeGenerator* codegen) const {} |
436 | inline LiveRegisterSet clobbered() const { |
437 | return LiveRegisterSet(); // No register gets clobbered |
438 | } |
439 | }; |
440 | |
441 | class StoreRegisterTo { |
442 | private: |
443 | Register out_; |
444 | |
445 | public: |
446 | explicit StoreRegisterTo(Register out) : out_(out) {} |
447 | |
448 | inline void generate(CodeGenerator* codegen) const { |
449 | // It's okay to use storePointerResultTo here - the VMFunction wrapper |
450 | // ensures the upper bytes are zero for bool/int32 return values. |
451 | codegen->storePointerResultTo(out_); |
452 | } |
453 | inline LiveRegisterSet clobbered() const { |
454 | LiveRegisterSet set; |
455 | set.add(out_); |
456 | return set; |
457 | } |
458 | }; |
459 | |
460 | class StoreFloatRegisterTo { |
461 | private: |
462 | FloatRegister out_; |
463 | |
464 | public: |
465 | explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {} |
466 | |
467 | inline void generate(CodeGenerator* codegen) const { |
468 | codegen->storeFloatResultTo(out_); |
469 | } |
470 | inline LiveRegisterSet clobbered() const { |
471 | LiveRegisterSet set; |
472 | set.add(out_); |
473 | return set; |
474 | } |
475 | }; |
476 | |
477 | template <typename Output> |
478 | class StoreValueTo_ { |
479 | private: |
480 | Output out_; |
481 | |
482 | public: |
483 | explicit StoreValueTo_(const Output& out) : out_(out) {} |
484 | |
485 | inline void generate(CodeGenerator* codegen) const { |
486 | codegen->storeResultValueTo(out_); |
487 | } |
488 | inline LiveRegisterSet clobbered() const { |
489 | LiveRegisterSet set; |
490 | set.add(out_); |
491 | return set; |
492 | } |
493 | }; |
494 | |
495 | template <typename Output> |
496 | StoreValueTo_<Output> StoreValueTo(const Output& out) { |
497 | return StoreValueTo_<Output>(out); |
498 | } |
499 | |
500 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
501 | class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> { |
502 | private: |
503 | LInstruction* lir_; |
504 | ArgSeq args_; |
505 | StoreOutputTo out_; |
506 | |
507 | public: |
508 | OutOfLineCallVM(LInstruction* lir, const ArgSeq& args, |
509 | const StoreOutputTo& out) |
510 | : lir_(lir), args_(args), out_(out) {} |
511 | |
512 | void accept(CodeGenerator* codegen) override { |
513 | codegen->visitOutOfLineCallVM(this); |
514 | } |
515 | |
516 | LInstruction* lir() const { return lir_; } |
517 | const ArgSeq& args() const { return args_; } |
518 | const StoreOutputTo& out() const { return out_; } |
519 | }; |
520 | |
521 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
522 | OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args, |
523 | const StoreOutputTo& out) { |
524 | MOZ_ASSERT(lir->mirRaw())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mirRaw())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mirRaw()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mirRaw()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 524); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()" ")"); do { *((volatile int*)__null) = 524; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
525 | MOZ_ASSERT(lir->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mirRaw()->isInstruction())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mirRaw()->isInstruction ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 525); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()" ")"); do { *((volatile int*)__null) = 525; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
526 | |
527 | #ifdef DEBUG1 |
528 | VMFunctionId id = VMFunctionToId<Fn, fn>::id; |
529 | const VMFunctionData& fun = GetVMFunction(id); |
530 | MOZ_ASSERT(fun.explicitArgs == args.numArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.explicitArgs == args.numArgs)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.explicitArgs == args.numArgs ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "fun.explicitArgs == args.numArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 530); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs" ")"); do { *((volatile int*)__null) = 530; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
531 | MOZ_ASSERT(fun.returnsData() !=do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo , StoreNothing>))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v <StoreOutputTo, StoreNothing>)))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" ")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
532 | (std::is_same_v<StoreOutputTo, StoreNothing>))do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo , StoreNothing>))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v <StoreOutputTo, StoreNothing>)))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" ")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
533 | #endif |
534 | |
535 | OutOfLineCode* ool = new (alloc()) |
536 | OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out); |
537 | addOutOfLineCode(ool, lir->mirRaw()->toInstruction()); |
538 | return ool; |
539 | } |
540 | |
541 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
542 | void CodeGenerator::visitOutOfLineCallVM( |
543 | OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) { |
544 | LInstruction* lir = ool->lir(); |
545 | |
546 | #ifdef JS_JITSPEW1 |
547 | JitSpewStart(JitSpew_Codegen, " # LIR=%s", |
548 | lir->opName()); |
549 | if (const char* extra = lir->getExtraName()) { |
550 | JitSpewCont(JitSpew_Codegen, ":%s", extra); |
551 | } |
552 | JitSpewFin(JitSpew_Codegen); |
553 | #endif |
554 | perfSpewer_.recordInstruction(masm, lir); |
555 | saveLive(lir); |
556 | ool->args().generate(this); |
557 | callVM<Fn, fn>(lir); |
558 | ool->out().generate(this); |
559 | restoreLiveIgnore(lir, ool->out().clobbered()); |
560 | masm.jump(ool->rejoin()); |
561 | } |
562 | |
563 | class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> { |
564 | private: |
565 | LInstruction* lir_; |
566 | size_t cacheIndex_; |
567 | size_t cacheInfoIndex_; |
568 | |
569 | public: |
570 | OutOfLineICFallback(LInstruction* lir, size_t cacheIndex, |
571 | size_t cacheInfoIndex) |
572 | : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {} |
573 | |
574 | void bind(MacroAssembler* masm) override { |
575 | // The binding of the initial jump is done in |
576 | // CodeGenerator::visitOutOfLineICFallback. |
577 | } |
578 | |
579 | size_t cacheIndex() const { return cacheIndex_; } |
580 | size_t cacheInfoIndex() const { return cacheInfoIndex_; } |
581 | LInstruction* lir() const { return lir_; } |
582 | |
583 | void accept(CodeGenerator* codegen) override { |
584 | codegen->visitOutOfLineICFallback(this); |
585 | } |
586 | }; |
587 | |
588 | void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) { |
589 | if (cacheIndex == SIZE_MAX(18446744073709551615UL)) { |
590 | masm.setOOM(); |
591 | return; |
592 | } |
593 | |
594 | DataPtr<IonIC> cache(this, cacheIndex); |
595 | MInstruction* mir = lir->mirRaw()->toInstruction(); |
596 | cache->setScriptedLocation(mir->block()->info().script(), |
597 | mir->resumePoint()->pc()); |
598 | |
599 | Register temp = cache->scratchRegisterForEntryJump(); |
600 | icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp); |
601 | masm.jump(Address(temp, 0)); |
602 | |
603 | MOZ_ASSERT(!icInfo_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!icInfo_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!icInfo_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!icInfo_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!icInfo_.empty()" ")"); do { *((volatile int*)__null) = 603; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
604 | |
605 | OutOfLineICFallback* ool = |
606 | new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1); |
607 | addOutOfLineCode(ool, mir); |
608 | |
609 | masm.bind(ool->rejoin()); |
610 | cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset())); |
611 | } |
612 | |
613 | void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) { |
614 | LInstruction* lir = ool->lir(); |
615 | size_t cacheIndex = ool->cacheIndex(); |
616 | size_t cacheInfoIndex = ool->cacheInfoIndex(); |
617 | |
618 | DataPtr<IonIC> ic(this, cacheIndex); |
619 | |
620 | // Register the location of the OOL path in the IC. |
621 | ic->setFallbackOffset(CodeOffset(masm.currentOffset())); |
622 | |
623 | switch (ic->kind()) { |
624 | case CacheKind::GetProp: |
625 | case CacheKind::GetElem: { |
626 | IonGetPropertyIC* getPropIC = ic->asGetPropertyIC(); |
627 | |
628 | saveLive(lir); |
629 | |
630 | pushArg(getPropIC->id()); |
631 | pushArg(getPropIC->value()); |
632 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
633 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
634 | |
635 | using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*, |
636 | HandleValue, HandleValue, MutableHandleValue); |
637 | callVM<Fn, IonGetPropertyIC::update>(lir); |
638 | |
639 | StoreValueTo(getPropIC->output()).generate(this); |
640 | restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered()); |
641 | |
642 | masm.jump(ool->rejoin()); |
643 | return; |
644 | } |
645 | case CacheKind::GetPropSuper: |
646 | case CacheKind::GetElemSuper: { |
647 | IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC(); |
648 | |
649 | saveLive(lir); |
650 | |
651 | pushArg(getPropSuperIC->id()); |
652 | pushArg(getPropSuperIC->receiver()); |
653 | pushArg(getPropSuperIC->object()); |
654 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
655 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
656 | |
657 | using Fn = |
658 | bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject, |
659 | HandleValue, HandleValue, MutableHandleValue); |
660 | callVM<Fn, IonGetPropSuperIC::update>(lir); |
661 | |
662 | StoreValueTo(getPropSuperIC->output()).generate(this); |
663 | restoreLiveIgnore(lir, |
664 | StoreValueTo(getPropSuperIC->output()).clobbered()); |
665 | |
666 | masm.jump(ool->rejoin()); |
667 | return; |
668 | } |
669 | case CacheKind::SetProp: |
670 | case CacheKind::SetElem: { |
671 | IonSetPropertyIC* setPropIC = ic->asSetPropertyIC(); |
672 | |
673 | saveLive(lir); |
674 | |
675 | pushArg(setPropIC->rhs()); |
676 | pushArg(setPropIC->id()); |
677 | pushArg(setPropIC->object()); |
678 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
679 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
680 | |
681 | using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*, |
682 | HandleObject, HandleValue, HandleValue); |
683 | callVM<Fn, IonSetPropertyIC::update>(lir); |
684 | |
685 | restoreLive(lir); |
686 | |
687 | masm.jump(ool->rejoin()); |
688 | return; |
689 | } |
690 | case CacheKind::GetName: { |
691 | IonGetNameIC* getNameIC = ic->asGetNameIC(); |
692 | |
693 | saveLive(lir); |
694 | |
695 | pushArg(getNameIC->environment()); |
696 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
697 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
698 | |
699 | using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject, |
700 | MutableHandleValue); |
701 | callVM<Fn, IonGetNameIC::update>(lir); |
702 | |
703 | StoreValueTo(getNameIC->output()).generate(this); |
704 | restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered()); |
705 | |
706 | masm.jump(ool->rejoin()); |
707 | return; |
708 | } |
709 | case CacheKind::BindName: { |
710 | IonBindNameIC* bindNameIC = ic->asBindNameIC(); |
711 | |
712 | saveLive(lir); |
713 | |
714 | pushArg(bindNameIC->environment()); |
715 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
716 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
717 | |
718 | using Fn = |
719 | JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject); |
720 | callVM<Fn, IonBindNameIC::update>(lir); |
721 | |
722 | StoreRegisterTo(bindNameIC->output()).generate(this); |
723 | restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered()); |
724 | |
725 | masm.jump(ool->rejoin()); |
726 | return; |
727 | } |
728 | case CacheKind::GetIterator: { |
729 | IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC(); |
730 | |
731 | saveLive(lir); |
732 | |
733 | pushArg(getIteratorIC->value()); |
734 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
735 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
736 | |
737 | using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*, |
738 | HandleValue); |
739 | callVM<Fn, IonGetIteratorIC::update>(lir); |
740 | |
741 | StoreRegisterTo(getIteratorIC->output()).generate(this); |
742 | restoreLiveIgnore(lir, |
743 | StoreRegisterTo(getIteratorIC->output()).clobbered()); |
744 | |
745 | masm.jump(ool->rejoin()); |
746 | return; |
747 | } |
748 | case CacheKind::OptimizeSpreadCall: { |
749 | auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC(); |
750 | |
751 | saveLive(lir); |
752 | |
753 | pushArg(optimizeSpreadCallIC->value()); |
754 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
755 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
756 | |
757 | using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*, |
758 | HandleValue, MutableHandleValue); |
759 | callVM<Fn, IonOptimizeSpreadCallIC::update>(lir); |
760 | |
761 | StoreValueTo(optimizeSpreadCallIC->output()).generate(this); |
762 | restoreLiveIgnore( |
763 | lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered()); |
764 | |
765 | masm.jump(ool->rejoin()); |
766 | return; |
767 | } |
768 | case CacheKind::In: { |
769 | IonInIC* inIC = ic->asInIC(); |
770 | |
771 | saveLive(lir); |
772 | |
773 | pushArg(inIC->object()); |
774 | pushArg(inIC->key()); |
775 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
776 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
777 | |
778 | using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue, |
779 | HandleObject, bool*); |
780 | callVM<Fn, IonInIC::update>(lir); |
781 | |
782 | StoreRegisterTo(inIC->output()).generate(this); |
783 | restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered()); |
784 | |
785 | masm.jump(ool->rejoin()); |
786 | return; |
787 | } |
788 | case CacheKind::HasOwn: { |
789 | IonHasOwnIC* hasOwnIC = ic->asHasOwnIC(); |
790 | |
791 | saveLive(lir); |
792 | |
793 | pushArg(hasOwnIC->id()); |
794 | pushArg(hasOwnIC->value()); |
795 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
796 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
797 | |
798 | using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue, |
799 | HandleValue, int32_t*); |
800 | callVM<Fn, IonHasOwnIC::update>(lir); |
801 | |
802 | StoreRegisterTo(hasOwnIC->output()).generate(this); |
803 | restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered()); |
804 | |
805 | masm.jump(ool->rejoin()); |
806 | return; |
807 | } |
808 | case CacheKind::CheckPrivateField: { |
809 | IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC(); |
810 | |
811 | saveLive(lir); |
812 | |
813 | pushArg(checkPrivateFieldIC->id()); |
814 | pushArg(checkPrivateFieldIC->value()); |
815 | |
816 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
817 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
818 | |
819 | using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*, |
820 | HandleValue, HandleValue, bool*); |
821 | callVM<Fn, IonCheckPrivateFieldIC::update>(lir); |
822 | |
823 | StoreRegisterTo(checkPrivateFieldIC->output()).generate(this); |
824 | restoreLiveIgnore( |
825 | lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered()); |
826 | |
827 | masm.jump(ool->rejoin()); |
828 | return; |
829 | } |
830 | case CacheKind::InstanceOf: { |
831 | IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC(); |
832 | |
833 | saveLive(lir); |
834 | |
835 | pushArg(hasInstanceOfIC->rhs()); |
836 | pushArg(hasInstanceOfIC->lhs()); |
837 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
838 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
839 | |
840 | using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*, |
841 | HandleValue lhs, HandleObject rhs, bool* res); |
842 | callVM<Fn, IonInstanceOfIC::update>(lir); |
843 | |
844 | StoreRegisterTo(hasInstanceOfIC->output()).generate(this); |
845 | restoreLiveIgnore(lir, |
846 | StoreRegisterTo(hasInstanceOfIC->output()).clobbered()); |
847 | |
848 | masm.jump(ool->rejoin()); |
849 | return; |
850 | } |
851 | case CacheKind::UnaryArith: { |
852 | IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC(); |
853 | |
854 | saveLive(lir); |
855 | |
856 | pushArg(unaryArithIC->input()); |
857 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
858 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
859 | |
860 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
861 | IonUnaryArithIC* stub, HandleValue val, |
862 | MutableHandleValue res); |
863 | callVM<Fn, IonUnaryArithIC::update>(lir); |
864 | |
865 | StoreValueTo(unaryArithIC->output()).generate(this); |
866 | restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered()); |
867 | |
868 | masm.jump(ool->rejoin()); |
869 | return; |
870 | } |
871 | case CacheKind::ToPropertyKey: { |
872 | IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC(); |
873 | |
874 | saveLive(lir); |
875 | |
876 | pushArg(toPropertyKeyIC->input()); |
877 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
878 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
879 | |
880 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
881 | IonToPropertyKeyIC* ic, HandleValue val, |
882 | MutableHandleValue res); |
883 | callVM<Fn, IonToPropertyKeyIC::update>(lir); |
884 | |
885 | StoreValueTo(toPropertyKeyIC->output()).generate(this); |
886 | restoreLiveIgnore(lir, |
887 | StoreValueTo(toPropertyKeyIC->output()).clobbered()); |
888 | |
889 | masm.jump(ool->rejoin()); |
890 | return; |
891 | } |
892 | case CacheKind::BinaryArith: { |
893 | IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC(); |
894 | |
895 | saveLive(lir); |
896 | |
897 | pushArg(binaryArithIC->rhs()); |
898 | pushArg(binaryArithIC->lhs()); |
899 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
900 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
901 | |
902 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
903 | IonBinaryArithIC* stub, HandleValue lhs, |
904 | HandleValue rhs, MutableHandleValue res); |
905 | callVM<Fn, IonBinaryArithIC::update>(lir); |
906 | |
907 | StoreValueTo(binaryArithIC->output()).generate(this); |
908 | restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered()); |
909 | |
910 | masm.jump(ool->rejoin()); |
911 | return; |
912 | } |
913 | case CacheKind::Compare: { |
914 | IonCompareIC* compareIC = ic->asCompareIC(); |
915 | |
916 | saveLive(lir); |
917 | |
918 | pushArg(compareIC->rhs()); |
919 | pushArg(compareIC->lhs()); |
920 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
921 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
922 | |
923 | using Fn = |
924 | bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub, |
925 | HandleValue lhs, HandleValue rhs, bool* res); |
926 | callVM<Fn, IonCompareIC::update>(lir); |
927 | |
928 | StoreRegisterTo(compareIC->output()).generate(this); |
929 | restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered()); |
930 | |
931 | masm.jump(ool->rejoin()); |
932 | return; |
933 | } |
934 | case CacheKind::CloseIter: { |
935 | IonCloseIterIC* closeIterIC = ic->asCloseIterIC(); |
936 | |
937 | saveLive(lir); |
938 | |
939 | pushArg(closeIterIC->iter()); |
940 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
941 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
942 | |
943 | using Fn = |
944 | bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject); |
945 | callVM<Fn, IonCloseIterIC::update>(lir); |
946 | |
947 | restoreLive(lir); |
948 | |
949 | masm.jump(ool->rejoin()); |
950 | return; |
951 | } |
952 | case CacheKind::OptimizeGetIterator: { |
953 | auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC(); |
954 | |
955 | saveLive(lir); |
956 | |
957 | pushArg(optimizeGetIteratorIC->value()); |
958 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
959 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
960 | |
961 | using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*, |
962 | HandleValue, bool* res); |
963 | callVM<Fn, IonOptimizeGetIteratorIC::update>(lir); |
964 | |
965 | StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this); |
966 | restoreLiveIgnore( |
967 | lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered()); |
968 | |
969 | masm.jump(ool->rejoin()); |
970 | return; |
971 | } |
972 | case CacheKind::Call: |
973 | case CacheKind::TypeOf: |
974 | case CacheKind::TypeOfEq: |
975 | case CacheKind::ToBool: |
976 | case CacheKind::LazyConstant: |
977 | case CacheKind::NewArray: |
978 | case CacheKind::NewObject: |
979 | case CacheKind::Lambda: |
980 | case CacheKind::GetImport: |
981 | MOZ_CRASH("Unsupported IC")do { do { } while (false); MOZ_ReportCrash("" "Unsupported IC" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 981); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported IC" ")" ); do { *((volatile int*)__null) = 981; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
982 | } |
983 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 983); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 983; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
984 | } |
985 | |
986 | StringObject* MNewStringObject::templateObj() const { |
987 | return &templateObj_->as<StringObject>(); |
988 | } |
989 | |
990 | CodeGenerator::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 | |
997 | CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); } |
998 | |
999 | void CodeGenerator::visitValueToNumberInt32(LValueToNumberInt32* lir) { |
1000 | ValueOperand operand = ToValue(lir, LValueToNumberInt32::InputIndex); |
1001 | Register output = ToRegister(lir->output()); |
1002 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
1003 | |
1004 | Label fails; |
1005 | masm.convertValueToInt32(operand, temp, output, &fails, |
1006 | lir->mir()->needsNegativeZeroCheck(), |
1007 | lir->mir()->conversion()); |
1008 | |
1009 | bailoutFrom(&fails, lir->snapshot()); |
1010 | } |
1011 | |
1012 | void CodeGenerator::visitValueTruncateToInt32(LValueTruncateToInt32* lir) { |
1013 | ValueOperand operand = ToValue(lir, LValueTruncateToInt32::InputIndex); |
1014 | Register output = ToRegister(lir->output()); |
1015 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
1016 | Register stringReg = ToRegister(lir->temp1()); |
1017 | |
1018 | auto* oolDouble = oolTruncateDouble(temp, output, lir->mir()); |
1019 | |
1020 | using Fn = bool (*)(JSContext*, JSString*, double*); |
1021 | auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg), |
1022 | StoreFloatRegisterTo(temp)); |
1023 | Label* stringEntry = oolString->entry(); |
1024 | Label* stringRejoin = oolString->rejoin(); |
1025 | |
1026 | Label fails; |
1027 | masm.truncateValueToInt32(operand, stringEntry, stringRejoin, |
1028 | oolDouble->entry(), stringReg, temp, output, |
1029 | &fails); |
1030 | masm.bind(oolDouble->rejoin()); |
1031 | |
1032 | bailoutFrom(&fails, lir->snapshot()); |
1033 | } |
1034 | |
1035 | void CodeGenerator::visitValueToDouble(LValueToDouble* lir) { |
1036 | ValueOperand operand = ToValue(lir, LValueToDouble::InputIndex); |
1037 | FloatRegister output = ToFloatRegister(lir->output()); |
1038 | |
1039 | Label fail; |
1040 | masm.convertValueToDouble(operand, output, &fail); |
1041 | bailoutFrom(&fail, lir->snapshot()); |
1042 | } |
1043 | |
1044 | void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) { |
1045 | ValueOperand operand = ToValue(lir, LValueToFloat32::InputIndex); |
1046 | FloatRegister output = ToFloatRegister(lir->output()); |
1047 | |
1048 | Label fail; |
1049 | masm.convertValueToFloat32(operand, output, &fail); |
1050 | bailoutFrom(&fail, lir->snapshot()); |
1051 | } |
1052 | |
1053 | void CodeGenerator::visitValueToFloat16(LValueToFloat16* lir) { |
1054 | ValueOperand operand = ToValue(lir, LValueToFloat16::InputIndex); |
1055 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
1056 | FloatRegister output = ToFloatRegister(lir->output()); |
1057 | |
1058 | LiveRegisterSet volatileRegs; |
1059 | if (!MacroAssembler::SupportsFloat64To16()) { |
1060 | volatileRegs = liveVolatileRegs(lir); |
1061 | } |
1062 | |
1063 | Label fail; |
1064 | masm.convertValueToFloat16(operand, output, temp, volatileRegs, &fail); |
1065 | bailoutFrom(&fail, lir->snapshot()); |
1066 | } |
1067 | |
1068 | void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) { |
1069 | ValueOperand operand = ToValue(lir, LValueToBigInt::InputIndex); |
1070 | Register output = ToRegister(lir->output()); |
1071 | |
1072 | using Fn = BigInt* (*)(JSContext*, HandleValue); |
1073 | auto* ool = |
1074 | oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output)); |
1075 | |
1076 | Register tag = masm.extractTag(operand, output); |
1077 | |
1078 | Label notBigInt, done; |
1079 | masm.branchTestBigInt(Assembler::NotEqual, tag, ¬BigInt); |
1080 | masm.unboxBigInt(operand, output); |
1081 | masm.jump(&done); |
1082 | masm.bind(¬BigInt); |
1083 | |
1084 | masm.branchTestBoolean(Assembler::Equal, tag, ool->entry()); |
1085 | masm.branchTestString(Assembler::Equal, tag, ool->entry()); |
1086 | |
1087 | // ToBigInt(object) can have side-effects; all other types throw a TypeError. |
1088 | bailout(lir->snapshot()); |
1089 | |
1090 | masm.bind(ool->rejoin()); |
1091 | masm.bind(&done); |
1092 | } |
1093 | |
1094 | void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) { |
1095 | masm.convertInt32ToDouble(ToRegister(lir->input()), |
1096 | ToFloatRegister(lir->output())); |
1097 | } |
1098 | |
1099 | void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) { |
1100 | masm.convertFloat32ToDouble(ToFloatRegister(lir->input()), |
1101 | ToFloatRegister(lir->output())); |
1102 | } |
1103 | |
1104 | void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) { |
1105 | masm.convertDoubleToFloat32(ToFloatRegister(lir->input()), |
1106 | ToFloatRegister(lir->output())); |
1107 | } |
1108 | |
1109 | void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) { |
1110 | masm.convertInt32ToFloat32(ToRegister(lir->input()), |
1111 | ToFloatRegister(lir->output())); |
1112 | } |
1113 | |
1114 | void CodeGenerator::visitDoubleToFloat16(LDoubleToFloat16* lir) { |
1115 | LiveRegisterSet volatileRegs; |
1116 | if (!MacroAssembler::SupportsFloat64To16()) { |
1117 | volatileRegs = liveVolatileRegs(lir); |
1118 | } |
1119 | masm.convertDoubleToFloat16( |
1120 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
1121 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
1122 | } |
1123 | |
1124 | void CodeGenerator::visitDoubleToFloat32ToFloat16( |
1125 | LDoubleToFloat32ToFloat16* lir) { |
1126 | masm.convertDoubleToFloat16( |
1127 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
1128 | ToRegister(lir->temp0()), ToRegister(lir->temp1())); |
1129 | } |
1130 | |
1131 | void CodeGenerator::visitFloat32ToFloat16(LFloat32ToFloat16* lir) { |
1132 | LiveRegisterSet volatileRegs; |
1133 | if (!MacroAssembler::SupportsFloat32To16()) { |
1134 | volatileRegs = liveVolatileRegs(lir); |
1135 | } |
1136 | masm.convertFloat32ToFloat16( |
1137 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
1138 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
1139 | } |
1140 | |
1141 | void CodeGenerator::visitInt32ToFloat16(LInt32ToFloat16* lir) { |
1142 | LiveRegisterSet volatileRegs; |
1143 | if (!MacroAssembler::SupportsFloat32To16()) { |
1144 | volatileRegs = liveVolatileRegs(lir); |
1145 | } |
1146 | masm.convertInt32ToFloat16( |
1147 | ToRegister(lir->input()), ToFloatRegister(lir->output()), |
1148 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
1149 | } |
1150 | |
1151 | void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) { |
1152 | Label fail; |
1153 | FloatRegister input = ToFloatRegister(lir->input()); |
1154 | Register output = ToRegister(lir->output()); |
1155 | masm.convertDoubleToInt32(input, output, &fail, |
1156 | lir->mir()->needsNegativeZeroCheck()); |
1157 | bailoutFrom(&fail, lir->snapshot()); |
1158 | } |
1159 | |
1160 | void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) { |
1161 | Label fail; |
1162 | FloatRegister input = ToFloatRegister(lir->input()); |
1163 | Register output = ToRegister(lir->output()); |
1164 | masm.convertFloat32ToInt32(input, output, &fail, |
1165 | lir->mir()->needsNegativeZeroCheck()); |
1166 | bailoutFrom(&fail, lir->snapshot()); |
1167 | } |
1168 | |
1169 | void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) { |
1170 | #ifdef JS_64BIT1 |
1171 | // This LIR instruction is only used if the input can be negative. |
1172 | MOZ_ASSERT(lir->mir()->canBeNegative())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->canBeNegative())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->canBeNegative ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->canBeNegative()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1172); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->canBeNegative()" ")"); do { *((volatile int*)__null) = 1172; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1173 | |
1174 | Register output = ToRegister(lir->output()); |
1175 | const LAllocation* input = lir->input(); |
1176 | if (input->isRegister()) { |
1177 | masm.move32SignExtendToPtr(ToRegister(input), output); |
1178 | } else { |
1179 | masm.load32SignExtendToPtr(ToAddress(input), output); |
1180 | } |
1181 | #else |
1182 | MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1182); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { *((volatile int*)__null) = 1182; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
1183 | #endif |
1184 | } |
1185 | |
1186 | void CodeGenerator::visitNonNegativeIntPtrToInt32( |
1187 | LNonNegativeIntPtrToInt32* lir) { |
1188 | #ifdef JS_64BIT1 |
1189 | Register output = ToRegister(lir->output()); |
1190 | MOZ_ASSERT(ToRegister(lir->input()) == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->input()) == output)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(ToRegister(lir->input()) == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->input()) == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1190); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"); do { *((volatile int*)__null) = 1190; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1191 | |
1192 | Label bail; |
1193 | masm.guardNonNegativeIntPtrToInt32(output, &bail); |
1194 | bailoutFrom(&bail, lir->snapshot()); |
1195 | #else |
1196 | MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1196); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { *((volatile int*)__null) = 1196; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
1197 | #endif |
1198 | } |
1199 | |
1200 | void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) { |
1201 | Register input = ToRegister(lir->input()); |
1202 | FloatRegister output = ToFloatRegister(lir->output()); |
1203 | masm.convertIntPtrToDouble(input, output); |
1204 | } |
1205 | |
1206 | void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) { |
1207 | Register output = ToRegister(lir->output()); |
1208 | MOZ_ASSERT(ToRegister(lir->input()) == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->input()) == output)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(ToRegister(lir->input()) == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->input()) == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1208); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"); do { *((volatile int*)__null) = 1208; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1209 | |
1210 | uint32_t byteSize = lir->mir()->byteSize(); |
1211 | |
1212 | #ifdef DEBUG1 |
1213 | Label ok; |
1214 | masm.branchTestPtr(Assembler::NotSigned, output, output, &ok); |
1215 | masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength"); |
1216 | masm.bind(&ok); |
1217 | #endif |
1218 | |
1219 | Label bail; |
1220 | masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail); |
1221 | bailoutFrom(&bail, lir->snapshot()); |
1222 | } |
1223 | |
1224 | void CodeGenerator::emitOOLTestObject(Register objreg, |
1225 | Label* ifEmulatesUndefined, |
1226 | Label* ifDoesntEmulateUndefined, |
1227 | Register scratch) { |
1228 | saveVolatile(scratch); |
1229 | #if defined(DEBUG1) || defined(FUZZING) |
1230 | masm.loadPtr(AbsoluteAddress( |
1231 | gen->runtime->addressOfHasSeenObjectEmulateUndefinedFuse()), |
1232 | scratch); |
1233 | using Fn = bool (*)(JSObject* obj, size_t fuseValue); |
1234 | masm.setupAlignedABICall(); |
1235 | masm.passABIArg(objreg); |
1236 | masm.passABIArg(scratch); |
1237 | masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>(); |
1238 | #else |
1239 | using Fn = bool (*)(JSObject* obj); |
1240 | masm.setupAlignedABICall(); |
1241 | masm.passABIArg(objreg); |
1242 | masm.callWithABI<Fn, js::EmulatesUndefined>(); |
1243 | #endif |
1244 | masm.storeCallPointerResult(scratch); |
1245 | restoreVolatile(scratch); |
1246 | |
1247 | masm.branchIfTrueBool(scratch, ifEmulatesUndefined); |
1248 | masm.jump(ifDoesntEmulateUndefined); |
1249 | } |
1250 | |
1251 | // Base out-of-line code generator for all tests of the truthiness of an |
1252 | // object, where the object might not be truthy. (Recall that per spec all |
1253 | // objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class |
1254 | // flag to permit objects to look like |undefined| in certain contexts, |
1255 | // including in object truthiness testing.) We check truthiness inline except |
1256 | // when we're testing it on a proxy, in which case out-of-line code will call |
1257 | // EmulatesUndefined for a conclusive answer. |
1258 | class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> { |
1259 | Register objreg_; |
1260 | Register scratch_; |
1261 | |
1262 | Label* ifEmulatesUndefined_; |
1263 | Label* ifDoesntEmulateUndefined_; |
1264 | |
1265 | #ifdef DEBUG1 |
1266 | bool initialized() { return ifEmulatesUndefined_ != nullptr; } |
1267 | #endif |
1268 | |
1269 | public: |
1270 | OutOfLineTestObject() |
1271 | : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {} |
1272 | |
1273 | void accept(CodeGenerator* codegen) final { |
1274 | MOZ_ASSERT(initialized())do { static_assert( mozilla::detail::AssertionConditionType< decltype(initialized())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(initialized()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("initialized()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1274); AnnotateMozCrashReason("MOZ_ASSERT" "(" "initialized()" ")"); do { *((volatile int*)__null) = 1274; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1275 | codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_, |
1276 | ifDoesntEmulateUndefined_, scratch_); |
1277 | } |
1278 | |
1279 | // Specify the register where the object to be tested is found, labels to |
1280 | // jump to if the object is truthy or falsy, and a scratch register for |
1281 | // use in the out-of-line path. |
1282 | void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined, |
1283 | Label* ifDoesntEmulateUndefined, Register scratch) { |
1284 | MOZ_ASSERT(!initialized())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!initialized())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!initialized()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!initialized()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1284); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!initialized()" ")"); do { *((volatile int*)__null) = 1284; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1285 | MOZ_ASSERT(ifEmulatesUndefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ifEmulatesUndefined)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ifEmulatesUndefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ifEmulatesUndefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1285); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ifEmulatesUndefined" ")"); do { *((volatile int*)__null) = 1285; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1286 | objreg_ = objreg; |
1287 | scratch_ = scratch; |
1288 | ifEmulatesUndefined_ = ifEmulatesUndefined; |
1289 | ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined; |
1290 | } |
1291 | }; |
1292 | |
1293 | // A subclass of OutOfLineTestObject containing two extra labels, for use when |
1294 | // the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line |
1295 | // code. The user should bind these labels in inline code, and specify them as |
1296 | // targets via setInputAndTargets, as appropriate. |
1297 | class OutOfLineTestObjectWithLabels : public OutOfLineTestObject { |
1298 | Label label1_; |
1299 | Label label2_; |
1300 | |
1301 | public: |
1302 | OutOfLineTestObjectWithLabels() = default; |
1303 | |
1304 | Label* label1() { return &label1_; } |
1305 | Label* label2() { return &label2_; } |
1306 | }; |
1307 | |
1308 | void CodeGenerator::testObjectEmulatesUndefinedKernel( |
1309 | Register objreg, Label* ifEmulatesUndefined, |
1310 | Label* ifDoesntEmulateUndefined, Register scratch, |
1311 | OutOfLineTestObject* ool) { |
1312 | ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, |
1313 | scratch); |
1314 | |
1315 | // Perform a fast-path check of the object's class flags if the object's |
1316 | // not a proxy. Let out-of-line code handle the slow cases that require |
1317 | // saving registers, making a function call, and restoring registers. |
1318 | masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(), |
1319 | ifEmulatesUndefined); |
1320 | } |
1321 | |
1322 | void CodeGenerator::branchTestObjectEmulatesUndefined( |
1323 | Register objreg, Label* ifEmulatesUndefined, |
1324 | Label* ifDoesntEmulateUndefined, Register scratch, |
1325 | OutOfLineTestObject* ool) { |
1326 | MOZ_ASSERT(!ifDoesntEmulateUndefined->bound(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ifDoesntEmulateUndefined->bound())>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(!ifDoesntEmulateUndefined->bound()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!ifDoesntEmulateUndefined->bound()" " (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")"); do { *((volatile int*)__null) = 1327; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
1327 | "ifDoesntEmulateUndefined will be bound to the fallthrough path")do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ifDoesntEmulateUndefined->bound())>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(!ifDoesntEmulateUndefined->bound()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!ifDoesntEmulateUndefined->bound()" " (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")"); do { *((volatile int*)__null) = 1327; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1328 | |
1329 | testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, |
1330 | ifDoesntEmulateUndefined, scratch, ool); |
1331 | masm.bind(ifDoesntEmulateUndefined); |
1332 | } |
1333 | |
1334 | void CodeGenerator::testObjectEmulatesUndefined(Register objreg, |
1335 | Label* ifEmulatesUndefined, |
1336 | Label* ifDoesntEmulateUndefined, |
1337 | Register scratch, |
1338 | OutOfLineTestObject* ool) { |
1339 | testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, |
1340 | ifDoesntEmulateUndefined, scratch, ool); |
1341 | masm.jump(ifDoesntEmulateUndefined); |
1342 | } |
1343 | |
1344 | void CodeGenerator::testValueTruthyForType( |
1345 | JSValueType type, ScratchTagScope& tag, const ValueOperand& value, |
1346 | Register tempToUnbox, Register temp, FloatRegister floatTemp, |
1347 | Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool, |
1348 | bool skipTypeTest) { |
1349 | #ifdef DEBUG1 |
1350 | if (skipTypeTest) { |
1351 | Label expected; |
1352 | masm.branchTestType(Assembler::Equal, tag, type, &expected); |
1353 | masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType"); |
1354 | masm.bind(&expected); |
1355 | } |
1356 | #endif |
1357 | |
1358 | // Handle irregular types first. |
1359 | switch (type) { |
1360 | case JSVAL_TYPE_UNDEFINED: |
1361 | case JSVAL_TYPE_NULL: |
1362 | // Undefined and null are falsy. |
1363 | if (!skipTypeTest) { |
1364 | masm.branchTestType(Assembler::Equal, tag, type, ifFalsy); |
1365 | } else { |
1366 | masm.jump(ifFalsy); |
1367 | } |
1368 | return; |
1369 | case JSVAL_TYPE_SYMBOL: |
1370 | // Symbols are truthy. |
1371 | if (!skipTypeTest) { |
1372 | masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy); |
1373 | } else { |
1374 | masm.jump(ifTruthy); |
1375 | } |
1376 | return; |
1377 | case JSVAL_TYPE_OBJECT: { |
1378 | Label notObject; |
1379 | if (!skipTypeTest) { |
1380 | masm.branchTestObject(Assembler::NotEqual, tag, ¬Object); |
1381 | } |
1382 | ScratchTagScopeRelease _(&tag); |
1383 | Register objreg = masm.extractObject(value, tempToUnbox); |
1384 | testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool); |
1385 | masm.bind(¬Object); |
1386 | return; |
1387 | } |
1388 | default: |
1389 | break; |
1390 | } |
1391 | |
1392 | // Check the type of the value (unless this is the last possible type). |
1393 | Label differentType; |
1394 | if (!skipTypeTest) { |
1395 | masm.branchTestType(Assembler::NotEqual, tag, type, &differentType); |
1396 | } |
1397 | |
1398 | // Branch if the value is falsy. |
1399 | ScratchTagScopeRelease _(&tag); |
1400 | switch (type) { |
1401 | case JSVAL_TYPE_BOOLEAN: { |
1402 | masm.branchTestBooleanTruthy(false, value, ifFalsy); |
1403 | break; |
1404 | } |
1405 | case JSVAL_TYPE_INT32: { |
1406 | masm.branchTestInt32Truthy(false, value, ifFalsy); |
1407 | break; |
1408 | } |
1409 | case JSVAL_TYPE_STRING: { |
1410 | masm.branchTestStringTruthy(false, value, ifFalsy); |
1411 | break; |
1412 | } |
1413 | case JSVAL_TYPE_BIGINT: { |
1414 | masm.branchTestBigIntTruthy(false, value, ifFalsy); |
1415 | break; |
1416 | } |
1417 | case JSVAL_TYPE_DOUBLE: { |
1418 | masm.unboxDouble(value, floatTemp); |
1419 | masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy); |
1420 | break; |
1421 | } |
1422 | default: |
1423 | MOZ_CRASH("Unexpected value type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected value type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1423); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected value type" ")"); do { *((volatile int*)__null) = 1423; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
1424 | } |
1425 | |
1426 | // If we reach this point, the value is truthy. We fall through for |
1427 | // truthy on the last test; otherwise, branch. |
1428 | if (!skipTypeTest) { |
1429 | masm.jump(ifTruthy); |
1430 | } |
1431 | |
1432 | masm.bind(&differentType); |
1433 | } |
1434 | |
1435 | void CodeGenerator::testValueTruthy(const ValueOperand& value, |
1436 | Register tempToUnbox, Register temp, |
1437 | FloatRegister floatTemp, |
1438 | const TypeDataList& observedTypes, |
1439 | Label* ifTruthy, Label* ifFalsy, |
1440 | OutOfLineTestObject* ool) { |
1441 | ScratchTagScope tag(masm, value); |
1442 | masm.splitTagForTest(value, tag); |
1443 | |
1444 | const std::initializer_list<JSValueType> defaultOrder = { |
1445 | JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, |
1446 | JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING, |
1447 | JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT}; |
1448 | |
1449 | mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder); |
1450 | |
1451 | // Generate tests for previously observed types first. |
1452 | // The TypeDataList is sorted by descending frequency. |
1453 | for (auto& observed : observedTypes) { |
1454 | JSValueType type = observed.type(); |
1455 | remaining -= type; |
1456 | |
1457 | testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp, |
1458 | ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false); |
1459 | } |
1460 | |
1461 | // Generate tests for remaining types. |
1462 | for (auto type : defaultOrder) { |
1463 | if (!remaining.contains(type)) { |
1464 | continue; |
1465 | } |
1466 | remaining -= type; |
1467 | |
1468 | // We don't need a type test for the last possible type. |
1469 | bool skipTypeTest = remaining.isEmpty(); |
1470 | testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp, |
1471 | ifTruthy, ifFalsy, ool, skipTypeTest); |
1472 | } |
1473 | MOZ_ASSERT(remaining.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(remaining.isEmpty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(remaining.isEmpty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("remaining.isEmpty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { *((volatile int*)__null) = 1473; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1474 | |
1475 | // We fall through if the final test is truthy. |
1476 | } |
1477 | |
1478 | void CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test) { |
1479 | Register input = ToRegister(test->input()); |
1480 | MBasicBlock* ifTrue = test->ifTrue(); |
1481 | MBasicBlock* ifFalse = test->ifFalse(); |
1482 | |
1483 | if (isNextBlock(ifFalse->lir())) { |
1484 | masm.branchTest32(Assembler::NonZero, input, input, |
1485 | getJumpLabelForBranch(ifTrue)); |
1486 | } else { |
1487 | masm.branchTest32(Assembler::Zero, input, input, |
1488 | getJumpLabelForBranch(ifFalse)); |
1489 | jumpToBlock(ifTrue); |
1490 | } |
1491 | } |
1492 | |
1493 | void CodeGenerator::visitTestIPtrAndBranch(LTestIPtrAndBranch* test) { |
1494 | Register input = ToRegister(test->input()); |
1495 | MBasicBlock* ifTrue = test->ifTrue(); |
1496 | MBasicBlock* ifFalse = test->ifFalse(); |
1497 | |
1498 | if (isNextBlock(ifFalse->lir())) { |
1499 | masm.branchTestPtr(Assembler::NonZero, input, input, |
1500 | getJumpLabelForBranch(ifTrue)); |
1501 | } else { |
1502 | masm.branchTestPtr(Assembler::Zero, input, input, |
1503 | getJumpLabelForBranch(ifFalse)); |
1504 | jumpToBlock(ifTrue); |
1505 | } |
1506 | } |
1507 | |
1508 | void CodeGenerator::visitTestI64AndBranch(LTestI64AndBranch* test) { |
1509 | Register64 input = ToRegister64(test->input()); |
1510 | MBasicBlock* ifTrue = test->ifTrue(); |
1511 | MBasicBlock* ifFalse = test->ifFalse(); |
1512 | |
1513 | if (isNextBlock(ifFalse->lir())) { |
1514 | masm.branchTest64(Assembler::NonZero, input, input, |
1515 | getJumpLabelForBranch(ifTrue)); |
1516 | } else if (isNextBlock(ifTrue->lir())) { |
1517 | masm.branchTest64(Assembler::Zero, input, input, |
1518 | getJumpLabelForBranch(ifFalse)); |
1519 | } else { |
1520 | masm.branchTest64(Assembler::NonZero, input, input, |
1521 | getJumpLabelForBranch(ifTrue), |
1522 | getJumpLabelForBranch(ifFalse)); |
1523 | } |
1524 | } |
1525 | |
1526 | void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) { |
1527 | Register input = ToRegister(lir->input()); |
1528 | MBasicBlock* ifTrue = lir->ifTrue(); |
1529 | MBasicBlock* ifFalse = lir->ifFalse(); |
1530 | |
1531 | if (isNextBlock(ifFalse->lir())) { |
1532 | masm.branchIfBigIntIsNonZero(input, getJumpLabelForBranch(ifTrue)); |
1533 | } else { |
1534 | masm.branchIfBigIntIsZero(input, getJumpLabelForBranch(ifFalse)); |
1535 | jumpToBlock(ifTrue); |
1536 | } |
1537 | } |
1538 | |
1539 | static Assembler::Condition ReverseCondition(Assembler::Condition condition) { |
1540 | switch (condition) { |
1541 | case Assembler::Equal: |
1542 | case Assembler::NotEqual: |
1543 | return condition; |
1544 | case Assembler::Above: |
1545 | return Assembler::Below; |
1546 | case Assembler::AboveOrEqual: |
1547 | return Assembler::BelowOrEqual; |
1548 | case Assembler::Below: |
1549 | return Assembler::Above; |
1550 | case Assembler::BelowOrEqual: |
1551 | return Assembler::AboveOrEqual; |
1552 | case Assembler::GreaterThan: |
1553 | return Assembler::LessThan; |
1554 | case Assembler::GreaterThanOrEqual: |
1555 | return Assembler::LessThanOrEqual; |
1556 | case Assembler::LessThan: |
1557 | return Assembler::GreaterThan; |
1558 | case Assembler::LessThanOrEqual: |
1559 | return Assembler::GreaterThanOrEqual; |
1560 | default: |
1561 | break; |
1562 | } |
1563 | MOZ_CRASH("unhandled condition")do { do { } while (false); MOZ_ReportCrash("" "unhandled condition" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1563); AnnotateMozCrashReason("MOZ_CRASH(" "unhandled condition" ")"); do { *((volatile int*)__null) = 1563; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
1564 | } |
1565 | |
1566 | void CodeGenerator::visitCompare(LCompare* comp) { |
1567 | MCompare::CompareType compareType = comp->mir()->compareType(); |
1568 | Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop()); |
1569 | Register left = ToRegister(comp->left()); |
1570 | const LAllocation* right = comp->right(); |
1571 | Register output = ToRegister(comp->output()); |
1572 | |
1573 | if (compareType == MCompare::Compare_Object || |
1574 | compareType == MCompare::Compare_Symbol || |
1575 | compareType == MCompare::Compare_IntPtr || |
1576 | compareType == MCompare::Compare_UIntPtr || |
1577 | compareType == MCompare::Compare_WasmAnyRef) { |
1578 | if (right->isConstant()) { |
1579 | MOZ_ASSERT(compareType == MCompare::Compare_IntPtr ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { *((volatile int*)__null) = 1580; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
1580 | compareType == MCompare::Compare_UIntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { *((volatile int*)__null) = 1580; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1581 | masm.cmpPtrSet(cond, left, ImmWord(ToInt32(right)), output); |
1582 | } else if (right->isRegister()) { |
1583 | masm.cmpPtrSet(cond, left, ToRegister(right), output); |
1584 | } else { |
1585 | masm.cmpPtrSet(ReverseCondition(cond), ToAddress(right), left, output); |
1586 | } |
1587 | return; |
1588 | } |
1589 | |
1590 | MOZ_ASSERT(compareType == MCompare::Compare_Int32 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1591); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { *((volatile int*)__null) = 1591; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
1591 | compareType == MCompare::Compare_UInt32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1591); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { *((volatile int*)__null) = 1591; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1592 | |
1593 | if (right->isConstant()) { |
1594 | masm.cmp32Set(cond, left, Imm32(ToInt32(right)), output); |
1595 | } else if (right->isRegister()) { |
1596 | masm.cmp32Set(cond, left, ToRegister(right), output); |
1597 | } else { |
1598 | masm.cmp32Set(ReverseCondition(cond), ToAddress(right), left, output); |
1599 | } |
1600 | } |
1601 | |
1602 | void CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp) { |
1603 | MCompare::CompareType compareType = comp->cmpMir()->compareType(); |
1604 | Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop()); |
1605 | Register left = ToRegister(comp->left()); |
1606 | const LAllocation* right = comp->right(); |
1607 | |
1608 | MBasicBlock* ifTrue = comp->ifTrue(); |
1609 | MBasicBlock* ifFalse = comp->ifFalse(); |
1610 | |
1611 | // If the next block is the true case, invert the condition to fall through. |
1612 | Label* label; |
1613 | if (isNextBlock(ifTrue->lir())) { |
1614 | cond = Assembler::InvertCondition(cond); |
1615 | label = getJumpLabelForBranch(ifFalse); |
1616 | } else { |
1617 | label = getJumpLabelForBranch(ifTrue); |
1618 | } |
1619 | |
1620 | if (compareType == MCompare::Compare_Object || |
1621 | compareType == MCompare::Compare_Symbol || |
1622 | compareType == MCompare::Compare_IntPtr || |
1623 | compareType == MCompare::Compare_UIntPtr || |
1624 | compareType == MCompare::Compare_WasmAnyRef) { |
1625 | if (right->isConstant()) { |
1626 | MOZ_ASSERT(compareType == MCompare::Compare_IntPtr ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { *((volatile int*)__null) = 1627; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
1627 | compareType == MCompare::Compare_UIntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { *((volatile int*)__null) = 1627; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1628 | masm.branchPtr(cond, left, ImmWord(ToInt32(right)), label); |
1629 | } else if (right->isRegister()) { |
1630 | masm.branchPtr(cond, left, ToRegister(right), label); |
1631 | } else { |
1632 | masm.branchPtr(ReverseCondition(cond), ToAddress(right), left, label); |
1633 | } |
1634 | } else { |
1635 | MOZ_ASSERT(compareType == MCompare::Compare_Int32 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { *((volatile int*)__null) = 1636; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
1636 | compareType == MCompare::Compare_UInt32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { *((volatile int*)__null) = 1636; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1637 | |
1638 | if (right->isConstant()) { |
1639 | masm.branch32(cond, left, Imm32(ToInt32(right)), label); |
1640 | } else if (right->isRegister()) { |
1641 | masm.branch32(cond, left, ToRegister(right), label); |
1642 | } else { |
1643 | masm.branch32(ReverseCondition(cond), ToAddress(right), left, label); |
1644 | } |
1645 | } |
1646 | |
1647 | if (!isNextBlock(ifTrue->lir())) { |
1648 | jumpToBlock(ifFalse); |
1649 | } |
1650 | } |
1651 | |
1652 | void CodeGenerator::visitCompareI64(LCompareI64* lir) { |
1653 | MCompare::CompareType compareType = lir->mir()->compareType(); |
1654 | MOZ_ASSERT(compareType == MCompare::Compare_Int64 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { *((volatile int*)__null) = 1655; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
1655 | compareType == MCompare::Compare_UInt64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { *((volatile int*)__null) = 1655; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1656 | bool isSigned = compareType == MCompare::Compare_Int64; |
1657 | Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned); |
1658 | Register64 left = ToRegister64(lir->left()); |
1659 | LInt64Allocation right = lir->right(); |
1660 | Register output = ToRegister(lir->output()); |
1661 | |
1662 | if (IsConstant(right)) { |
1663 | masm.cmp64Set(cond, left, Imm64(ToInt64(right)), output); |
1664 | } else if (IsRegister64(right)) { |
1665 | masm.cmp64Set(cond, left, ToRegister64(right), output); |
1666 | } else { |
1667 | masm.cmp64Set(ReverseCondition(cond), ToAddress(right), left, output); |
1668 | } |
1669 | } |
1670 | |
1671 | void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) { |
1672 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
1673 | MOZ_ASSERT(compareType == MCompare::Compare_Int64 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { *((volatile int*)__null) = 1674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
1674 | compareType == MCompare::Compare_UInt64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { *((volatile int*)__null) = 1674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1675 | bool isSigned = compareType == MCompare::Compare_Int64; |
1676 | Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned); |
1677 | Register64 left = ToRegister64(lir->left()); |
1678 | LInt64Allocation right = lir->right(); |
1679 | |
1680 | MBasicBlock* ifTrue = lir->ifTrue(); |
1681 | MBasicBlock* ifFalse = lir->ifFalse(); |
1682 | |
1683 | Label* trueLabel = getJumpLabelForBranch(ifTrue); |
1684 | Label* falseLabel = getJumpLabelForBranch(ifFalse); |
1685 | |
1686 | // If the next block is the true case, invert the condition to fall through. |
1687 | if (isNextBlock(ifTrue->lir())) { |
1688 | cond = Assembler::InvertCondition(cond); |
1689 | trueLabel = falseLabel; |
1690 | falseLabel = nullptr; |
1691 | } else if (isNextBlock(ifFalse->lir())) { |
1692 | falseLabel = nullptr; |
1693 | } |
1694 | |
1695 | if (IsConstant(right)) { |
1696 | masm.branch64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel); |
1697 | } else if (IsRegister64(right)) { |
1698 | masm.branch64(cond, left, ToRegister64(right), trueLabel, falseLabel); |
1699 | } else { |
1700 | masm.branch64(ReverseCondition(cond), ToAddress(right), left, trueLabel, |
1701 | falseLabel); |
1702 | } |
1703 | } |
1704 | |
1705 | void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) { |
1706 | Assembler::Condition cond = baab->cond(); |
1707 | Register left = ToRegister(baab->left()); |
1708 | const LAllocation* right = baab->right(); |
1709 | |
1710 | MBasicBlock* ifTrue = baab->ifTrue(); |
1711 | MBasicBlock* ifFalse = baab->ifFalse(); |
1712 | |
1713 | // If the next block is the true case, invert the condition to fall through. |
1714 | Label* label; |
1715 | if (isNextBlock(ifTrue->lir())) { |
1716 | cond = Assembler::InvertCondition(cond); |
1717 | label = getJumpLabelForBranch(ifFalse); |
1718 | } else { |
1719 | label = getJumpLabelForBranch(ifTrue); |
1720 | } |
1721 | |
1722 | if (right->isConstant()) { |
1723 | masm.branchTest32(cond, left, Imm32(ToInt32(right)), label); |
1724 | } else { |
1725 | masm.branchTest32(cond, left, ToRegister(right), label); |
1726 | } |
1727 | |
1728 | if (!isNextBlock(ifTrue->lir())) { |
1729 | jumpToBlock(ifFalse); |
1730 | } |
1731 | } |
1732 | |
1733 | void CodeGenerator::visitBitAnd64AndBranch(LBitAnd64AndBranch* baab) { |
1734 | Assembler::Condition cond = baab->cond(); |
1735 | Register64 left = ToRegister64(baab->left()); |
1736 | LInt64Allocation right = baab->right(); |
1737 | |
1738 | MBasicBlock* ifTrue = baab->ifTrue(); |
1739 | MBasicBlock* ifFalse = baab->ifFalse(); |
1740 | |
1741 | Label* trueLabel = getJumpLabelForBranch(ifTrue); |
1742 | Label* falseLabel = getJumpLabelForBranch(ifFalse); |
1743 | |
1744 | // If the next block is the true case, invert the condition to fall through. |
1745 | if (isNextBlock(ifTrue->lir())) { |
1746 | cond = Assembler::InvertCondition(cond); |
1747 | trueLabel = falseLabel; |
1748 | falseLabel = nullptr; |
1749 | } else if (isNextBlock(ifFalse->lir())) { |
1750 | falseLabel = nullptr; |
1751 | } |
1752 | |
1753 | if (IsConstant(right)) { |
1754 | masm.branchTest64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel); |
1755 | } else { |
1756 | masm.branchTest64(cond, left, ToRegister64(right), trueLabel, falseLabel); |
1757 | } |
1758 | } |
1759 | |
1760 | void CodeGenerator::assertObjectDoesNotEmulateUndefined( |
1761 | Register input, Register temp, const MInstruction* mir) { |
1762 | #if defined(DEBUG1) || defined(FUZZING) |
1763 | // Validate that the object indeed doesn't have the emulates undefined flag. |
1764 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
1765 | addOutOfLineCode(ool, mir); |
1766 | |
1767 | Label* doesNotEmulateUndefined = ool->label1(); |
1768 | Label* emulatesUndefined = ool->label2(); |
1769 | |
1770 | testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined, |
1771 | temp, ool); |
1772 | masm.bind(emulatesUndefined); |
1773 | masm.assumeUnreachable( |
1774 | "Found an object emulating undefined while the fuse is intact"); |
1775 | masm.bind(doesNotEmulateUndefined); |
1776 | #endif |
1777 | } |
1778 | |
1779 | void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) { |
1780 | Label* truthy = getJumpLabelForBranch(lir->ifTruthy()); |
1781 | Label* falsy = getJumpLabelForBranch(lir->ifFalsy()); |
1782 | Register input = ToRegister(lir->input()); |
1783 | |
1784 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
1785 | if (intact) { |
1786 | assertObjectDoesNotEmulateUndefined(input, ToRegister(lir->temp()), |
1787 | lir->mir()); |
1788 | // Bug 1874905: It would be fantastic if this could be optimized out |
1789 | masm.jump(truthy); |
1790 | } else { |
1791 | auto* ool = new (alloc()) OutOfLineTestObject(); |
1792 | addOutOfLineCode(ool, lir->mir()); |
1793 | |
1794 | testObjectEmulatesUndefined(input, falsy, truthy, ToRegister(lir->temp()), |
1795 | ool); |
1796 | } |
1797 | } |
1798 | |
1799 | void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) { |
1800 | auto* ool = new (alloc()) OutOfLineTestObject(); |
1801 | addOutOfLineCode(ool, lir->mir()); |
1802 | |
1803 | Label* truthy = getJumpLabelForBranch(lir->ifTruthy()); |
1804 | Label* falsy = getJumpLabelForBranch(lir->ifFalsy()); |
1805 | |
1806 | ValueOperand input = ToValue(lir, LTestVAndBranch::Input); |
1807 | Register tempToUnbox = ToTempUnboxRegister(lir->temp1()); |
1808 | Register temp = ToRegister(lir->temp2()); |
1809 | FloatRegister floatTemp = ToFloatRegister(lir->tempFloat()); |
1810 | const TypeDataList& observedTypes = lir->mir()->observedTypes(); |
1811 | |
1812 | testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy, |
1813 | falsy, ool); |
1814 | masm.jump(truthy); |
1815 | } |
1816 | |
1817 | void CodeGenerator::visitBooleanToString(LBooleanToString* lir) { |
1818 | Register input = ToRegister(lir->input()); |
1819 | Register output = ToRegister(lir->output()); |
1820 | const JSAtomState& names = gen->runtime->names(); |
1821 | Label true_, done; |
1822 | |
1823 | masm.branchTest32(Assembler::NonZero, input, input, &true_); |
1824 | masm.movePtr(ImmGCPtr(names.false_), output); |
1825 | masm.jump(&done); |
1826 | |
1827 | masm.bind(&true_); |
1828 | masm.movePtr(ImmGCPtr(names.true_), output); |
1829 | |
1830 | masm.bind(&done); |
1831 | } |
1832 | |
1833 | void CodeGenerator::visitIntToString(LIntToString* lir) { |
1834 | Register input = ToRegister(lir->input()); |
1835 | Register output = ToRegister(lir->output()); |
1836 | |
1837 | using Fn = JSLinearString* (*)(JSContext*, int); |
1838 | OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>( |
1839 | lir, ArgList(input), StoreRegisterTo(output)); |
1840 | |
1841 | masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(), |
1842 | ool->entry()); |
1843 | |
1844 | masm.bind(ool->rejoin()); |
1845 | } |
1846 | |
1847 | void CodeGenerator::visitDoubleToString(LDoubleToString* lir) { |
1848 | FloatRegister input = ToFloatRegister(lir->input()); |
1849 | Register temp = ToRegister(lir->temp0()); |
1850 | Register output = ToRegister(lir->output()); |
1851 | |
1852 | using Fn = JSString* (*)(JSContext*, double); |
1853 | OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>( |
1854 | lir, ArgList(input), StoreRegisterTo(output)); |
1855 | |
1856 | // Try double to integer conversion and run integer to string code. |
1857 | masm.convertDoubleToInt32(input, temp, ool->entry(), false); |
1858 | masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(), |
1859 | ool->entry()); |
1860 | |
1861 | masm.bind(ool->rejoin()); |
1862 | } |
1863 | |
1864 | void CodeGenerator::visitValueToString(LValueToString* lir) { |
1865 | ValueOperand input = ToValue(lir, LValueToString::InputIndex); |
1866 | Register output = ToRegister(lir->output()); |
1867 | |
1868 | using Fn = JSString* (*)(JSContext*, HandleValue); |
1869 | OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>( |
1870 | lir, ArgList(input), StoreRegisterTo(output)); |
1871 | |
1872 | Label done; |
1873 | Register tag = masm.extractTag(input, output); |
1874 | const JSAtomState& names = gen->runtime->names(); |
1875 | |
1876 | // String |
1877 | { |
1878 | Label notString; |
1879 | masm.branchTestString(Assembler::NotEqual, tag, ¬String); |
1880 | masm.unboxString(input, output); |
1881 | masm.jump(&done); |
1882 | masm.bind(¬String); |
1883 | } |
1884 | |
1885 | // Integer |
1886 | { |
1887 | Label notInteger; |
1888 | masm.branchTestInt32(Assembler::NotEqual, tag, ¬Integer); |
1889 | Register unboxed = ToTempUnboxRegister(lir->temp0()); |
1890 | unboxed = masm.extractInt32(input, unboxed); |
1891 | masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(), |
1892 | ool->entry()); |
1893 | masm.jump(&done); |
1894 | masm.bind(¬Integer); |
1895 | } |
1896 | |
1897 | // Double |
1898 | { |
1899 | // Note: no fastpath. Need two extra registers and can only convert doubles |
1900 | // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT. |
1901 | masm.branchTestDouble(Assembler::Equal, tag, ool->entry()); |
1902 | } |
1903 | |
1904 | // Undefined |
1905 | { |
1906 | Label notUndefined; |
1907 | masm.branchTestUndefined(Assembler::NotEqual, tag, ¬Undefined); |
1908 | masm.movePtr(ImmGCPtr(names.undefined), output); |
1909 | masm.jump(&done); |
1910 | masm.bind(¬Undefined); |
1911 | } |
1912 | |
1913 | // Null |
1914 | { |
1915 | Label notNull; |
1916 | masm.branchTestNull(Assembler::NotEqual, tag, ¬Null); |
1917 | masm.movePtr(ImmGCPtr(names.null), output); |
1918 | masm.jump(&done); |
1919 | masm.bind(¬Null); |
1920 | } |
1921 | |
1922 | // Boolean |
1923 | { |
1924 | Label notBoolean, true_; |
1925 | masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean); |
1926 | masm.branchTestBooleanTruthy(true, input, &true_); |
1927 | masm.movePtr(ImmGCPtr(names.false_), output); |
1928 | masm.jump(&done); |
1929 | masm.bind(&true_); |
1930 | masm.movePtr(ImmGCPtr(names.true_), output); |
1931 | masm.jump(&done); |
1932 | masm.bind(¬Boolean); |
1933 | } |
1934 | |
1935 | // Objects/symbols are only possible when |mir->mightHaveSideEffects()|. |
1936 | if (lir->mir()->mightHaveSideEffects()) { |
1937 | // Object |
1938 | if (lir->mir()->supportSideEffects()) { |
1939 | masm.branchTestObject(Assembler::Equal, tag, ool->entry()); |
1940 | } else { |
1941 | // Bail. |
1942 | MOZ_ASSERT(lir->mir()->needsSnapshot())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->needsSnapshot())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->needsSnapshot ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->needsSnapshot()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1942); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()" ")"); do { *((volatile int*)__null) = 1942; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1943 | Label bail; |
1944 | masm.branchTestObject(Assembler::Equal, tag, &bail); |
1945 | bailoutFrom(&bail, lir->snapshot()); |
1946 | } |
1947 | |
1948 | // Symbol |
1949 | if (lir->mir()->supportSideEffects()) { |
1950 | masm.branchTestSymbol(Assembler::Equal, tag, ool->entry()); |
1951 | } else { |
1952 | // Bail. |
1953 | MOZ_ASSERT(lir->mir()->needsSnapshot())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->needsSnapshot())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->needsSnapshot ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->needsSnapshot()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1953); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()" ")"); do { *((volatile int*)__null) = 1953; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1954 | Label bail; |
1955 | masm.branchTestSymbol(Assembler::Equal, tag, &bail); |
1956 | bailoutFrom(&bail, lir->snapshot()); |
1957 | } |
1958 | } |
1959 | |
1960 | // BigInt |
1961 | { |
1962 | // No fastpath currently implemented. |
1963 | masm.branchTestBigInt(Assembler::Equal, tag, ool->entry()); |
1964 | } |
1965 | |
1966 | masm.assumeUnreachable("Unexpected type for LValueToString."); |
1967 | |
1968 | masm.bind(&done); |
1969 | masm.bind(ool->rejoin()); |
1970 | } |
1971 | |
1972 | using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**); |
1973 | |
1974 | static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder, |
1975 | size_t offset, Register buffer, |
1976 | LiveGeneralRegisterSet& liveVolatiles, |
1977 | StoreBufferMutationFn fun) { |
1978 | Label callVM; |
1979 | Label exit; |
1980 | |
1981 | // Call into the VM to barrier the write. The only registers that need to |
1982 | // be preserved are those in liveVolatiles, so once they are saved on the |
1983 | // stack all volatile registers are available for use. |
1984 | masm.bind(&callVM); |
1985 | masm.PushRegsInMask(liveVolatiles); |
1986 | |
1987 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
1988 | regs.takeUnchecked(buffer); |
1989 | regs.takeUnchecked(holder); |
1990 | Register addrReg = regs.takeAny(); |
1991 | |
1992 | masm.computeEffectiveAddress(Address(holder, offset), addrReg); |
1993 | |
1994 | bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>(); |
1995 | if (needExtraReg) { |
1996 | masm.push(holder); |
1997 | masm.setupUnalignedABICall(holder); |
1998 | } else { |
1999 | masm.setupUnalignedABICall(regs.takeAny()); |
2000 | } |
2001 | masm.passABIArg(buffer); |
2002 | masm.passABIArg(addrReg); |
2003 | masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun), |
2004 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
2005 | |
2006 | if (needExtraReg) { |
2007 | masm.pop(holder); |
2008 | } |
2009 | masm.PopRegsInMask(liveVolatiles); |
2010 | masm.bind(&exit); |
2011 | } |
2012 | |
2013 | // Warning: this function modifies prev and next. |
2014 | static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder, |
2015 | size_t offset, Register prev, Register next, |
2016 | LiveGeneralRegisterSet& liveVolatiles) { |
2017 | Label exit; |
2018 | Label checkRemove, putCell; |
2019 | |
2020 | // if (next && (buffer = next->storeBuffer())) |
2021 | // but we never pass in nullptr for next. |
2022 | Register storebuffer = next; |
2023 | masm.loadStoreBuffer(next, storebuffer); |
2024 | masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove); |
2025 | |
2026 | // if (prev && prev->storeBuffer()) |
2027 | masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell); |
2028 | masm.loadStoreBuffer(prev, prev); |
2029 | masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit); |
2030 | |
2031 | // buffer->putCell(cellp) |
2032 | masm.bind(&putCell); |
2033 | EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles, |
2034 | JSString::addCellAddressToStoreBuffer); |
2035 | masm.jump(&exit); |
2036 | |
2037 | // if (prev && (buffer = prev->storeBuffer())) |
2038 | masm.bind(&checkRemove); |
2039 | masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit); |
2040 | masm.loadStoreBuffer(prev, storebuffer); |
2041 | masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit); |
2042 | EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles, |
2043 | JSString::removeCellAddressFromStoreBuffer); |
2044 | |
2045 | masm.bind(&exit); |
2046 | } |
2047 | |
2048 | void CodeGenerator::visitRegExp(LRegExp* lir) { |
2049 | Register output = ToRegister(lir->output()); |
2050 | Register temp = ToRegister(lir->temp0()); |
2051 | JSObject* source = lir->mir()->source(); |
2052 | |
2053 | using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>); |
2054 | OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>( |
2055 | lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output)); |
2056 | if (lir->mir()->hasShared()) { |
2057 | TemplateObject templateObject(source); |
2058 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
2059 | ool->entry()); |
2060 | } else { |
2061 | masm.jump(ool->entry()); |
2062 | } |
2063 | masm.bind(ool->rejoin()); |
2064 | } |
2065 | |
2066 | static constexpr int32_t RegExpPairsVectorStartOffset( |
2067 | int32_t inputOutputDataStartOffset) { |
2068 | return inputOutputDataStartOffset + int32_t(InputOutputDataSize) + |
2069 | int32_t(sizeof(MatchPairs)); |
2070 | } |
2071 | |
2072 | static Address RegExpPairCountAddress(MacroAssembler& masm, |
2073 | int32_t inputOutputDataStartOffset) { |
2074 | return Address(FramePointer, inputOutputDataStartOffset + |
2075 | int32_t(InputOutputDataSize) + |
2076 | MatchPairs::offsetOfPairCount()); |
2077 | } |
2078 | |
2079 | static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp, |
2080 | Register input, Register lastIndex, |
2081 | Register staticsReg, Register temp1, |
2082 | Register temp2, gc::Heap initialStringHeap, |
2083 | LiveGeneralRegisterSet& volatileRegs) { |
2084 | Address pendingInputAddress(staticsReg, |
2085 | RegExpStatics::offsetOfPendingInput()); |
2086 | Address matchesInputAddress(staticsReg, |
2087 | RegExpStatics::offsetOfMatchesInput()); |
2088 | Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource()); |
2089 | Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex()); |
2090 | |
2091 | masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String); |
2092 | masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String); |
2093 | masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String); |
2094 | |
2095 | if (initialStringHeap == gc::Heap::Default) { |
2096 | // Writing into RegExpStatics tenured memory; must post-barrier. |
2097 | if (staticsReg.volatile_()) { |
2098 | volatileRegs.add(staticsReg); |
2099 | } |
2100 | |
2101 | masm.loadPtr(pendingInputAddress, temp1); |
2102 | masm.storePtr(input, pendingInputAddress); |
2103 | masm.movePtr(input, temp2); |
2104 | EmitPostWriteBarrierS(masm, staticsReg, |
2105 | RegExpStatics::offsetOfPendingInput(), |
2106 | temp1 /* prev */, temp2 /* next */, volatileRegs); |
2107 | |
2108 | masm.loadPtr(matchesInputAddress, temp1); |
2109 | masm.storePtr(input, matchesInputAddress); |
2110 | masm.movePtr(input, temp2); |
2111 | EmitPostWriteBarrierS(masm, staticsReg, |
2112 | RegExpStatics::offsetOfMatchesInput(), |
2113 | temp1 /* prev */, temp2 /* next */, volatileRegs); |
2114 | } else { |
2115 | masm.debugAssertGCThingIsTenured(input, temp1); |
2116 | masm.storePtr(input, pendingInputAddress); |
2117 | masm.storePtr(input, matchesInputAddress); |
2118 | } |
2119 | |
2120 | masm.storePtr(lastIndex, |
2121 | Address(staticsReg, RegExpStatics::offsetOfLazyIndex())); |
2122 | masm.store32( |
2123 | Imm32(1), |
2124 | Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation())); |
2125 | |
2126 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset( |
2127 | RegExpObject::SHARED_SLOT)), |
2128 | temp1, JSVAL_TYPE_PRIVATE_GCTHING); |
2129 | masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2); |
2130 | masm.storePtr(temp2, lazySourceAddress); |
2131 | static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size"); |
2132 | masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2); |
2133 | masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags())); |
2134 | } |
2135 | |
2136 | // Prepare an InputOutputData and optional MatchPairs which space has been |
2137 | // allocated for on the stack, and try to execute a RegExp on a string input. |
2138 | // If the RegExp was successfully executed and matched the input, fallthrough. |
2139 | // Otherwise, jump to notFound or failure. |
2140 | // |
2141 | // inputOutputDataStartOffset is the offset relative to the frame pointer |
2142 | // register. This offset is negative for the RegExpExecTest stub. |
2143 | static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp, |
2144 | Register input, Register lastIndex, |
2145 | Register temp1, Register temp2, |
2146 | Register temp3, |
2147 | int32_t inputOutputDataStartOffset, |
2148 | gc::Heap initialStringHeap, Label* notFound, |
2149 | Label* failure) { |
2150 | JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp"); |
2151 | |
2152 | using irregexp::InputOutputData; |
2153 | |
2154 | /* |
2155 | * [SMDOC] Stack layout for PrepareAndExecuteRegExp |
2156 | * |
2157 | * Before this function is called, the caller is responsible for |
2158 | * allocating enough stack space for the following data: |
2159 | * |
2160 | * inputOutputDataStartOffset +-----> +---------------+ |
2161 | * |InputOutputData| |
2162 | * inputStartAddress +----------> inputStart| |
2163 | * inputEndAddress +----------> inputEnd| |
2164 | * startIndexAddress +----------> startIndex| |
2165 | * matchesAddress +----------> matches|-----+ |
2166 | * +---------------+ | |
2167 | * matchPairs(Address|Offset) +-----> +---------------+ <--+ |
2168 | * | MatchPairs | |
2169 | * pairCountAddress +----------> count | |
2170 | * pairsPointerAddress +----------> pairs |-----+ |
2171 | * +---------------+ | |
2172 | * pairsArray(Address|Offset) +-----> +---------------+ <--+ |
2173 | * | MatchPair | |
2174 | * firstMatchStartAddress +----------> start | <--+ |
2175 | * | limit | | |
2176 | * +---------------+ | |
2177 | * . | |
2178 | * . Reserved space for |
2179 | * . RegExpObject::MaxPairCount |
2180 | * . MatchPair objects |
2181 | * . | |
2182 | * +---------------+ | |
2183 | * | MatchPair | | |
2184 | * | start | | |
2185 | * | limit | <--+ |
2186 | * +---------------+ |
2187 | */ |
2188 | |
2189 | int32_t ioOffset = inputOutputDataStartOffset; |
2190 | int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData)); |
2191 | int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs)); |
2192 | |
2193 | Address inputStartAddress(FramePointer, |
2194 | ioOffset + InputOutputData::offsetOfInputStart()); |
2195 | Address inputEndAddress(FramePointer, |
2196 | ioOffset + InputOutputData::offsetOfInputEnd()); |
2197 | Address startIndexAddress(FramePointer, |
2198 | ioOffset + InputOutputData::offsetOfStartIndex()); |
2199 | Address matchesAddress(FramePointer, |
2200 | ioOffset + InputOutputData::offsetOfMatches()); |
2201 | |
2202 | Address matchPairsAddress(FramePointer, matchPairsOffset); |
2203 | Address pairCountAddress(FramePointer, |
2204 | matchPairsOffset + MatchPairs::offsetOfPairCount()); |
2205 | Address pairsPointerAddress(FramePointer, |
2206 | matchPairsOffset + MatchPairs::offsetOfPairs()); |
2207 | |
2208 | Address pairsArrayAddress(FramePointer, pairsArrayOffset); |
2209 | Address firstMatchStartAddress(FramePointer, |
2210 | pairsArrayOffset + MatchPair::offsetOfStart()); |
2211 | |
2212 | // First, fill in a skeletal MatchPairs instance on the stack. This will be |
2213 | // passed to the OOL stub in the caller if we aren't able to execute the |
2214 | // RegExp inline, and that stub needs to be able to determine whether the |
2215 | // execution finished successfully. |
2216 | |
2217 | // Initialize MatchPairs::pairCount to 1. The correct value can only |
2218 | // be determined after loading the RegExpShared. If the RegExpShared |
2219 | // has Kind::Atom, this is the correct pairCount. |
2220 | masm.store32(Imm32(1), pairCountAddress); |
2221 | |
2222 | // Initialize MatchPairs::pairs pointer |
2223 | masm.computeEffectiveAddress(pairsArrayAddress, temp1); |
2224 | masm.storePtr(temp1, pairsPointerAddress); |
2225 | |
2226 | // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch |
2227 | masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress); |
2228 | |
2229 | // Determine the set of volatile inputs to save when calling into C++ or |
2230 | // regexp code. |
2231 | LiveGeneralRegisterSet volatileRegs; |
2232 | if (lastIndex.volatile_()) { |
2233 | volatileRegs.add(lastIndex); |
2234 | } |
2235 | if (input.volatile_()) { |
2236 | volatileRegs.add(input); |
2237 | } |
2238 | if (regexp.volatile_()) { |
2239 | volatileRegs.add(regexp); |
2240 | } |
2241 | |
2242 | // Ensure the input string is not a rope. |
2243 | Label isLinear; |
2244 | masm.branchIfNotRope(input, &isLinear); |
2245 | { |
2246 | masm.PushRegsInMask(volatileRegs); |
2247 | |
2248 | using Fn = JSLinearString* (*)(JSString*); |
2249 | masm.setupUnalignedABICall(temp1); |
2250 | masm.passABIArg(input); |
2251 | masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>(); |
2252 | |
2253 | MOZ_ASSERT(!volatileRegs.has(temp1))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!volatileRegs.has(temp1))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!volatileRegs.has(temp1)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!volatileRegs.has(temp1)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2253); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { *((volatile int*)__null) = 2253; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2254 | masm.storeCallPointerResult(temp1); |
2255 | masm.PopRegsInMask(volatileRegs); |
2256 | |
2257 | masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure); |
2258 | } |
2259 | masm.bind(&isLinear); |
2260 | |
2261 | // Load the RegExpShared. |
2262 | Register regexpReg = temp1; |
2263 | Address sharedSlot = Address( |
2264 | regexp, NativeObject::getFixedSlotOffset(RegExpObject::SHARED_SLOT)); |
2265 | masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure); |
2266 | masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING); |
2267 | |
2268 | // Handle Atom matches |
2269 | Label notAtom, checkSuccess; |
2270 | masm.branchPtr(Assembler::Equal, |
2271 | Address(regexpReg, RegExpShared::offsetOfPatternAtom()), |
2272 | ImmWord(0), ¬Atom); |
2273 | { |
2274 | masm.computeEffectiveAddress(matchPairsAddress, temp3); |
2275 | |
2276 | masm.PushRegsInMask(volatileRegs); |
2277 | using Fn = |
2278 | RegExpRunStatus (*)(RegExpShared* re, const JSLinearString* input, |
2279 | size_t start, MatchPairs* matchPairs); |
2280 | masm.setupUnalignedABICall(temp2); |
2281 | masm.passABIArg(regexpReg); |
2282 | masm.passABIArg(input); |
2283 | masm.passABIArg(lastIndex); |
2284 | masm.passABIArg(temp3); |
2285 | masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>(); |
2286 | |
2287 | MOZ_ASSERT(!volatileRegs.has(temp1))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!volatileRegs.has(temp1))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!volatileRegs.has(temp1)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!volatileRegs.has(temp1)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2287); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { *((volatile int*)__null) = 2287; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2288 | masm.storeCallInt32Result(temp1); |
2289 | masm.PopRegsInMask(volatileRegs); |
2290 | |
2291 | masm.jump(&checkSuccess); |
2292 | } |
2293 | masm.bind(¬Atom); |
2294 | |
2295 | // Don't handle regexps with too many capture pairs. |
2296 | masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2); |
2297 | masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount), |
2298 | failure); |
2299 | |
2300 | // Fill in the pair count in the MatchPairs on the stack. |
2301 | masm.store32(temp2, pairCountAddress); |
2302 | |
2303 | // Load code pointer and length of input (in bytes). |
2304 | // Store the input start in the InputOutputData. |
2305 | Register codePointer = temp1; // Note: temp1 was previously regexpReg. |
2306 | Register byteLength = temp3; |
2307 | { |
2308 | Label isLatin1, done; |
2309 | masm.loadStringLength(input, byteLength); |
2310 | |
2311 | masm.branchLatin1String(input, &isLatin1); |
2312 | |
2313 | // Two-byte input |
2314 | masm.loadStringChars(input, temp2, CharEncoding::TwoByte); |
2315 | masm.storePtr(temp2, inputStartAddress); |
2316 | masm.loadPtr( |
2317 | Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)), |
2318 | codePointer); |
2319 | masm.lshiftPtr(Imm32(1), byteLength); |
2320 | masm.jump(&done); |
2321 | |
2322 | // Latin1 input |
2323 | masm.bind(&isLatin1); |
2324 | masm.loadStringChars(input, temp2, CharEncoding::Latin1); |
2325 | masm.storePtr(temp2, inputStartAddress); |
2326 | masm.loadPtr( |
2327 | Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)), |
2328 | codePointer); |
2329 | |
2330 | masm.bind(&done); |
2331 | |
2332 | // Store end pointer |
2333 | masm.addPtr(byteLength, temp2); |
2334 | masm.storePtr(temp2, inputEndAddress); |
2335 | } |
2336 | |
2337 | // Guard that the RegExpShared has been compiled for this type of input. |
2338 | // If it has not been compiled, we fall back to the OOL case, which will |
2339 | // do a VM call into the interpreter. |
2340 | // TODO: add an interpreter trampoline? |
2341 | masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure); |
2342 | masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer); |
2343 | |
2344 | // Finish filling in the InputOutputData instance on the stack |
2345 | masm.computeEffectiveAddress(matchPairsAddress, temp2); |
2346 | masm.storePtr(temp2, matchesAddress); |
2347 | masm.storePtr(lastIndex, startIndexAddress); |
2348 | |
2349 | // Execute the RegExp. |
2350 | masm.computeEffectiveAddress( |
2351 | Address(FramePointer, inputOutputDataStartOffset), temp2); |
2352 | masm.PushRegsInMask(volatileRegs); |
2353 | masm.setupUnalignedABICall(temp3); |
2354 | masm.passABIArg(temp2); |
2355 | masm.callWithABI(codePointer); |
2356 | masm.storeCallInt32Result(temp1); |
2357 | masm.PopRegsInMask(volatileRegs); |
2358 | |
2359 | masm.bind(&checkSuccess); |
2360 | masm.branch32(Assembler::Equal, temp1, |
2361 | Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound); |
2362 | masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)), |
2363 | failure); |
2364 | |
2365 | // Lazily update the RegExpStatics. |
2366 | size_t offset = GlobalObjectData::offsetOfRegExpRealm() + |
2367 | RegExpRealm::offsetOfRegExpStatics(); |
2368 | masm.loadGlobalObjectData(temp1); |
2369 | masm.loadPtr(Address(temp1, offset), temp1); |
2370 | UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3, |
2371 | initialStringHeap, volatileRegs); |
2372 | |
2373 | return true; |
2374 | } |
2375 | |
2376 | static void EmitInitDependentStringBase(MacroAssembler& masm, |
2377 | Register dependent, Register base, |
2378 | Register temp1, Register temp2, |
2379 | bool needsPostBarrier) { |
2380 | // Determine the base string to use and store it in temp2. |
2381 | Label notDependent, markedDependedOn; |
2382 | masm.load32(Address(base, JSString::offsetOfFlags()), temp1); |
2383 | masm.branchTest32(Assembler::Zero, temp1, Imm32(JSString::DEPENDENT_BIT), |
2384 | ¬Dependent); |
2385 | { |
2386 | // The base is also a dependent string. Load its base to prevent chains of |
2387 | // dependent strings in most cases. This must either be an atom or already |
2388 | // have the DEPENDED_ON_BIT set. |
2389 | masm.loadDependentStringBase(base, temp2); |
2390 | masm.jump(&markedDependedOn); |
2391 | } |
2392 | masm.bind(¬Dependent); |
2393 | { |
2394 | // The base is not a dependent string. Set the DEPENDED_ON_BIT if it's not |
2395 | // an atom. |
2396 | masm.movePtr(base, temp2); |
2397 | masm.branchTest32(Assembler::NonZero, temp1, Imm32(JSString::ATOM_BIT), |
2398 | &markedDependedOn); |
2399 | masm.or32(Imm32(JSString::DEPENDED_ON_BIT), temp1); |
2400 | masm.store32(temp1, Address(temp2, JSString::offsetOfFlags())); |
2401 | } |
2402 | masm.bind(&markedDependedOn); |
2403 | |
2404 | #ifdef DEBUG1 |
2405 | // Assert the base has the DEPENDED_ON_BIT set or is an atom. |
2406 | Label isAppropriatelyMarked; |
2407 | masm.branchTest32(Assembler::NonZero, |
2408 | Address(temp2, JSString::offsetOfFlags()), |
2409 | Imm32(JSString::ATOM_BIT | JSString::DEPENDED_ON_BIT), |
2410 | &isAppropriatelyMarked); |
2411 | masm.assumeUnreachable("Base string is missing DEPENDED_ON_BIT"); |
2412 | masm.bind(&isAppropriatelyMarked); |
2413 | #endif |
2414 | masm.storeDependentStringBase(temp2, dependent); |
2415 | |
2416 | // Post-barrier the base store. The base is still in temp2. |
2417 | if (needsPostBarrier) { |
2418 | Label done; |
2419 | masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done); |
2420 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done); |
2421 | |
2422 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
2423 | regsToSave.takeUnchecked(temp1); |
2424 | regsToSave.takeUnchecked(temp2); |
2425 | |
2426 | masm.PushRegsInMask(regsToSave); |
2427 | |
2428 | masm.mov(ImmPtr(masm.runtime()), temp1); |
2429 | |
2430 | using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell); |
2431 | masm.setupUnalignedABICall(temp2); |
2432 | masm.passABIArg(temp1); |
2433 | masm.passABIArg(dependent); |
2434 | masm.callWithABI<Fn, PostWriteBarrier>(); |
2435 | |
2436 | masm.PopRegsInMask(regsToSave); |
2437 | |
2438 | masm.bind(&done); |
2439 | } else { |
2440 | #ifdef DEBUG1 |
2441 | Label done; |
2442 | masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done); |
2443 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done); |
2444 | masm.assumeUnreachable("Missing post barrier for dependent string base"); |
2445 | masm.bind(&done); |
2446 | #endif |
2447 | } |
2448 | } |
2449 | |
2450 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
2451 | Register len, Register byteOpScratch, |
2452 | CharEncoding encoding, |
2453 | size_t maximumLength = SIZE_MAX(18446744073709551615UL)); |
2454 | |
2455 | class CreateDependentString { |
2456 | CharEncoding encoding_; |
2457 | Register string_; |
2458 | Register temp1_; |
2459 | Register temp2_; |
2460 | Label* failure_; |
2461 | |
2462 | enum class FallbackKind : uint8_t { |
2463 | InlineString, |
2464 | FatInlineString, |
2465 | NotInlineString, |
2466 | Count |
2467 | }; |
2468 | mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)> |
2469 | fallbacks_, joins_; |
2470 | |
2471 | public: |
2472 | CreateDependentString(CharEncoding encoding, Register string, Register temp1, |
2473 | Register temp2, Label* failure) |
2474 | : encoding_(encoding), |
2475 | string_(string), |
2476 | temp1_(temp1), |
2477 | temp2_(temp2), |
2478 | failure_(failure) {} |
2479 | |
2480 | Register string() const { return string_; } |
2481 | CharEncoding encoding() const { return encoding_; } |
2482 | |
2483 | // Generate code that creates DependentString. |
2484 | // Caller should call generateFallback after masm.ret(), to generate |
2485 | // fallback path. |
2486 | void generate(MacroAssembler& masm, const JSAtomState& names, |
2487 | CompileRuntime* runtime, Register base, |
2488 | BaseIndex startIndexAddress, BaseIndex limitIndexAddress, |
2489 | gc::Heap initialStringHeap); |
2490 | |
2491 | // Generate fallback path for creating DependentString. |
2492 | void generateFallback(MacroAssembler& masm); |
2493 | }; |
2494 | |
2495 | void CreateDependentString::generate(MacroAssembler& masm, |
2496 | const JSAtomState& names, |
2497 | CompileRuntime* runtime, Register base, |
2498 | BaseIndex startIndexAddress, |
2499 | BaseIndex limitIndexAddress, |
2500 | gc::Heap initialStringHeap) { |
2501 | JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)", |
2502 | (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
2503 | |
2504 | auto newGCString = [&](FallbackKind kind) { |
2505 | uint32_t flags = kind == FallbackKind::InlineString |
2506 | ? JSString::INIT_THIN_INLINE_FLAGS |
2507 | : kind == FallbackKind::FatInlineString |
2508 | ? JSString::INIT_FAT_INLINE_FLAGS |
2509 | : JSString::INIT_DEPENDENT_FLAGS; |
2510 | if (encoding_ == CharEncoding::Latin1) { |
2511 | flags |= JSString::LATIN1_CHARS_BIT; |
2512 | } |
2513 | |
2514 | if (kind != FallbackKind::FatInlineString) { |
2515 | masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]); |
2516 | } else { |
2517 | masm.newGCFatInlineString(string_, temp2_, initialStringHeap, |
2518 | &fallbacks_[kind]); |
2519 | } |
2520 | masm.bind(&joins_[kind]); |
2521 | masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags())); |
2522 | }; |
2523 | |
2524 | // Compute the string length. |
2525 | masm.load32(startIndexAddress, temp2_); |
2526 | masm.load32(limitIndexAddress, temp1_); |
2527 | masm.sub32(temp2_, temp1_); |
2528 | |
2529 | Label done, nonEmpty; |
2530 | |
2531 | // Zero length matches use the empty string. |
2532 | masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty); |
2533 | masm.movePtr(ImmGCPtr(names.empty_), string_); |
2534 | masm.jump(&done); |
2535 | |
2536 | masm.bind(&nonEmpty); |
2537 | |
2538 | // Complete matches use the base string. |
2539 | Label nonBaseStringMatch; |
2540 | masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch); |
2541 | masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()), |
2542 | temp1_, &nonBaseStringMatch); |
2543 | masm.movePtr(base, string_); |
2544 | masm.jump(&done); |
2545 | |
2546 | masm.bind(&nonBaseStringMatch); |
2547 | |
2548 | Label notInline; |
2549 | |
2550 | int32_t maxInlineLength = encoding_ == CharEncoding::Latin1 |
2551 | ? JSFatInlineString::MAX_LENGTH_LATIN1 |
2552 | : JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
2553 | masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), ¬Inline); |
2554 | { |
2555 | // Make a thin or fat inline string. |
2556 | Label stringAllocated, fatInline; |
2557 | |
2558 | int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1 |
2559 | ? JSThinInlineString::MAX_LENGTH_LATIN1 |
2560 | : JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
2561 | masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength), |
2562 | &fatInline); |
2563 | if (encoding_ == CharEncoding::Latin1) { |
2564 | // One character Latin-1 strings can be loaded directly from the |
2565 | // static strings table. |
2566 | Label thinInline; |
2567 | masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline); |
2568 | { |
2569 | static_assert( |
2570 | StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR, |
2571 | "Latin-1 strings can be loaded from static strings"); |
2572 | |
2573 | masm.loadStringChars(base, temp1_, encoding_); |
2574 | masm.loadChar(temp1_, temp2_, temp1_, encoding_); |
2575 | |
2576 | masm.lookupStaticString(temp1_, string_, runtime->staticStrings()); |
2577 | |
2578 | masm.jump(&done); |
2579 | } |
2580 | masm.bind(&thinInline); |
2581 | } |
2582 | { |
2583 | newGCString(FallbackKind::InlineString); |
2584 | masm.jump(&stringAllocated); |
2585 | } |
2586 | masm.bind(&fatInline); |
2587 | { |
2588 | newGCString(FallbackKind::FatInlineString); |
2589 | } |
2590 | masm.bind(&stringAllocated); |
2591 | |
2592 | masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); |
2593 | |
2594 | masm.push(string_); |
2595 | masm.push(base); |
2596 | |
2597 | MOZ_ASSERT(startIndexAddress.base == FramePointer,do { static_assert( mozilla::detail::AssertionConditionType< decltype(startIndexAddress.base == FramePointer)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(startIndexAddress.base == FramePointer))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("startIndexAddress.base == FramePointer" " (" "startIndexAddress is still valid after stack pushes" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2598); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") (" "startIndexAddress is still valid after stack pushes" ")" ); do { *((volatile int*)__null) = 2598; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
2598 | "startIndexAddress is still valid after stack pushes")do { static_assert( mozilla::detail::AssertionConditionType< decltype(startIndexAddress.base == FramePointer)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(startIndexAddress.base == FramePointer))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("startIndexAddress.base == FramePointer" " (" "startIndexAddress is still valid after stack pushes" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2598); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") (" "startIndexAddress is still valid after stack pushes" ")" ); do { *((volatile int*)__null) = 2598; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2599 | |
2600 | // Load chars pointer for the new string. |
2601 | masm.loadInlineStringCharsForStore(string_, string_); |
2602 | |
2603 | // Load the source characters pointer. |
2604 | masm.loadStringChars(base, temp2_, encoding_); |
2605 | masm.load32(startIndexAddress, base); |
2606 | masm.addToCharPtr(temp2_, base, encoding_); |
2607 | |
2608 | CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_); |
2609 | |
2610 | masm.pop(base); |
2611 | masm.pop(string_); |
2612 | |
2613 | masm.jump(&done); |
2614 | } |
2615 | |
2616 | masm.bind(¬Inline); |
2617 | |
2618 | { |
2619 | // Make a dependent string. |
2620 | // Warning: string may be tenured (if the fallback case is hit), so |
2621 | // stores into it must be post barriered. |
2622 | newGCString(FallbackKind::NotInlineString); |
2623 | |
2624 | masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); |
2625 | |
2626 | masm.loadNonInlineStringChars(base, temp1_, encoding_); |
2627 | masm.load32(startIndexAddress, temp2_); |
2628 | masm.addToCharPtr(temp1_, temp2_, encoding_); |
2629 | masm.storeNonInlineStringChars(temp1_, string_); |
2630 | |
2631 | EmitInitDependentStringBase(masm, string_, base, temp1_, temp2_, |
2632 | /* needsPostBarrier = */ true); |
2633 | } |
2634 | |
2635 | masm.bind(&done); |
2636 | } |
2637 | |
2638 | void CreateDependentString::generateFallback(MacroAssembler& masm) { |
2639 | JitSpew(JitSpew_Codegen, |
2640 | "# Emitting CreateDependentString fallback (encoding=%s)", |
2641 | (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
2642 | |
2643 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
2644 | regsToSave.takeUnchecked(string_); |
2645 | regsToSave.takeUnchecked(temp2_); |
2646 | |
2647 | for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) { |
2648 | masm.bind(&fallbacks_[kind]); |
2649 | |
2650 | masm.PushRegsInMask(regsToSave); |
2651 | |
2652 | using Fn = void* (*)(JSContext* cx); |
2653 | masm.setupUnalignedABICall(string_); |
2654 | masm.loadJSContext(string_); |
2655 | masm.passABIArg(string_); |
2656 | if (kind == FallbackKind::FatInlineString) { |
2657 | masm.callWithABI<Fn, AllocateFatInlineString>(); |
2658 | } else { |
2659 | masm.callWithABI<Fn, AllocateDependentString>(); |
2660 | } |
2661 | masm.storeCallPointerResult(string_); |
2662 | |
2663 | masm.PopRegsInMask(regsToSave); |
2664 | |
2665 | masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_); |
2666 | |
2667 | masm.jump(&joins_[kind]); |
2668 | } |
2669 | } |
2670 | |
2671 | // Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar, |
2672 | // but RegExpExecMatch also has to load and update .lastIndex for global/sticky |
2673 | // regular expressions. |
2674 | static JitCode* GenerateRegExpMatchStubShared(JSContext* cx, |
2675 | gc::Heap initialStringHeap, |
2676 | bool isExecMatch) { |
2677 | if (isExecMatch) { |
2678 | JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub"); |
2679 | } else { |
2680 | JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub"); |
2681 | } |
2682 | |
2683 | // |initialStringHeap| could be stale after a GC. |
2684 | JS::AutoCheckCannotGC nogc(cx); |
2685 | |
2686 | Register regexp = RegExpMatcherRegExpReg; |
2687 | Register input = RegExpMatcherStringReg; |
2688 | Register lastIndex = RegExpMatcherLastIndexReg; |
2689 | ValueOperand result = JSReturnOperand; |
2690 | |
2691 | // We are free to clobber all registers, as LRegExpMatcher is a call |
2692 | // instruction. |
2693 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
2694 | regs.take(input); |
2695 | regs.take(regexp); |
2696 | regs.take(lastIndex); |
2697 | |
2698 | Register temp1 = regs.takeAny(); |
2699 | Register temp2 = regs.takeAny(); |
2700 | Register temp3 = regs.takeAny(); |
2701 | Register maybeTemp4 = InvalidReg; |
2702 | if (!regs.empty()) { |
2703 | // There are not enough registers on x86. |
2704 | maybeTemp4 = regs.takeAny(); |
2705 | } |
2706 | Register maybeTemp5 = InvalidReg; |
2707 | if (!regs.empty()) { |
2708 | // There are not enough registers on x86. |
2709 | maybeTemp5 = regs.takeAny(); |
2710 | } |
2711 | |
2712 | Address flagsSlot(regexp, RegExpObject::offsetOfFlags()); |
2713 | Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex()); |
2714 | |
2715 | TempAllocator temp(&cx->tempLifoAlloc()); |
2716 | JitContext jcx(cx); |
2717 | StackMacroAssembler masm(cx, temp); |
2718 | AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared"); |
2719 | |
2720 | #ifdef JS_USE_LINK_REGISTER |
2721 | masm.pushReturnAddress(); |
2722 | #endif |
2723 | masm.push(FramePointer); |
2724 | masm.moveStackPtrTo(FramePointer); |
2725 | |
2726 | Label notFoundZeroLastIndex; |
2727 | if (isExecMatch) { |
2728 | masm.loadRegExpLastIndex(regexp, input, lastIndex, ¬FoundZeroLastIndex); |
2729 | } |
2730 | |
2731 | // The InputOutputData is placed above the frame pointer and return address on |
2732 | // the stack. |
2733 | int32_t inputOutputDataStartOffset = 2 * sizeof(void*); |
2734 | |
2735 | Label notFound, oolEntry; |
2736 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
2737 | temp3, inputOutputDataStartOffset, |
2738 | initialStringHeap, ¬Found, &oolEntry)) { |
2739 | return nullptr; |
2740 | } |
2741 | |
2742 | // If a regexp has named captures, fall back to the OOL stub, which |
2743 | // will end up calling CreateRegExpMatchResults. |
2744 | Register shared = temp2; |
2745 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset( |
2746 | RegExpObject::SHARED_SLOT)), |
2747 | shared, JSVAL_TYPE_PRIVATE_GCTHING); |
2748 | masm.branchPtr(Assembler::NotEqual, |
2749 | Address(shared, RegExpShared::offsetOfGroupsTemplate()), |
2750 | ImmWord(0), &oolEntry); |
2751 | |
2752 | // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub. |
2753 | masm.branchTest32(Assembler::NonZero, |
2754 | Address(shared, RegExpShared::offsetOfFlags()), |
2755 | Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry); |
2756 | |
2757 | Address pairCountAddress = |
2758 | RegExpPairCountAddress(masm, inputOutputDataStartOffset); |
2759 | |
2760 | // Construct the result. |
2761 | Register object = temp1; |
2762 | { |
2763 | // In most cases, the array will have just 1-2 elements, so we optimize for |
2764 | // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots |
2765 | // because two slots are used for the elements header). |
2766 | |
2767 | // Load the array length in temp2 and the shape in temp3. |
2768 | Label allocated; |
2769 | masm.load32(pairCountAddress, temp2); |
2770 | size_t offset = GlobalObjectData::offsetOfRegExpRealm() + |
2771 | RegExpRealm::offsetOfNormalMatchResultShape(); |
2772 | masm.loadGlobalObjectData(temp3); |
2773 | masm.loadPtr(Address(temp3, offset), temp3); |
2774 | |
2775 | auto emitAllocObject = [&](size_t elementCapacity) { |
2776 | gc::AllocKind kind = GuessArrayGCKind(elementCapacity); |
2777 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(kind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(kind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2777); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 2777; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2778 | kind = ForegroundToBackgroundAllocKind(kind); |
2779 | |
2780 | #ifdef DEBUG1 |
2781 | // Assert all of the available slots are used for |elementCapacity| |
2782 | // elements. |
2783 | size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity; |
2784 | MOZ_ASSERT(usedSlots == GetGCKindSlots(kind))do { static_assert( mozilla::detail::AssertionConditionType< decltype(usedSlots == GetGCKindSlots(kind))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(usedSlots == GetGCKindSlots( kind)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("usedSlots == GetGCKindSlots(kind)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2784); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)" ")"); do { *((volatile int*)__null) = 2784; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2785 | #endif |
2786 | |
2787 | constexpr size_t numUsedDynamicSlots = |
2788 | RegExpRealm::MatchResultObjectSlotSpan; |
2789 | constexpr size_t numDynamicSlots = |
2790 | RegExpRealm::MatchResultObjectNumDynamicSlots; |
2791 | constexpr size_t arrayLength = 1; |
2792 | masm.createArrayWithFixedElements(object, temp3, temp2, temp3, |
2793 | arrayLength, elementCapacity, |
2794 | numUsedDynamicSlots, numDynamicSlots, |
2795 | kind, gc::Heap::Default, &oolEntry); |
2796 | }; |
2797 | |
2798 | Label moreThan2; |
2799 | masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2); |
2800 | emitAllocObject(2); |
2801 | masm.jump(&allocated); |
2802 | |
2803 | Label moreThan6; |
2804 | masm.bind(&moreThan2); |
2805 | masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6); |
2806 | emitAllocObject(6); |
2807 | masm.jump(&allocated); |
2808 | |
2809 | masm.bind(&moreThan6); |
2810 | static_assert(RegExpObject::MaxPairCount == 14); |
2811 | emitAllocObject(RegExpObject::MaxPairCount); |
2812 | |
2813 | masm.bind(&allocated); |
2814 | } |
2815 | |
2816 | // clang-format off |
2817 | /* |
2818 | * [SMDOC] Stack layout for the RegExpMatcher stub |
2819 | * |
2820 | * +---------------+ |
2821 | * FramePointer +-----> |Caller-FramePtr| |
2822 | * +---------------+ |
2823 | * |Return-Address | |
2824 | * +---------------+ |
2825 | * inputOutputDataStartOffset +-----> +---------------+ |
2826 | * |InputOutputData| |
2827 | * +---------------+ |
2828 | * +---------------+ |
2829 | * | MatchPairs | |
2830 | * pairsCountAddress +-----------> count | |
2831 | * | pairs | |
2832 | * | | |
2833 | * +---------------+ |
2834 | * pairsVectorStartOffset +-----> +---------------+ |
2835 | * | MatchPair | |
2836 | * matchPairStart +------------> start | <-------+ |
2837 | * matchPairLimit +------------> limit | | Reserved space for |
2838 | * +---------------+ | `RegExpObject::MaxPairCount` |
2839 | * . | MatchPair objects. |
2840 | * . | |
2841 | * . | `count` objects will be |
2842 | * +---------------+ | initialized and can be |
2843 | * | MatchPair | | accessed below. |
2844 | * | start | <-------+ |
2845 | * | limit | |
2846 | * +---------------+ |
2847 | */ |
2848 | // clang-format on |
2849 | |
2850 | static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t), |
2851 | "MatchPair consists of two int32 values representing the start" |
2852 | "and the end offset of the match"); |
2853 | |
2854 | int32_t pairsVectorStartOffset = |
2855 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
2856 | |
2857 | // Incremented by one below for each match pair. |
2858 | Register matchIndex = temp2; |
2859 | masm.move32(Imm32(0), matchIndex); |
2860 | |
2861 | // The element in which to store the result of the current match. |
2862 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
2863 | BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset); |
2864 | |
2865 | // The current match pair's "start" and "limit" member. |
2866 | BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight, |
2867 | pairsVectorStartOffset + MatchPair::offsetOfStart()); |
2868 | BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight, |
2869 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
2870 | |
2871 | Label* depStrFailure = &oolEntry; |
2872 | Label restoreRegExpAndLastIndex; |
2873 | |
2874 | Register temp4; |
2875 | if (maybeTemp4 == InvalidReg) { |
2876 | depStrFailure = &restoreRegExpAndLastIndex; |
2877 | |
2878 | // We don't have enough registers for a fourth temporary. Reuse |regexp| |
2879 | // as a temporary. We restore its value at |restoreRegExpAndLastIndex|. |
2880 | masm.push(regexp); |
2881 | temp4 = regexp; |
2882 | } else { |
2883 | temp4 = maybeTemp4; |
2884 | } |
2885 | |
2886 | Register temp5; |
2887 | if (maybeTemp5 == InvalidReg) { |
2888 | depStrFailure = &restoreRegExpAndLastIndex; |
2889 | |
2890 | // We don't have enough registers for a fifth temporary. Reuse |lastIndex| |
2891 | // as a temporary. We restore its value at |restoreRegExpAndLastIndex|. |
2892 | masm.push(lastIndex); |
2893 | temp5 = lastIndex; |
2894 | } else { |
2895 | temp5 = maybeTemp5; |
2896 | } |
2897 | |
2898 | auto maybeRestoreRegExpAndLastIndex = [&]() { |
2899 | if (maybeTemp5 == InvalidReg) { |
2900 | masm.pop(lastIndex); |
2901 | } |
2902 | if (maybeTemp4 == InvalidReg) { |
2903 | masm.pop(regexp); |
2904 | } |
2905 | }; |
2906 | |
2907 | // Loop to construct the match strings. There are two different loops, |
2908 | // depending on whether the input is a Two-Byte or a Latin-1 string. |
2909 | CreateDependentString depStrs[]{ |
2910 | {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure}, |
2911 | {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure}, |
2912 | }; |
2913 | |
2914 | { |
2915 | Label isLatin1, done; |
2916 | masm.branchLatin1String(input, &isLatin1); |
2917 | |
2918 | for (auto& depStr : depStrs) { |
2919 | if (depStr.encoding() == CharEncoding::Latin1) { |
2920 | masm.bind(&isLatin1); |
2921 | } |
2922 | |
2923 | Label matchLoop; |
2924 | masm.bind(&matchLoop); |
2925 | |
2926 | static_assert(MatchPair::NoMatch == -1, |
2927 | "MatchPair::start is negative if no match was found"); |
2928 | |
2929 | Label isUndefined, storeDone; |
2930 | masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0), |
2931 | &isUndefined); |
2932 | { |
2933 | depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()), |
2934 | input, matchPairStart, matchPairLimit, |
2935 | initialStringHeap); |
2936 | |
2937 | // Storing into nursery-allocated results object's elements; no post |
2938 | // barrier. |
2939 | masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement); |
2940 | masm.jump(&storeDone); |
2941 | } |
2942 | masm.bind(&isUndefined); |
2943 | { |
2944 | masm.storeValue(UndefinedValue(), objectMatchElement); |
2945 | } |
2946 | masm.bind(&storeDone); |
2947 | |
2948 | masm.add32(Imm32(1), matchIndex); |
2949 | masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, |
2950 | &done); |
2951 | masm.jump(&matchLoop); |
2952 | } |
2953 | |
2954 | #ifdef DEBUG1 |
2955 | masm.assumeUnreachable("The match string loop doesn't fall through."); |
2956 | #endif |
2957 | |
2958 | masm.bind(&done); |
2959 | } |
2960 | |
2961 | maybeRestoreRegExpAndLastIndex(); |
2962 | |
2963 | // Fill in the rest of the output object. |
2964 | masm.store32( |
2965 | matchIndex, |
2966 | Address(object, |
2967 | elementsOffset + ObjectElements::offsetOfInitializedLength())); |
2968 | masm.store32( |
2969 | matchIndex, |
2970 | Address(object, elementsOffset + ObjectElements::offsetOfLength())); |
2971 | |
2972 | Address firstMatchPairStartAddress( |
2973 | FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart()); |
2974 | Address firstMatchPairLimitAddress( |
2975 | FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
2976 | |
2977 | static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0, |
2978 | "First slot holds the 'index' property"); |
2979 | static_assert(RegExpRealm::MatchResultObjectInputSlot == 1, |
2980 | "Second slot holds the 'input' property"); |
2981 | |
2982 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
2983 | |
2984 | masm.load32(firstMatchPairStartAddress, temp3); |
2985 | masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0)); |
2986 | |
2987 | // No post barrier needed (address is within nursery object.) |
2988 | masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value))); |
2989 | |
2990 | // For the ExecMatch stub, if the regular expression is global or sticky, we |
2991 | // have to update its .lastIndex slot. |
2992 | if (isExecMatch) { |
2993 | MOZ_ASSERT(object != lastIndex)do { static_assert( mozilla::detail::AssertionConditionType< decltype(object != lastIndex)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(object != lastIndex))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("object != lastIndex" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "object != lastIndex" ")"); do { *((volatile int*)__null) = 2993; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2994 | Label notGlobalOrSticky; |
2995 | masm.branchTest32(Assembler::Zero, flagsSlot, |
2996 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
2997 | ¬GlobalOrSticky); |
2998 | masm.load32(firstMatchPairLimitAddress, lastIndex); |
2999 | masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot); |
3000 | masm.bind(¬GlobalOrSticky); |
3001 | } |
3002 | |
3003 | // All done! |
3004 | masm.tagValue(JSVAL_TYPE_OBJECT, object, result); |
3005 | masm.pop(FramePointer); |
3006 | masm.ret(); |
3007 | |
3008 | masm.bind(¬Found); |
3009 | if (isExecMatch) { |
3010 | Label notGlobalOrSticky; |
3011 | masm.branchTest32(Assembler::Zero, flagsSlot, |
3012 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
3013 | ¬GlobalOrSticky); |
3014 | masm.bind(¬FoundZeroLastIndex); |
3015 | masm.storeValue(Int32Value(0), lastIndexSlot); |
3016 | masm.bind(¬GlobalOrSticky); |
3017 | } |
3018 | masm.moveValue(NullValue(), result); |
3019 | masm.pop(FramePointer); |
3020 | masm.ret(); |
3021 | |
3022 | // Fallback paths for CreateDependentString. |
3023 | for (auto& depStr : depStrs) { |
3024 | depStr.generateFallback(masm); |
3025 | } |
3026 | |
3027 | // Fall-through to the ool entry after restoring the registers. |
3028 | masm.bind(&restoreRegExpAndLastIndex); |
3029 | maybeRestoreRegExpAndLastIndex(); |
3030 | |
3031 | // Use an undefined value to signal to the caller that the OOL stub needs to |
3032 | // be called. |
3033 | masm.bind(&oolEntry); |
3034 | masm.moveValue(UndefinedValue(), result); |
3035 | masm.pop(FramePointer); |
3036 | masm.ret(); |
3037 | |
3038 | Linker linker(masm); |
3039 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
3040 | if (!code) { |
3041 | return nullptr; |
3042 | } |
3043 | |
3044 | const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub"; |
3045 | CollectPerfSpewerJitCodeProfile(code, name); |
3046 | #ifdef MOZ_VTUNE1 |
3047 | vtune::MarkStub(code, name); |
3048 | #endif |
3049 | |
3050 | return code; |
3051 | } |
3052 | |
3053 | JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) { |
3054 | return GenerateRegExpMatchStubShared(cx, initialStringHeap, |
3055 | /* isExecMatch = */ false); |
3056 | } |
3057 | |
3058 | JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) { |
3059 | return GenerateRegExpMatchStubShared(cx, initialStringHeap, |
3060 | /* isExecMatch = */ true); |
3061 | } |
3062 | |
3063 | class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator> { |
3064 | LRegExpMatcher* lir_; |
3065 | |
3066 | public: |
3067 | explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir) : lir_(lir) {} |
3068 | |
3069 | void accept(CodeGenerator* codegen) override { |
3070 | codegen->visitOutOfLineRegExpMatcher(this); |
3071 | } |
3072 | |
3073 | LRegExpMatcher* lir() const { return lir_; } |
3074 | }; |
3075 | |
3076 | void CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool) { |
3077 | LRegExpMatcher* lir = ool->lir(); |
3078 | Register lastIndex = ToRegister(lir->lastIndex()); |
3079 | Register input = ToRegister(lir->string()); |
3080 | Register regexp = ToRegister(lir->regexp()); |
3081 | |
3082 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
3083 | regs.take(lastIndex); |
3084 | regs.take(input); |
3085 | regs.take(regexp); |
3086 | Register temp = regs.takeAny(); |
3087 | |
3088 | masm.computeEffectiveAddress( |
3089 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
3090 | |
3091 | pushArg(temp); |
3092 | pushArg(lastIndex); |
3093 | pushArg(input); |
3094 | pushArg(regexp); |
3095 | |
3096 | // We are not using oolCallVM because we are in a Call, and that live |
3097 | // registers are already saved by the the register allocator. |
3098 | using Fn = |
3099 | bool (*)(JSContext*, HandleObject regexp, HandleString input, |
3100 | int32_t lastIndex, MatchPairs* pairs, MutableHandleValue output); |
3101 | callVM<Fn, RegExpMatcherRaw>(lir); |
3102 | |
3103 | masm.jump(ool->rejoin()); |
3104 | } |
3105 | |
3106 | void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) { |
3107 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3107); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg" ")"); do { *((volatile int*)__null) = 3107; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3108 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpMatcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpMatcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpMatcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg" ")"); do { *((volatile int*)__null) = 3108; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3109 | MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg" ")"); do { *((volatile int*)__null) = 3109; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3110 | MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir) == JSReturnOperand)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutValue(lir) == JSReturnOperand ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutValue(lir) == JSReturnOperand", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3110); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand" ")"); do { *((volatile int*)__null) = 3110; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3111 | |
3112 | #if defined(JS_NUNBOX32) |
3113 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type); |
3114 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data); |
3115 | static_assert(RegExpMatcherStringReg != JSReturnReg_Type); |
3116 | static_assert(RegExpMatcherStringReg != JSReturnReg_Data); |
3117 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type); |
3118 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data); |
3119 | #elif defined(JS_PUNBOX641) |
3120 | static_assert(RegExpMatcherRegExpReg != JSReturnReg); |
3121 | static_assert(RegExpMatcherStringReg != JSReturnReg); |
3122 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg); |
3123 | #endif |
3124 | |
3125 | masm.reserveStack(RegExpReservedStack); |
3126 | |
3127 | OutOfLineRegExpMatcher* ool = new (alloc()) OutOfLineRegExpMatcher(lir); |
3128 | addOutOfLineCode(ool, lir->mir()); |
3129 | |
3130 | JitCode* regExpMatcherStub = |
3131 | snapshot_->getZoneStub(JitZone::StubKind::RegExpMatcher); |
3132 | masm.call(regExpMatcherStub); |
3133 | masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry()); |
3134 | masm.bind(ool->rejoin()); |
3135 | |
3136 | masm.freeStack(RegExpReservedStack); |
3137 | } |
3138 | |
3139 | class OutOfLineRegExpExecMatch : public OutOfLineCodeBase<CodeGenerator> { |
3140 | LRegExpExecMatch* lir_; |
3141 | |
3142 | public: |
3143 | explicit OutOfLineRegExpExecMatch(LRegExpExecMatch* lir) : lir_(lir) {} |
3144 | |
3145 | void accept(CodeGenerator* codegen) override { |
3146 | codegen->visitOutOfLineRegExpExecMatch(this); |
3147 | } |
3148 | |
3149 | LRegExpExecMatch* lir() const { return lir_; } |
3150 | }; |
3151 | |
3152 | void CodeGenerator::visitOutOfLineRegExpExecMatch( |
3153 | OutOfLineRegExpExecMatch* ool) { |
3154 | LRegExpExecMatch* lir = ool->lir(); |
3155 | Register input = ToRegister(lir->string()); |
3156 | Register regexp = ToRegister(lir->regexp()); |
3157 | |
3158 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
3159 | regs.take(input); |
3160 | regs.take(regexp); |
3161 | Register temp = regs.takeAny(); |
3162 | |
3163 | masm.computeEffectiveAddress( |
3164 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
3165 | |
3166 | pushArg(temp); |
3167 | pushArg(input); |
3168 | pushArg(regexp); |
3169 | |
3170 | // We are not using oolCallVM because we are in a Call and live registers have |
3171 | // already been saved by the register allocator. |
3172 | using Fn = |
3173 | bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input, |
3174 | MatchPairs* pairs, MutableHandleValue output); |
3175 | callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir); |
3176 | masm.jump(ool->rejoin()); |
3177 | } |
3178 | |
3179 | void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) { |
3180 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3180); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg" ")"); do { *((volatile int*)__null) = 3180; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3181 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpMatcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpMatcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpMatcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg" ")"); do { *((volatile int*)__null) = 3181; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3182 | MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir) == JSReturnOperand)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutValue(lir) == JSReturnOperand ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutValue(lir) == JSReturnOperand", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand" ")"); do { *((volatile int*)__null) = 3182; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3183 | |
3184 | #if defined(JS_NUNBOX32) |
3185 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type); |
3186 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data); |
3187 | static_assert(RegExpMatcherStringReg != JSReturnReg_Type); |
3188 | static_assert(RegExpMatcherStringReg != JSReturnReg_Data); |
3189 | #elif defined(JS_PUNBOX641) |
3190 | static_assert(RegExpMatcherRegExpReg != JSReturnReg); |
3191 | static_assert(RegExpMatcherStringReg != JSReturnReg); |
3192 | #endif |
3193 | |
3194 | masm.reserveStack(RegExpReservedStack); |
3195 | |
3196 | auto* ool = new (alloc()) OutOfLineRegExpExecMatch(lir); |
3197 | addOutOfLineCode(ool, lir->mir()); |
3198 | |
3199 | JitCode* regExpExecMatchStub = |
3200 | snapshot_->getZoneStub(JitZone::StubKind::RegExpExecMatch); |
3201 | masm.call(regExpExecMatchStub); |
3202 | masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry()); |
3203 | |
3204 | masm.bind(ool->rejoin()); |
3205 | masm.freeStack(RegExpReservedStack); |
3206 | } |
3207 | |
3208 | JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) { |
3209 | JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub"); |
3210 | |
3211 | Register regexp = RegExpSearcherRegExpReg; |
3212 | Register input = RegExpSearcherStringReg; |
3213 | Register lastIndex = RegExpSearcherLastIndexReg; |
3214 | Register result = ReturnReg; |
3215 | |
3216 | // We are free to clobber all registers, as LRegExpSearcher is a call |
3217 | // instruction. |
3218 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
3219 | regs.take(input); |
3220 | regs.take(regexp); |
3221 | regs.take(lastIndex); |
3222 | |
3223 | Register temp1 = regs.takeAny(); |
3224 | Register temp2 = regs.takeAny(); |
3225 | Register temp3 = regs.takeAny(); |
3226 | |
3227 | TempAllocator temp(&cx->tempLifoAlloc()); |
3228 | JitContext jcx(cx); |
3229 | StackMacroAssembler masm(cx, temp); |
3230 | AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub"); |
3231 | |
3232 | #ifdef JS_USE_LINK_REGISTER |
3233 | masm.pushReturnAddress(); |
3234 | #endif |
3235 | masm.push(FramePointer); |
3236 | masm.moveStackPtrTo(FramePointer); |
3237 | |
3238 | #ifdef DEBUG1 |
3239 | // Store sentinel value to cx->regExpSearcherLastLimit. |
3240 | // See comment in RegExpSearcherImpl. |
3241 | masm.loadJSContext(temp1); |
3242 | masm.store32(Imm32(RegExpSearcherLastLimitSentinel), |
3243 | Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit())); |
3244 | #endif |
3245 | |
3246 | // The InputOutputData is placed above the frame pointer and return address on |
3247 | // the stack. |
3248 | int32_t inputOutputDataStartOffset = 2 * sizeof(void*); |
3249 | |
3250 | Label notFound, oolEntry; |
3251 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
3252 | temp3, inputOutputDataStartOffset, |
3253 | initialStringHeap, ¬Found, &oolEntry)) { |
3254 | return nullptr; |
3255 | } |
3256 | |
3257 | // clang-format off |
3258 | /* |
3259 | * [SMDOC] Stack layout for the RegExpSearcher stub |
3260 | * |
3261 | * +---------------+ |
3262 | * FramePointer +-----> |Caller-FramePtr| |
3263 | * +---------------+ |
3264 | * |Return-Address | |
3265 | * +---------------+ |
3266 | * inputOutputDataStartOffset +-----> +---------------+ |
3267 | * |InputOutputData| |
3268 | * +---------------+ |
3269 | * +---------------+ |
3270 | * | MatchPairs | |
3271 | * | count | |
3272 | * | pairs | |
3273 | * | | |
3274 | * +---------------+ |
3275 | * pairsVectorStartOffset +-----> +---------------+ |
3276 | * | MatchPair | |
3277 | * matchPairStart +------------> start | <-------+ |
3278 | * matchPairLimit +------------> limit | | Reserved space for |
3279 | * +---------------+ | `RegExpObject::MaxPairCount` |
3280 | * . | MatchPair objects. |
3281 | * . | |
3282 | * . | Only a single object will |
3283 | * +---------------+ | be initialized and can be |
3284 | * | MatchPair | | accessed below. |
3285 | * | start | <-------+ |
3286 | * | limit | |
3287 | * +---------------+ |
3288 | */ |
3289 | // clang-format on |
3290 | |
3291 | int32_t pairsVectorStartOffset = |
3292 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
3293 | Address matchPairStart(FramePointer, |
3294 | pairsVectorStartOffset + MatchPair::offsetOfStart()); |
3295 | Address matchPairLimit(FramePointer, |
3296 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
3297 | |
3298 | // Store match limit to cx->regExpSearcherLastLimit and return the index. |
3299 | masm.load32(matchPairLimit, result); |
3300 | masm.loadJSContext(input); |
3301 | masm.store32(result, |
3302 | Address(input, JSContext::offsetOfRegExpSearcherLastLimit())); |
3303 | masm.load32(matchPairStart, result); |
3304 | masm.pop(FramePointer); |
3305 | masm.ret(); |
3306 | |
3307 | masm.bind(¬Found); |
3308 | masm.move32(Imm32(RegExpSearcherResultNotFound), result); |
3309 | masm.pop(FramePointer); |
3310 | masm.ret(); |
3311 | |
3312 | masm.bind(&oolEntry); |
3313 | masm.move32(Imm32(RegExpSearcherResultFailed), result); |
3314 | masm.pop(FramePointer); |
3315 | masm.ret(); |
3316 | |
3317 | Linker linker(masm); |
3318 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
3319 | if (!code) { |
3320 | return nullptr; |
3321 | } |
3322 | |
3323 | CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub"); |
3324 | #ifdef MOZ_VTUNE1 |
3325 | vtune::MarkStub(code, "RegExpSearcherStub"); |
3326 | #endif |
3327 | |
3328 | return code; |
3329 | } |
3330 | |
3331 | class OutOfLineRegExpSearcher : public OutOfLineCodeBase<CodeGenerator> { |
3332 | LRegExpSearcher* lir_; |
3333 | |
3334 | public: |
3335 | explicit OutOfLineRegExpSearcher(LRegExpSearcher* lir) : lir_(lir) {} |
3336 | |
3337 | void accept(CodeGenerator* codegen) override { |
3338 | codegen->visitOutOfLineRegExpSearcher(this); |
3339 | } |
3340 | |
3341 | LRegExpSearcher* lir() const { return lir_; } |
3342 | }; |
3343 | |
3344 | void CodeGenerator::visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool) { |
3345 | LRegExpSearcher* lir = ool->lir(); |
3346 | Register lastIndex = ToRegister(lir->lastIndex()); |
3347 | Register input = ToRegister(lir->string()); |
3348 | Register regexp = ToRegister(lir->regexp()); |
3349 | |
3350 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
3351 | regs.take(lastIndex); |
3352 | regs.take(input); |
3353 | regs.take(regexp); |
3354 | Register temp = regs.takeAny(); |
3355 | |
3356 | masm.computeEffectiveAddress( |
3357 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
3358 | |
3359 | pushArg(temp); |
3360 | pushArg(lastIndex); |
3361 | pushArg(input); |
3362 | pushArg(regexp); |
3363 | |
3364 | // We are not using oolCallVM because we are in a Call, and that live |
3365 | // registers are already saved by the the register allocator. |
3366 | using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input, |
3367 | int32_t lastIndex, MatchPairs* pairs, int32_t* result); |
3368 | callVM<Fn, RegExpSearcherRaw>(lir); |
3369 | |
3370 | masm.jump(ool->rejoin()); |
3371 | } |
3372 | |
3373 | void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) { |
3374 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3374); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg" ")"); do { *((volatile int*)__null) = 3374; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3375 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpSearcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpSearcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpSearcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpSearcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3375); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg" ")"); do { *((volatile int*)__null) = 3375; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3376 | MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg" ")"); do { *((volatile int*)__null) = 3376; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3377 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3377); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 3377; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3378 | |
3379 | static_assert(RegExpSearcherRegExpReg != ReturnReg); |
3380 | static_assert(RegExpSearcherStringReg != ReturnReg); |
3381 | static_assert(RegExpSearcherLastIndexReg != ReturnReg); |
3382 | |
3383 | masm.reserveStack(RegExpReservedStack); |
3384 | |
3385 | OutOfLineRegExpSearcher* ool = new (alloc()) OutOfLineRegExpSearcher(lir); |
3386 | addOutOfLineCode(ool, lir->mir()); |
3387 | |
3388 | JitCode* regExpSearcherStub = |
3389 | snapshot_->getZoneStub(JitZone::StubKind::RegExpSearcher); |
3390 | masm.call(regExpSearcherStub); |
3391 | masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed), |
3392 | ool->entry()); |
3393 | masm.bind(ool->rejoin()); |
3394 | |
3395 | masm.freeStack(RegExpReservedStack); |
3396 | } |
3397 | |
3398 | void CodeGenerator::visitRegExpSearcherLastLimit( |
3399 | LRegExpSearcherLastLimit* lir) { |
3400 | Register result = ToRegister(lir->output()); |
3401 | Register scratch = ToRegister(lir->temp0()); |
3402 | |
3403 | masm.loadAndClearRegExpSearcherLastLimit(result, scratch); |
3404 | } |
3405 | |
3406 | JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) { |
3407 | JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub"); |
3408 | |
3409 | Register regexp = RegExpExecTestRegExpReg; |
3410 | Register input = RegExpExecTestStringReg; |
3411 | Register result = ReturnReg; |
3412 | |
3413 | TempAllocator temp(&cx->tempLifoAlloc()); |
3414 | JitContext jcx(cx); |
3415 | StackMacroAssembler masm(cx, temp); |
3416 | AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub"); |
3417 | |
3418 | #ifdef JS_USE_LINK_REGISTER |
3419 | masm.pushReturnAddress(); |
3420 | #endif |
3421 | masm.push(FramePointer); |
3422 | masm.moveStackPtrTo(FramePointer); |
3423 | |
3424 | // We are free to clobber all registers, as LRegExpExecTest is a call |
3425 | // instruction. |
3426 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
3427 | regs.take(input); |
3428 | regs.take(regexp); |
3429 | |
3430 | // Ensure lastIndex != result. |
3431 | regs.take(result); |
3432 | Register lastIndex = regs.takeAny(); |
3433 | regs.add(result); |
3434 | Register temp1 = regs.takeAny(); |
3435 | Register temp2 = regs.takeAny(); |
3436 | Register temp3 = regs.takeAny(); |
3437 | |
3438 | Address flagsSlot(regexp, RegExpObject::offsetOfFlags()); |
3439 | Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex()); |
3440 | |
3441 | masm.reserveStack(RegExpReservedStack); |
3442 | |
3443 | // Load lastIndex and skip RegExp execution if needed. |
3444 | Label notFoundZeroLastIndex; |
3445 | masm.loadRegExpLastIndex(regexp, input, lastIndex, ¬FoundZeroLastIndex); |
3446 | |
3447 | // In visitRegExpMatcher and visitRegExpSearcher, we reserve stack space |
3448 | // before calling the stub. For RegExpExecTest we call the stub before |
3449 | // reserving stack space, so the offset of the InputOutputData relative to the |
3450 | // frame pointer is negative. |
3451 | constexpr int32_t inputOutputDataStartOffset = -int32_t(RegExpReservedStack); |
3452 | |
3453 | // On ARM64, load/store instructions can encode an immediate offset in the |
3454 | // range [-256, 4095]. If we ever fail this assertion, it would be more |
3455 | // efficient to store the data above the frame pointer similar to |
3456 | // RegExpMatcher and RegExpSearcher. |
3457 | static_assert(inputOutputDataStartOffset >= -256); |
3458 | |
3459 | Label notFound, oolEntry; |
3460 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
3461 | temp3, inputOutputDataStartOffset, |
3462 | initialStringHeap, ¬Found, &oolEntry)) { |
3463 | return nullptr; |
3464 | } |
3465 | |
3466 | // Set `result` to true/false to indicate found/not-found, or to |
3467 | // RegExpExecTestResultFailed if we have to retry in C++. If the regular |
3468 | // expression is global or sticky, we also have to update its .lastIndex slot. |
3469 | |
3470 | Label done; |
3471 | int32_t pairsVectorStartOffset = |
3472 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
3473 | Address matchPairLimit(FramePointer, |
3474 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
3475 | |
3476 | masm.move32(Imm32(1), result); |
3477 | masm.branchTest32(Assembler::Zero, flagsSlot, |
3478 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
3479 | &done); |
3480 | masm.load32(matchPairLimit, lastIndex); |
3481 | masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot); |
3482 | masm.jump(&done); |
3483 | |
3484 | masm.bind(¬Found); |
3485 | masm.move32(Imm32(0), result); |
3486 | masm.branchTest32(Assembler::Zero, flagsSlot, |
3487 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
3488 | &done); |
3489 | masm.storeValue(Int32Value(0), lastIndexSlot); |
3490 | masm.jump(&done); |
3491 | |
3492 | masm.bind(¬FoundZeroLastIndex); |
3493 | masm.move32(Imm32(0), result); |
3494 | masm.storeValue(Int32Value(0), lastIndexSlot); |
3495 | masm.jump(&done); |
3496 | |
3497 | masm.bind(&oolEntry); |
3498 | masm.move32(Imm32(RegExpExecTestResultFailed), result); |
3499 | |
3500 | masm.bind(&done); |
3501 | masm.freeStack(RegExpReservedStack); |
3502 | masm.pop(FramePointer); |
3503 | masm.ret(); |
3504 | |
3505 | Linker linker(masm); |
3506 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
3507 | if (!code) { |
3508 | return nullptr; |
3509 | } |
3510 | |
3511 | CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub"); |
3512 | #ifdef MOZ_VTUNE1 |
3513 | vtune::MarkStub(code, "RegExpExecTestStub"); |
3514 | #endif |
3515 | |
3516 | return code; |
3517 | } |
3518 | |
3519 | class OutOfLineRegExpExecTest : public OutOfLineCodeBase<CodeGenerator> { |
3520 | LRegExpExecTest* lir_; |
3521 | |
3522 | public: |
3523 | explicit OutOfLineRegExpExecTest(LRegExpExecTest* lir) : lir_(lir) {} |
3524 | |
3525 | void accept(CodeGenerator* codegen) override { |
3526 | codegen->visitOutOfLineRegExpExecTest(this); |
3527 | } |
3528 | |
3529 | LRegExpExecTest* lir() const { return lir_; } |
3530 | }; |
3531 | |
3532 | void CodeGenerator::visitOutOfLineRegExpExecTest(OutOfLineRegExpExecTest* ool) { |
3533 | LRegExpExecTest* lir = ool->lir(); |
3534 | Register input = ToRegister(lir->string()); |
3535 | Register regexp = ToRegister(lir->regexp()); |
3536 | |
3537 | pushArg(input); |
3538 | pushArg(regexp); |
3539 | |
3540 | // We are not using oolCallVM because we are in a Call and live registers have |
3541 | // already been saved by the register allocator. |
3542 | using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp, |
3543 | HandleString input, bool* result); |
3544 | callVM<Fn, RegExpBuiltinExecTestFromJit>(lir); |
3545 | |
3546 | masm.jump(ool->rejoin()); |
3547 | } |
3548 | |
3549 | void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) { |
3550 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3550); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg" ")"); do { *((volatile int*)__null) = 3550; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3551 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpExecTestStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpExecTestStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpExecTestStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpExecTestStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3551); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg" ")"); do { *((volatile int*)__null) = 3551; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3552 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3552); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 3552; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3553 | |
3554 | static_assert(RegExpExecTestRegExpReg != ReturnReg); |
3555 | static_assert(RegExpExecTestStringReg != ReturnReg); |
3556 | |
3557 | auto* ool = new (alloc()) OutOfLineRegExpExecTest(lir); |
3558 | addOutOfLineCode(ool, lir->mir()); |
3559 | |
3560 | JitCode* regExpExecTestStub = |
3561 | snapshot_->getZoneStub(JitZone::StubKind::RegExpExecTest); |
3562 | masm.call(regExpExecTestStub); |
3563 | |
3564 | masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed), |
3565 | ool->entry()); |
3566 | |
3567 | masm.bind(ool->rejoin()); |
3568 | } |
3569 | |
3570 | void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) { |
3571 | Register regexp = ToRegister(ins->regexp()); |
3572 | Register input = ToRegister(ins->input()); |
3573 | Register output = ToRegister(ins->output()); |
3574 | |
3575 | using Fn = |
3576 | bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*); |
3577 | auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>( |
3578 | ins, ArgList(regexp, input), StoreRegisterTo(output)); |
3579 | |
3580 | // Load RegExpShared in |output|. |
3581 | Label vmCall; |
3582 | masm.loadParsedRegExpShared(regexp, output, ool->entry()); |
3583 | |
3584 | // Return true iff pairCount > 1. |
3585 | Label returnTrue; |
3586 | masm.branch32(Assembler::Above, |
3587 | Address(output, RegExpShared::offsetOfPairCount()), Imm32(1), |
3588 | &returnTrue); |
3589 | masm.move32(Imm32(0), output); |
3590 | masm.jump(ool->rejoin()); |
3591 | |
3592 | masm.bind(&returnTrue); |
3593 | masm.move32(Imm32(1), output); |
3594 | |
3595 | masm.bind(ool->rejoin()); |
3596 | } |
3597 | |
3598 | class OutOfLineRegExpPrototypeOptimizable |
3599 | : public OutOfLineCodeBase<CodeGenerator> { |
3600 | LRegExpPrototypeOptimizable* ins_; |
3601 | |
3602 | public: |
3603 | explicit OutOfLineRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins) |
3604 | : ins_(ins) {} |
3605 | |
3606 | void accept(CodeGenerator* codegen) override { |
3607 | codegen->visitOutOfLineRegExpPrototypeOptimizable(this); |
3608 | } |
3609 | LRegExpPrototypeOptimizable* ins() const { return ins_; } |
3610 | }; |
3611 | |
3612 | void CodeGenerator::visitRegExpPrototypeOptimizable( |
3613 | LRegExpPrototypeOptimizable* ins) { |
3614 | Register object = ToRegister(ins->object()); |
3615 | Register output = ToRegister(ins->output()); |
3616 | Register temp = ToRegister(ins->temp0()); |
3617 | |
3618 | OutOfLineRegExpPrototypeOptimizable* ool = |
3619 | new (alloc()) OutOfLineRegExpPrototypeOptimizable(ins); |
3620 | addOutOfLineCode(ool, ins->mir()); |
3621 | |
3622 | const GlobalObject* global = gen->realm->maybeGlobal(); |
3623 | MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType< decltype(global)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("global", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3623); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")" ); do { *((volatile int*)__null) = 3623; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3624 | masm.branchIfNotRegExpPrototypeOptimizable(object, temp, global, |
3625 | ool->entry()); |
3626 | masm.move32(Imm32(0x1), output); |
3627 | |
3628 | masm.bind(ool->rejoin()); |
3629 | } |
3630 | |
3631 | void CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable( |
3632 | OutOfLineRegExpPrototypeOptimizable* ool) { |
3633 | LRegExpPrototypeOptimizable* ins = ool->ins(); |
3634 | Register object = ToRegister(ins->object()); |
3635 | Register output = ToRegister(ins->output()); |
3636 | |
3637 | saveVolatile(output); |
3638 | |
3639 | using Fn = bool (*)(JSContext* cx, JSObject* proto); |
3640 | masm.setupAlignedABICall(); |
3641 | masm.loadJSContext(output); |
3642 | masm.passABIArg(output); |
3643 | masm.passABIArg(object); |
3644 | masm.callWithABI<Fn, RegExpPrototypeOptimizableRaw>(); |
3645 | masm.storeCallBoolResult(output); |
3646 | |
3647 | restoreVolatile(output); |
3648 | |
3649 | masm.jump(ool->rejoin()); |
3650 | } |
3651 | |
3652 | class OutOfLineRegExpInstanceOptimizable |
3653 | : public OutOfLineCodeBase<CodeGenerator> { |
3654 | LRegExpInstanceOptimizable* ins_; |
3655 | |
3656 | public: |
3657 | explicit OutOfLineRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins) |
3658 | : ins_(ins) {} |
3659 | |
3660 | void accept(CodeGenerator* codegen) override { |
3661 | codegen->visitOutOfLineRegExpInstanceOptimizable(this); |
3662 | } |
3663 | LRegExpInstanceOptimizable* ins() const { return ins_; } |
3664 | }; |
3665 | |
3666 | void CodeGenerator::visitRegExpInstanceOptimizable( |
3667 | LRegExpInstanceOptimizable* ins) { |
3668 | Register object = ToRegister(ins->object()); |
3669 | Register output = ToRegister(ins->output()); |
3670 | Register temp = ToRegister(ins->temp0()); |
3671 | |
3672 | OutOfLineRegExpInstanceOptimizable* ool = |
3673 | new (alloc()) OutOfLineRegExpInstanceOptimizable(ins); |
3674 | addOutOfLineCode(ool, ins->mir()); |
3675 | |
3676 | const GlobalObject* global = gen->realm->maybeGlobal(); |
3677 | MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType< decltype(global)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("global", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")" ); do { *((volatile int*)__null) = 3677; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3678 | masm.branchIfNotRegExpInstanceOptimizable(object, temp, global, ool->entry()); |
3679 | masm.move32(Imm32(0x1), output); |
3680 | |
3681 | masm.bind(ool->rejoin()); |
3682 | } |
3683 | |
3684 | void CodeGenerator::visitOutOfLineRegExpInstanceOptimizable( |
3685 | OutOfLineRegExpInstanceOptimizable* ool) { |
3686 | LRegExpInstanceOptimizable* ins = ool->ins(); |
3687 | Register object = ToRegister(ins->object()); |
3688 | Register proto = ToRegister(ins->proto()); |
3689 | Register output = ToRegister(ins->output()); |
3690 | |
3691 | saveVolatile(output); |
3692 | |
3693 | using Fn = bool (*)(JSContext* cx, JSObject* obj, JSObject* proto); |
3694 | masm.setupAlignedABICall(); |
3695 | masm.loadJSContext(output); |
3696 | masm.passABIArg(output); |
3697 | masm.passABIArg(object); |
3698 | masm.passABIArg(proto); |
3699 | masm.callWithABI<Fn, RegExpInstanceOptimizableRaw>(); |
3700 | masm.storeCallBoolResult(output); |
3701 | |
3702 | restoreVolatile(output); |
3703 | |
3704 | masm.jump(ool->rejoin()); |
3705 | } |
3706 | |
3707 | static void FindFirstDollarIndex(MacroAssembler& masm, Register str, |
3708 | Register len, Register temp0, Register temp1, |
3709 | Register output, CharEncoding encoding) { |
3710 | #ifdef DEBUG1 |
3711 | Label ok; |
3712 | masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); |
3713 | masm.assumeUnreachable("Length should be greater than 0."); |
3714 | masm.bind(&ok); |
3715 | #endif |
3716 | |
3717 | Register chars = temp0; |
3718 | masm.loadStringChars(str, chars, encoding); |
3719 | |
3720 | masm.move32(Imm32(0), output); |
3721 | |
3722 | Label start, done; |
3723 | masm.bind(&start); |
3724 | |
3725 | Register currentChar = temp1; |
3726 | masm.loadChar(chars, output, currentChar, encoding); |
3727 | masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done); |
3728 | |
3729 | masm.add32(Imm32(1), output); |
3730 | masm.branch32(Assembler::NotEqual, output, len, &start); |
3731 | |
3732 | masm.move32(Imm32(-1), output); |
3733 | |
3734 | masm.bind(&done); |
3735 | } |
3736 | |
3737 | void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) { |
3738 | Register str = ToRegister(ins->str()); |
3739 | Register output = ToRegister(ins->output()); |
3740 | Register temp0 = ToRegister(ins->temp0()); |
3741 | Register temp1 = ToRegister(ins->temp1()); |
3742 | Register len = ToRegister(ins->temp2()); |
3743 | |
3744 | using Fn = bool (*)(JSContext*, JSString*, int32_t*); |
3745 | OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>( |
3746 | ins, ArgList(str), StoreRegisterTo(output)); |
3747 | |
3748 | masm.branchIfRope(str, ool->entry()); |
3749 | masm.loadStringLength(str, len); |
3750 | |
3751 | Label isLatin1, done; |
3752 | masm.branchLatin1String(str, &isLatin1); |
3753 | { |
3754 | FindFirstDollarIndex(masm, str, len, temp0, temp1, output, |
3755 | CharEncoding::TwoByte); |
3756 | masm.jump(&done); |
3757 | } |
3758 | masm.bind(&isLatin1); |
3759 | { |
3760 | FindFirstDollarIndex(masm, str, len, temp0, temp1, output, |
3761 | CharEncoding::Latin1); |
3762 | } |
3763 | masm.bind(&done); |
3764 | masm.bind(ool->rejoin()); |
3765 | } |
3766 | |
3767 | void CodeGenerator::visitStringReplace(LStringReplace* lir) { |
3768 | if (lir->replacement()->isConstant()) { |
3769 | pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString())); |
3770 | } else { |
3771 | pushArg(ToRegister(lir->replacement())); |
3772 | } |
3773 | |
3774 | if (lir->pattern()->isConstant()) { |
3775 | pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString())); |
3776 | } else { |
3777 | pushArg(ToRegister(lir->pattern())); |
3778 | } |
3779 | |
3780 | if (lir->string()->isConstant()) { |
3781 | pushArg(ImmGCPtr(lir->string()->toConstant()->toString())); |
3782 | } else { |
3783 | pushArg(ToRegister(lir->string())); |
3784 | } |
3785 | |
3786 | using Fn = |
3787 | JSString* (*)(JSContext*, HandleString, HandleString, HandleString); |
3788 | if (lir->mir()->isFlatReplacement()) { |
3789 | callVM<Fn, StringFlatReplaceString>(lir); |
3790 | } else { |
3791 | callVM<Fn, StringReplace>(lir); |
3792 | } |
3793 | } |
3794 | |
3795 | void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) { |
3796 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
3797 | TypedOrValueRegister lhs = |
3798 | TypedOrValueRegister(ToValue(lir, LBinaryValueCache::LhsIndex)); |
3799 | TypedOrValueRegister rhs = |
3800 | TypedOrValueRegister(ToValue(lir, LBinaryValueCache::RhsIndex)); |
3801 | ValueOperand output = ToOutValue(lir); |
3802 | |
3803 | JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc()); |
3804 | |
3805 | switch (jsop) { |
3806 | case JSOp::Add: |
3807 | case JSOp::Sub: |
3808 | case JSOp::Mul: |
3809 | case JSOp::Div: |
3810 | case JSOp::Mod: |
3811 | case JSOp::Pow: |
3812 | case JSOp::BitAnd: |
3813 | case JSOp::BitOr: |
3814 | case JSOp::BitXor: |
3815 | case JSOp::Lsh: |
3816 | case JSOp::Rsh: |
3817 | case JSOp::Ursh: { |
3818 | IonBinaryArithIC ic(liveRegs, lhs, rhs, output); |
3819 | addIC(lir, allocateIC(ic)); |
3820 | return; |
3821 | } |
3822 | default: |
3823 | MOZ_CRASH("Unsupported jsop in MBinaryValueCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryValueCache" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3823); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache" ")"); do { *((volatile int*)__null) = 3823; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
3824 | } |
3825 | } |
3826 | |
3827 | void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) { |
3828 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
3829 | TypedOrValueRegister lhs = |
3830 | TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::LhsIndex)); |
3831 | TypedOrValueRegister rhs = |
3832 | TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::RhsIndex)); |
3833 | Register output = ToRegister(lir->output()); |
3834 | |
3835 | JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc()); |
3836 | |
3837 | switch (jsop) { |
3838 | case JSOp::Lt: |
3839 | case JSOp::Le: |
3840 | case JSOp::Gt: |
3841 | case JSOp::Ge: |
3842 | case JSOp::Eq: |
3843 | case JSOp::Ne: |
3844 | case JSOp::StrictEq: |
3845 | case JSOp::StrictNe: { |
3846 | IonCompareIC ic(liveRegs, lhs, rhs, output); |
3847 | addIC(lir, allocateIC(ic)); |
3848 | return; |
3849 | } |
3850 | default: |
3851 | MOZ_CRASH("Unsupported jsop in MBinaryBoolCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryBoolCache" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3851); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache" ")"); do { *((volatile int*)__null) = 3851; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
3852 | } |
3853 | } |
3854 | |
3855 | void CodeGenerator::visitUnaryCache(LUnaryCache* lir) { |
3856 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
3857 | TypedOrValueRegister input = |
3858 | TypedOrValueRegister(ToValue(lir, LUnaryCache::InputIndex)); |
3859 | ValueOperand output = ToOutValue(lir); |
3860 | |
3861 | IonUnaryArithIC ic(liveRegs, input, output); |
3862 | addIC(lir, allocateIC(ic)); |
3863 | } |
3864 | |
3865 | void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) { |
3866 | pushArg(ImmPtr(lir->mir()->module())); |
3867 | |
3868 | using Fn = JSObject* (*)(JSContext*, HandleObject); |
3869 | callVM<Fn, js::GetOrCreateModuleMetaObject>(lir); |
3870 | } |
3871 | |
3872 | void CodeGenerator::visitDynamicImport(LDynamicImport* lir) { |
3873 | pushArg(ToValue(lir, LDynamicImport::OptionsIndex)); |
3874 | pushArg(ToValue(lir, LDynamicImport::SpecifierIndex)); |
3875 | pushArg(ImmGCPtr(current->mir()->info().script())); |
3876 | |
3877 | using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue); |
3878 | callVM<Fn, js::StartDynamicModuleImport>(lir); |
3879 | } |
3880 | |
3881 | void CodeGenerator::visitLambda(LLambda* lir) { |
3882 | Register envChain = ToRegister(lir->environmentChain()); |
3883 | Register output = ToRegister(lir->output()); |
3884 | Register tempReg = ToRegister(lir->temp0()); |
3885 | |
3886 | JSFunction* fun = lir->mir()->templateFunction(); |
3887 | |
3888 | using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject); |
3889 | OutOfLineCode* ool = oolCallVM<Fn, js::Lambda>( |
3890 | lir, ArgList(ImmGCPtr(fun), envChain), StoreRegisterTo(output)); |
3891 | |
3892 | TemplateObject templateObject(fun); |
3893 | masm.createGCObject(output, tempReg, templateObject, gc::Heap::Default, |
3894 | ool->entry()); |
3895 | |
3896 | masm.storeValue(JSVAL_TYPE_OBJECT, envChain, |
3897 | Address(output, JSFunction::offsetOfEnvironment())); |
3898 | // No post barrier needed because output is guaranteed to be allocated in |
3899 | // the nursery. |
3900 | |
3901 | masm.bind(ool->rejoin()); |
3902 | } |
3903 | |
3904 | void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) { |
3905 | Register envChain = ToRegister(lir->envChain()); |
3906 | Register prototype = ToRegister(lir->prototype()); |
3907 | |
3908 | pushArg(prototype); |
3909 | pushArg(envChain); |
3910 | pushArg(ImmGCPtr(lir->mir()->function())); |
3911 | |
3912 | using Fn = |
3913 | JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject); |
3914 | callVM<Fn, js::FunWithProtoOperation>(lir); |
3915 | } |
3916 | |
3917 | void CodeGenerator::visitSetFunName(LSetFunName* lir) { |
3918 | pushArg(Imm32(lir->mir()->prefixKind())); |
3919 | pushArg(ToValue(lir, LSetFunName::NameIndex)); |
3920 | pushArg(ToRegister(lir->fun())); |
3921 | |
3922 | using Fn = |
3923 | bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind); |
3924 | callVM<Fn, js::SetFunctionName>(lir); |
3925 | } |
3926 | |
3927 | void CodeGenerator::visitOsiPoint(LOsiPoint* lir) { |
3928 | // Note: markOsiPoint ensures enough space exists between the last |
3929 | // LOsiPoint and this one to patch adjacent call instructions. |
3930 | |
3931 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3931); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 3931; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3932 | |
3933 | uint32_t osiCallPointOffset = markOsiPoint(lir); |
3934 | |
3935 | LSafepoint* safepoint = lir->associatedSafepoint(); |
3936 | MOZ_ASSERT(!safepoint->osiCallPointOffset())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!safepoint->osiCallPointOffset())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!safepoint->osiCallPointOffset ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!safepoint->osiCallPointOffset()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3936); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()" ")"); do { *((volatile int*)__null) = 3936; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3937 | safepoint->setOsiCallPointOffset(osiCallPointOffset); |
3938 | |
3939 | #ifdef DEBUG1 |
3940 | // There should be no movegroups or other instructions between |
3941 | // an instruction and its OsiPoint. This is necessary because |
3942 | // we use the OsiPoint's snapshot from within VM calls. |
3943 | for (LInstructionReverseIterator iter(current->rbegin(lir)); |
3944 | iter != current->rend(); iter++) { |
3945 | if (*iter == lir) { |
3946 | continue; |
3947 | } |
3948 | MOZ_ASSERT(!iter->isMoveGroup())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!iter->isMoveGroup())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!iter->isMoveGroup()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!iter->isMoveGroup()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3948); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!iter->isMoveGroup()" ")"); do { *((volatile int*)__null) = 3948; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3949 | MOZ_ASSERT(iter->safepoint() == safepoint)do { static_assert( mozilla::detail::AssertionConditionType< decltype(iter->safepoint() == safepoint)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(iter->safepoint() == safepoint ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "iter->safepoint() == safepoint", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3949); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint" ")"); do { *((volatile int*)__null) = 3949; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3950 | break; |
3951 | } |
3952 | #endif |
3953 | |
3954 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
3955 | if (shouldVerifyOsiPointRegs(safepoint)) { |
3956 | verifyOsiPointRegs(safepoint); |
3957 | } |
3958 | #endif |
3959 | } |
3960 | |
3961 | void CodeGenerator::visitPhi(LPhi* lir) { |
3962 | MOZ_CRASH("Unexpected LPhi in CodeGenerator")do { do { } while (false); MOZ_ReportCrash("" "Unexpected LPhi in CodeGenerator" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3962); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator" ")"); do { *((volatile int*)__null) = 3962; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
3963 | } |
3964 | |
3965 | void CodeGenerator::visitGoto(LGoto* lir) { jumpToBlock(lir->target()); } |
3966 | |
3967 | void CodeGenerator::visitTableSwitch(LTableSwitch* ins) { |
3968 | MTableSwitch* mir = ins->mir(); |
3969 | Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); |
3970 | const LAllocation* temp; |
3971 | |
3972 | if (mir->getOperand(0)->type() != MIRType::Int32) { |
3973 | temp = ins->tempInt()->output(); |
3974 | |
3975 | // The input is a double, so try and convert it to an integer. |
3976 | // If it does not fit in an integer, take the default case. |
3977 | masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp), |
3978 | defaultcase, false); |
3979 | } else { |
3980 | temp = ins->index(); |
3981 | } |
3982 | |
3983 | emitTableSwitchDispatch(mir, ToRegister(temp), |
3984 | ToRegisterOrInvalid(ins->tempPointer())); |
3985 | } |
3986 | |
3987 | void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) { |
3988 | MTableSwitch* mir = ins->mir(); |
3989 | Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); |
3990 | |
3991 | Register index = ToRegister(ins->tempInt()); |
3992 | ValueOperand value = ToValue(ins, LTableSwitchV::InputValue); |
3993 | Register tag = masm.extractTag(value, index); |
3994 | masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase); |
3995 | |
3996 | Label unboxInt, isInt; |
3997 | masm.branchTestInt32(Assembler::Equal, tag, &unboxInt); |
3998 | { |
3999 | FloatRegister floatIndex = ToFloatRegister(ins->tempFloat()); |
4000 | masm.unboxDouble(value, floatIndex); |
4001 | masm.convertDoubleToInt32(floatIndex, index, defaultcase, false); |
4002 | masm.jump(&isInt); |
4003 | } |
4004 | |
4005 | masm.bind(&unboxInt); |
4006 | masm.unboxInt32(value, index); |
4007 | |
4008 | masm.bind(&isInt); |
4009 | |
4010 | emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer())); |
4011 | } |
4012 | |
4013 | void CodeGenerator::visitParameter(LParameter* lir) {} |
4014 | |
4015 | void CodeGenerator::visitCallee(LCallee* lir) { |
4016 | Register callee = ToRegister(lir->output()); |
4017 | Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
4018 | |
4019 | masm.loadFunctionFromCalleeToken(ptr, callee); |
4020 | } |
4021 | |
4022 | void CodeGenerator::visitIsConstructing(LIsConstructing* lir) { |
4023 | Register output = ToRegister(lir->output()); |
4024 | Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
4025 | masm.loadPtr(calleeToken, output); |
4026 | |
4027 | // We must be inside a function. |
4028 | MOZ_ASSERT(current->mir()->info().script()->function())do { static_assert( mozilla::detail::AssertionConditionType< decltype(current->mir()->info().script()->function() )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(current->mir()->info().script()->function() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "current->mir()->info().script()->function()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4028); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()" ")"); do { *((volatile int*)__null) = 4028; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4029 | |
4030 | // The low bit indicates whether this call is constructing, just clear the |
4031 | // other bits. |
4032 | static_assert(CalleeToken_Function == 0x0, |
4033 | "CalleeTokenTag value should match"); |
4034 | static_assert(CalleeToken_FunctionConstructing == 0x1, |
4035 | "CalleeTokenTag value should match"); |
4036 | masm.andPtr(Imm32(0x1), output); |
4037 | } |
4038 | |
4039 | void CodeGenerator::visitReturn(LReturn* lir) { |
4040 | #if defined(JS_NUNBOX32) |
4041 | DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX); |
4042 | DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX); |
4043 | MOZ_ASSERT(ToRegister(type) == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(type) == JSReturnReg_Type)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToRegister(type) == JSReturnReg_Type ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(type) == JSReturnReg_Type", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4043); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type" ")"); do { *((volatile int*)__null) = 4043; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4044 | MOZ_ASSERT(ToRegister(payload) == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(payload) == JSReturnReg_Data)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(payload) == JSReturnReg_Data))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(payload) == JSReturnReg_Data" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data" ")"); do { *((volatile int*)__null) = 4044; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4045 | #elif defined(JS_PUNBOX641) |
4046 | DebugOnly<LAllocation*> result = lir->getOperand(0); |
4047 | MOZ_ASSERT(ToRegister(result) == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(result) == JSReturnReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToRegister(result) == JSReturnReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(result) == JSReturnReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg" ")"); do { *((volatile int*)__null) = 4047; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4048 | #endif |
4049 | // Don't emit a jump to the return label if this is the last block, as |
4050 | // it'll fall through to the epilogue. |
4051 | // |
4052 | // This is -not- true however for a Generator-return, which may appear in the |
4053 | // middle of the last block, so we should always emit the jump there. |
4054 | if (current->mir() != *gen->graph().poBegin() || lir->isGenerator()) { |
4055 | masm.jump(&returnLabel_); |
4056 | } |
4057 | } |
4058 | |
4059 | void CodeGenerator::visitOsrEntry(LOsrEntry* lir) { |
4060 | Register temp = ToRegister(lir->temp()); |
4061 | |
4062 | // Remember the OSR entry offset into the code buffer. |
4063 | masm.flushBuffer(); |
4064 | setOsrEntryOffset(masm.size()); |
4065 | |
4066 | // Allocate the full frame for this function |
4067 | // Note we have a new entry here. So we reset MacroAssembler::framePushed() |
4068 | // to 0, before reserving the stack. |
4069 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 4069; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4070 | masm.setFramePushed(0); |
4071 | |
4072 | // The Baseline code ensured both the frame pointer and stack pointer point to |
4073 | // the JitFrameLayout on the stack. |
4074 | |
4075 | // If profiling, save the current frame pointer to a per-thread global field. |
4076 | if (isProfilerInstrumentationEnabled()) { |
4077 | masm.profilerEnterFrame(FramePointer, temp); |
4078 | } |
4079 | |
4080 | masm.reserveStack(frameSize()); |
4081 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4081); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 4081; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4082 | |
4083 | // Ensure that the Ion frames is properly aligned. |
4084 | masm.assertStackAlignment(JitStackAlignment, 0); |
4085 | } |
4086 | |
4087 | void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) { |
4088 | const LAllocation* frame = lir->getOperand(0); |
4089 | const LDefinition* object = lir->getDef(0); |
4090 | |
4091 | const ptrdiff_t frameOffset = |
4092 | BaselineFrame::reverseOffsetOfEnvironmentChain(); |
4093 | |
4094 | masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
4095 | } |
4096 | |
4097 | void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) { |
4098 | const LAllocation* frame = lir->getOperand(0); |
4099 | const LDefinition* object = lir->getDef(0); |
4100 | |
4101 | const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj(); |
4102 | |
4103 | masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
4104 | } |
4105 | |
4106 | void CodeGenerator::visitOsrValue(LOsrValue* value) { |
4107 | const LAllocation* frame = value->getOperand(0); |
4108 | const ValueOperand out = ToOutValue(value); |
4109 | |
4110 | const ptrdiff_t frameOffset = value->mir()->frameOffset(); |
4111 | |
4112 | masm.loadValue(Address(ToRegister(frame), frameOffset), out); |
4113 | } |
4114 | |
4115 | void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) { |
4116 | const LAllocation* frame = lir->getOperand(0); |
4117 | const ValueOperand out = ToOutValue(lir); |
4118 | |
4119 | Address flags = |
4120 | Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags()); |
4121 | Address retval = |
4122 | Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue()); |
4123 | |
4124 | masm.moveValue(UndefinedValue(), out); |
4125 | |
4126 | Label done; |
4127 | masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), |
4128 | &done); |
4129 | masm.loadValue(retval, out); |
4130 | masm.bind(&done); |
4131 | } |
4132 | |
4133 | void CodeGenerator::visitStackArgT(LStackArgT* lir) { |
4134 | const LAllocation* arg = lir->arg(); |
4135 | MIRType argType = lir->type(); |
4136 | uint32_t argslot = lir->argslot(); |
4137 | MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount())do { static_assert( mozilla::detail::AssertionConditionType< decltype(argslot - 1u < graph.argumentSlotCount())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argslot - 1u < graph.argumentSlotCount()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argslot - 1u < graph.argumentSlotCount()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()" ")"); do { *((volatile int*)__null) = 4137; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4138 | |
4139 | Address dest = AddressOfPassedArg(argslot); |
4140 | |
4141 | if (arg->isFloatReg()) { |
4142 | masm.boxDouble(ToFloatRegister(arg), dest); |
4143 | } else if (arg->isRegister()) { |
4144 | masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest); |
4145 | } else { |
4146 | masm.storeValue(arg->toConstant()->toJSValue(), dest); |
4147 | } |
4148 | } |
4149 | |
4150 | void CodeGenerator::visitStackArgV(LStackArgV* lir) { |
4151 | ValueOperand val = ToValue(lir, 0); |
4152 | uint32_t argslot = lir->argslot(); |
4153 | MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount())do { static_assert( mozilla::detail::AssertionConditionType< decltype(argslot - 1u < graph.argumentSlotCount())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argslot - 1u < graph.argumentSlotCount()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argslot - 1u < graph.argumentSlotCount()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4153); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()" ")"); do { *((volatile int*)__null) = 4153; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4154 | |
4155 | masm.storeValue(val, AddressOfPassedArg(argslot)); |
4156 | } |
4157 | |
4158 | void CodeGenerator::visitMoveGroup(LMoveGroup* group) { |
4159 | if (!group->numMoves()) { |
4160 | return; |
4161 | } |
4162 | |
4163 | MoveResolver& resolver = masm.moveResolver(); |
4164 | |
4165 | for (size_t i = 0; i < group->numMoves(); i++) { |
4166 | const LMove& move = group->getMove(i); |
4167 | |
4168 | LAllocation from = move.from(); |
4169 | LAllocation to = move.to(); |
4170 | LDefinition::Type type = move.type(); |
4171 | |
4172 | // No bogus moves. |
4173 | MOZ_ASSERT(from != to)do { static_assert( mozilla::detail::AssertionConditionType< decltype(from != to)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(from != to))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("from != to", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to" ")"); do { *((volatile int*)__null) = 4173; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4174 | MOZ_ASSERT(!from.isConstant())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!from.isConstant())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!from.isConstant()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!from.isConstant()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!from.isConstant()" ")"); do { *((volatile int*)__null) = 4174; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4175 | MoveOp::Type moveType; |
4176 | switch (type) { |
4177 | case LDefinition::OBJECT: |
4178 | case LDefinition::SLOTS: |
4179 | case LDefinition::WASM_ANYREF: |
4180 | #ifdef JS_NUNBOX32 |
4181 | case LDefinition::TYPE: |
4182 | case LDefinition::PAYLOAD: |
4183 | #else |
4184 | case LDefinition::BOX: |
4185 | #endif |
4186 | case LDefinition::GENERAL: |
4187 | case LDefinition::STACKRESULTS: |
4188 | moveType = MoveOp::GENERAL; |
4189 | break; |
4190 | case LDefinition::INT32: |
4191 | moveType = MoveOp::INT32; |
4192 | break; |
4193 | case LDefinition::FLOAT32: |
4194 | moveType = MoveOp::FLOAT32; |
4195 | break; |
4196 | case LDefinition::DOUBLE: |
4197 | moveType = MoveOp::DOUBLE; |
4198 | break; |
4199 | case LDefinition::SIMD128: |
4200 | moveType = MoveOp::SIMD128; |
4201 | break; |
4202 | default: |
4203 | MOZ_CRASH("Unexpected move type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected move type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4203); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected move type" ")"); do { *((volatile int*)__null) = 4203; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
4204 | } |
4205 | |
4206 | masm.propagateOOM( |
4207 | resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType)); |
4208 | } |
4209 | |
4210 | masm.propagateOOM(resolver.resolve()); |
4211 | if (masm.oom()) { |
4212 | return; |
4213 | } |
4214 | |
4215 | MoveEmitter emitter(masm); |
4216 | |
4217 | #ifdef JS_CODEGEN_X86 |
4218 | if (group->maybeScratchRegister().isGeneralReg()) { |
4219 | emitter.setScratchRegister( |
4220 | group->maybeScratchRegister().toGeneralReg()->reg()); |
4221 | } else { |
4222 | resolver.sortMemoryToMemoryMoves(); |
4223 | } |
4224 | #endif |
4225 | |
4226 | emitter.emit(resolver); |
4227 | emitter.finish(); |
4228 | } |
4229 | |
4230 | void CodeGenerator::visitInteger(LInteger* lir) { |
4231 | masm.move32(Imm32(lir->i32()), ToRegister(lir->output())); |
4232 | } |
4233 | |
4234 | void CodeGenerator::visitInteger64(LInteger64* lir) { |
4235 | masm.move64(Imm64(lir->i64()), ToOutRegister64(lir)); |
4236 | } |
4237 | |
4238 | void CodeGenerator::visitPointer(LPointer* lir) { |
4239 | masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output())); |
4240 | } |
4241 | |
4242 | void CodeGenerator::visitDouble(LDouble* ins) { |
4243 | masm.loadConstantDouble(ins->value(), ToFloatRegister(ins->output())); |
4244 | } |
4245 | |
4246 | void CodeGenerator::visitFloat32(LFloat32* ins) { |
4247 | masm.loadConstantFloat32(ins->value(), ToFloatRegister(ins->output())); |
4248 | } |
4249 | |
4250 | void CodeGenerator::visitValue(LValue* value) { |
4251 | ValueOperand result = ToOutValue(value); |
4252 | masm.moveValue(value->value(), result); |
4253 | } |
4254 | |
4255 | void CodeGenerator::visitNurseryObject(LNurseryObject* lir) { |
4256 | Register output = ToRegister(lir->output()); |
4257 | uint32_t nurseryIndex = lir->mir()->nurseryIndex(); |
4258 | |
4259 | // Load a pointer to the entry in IonScript's nursery objects list. |
4260 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output); |
4261 | masm.propagateOOM(ionNurseryObjectLabels_.emplaceBack(label, nurseryIndex)); |
4262 | |
4263 | // Load the JSObject*. |
4264 | masm.loadPtr(Address(output, 0), output); |
4265 | } |
4266 | |
4267 | void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) { |
4268 | // No-op. |
4269 | } |
4270 | |
4271 | void CodeGenerator::visitDebugEnterGCUnsafeRegion( |
4272 | LDebugEnterGCUnsafeRegion* lir) { |
4273 | Register temp = ToRegister(lir->temp0()); |
4274 | |
4275 | masm.loadJSContext(temp); |
4276 | |
4277 | Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion()); |
4278 | masm.add32(Imm32(1), inUnsafeRegion); |
4279 | |
4280 | Label ok; |
4281 | masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok); |
4282 | masm.assumeUnreachable("unbalanced enter/leave GC unsafe region"); |
4283 | masm.bind(&ok); |
4284 | } |
4285 | |
4286 | void CodeGenerator::visitDebugLeaveGCUnsafeRegion( |
4287 | LDebugLeaveGCUnsafeRegion* lir) { |
4288 | Register temp = ToRegister(lir->temp0()); |
4289 | |
4290 | masm.loadJSContext(temp); |
4291 | |
4292 | Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion()); |
4293 | masm.add32(Imm32(-1), inUnsafeRegion); |
4294 | |
4295 | Label ok; |
4296 | masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok); |
4297 | masm.assumeUnreachable("unbalanced enter/leave GC unsafe region"); |
4298 | masm.bind(&ok); |
4299 | } |
4300 | |
4301 | void CodeGenerator::visitSlots(LSlots* lir) { |
4302 | Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots()); |
4303 | masm.loadPtr(slots, ToRegister(lir->output())); |
4304 | } |
4305 | |
4306 | void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) { |
4307 | ValueOperand dest = ToOutValue(lir); |
4308 | Register base = ToRegister(lir->input()); |
4309 | int32_t offset = lir->mir()->slot() * sizeof(js::Value); |
4310 | |
4311 | masm.loadValue(Address(base, offset), dest); |
4312 | } |
4313 | |
4314 | static ConstantOrRegister ToConstantOrRegister(const LAllocation* value, |
4315 | MIRType valueType) { |
4316 | if (value->isConstant()) { |
4317 | return ConstantOrRegister(value->toConstant()->toJSValue()); |
4318 | } |
4319 | return TypedOrValueRegister(valueType, ToAnyRegister(value)); |
4320 | } |
4321 | |
4322 | void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) { |
4323 | Register base = ToRegister(lir->slots()); |
4324 | int32_t offset = lir->mir()->slot() * sizeof(js::Value); |
4325 | Address dest(base, offset); |
4326 | |
4327 | if (lir->mir()->needsBarrier()) { |
4328 | emitPreBarrier(dest); |
4329 | } |
4330 | |
4331 | MIRType valueType = lir->mir()->value()->type(); |
4332 | ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType); |
4333 | masm.storeUnboxedValue(value, valueType, dest); |
4334 | } |
4335 | |
4336 | void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) { |
4337 | Register base = ToRegister(lir->slots()); |
4338 | int32_t offset = lir->mir()->slot() * sizeof(Value); |
4339 | |
4340 | const ValueOperand value = ToValue(lir, LStoreDynamicSlotV::ValueIndex); |
4341 | |
4342 | if (lir->mir()->needsBarrier()) { |
4343 | emitPreBarrier(Address(base, offset)); |
4344 | } |
4345 | |
4346 | masm.storeValue(value, Address(base, offset)); |
4347 | } |
4348 | |
4349 | void CodeGenerator::visitElements(LElements* lir) { |
4350 | Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); |
4351 | masm.loadPtr(elements, ToRegister(lir->output())); |
4352 | } |
4353 | |
4354 | void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) { |
4355 | Address environment(ToRegister(lir->function()), |
4356 | JSFunction::offsetOfEnvironment()); |
4357 | masm.unboxObject(environment, ToRegister(lir->output())); |
4358 | } |
4359 | |
4360 | void CodeGenerator::visitHomeObject(LHomeObject* lir) { |
4361 | Register func = ToRegister(lir->function()); |
4362 | Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot()); |
4363 | |
4364 | masm.assertFunctionIsExtended(func); |
4365 | #ifdef DEBUG1 |
4366 | Label isObject; |
4367 | masm.branchTestObject(Assembler::Equal, homeObject, &isObject); |
4368 | masm.assumeUnreachable("[[HomeObject]] must be Object"); |
4369 | masm.bind(&isObject); |
4370 | #endif |
4371 | |
4372 | masm.unboxObject(homeObject, ToRegister(lir->output())); |
4373 | } |
4374 | |
4375 | void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) { |
4376 | Register homeObject = ToRegister(lir->homeObject()); |
4377 | ValueOperand output = ToOutValue(lir); |
4378 | Register temp = output.scratchReg(); |
4379 | |
4380 | masm.loadObjProto(homeObject, temp); |
4381 | |
4382 | #ifdef DEBUG1 |
4383 | // We won't encounter a lazy proto, because the prototype is guaranteed to |
4384 | // either be a JSFunction or a PlainObject, and only proxy objects can have a |
4385 | // lazy proto. |
4386 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 4386; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4387 | |
4388 | Label proxyCheckDone; |
4389 | masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone); |
4390 | masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase"); |
4391 | masm.bind(&proxyCheckDone); |
4392 | #endif |
4393 | |
4394 | Label nullProto, done; |
4395 | masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto); |
4396 | |
4397 | // Box prototype and return |
4398 | masm.tagValue(JSVAL_TYPE_OBJECT, temp, output); |
4399 | masm.jump(&done); |
4400 | |
4401 | masm.bind(&nullProto); |
4402 | masm.moveValue(NullValue(), output); |
4403 | |
4404 | masm.bind(&done); |
4405 | } |
4406 | |
4407 | template <class T> |
4408 | static T* ToConstantObject(MDefinition* def) { |
4409 | MOZ_ASSERT(def->isConstant())do { static_assert( mozilla::detail::AssertionConditionType< decltype(def->isConstant())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(def->isConstant()))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("def->isConstant()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4409); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->isConstant()" ")"); do { *((volatile int*)__null) = 4409; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4410 | return &def->toConstant()->toObject().as<T>(); |
4411 | } |
4412 | |
4413 | void CodeGenerator::visitNewLexicalEnvironmentObject( |
4414 | LNewLexicalEnvironmentObject* lir) { |
4415 | Register output = ToRegister(lir->output()); |
4416 | Register temp = ToRegister(lir->temp0()); |
4417 | |
4418 | auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>( |
4419 | lir->mir()->templateObj()); |
4420 | auto* scope = &templateObj->scope(); |
4421 | gc::Heap initialHeap = gc::Heap::Default; |
4422 | |
4423 | using Fn = |
4424 | BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>); |
4425 | auto* ool = |
4426 | oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>( |
4427 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
4428 | |
4429 | TemplateObject templateObject(templateObj); |
4430 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
4431 | |
4432 | masm.bind(ool->rejoin()); |
4433 | } |
4434 | |
4435 | void CodeGenerator::visitNewClassBodyEnvironmentObject( |
4436 | LNewClassBodyEnvironmentObject* lir) { |
4437 | Register output = ToRegister(lir->output()); |
4438 | Register temp = ToRegister(lir->temp0()); |
4439 | |
4440 | auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>( |
4441 | lir->mir()->templateObj()); |
4442 | auto* scope = &templateObj->scope(); |
4443 | gc::Heap initialHeap = gc::Heap::Default; |
4444 | |
4445 | using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*, |
4446 | Handle<ClassBodyScope*>); |
4447 | auto* ool = |
4448 | oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>( |
4449 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
4450 | |
4451 | TemplateObject templateObject(templateObj); |
4452 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
4453 | |
4454 | masm.bind(ool->rejoin()); |
4455 | } |
4456 | |
4457 | void CodeGenerator::visitNewVarEnvironmentObject( |
4458 | LNewVarEnvironmentObject* lir) { |
4459 | Register output = ToRegister(lir->output()); |
4460 | Register temp = ToRegister(lir->temp0()); |
4461 | |
4462 | auto* templateObj = |
4463 | ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj()); |
4464 | auto* scope = &templateObj->scope().as<VarScope>(); |
4465 | gc::Heap initialHeap = gc::Heap::Default; |
4466 | |
4467 | using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>); |
4468 | auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>( |
4469 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
4470 | |
4471 | TemplateObject templateObject(templateObj); |
4472 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
4473 | |
4474 | masm.bind(ool->rejoin()); |
4475 | } |
4476 | |
4477 | void CodeGenerator::visitGuardShape(LGuardShape* guard) { |
4478 | Register obj = ToRegister(guard->input()); |
4479 | Register temp = ToTempRegisterOrInvalid(guard->temp0()); |
4480 | Label bail; |
4481 | masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp, |
4482 | obj, &bail); |
4483 | bailoutFrom(&bail, guard->snapshot()); |
4484 | } |
4485 | |
4486 | void CodeGenerator::visitGuardFuse(LGuardFuse* guard) { |
4487 | auto fuseIndex = guard->mir()->fuseIndex(); |
4488 | |
4489 | Register temp = ToRegister(guard->temp0()); |
4490 | Label bail; |
4491 | |
4492 | // Bake specific fuse address for Ion code, because we won't share this code |
4493 | // across realms. |
4494 | GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex); |
4495 | masm.loadPtr(AbsoluteAddress(fuse->fuseRef()), temp); |
4496 | masm.branchPtr(Assembler::NotEqual, temp, ImmPtr(nullptr), &bail); |
4497 | |
4498 | bailoutFrom(&bail, guard->snapshot()); |
4499 | } |
4500 | |
4501 | void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) { |
4502 | Register obj = ToRegister(guard->object()); |
4503 | Register shapeList = ToRegister(guard->shapeList()); |
4504 | Register temp = ToRegister(guard->temp0()); |
4505 | Register temp2 = ToRegister(guard->temp1()); |
4506 | Register temp3 = ToRegister(guard->temp2()); |
4507 | Register spectre = ToTempRegisterOrInvalid(guard->temp3()); |
4508 | |
4509 | Label bail; |
4510 | masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp); |
4511 | masm.branchTestObjShapeList(Assembler::NotEqual, obj, temp, temp2, temp3, |
4512 | spectre, &bail); |
4513 | bailoutFrom(&bail, guard->snapshot()); |
4514 | } |
4515 | |
4516 | void CodeGenerator::visitGuardProto(LGuardProto* guard) { |
4517 | Register obj = ToRegister(guard->object()); |
4518 | Register expected = ToRegister(guard->expected()); |
4519 | Register temp = ToRegister(guard->temp0()); |
4520 | |
4521 | masm.loadObjProto(obj, temp); |
4522 | |
4523 | Label bail; |
4524 | masm.branchPtr(Assembler::NotEqual, temp, expected, &bail); |
4525 | bailoutFrom(&bail, guard->snapshot()); |
4526 | } |
4527 | |
4528 | void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) { |
4529 | Register obj = ToRegister(guard->input()); |
4530 | Register temp = ToRegister(guard->temp0()); |
4531 | |
4532 | masm.loadObjProto(obj, temp); |
4533 | |
4534 | Label bail; |
4535 | masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail); |
4536 | bailoutFrom(&bail, guard->snapshot()); |
4537 | } |
4538 | |
4539 | void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) { |
4540 | Register obj = ToRegister(guard->input()); |
4541 | Register temp = ToRegister(guard->temp0()); |
4542 | |
4543 | Label bail; |
4544 | masm.branchIfNonNativeObj(obj, temp, &bail); |
4545 | bailoutFrom(&bail, guard->snapshot()); |
4546 | } |
4547 | |
4548 | void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) { |
4549 | Register temp = ToRegister(guard->temp0()); |
4550 | Label bail; |
4551 | |
4552 | masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp); |
4553 | masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()), |
4554 | &bail); |
4555 | bailoutFrom(&bail, guard->snapshot()); |
4556 | } |
4557 | |
4558 | void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) { |
4559 | Register obj = ToRegister(guard->input()); |
4560 | Register temp = ToRegister(guard->temp0()); |
4561 | |
4562 | Label bail; |
4563 | masm.branchTestObjectIsProxy(false, obj, temp, &bail); |
4564 | bailoutFrom(&bail, guard->snapshot()); |
4565 | } |
4566 | |
4567 | void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) { |
4568 | Register obj = ToRegister(guard->input()); |
4569 | Register temp = ToRegister(guard->temp0()); |
4570 | |
4571 | Label bail; |
4572 | masm.branchTestObjectIsProxy(true, obj, temp, &bail); |
4573 | bailoutFrom(&bail, guard->snapshot()); |
4574 | } |
4575 | |
4576 | void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) { |
4577 | Register proxy = ToRegister(guard->proxy()); |
4578 | Register temp = ToRegister(guard->temp0()); |
4579 | |
4580 | Label bail; |
4581 | masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp, |
4582 | GetDOMProxyHandlerFamily(), &bail); |
4583 | bailoutFrom(&bail, guard->snapshot()); |
4584 | } |
4585 | |
4586 | void CodeGenerator::visitProxyGet(LProxyGet* lir) { |
4587 | Register proxy = ToRegister(lir->proxy()); |
4588 | Register temp = ToRegister(lir->temp0()); |
4589 | |
4590 | pushArg(lir->mir()->id(), temp); |
4591 | pushArg(proxy); |
4592 | |
4593 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue); |
4594 | callVM<Fn, ProxyGetProperty>(lir); |
4595 | } |
4596 | |
4597 | void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) { |
4598 | Register proxy = ToRegister(lir->proxy()); |
4599 | ValueOperand idVal = ToValue(lir, LProxyGetByValue::IdIndex); |
4600 | |
4601 | pushArg(idVal); |
4602 | pushArg(proxy); |
4603 | |
4604 | using Fn = |
4605 | bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue); |
4606 | callVM<Fn, ProxyGetPropertyByValue>(lir); |
4607 | } |
4608 | |
4609 | void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) { |
4610 | Register proxy = ToRegister(lir->proxy()); |
4611 | ValueOperand idVal = ToValue(lir, LProxyHasProp::IdIndex); |
4612 | |
4613 | pushArg(idVal); |
4614 | pushArg(proxy); |
4615 | |
4616 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*); |
4617 | if (lir->mir()->hasOwn()) { |
4618 | callVM<Fn, ProxyHasOwn>(lir); |
4619 | } else { |
4620 | callVM<Fn, ProxyHas>(lir); |
4621 | } |
4622 | } |
4623 | |
4624 | void CodeGenerator::visitProxySet(LProxySet* lir) { |
4625 | Register proxy = ToRegister(lir->proxy()); |
4626 | ValueOperand rhs = ToValue(lir, LProxySet::RhsIndex); |
4627 | Register temp = ToRegister(lir->temp0()); |
4628 | |
4629 | pushArg(Imm32(lir->mir()->strict())); |
4630 | pushArg(rhs); |
4631 | pushArg(lir->mir()->id(), temp); |
4632 | pushArg(proxy); |
4633 | |
4634 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool); |
4635 | callVM<Fn, ProxySetProperty>(lir); |
4636 | } |
4637 | |
4638 | void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) { |
4639 | Register proxy = ToRegister(lir->proxy()); |
4640 | ValueOperand idVal = ToValue(lir, LProxySetByValue::IdIndex); |
4641 | ValueOperand rhs = ToValue(lir, LProxySetByValue::RhsIndex); |
4642 | |
4643 | pushArg(Imm32(lir->mir()->strict())); |
4644 | pushArg(rhs); |
4645 | pushArg(idVal); |
4646 | pushArg(proxy); |
4647 | |
4648 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool); |
4649 | callVM<Fn, ProxySetPropertyByValue>(lir); |
4650 | } |
4651 | |
4652 | void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) { |
4653 | Register obj = ToRegister(lir->obj()); |
4654 | ValueOperand rhs = ToValue(lir, LCallSetArrayLength::RhsIndex); |
4655 | |
4656 | pushArg(Imm32(lir->mir()->strict())); |
4657 | pushArg(rhs); |
4658 | pushArg(obj); |
4659 | |
4660 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool); |
4661 | callVM<Fn, jit::SetArrayLength>(lir); |
4662 | } |
4663 | |
4664 | void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) { |
4665 | Register obj = ToRegister(lir->object()); |
4666 | Register temp0 = ToRegister(lir->temp0()); |
4667 | Register temp1 = ToRegister(lir->temp1()); |
4668 | Register temp2 = ToRegister(lir->temp2()); |
4669 | Register temp3 = ToRegister(lir->temp3()); |
4670 | ValueOperand output = ToOutValue(lir); |
4671 | |
4672 | Label cacheHit; |
4673 | masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2, |
4674 | output, &cacheHit); |
4675 | |
4676 | Label bail; |
4677 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
4678 | |
4679 | masm.Push(UndefinedValue()); |
4680 | masm.moveStackPtrTo(temp3); |
4681 | |
4682 | using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id, |
4683 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
4684 | masm.setupAlignedABICall(); |
4685 | masm.loadJSContext(temp0); |
4686 | masm.passABIArg(temp0); |
4687 | masm.passABIArg(obj); |
4688 | masm.movePropertyKey(lir->mir()->name(), temp1); |
4689 | masm.passABIArg(temp1); |
4690 | masm.passABIArg(temp2); |
4691 | masm.passABIArg(temp3); |
4692 | |
4693 | masm.callWithABI<Fn, GetNativeDataPropertyPure>(); |
4694 | |
4695 | MOZ_ASSERT(!output.aliases(ReturnReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!output.aliases(ReturnReg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!output.aliases(ReturnReg))) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!output.aliases(ReturnReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4695); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)" ")"); do { *((volatile int*)__null) = 4695; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4696 | masm.Pop(output); |
4697 | |
4698 | masm.branchIfFalseBool(ReturnReg, &bail); |
4699 | masm.bind(&cacheHit); |
4700 | |
4701 | bailoutFrom(&bail, lir->snapshot()); |
4702 | } |
4703 | |
4704 | void CodeGenerator::visitMegamorphicLoadSlotPermissive( |
4705 | LMegamorphicLoadSlotPermissive* lir) { |
4706 | Register obj = ToRegister(lir->object()); |
4707 | Register temp0 = ToRegister(lir->temp0()); |
4708 | Register temp1 = ToRegister(lir->temp1()); |
4709 | Register temp2 = ToRegister(lir->temp2()); |
4710 | ValueOperand output = ToOutValue(lir); |
4711 | |
4712 | Label cacheHit; |
4713 | masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2, |
4714 | output, &cacheHit); |
4715 | |
4716 | masm.movePropertyKey(lir->mir()->name(), temp1); |
4717 | pushArg(temp2); |
4718 | pushArg(temp1); |
4719 | pushArg(obj); |
4720 | |
4721 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, |
4722 | MegamorphicCacheEntry*, MutableHandleValue); |
4723 | callVM<Fn, GetPropMaybeCached>(lir); |
4724 | |
4725 | masm.bind(&cacheHit); |
4726 | } |
4727 | |
4728 | void CodeGenerator::visitMegamorphicLoadSlotByValue( |
4729 | LMegamorphicLoadSlotByValue* lir) { |
4730 | Register obj = ToRegister(lir->object()); |
4731 | ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex); |
4732 | Register temp0 = ToRegister(lir->temp0()); |
4733 | Register temp1 = ToRegister(lir->temp1()); |
4734 | Register temp2 = ToRegister(lir->temp2()); |
4735 | ValueOperand output = ToOutValue(lir); |
4736 | |
4737 | Label cacheHit, bail; |
4738 | masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2, |
4739 | output, &cacheHit); |
4740 | |
4741 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
4742 | |
4743 | // idVal will be in vp[0], result will be stored in vp[1]. |
4744 | masm.reserveStack(sizeof(Value)); |
4745 | masm.Push(idVal); |
4746 | masm.moveStackPtrTo(temp0); |
4747 | |
4748 | using Fn = bool (*)(JSContext* cx, JSObject* obj, |
4749 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
4750 | masm.setupAlignedABICall(); |
4751 | masm.loadJSContext(temp1); |
4752 | masm.passABIArg(temp1); |
4753 | masm.passABIArg(obj); |
4754 | masm.passABIArg(temp2); |
4755 | masm.passABIArg(temp0); |
4756 | masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>(); |
4757 | |
4758 | MOZ_ASSERT(!idVal.aliases(temp0))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!idVal.aliases(temp0))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!idVal.aliases(temp0)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("!idVal.aliases(temp0)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4758); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { *((volatile int*)__null) = 4758; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4759 | masm.storeCallPointerResult(temp0); |
4760 | masm.Pop(idVal); |
4761 | |
4762 | uint32_t framePushed = masm.framePushed(); |
4763 | Label ok; |
4764 | masm.branchIfTrueBool(temp0, &ok); |
4765 | masm.freeStack(sizeof(Value)); // Discard result Value. |
4766 | masm.jump(&bail); |
4767 | |
4768 | masm.bind(&ok); |
4769 | masm.setFramePushed(framePushed); |
4770 | masm.Pop(output); |
4771 | |
4772 | masm.bind(&cacheHit); |
4773 | |
4774 | bailoutFrom(&bail, lir->snapshot()); |
4775 | } |
4776 | |
4777 | void CodeGenerator::visitMegamorphicLoadSlotByValuePermissive( |
4778 | LMegamorphicLoadSlotByValuePermissive* lir) { |
4779 | Register obj = ToRegister(lir->object()); |
4780 | ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex); |
4781 | Register temp0 = ToRegister(lir->temp0()); |
4782 | Register temp1 = ToRegister(lir->temp1()); |
4783 | Register temp2 = ToRegister(lir->temp2()); |
4784 | ValueOperand output = ToOutValue(lir); |
4785 | |
4786 | Label cacheHit; |
4787 | masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2, |
4788 | output, &cacheHit); |
4789 | |
4790 | pushArg(temp2); |
4791 | pushArg(idVal); |
4792 | pushArg(obj); |
4793 | |
4794 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, |
4795 | MegamorphicCacheEntry*, MutableHandleValue); |
4796 | callVM<Fn, GetElemMaybeCached>(lir); |
4797 | |
4798 | masm.bind(&cacheHit); |
4799 | } |
4800 | |
4801 | void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) { |
4802 | Register obj = ToRegister(lir->object()); |
4803 | ValueOperand value = ToValue(lir, LMegamorphicStoreSlot::RhsIndex); |
4804 | |
4805 | Register temp0 = ToRegister(lir->temp0()); |
4806 | #ifndef JS_CODEGEN_X86 |
4807 | Register temp1 = ToRegister(lir->temp1()); |
4808 | Register temp2 = ToRegister(lir->temp2()); |
4809 | #endif |
4810 | |
4811 | // The instruction is marked as call-instruction so only these registers are |
4812 | // live. |
4813 | LiveRegisterSet liveRegs; |
4814 | liveRegs.addUnchecked(obj); |
4815 | liveRegs.addUnchecked(value); |
4816 | liveRegs.addUnchecked(temp0); |
4817 | #ifndef JS_CODEGEN_X86 |
4818 | liveRegs.addUnchecked(temp1); |
4819 | liveRegs.addUnchecked(temp2); |
4820 | #endif |
4821 | |
4822 | Label cacheHit, done; |
4823 | #ifdef JS_CODEGEN_X86 |
4824 | masm.emitMegamorphicCachedSetSlot( |
4825 | lir->mir()->name(), obj, temp0, value, liveRegs, &cacheHit, |
4826 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
4827 | EmitPreBarrier(masm, addr, mirType); |
4828 | }); |
4829 | #else |
4830 | masm.emitMegamorphicCachedSetSlot( |
4831 | lir->mir()->name(), obj, temp0, temp1, temp2, value, liveRegs, &cacheHit, |
4832 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
4833 | EmitPreBarrier(masm, addr, mirType); |
4834 | }); |
4835 | #endif |
4836 | |
4837 | pushArg(Imm32(lir->mir()->strict())); |
4838 | pushArg(value); |
4839 | pushArg(lir->mir()->name(), temp0); |
4840 | pushArg(obj); |
4841 | |
4842 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool); |
4843 | callVM<Fn, SetPropertyMegamorphic<true>>(lir); |
4844 | |
4845 | masm.jump(&done); |
4846 | masm.bind(&cacheHit); |
4847 | |
4848 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done); |
4849 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done); |
4850 | |
4851 | // Note: because this is a call-instruction, no registers need to be saved. |
4852 | MOZ_ASSERT(lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4852); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { *((volatile int*)__null) = 4852; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4853 | emitPostWriteBarrier(obj); |
4854 | |
4855 | masm.bind(&done); |
4856 | } |
4857 | |
4858 | void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) { |
4859 | Register obj = ToRegister(lir->object()); |
4860 | ValueOperand idVal = ToValue(lir, LMegamorphicHasProp::IdIndex); |
4861 | Register temp0 = ToRegister(lir->temp0()); |
4862 | Register temp1 = ToRegister(lir->temp1()); |
4863 | Register temp2 = ToRegister(lir->temp2()); |
4864 | Register output = ToRegister(lir->output()); |
4865 | |
4866 | Label bail, cacheHit; |
4867 | masm.emitMegamorphicCacheLookupExists(idVal, obj, temp0, temp1, temp2, output, |
4868 | &cacheHit, lir->mir()->hasOwn()); |
4869 | |
4870 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
4871 | |
4872 | // idVal will be in vp[0], result will be stored in vp[1]. |
4873 | masm.reserveStack(sizeof(Value)); |
4874 | masm.Push(idVal); |
4875 | masm.moveStackPtrTo(temp0); |
4876 | |
4877 | using Fn = bool (*)(JSContext* cx, JSObject* obj, |
4878 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
4879 | masm.setupAlignedABICall(); |
4880 | masm.loadJSContext(temp1); |
4881 | masm.passABIArg(temp1); |
4882 | masm.passABIArg(obj); |
4883 | masm.passABIArg(temp2); |
4884 | masm.passABIArg(temp0); |
4885 | if (lir->mir()->hasOwn()) { |
4886 | masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>(); |
4887 | } else { |
4888 | masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>(); |
4889 | } |
4890 | |
4891 | MOZ_ASSERT(!idVal.aliases(temp0))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!idVal.aliases(temp0))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!idVal.aliases(temp0)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("!idVal.aliases(temp0)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { *((volatile int*)__null) = 4891; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4892 | masm.storeCallPointerResult(temp0); |
4893 | masm.Pop(idVal); |
4894 | |
4895 | uint32_t framePushed = masm.framePushed(); |
4896 | Label ok; |
4897 | masm.branchIfTrueBool(temp0, &ok); |
4898 | masm.freeStack(sizeof(Value)); // Discard result Value. |
4899 | masm.jump(&bail); |
4900 | |
4901 | masm.bind(&ok); |
4902 | masm.setFramePushed(framePushed); |
4903 | masm.unboxBoolean(Address(masm.getStackPointer(), 0), output); |
4904 | masm.freeStack(sizeof(Value)); |
4905 | masm.bind(&cacheHit); |
4906 | |
4907 | bailoutFrom(&bail, lir->snapshot()); |
4908 | } |
4909 | |
4910 | void CodeGenerator::visitSmallObjectVariableKeyHasProp( |
4911 | LSmallObjectVariableKeyHasProp* lir) { |
4912 | Register id = ToRegister(lir->id()); |
4913 | Register output = ToRegister(lir->output()); |
4914 | |
4915 | #ifdef DEBUG1 |
4916 | Label isAtom; |
4917 | masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()), |
4918 | Imm32(JSString::ATOM_BIT), &isAtom); |
4919 | masm.assumeUnreachable("Expected atom input"); |
4920 | masm.bind(&isAtom); |
4921 | #endif |
4922 | |
4923 | SharedShape* shape = &lir->mir()->shape()->asShared(); |
4924 | |
4925 | Label done, success; |
4926 | for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) { |
4927 | masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()), |
4928 | &success); |
4929 | } |
4930 | masm.move32(Imm32(0), output); |
4931 | masm.jump(&done); |
4932 | masm.bind(&success); |
4933 | masm.move32(Imm32(1), output); |
4934 | masm.bind(&done); |
4935 | } |
4936 | |
4937 | void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared( |
4938 | LGuardIsNotArrayBufferMaybeShared* guard) { |
4939 | Register obj = ToRegister(guard->input()); |
4940 | Register temp = ToRegister(guard->temp0()); |
4941 | |
4942 | Label bail; |
4943 | masm.loadObjClassUnsafe(obj, temp); |
4944 | masm.branchPtr(Assembler::Equal, temp, |
4945 | ImmPtr(&FixedLengthArrayBufferObject::class_), &bail); |
4946 | masm.branchPtr(Assembler::Equal, temp, |
4947 | ImmPtr(&FixedLengthSharedArrayBufferObject::class_), &bail); |
4948 | masm.branchPtr(Assembler::Equal, temp, |
4949 | ImmPtr(&ResizableArrayBufferObject::class_), &bail); |
4950 | masm.branchPtr(Assembler::Equal, temp, |
4951 | ImmPtr(&GrowableSharedArrayBufferObject::class_), &bail); |
4952 | bailoutFrom(&bail, guard->snapshot()); |
4953 | } |
4954 | |
4955 | void CodeGenerator::visitGuardIsTypedArray(LGuardIsTypedArray* guard) { |
4956 | Register obj = ToRegister(guard->input()); |
4957 | Register temp = ToRegister(guard->temp0()); |
4958 | |
4959 | Label bail; |
4960 | masm.loadObjClassUnsafe(obj, temp); |
4961 | masm.branchIfClassIsNotTypedArray(temp, &bail); |
4962 | bailoutFrom(&bail, guard->snapshot()); |
4963 | } |
4964 | |
4965 | void CodeGenerator::visitGuardIsFixedLengthTypedArray( |
4966 | LGuardIsFixedLengthTypedArray* guard) { |
4967 | Register obj = ToRegister(guard->input()); |
4968 | Register temp = ToRegister(guard->temp0()); |
4969 | |
4970 | Label bail; |
4971 | masm.loadObjClassUnsafe(obj, temp); |
4972 | masm.branchIfClassIsNotFixedLengthTypedArray(temp, &bail); |
4973 | bailoutFrom(&bail, guard->snapshot()); |
4974 | } |
4975 | |
4976 | void CodeGenerator::visitGuardIsResizableTypedArray( |
4977 | LGuardIsResizableTypedArray* guard) { |
4978 | Register obj = ToRegister(guard->input()); |
4979 | Register temp = ToRegister(guard->temp0()); |
4980 | |
4981 | Label bail; |
4982 | masm.loadObjClassUnsafe(obj, temp); |
4983 | masm.branchIfClassIsNotResizableTypedArray(temp, &bail); |
4984 | bailoutFrom(&bail, guard->snapshot()); |
4985 | } |
4986 | |
4987 | void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) { |
4988 | Register obj = ToRegister(guard->input()); |
4989 | |
4990 | Label bail; |
4991 | |
4992 | Address handlerAddr(obj, ProxyObject::offsetOfHandler()); |
4993 | masm.branchPtr(Assembler::NotEqual, handlerAddr, |
4994 | ImmPtr(guard->mir()->handler()), &bail); |
4995 | |
4996 | bailoutFrom(&bail, guard->snapshot()); |
4997 | } |
4998 | |
4999 | void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) { |
5000 | Register input = ToRegister(guard->input()); |
5001 | Register expected = ToRegister(guard->expected()); |
5002 | |
5003 | Assembler::Condition cond = |
5004 | guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
5005 | bailoutCmpPtr(cond, input, expected, guard->snapshot()); |
5006 | } |
5007 | |
5008 | void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) { |
5009 | Register input = ToRegister(guard->input()); |
5010 | Register expected = ToRegister(guard->expected()); |
5011 | |
5012 | bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot()); |
5013 | } |
5014 | |
5015 | void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) { |
5016 | Register str = ToRegister(guard->str()); |
5017 | Register scratch = ToRegister(guard->temp0()); |
5018 | |
5019 | LiveRegisterSet volatileRegs = liveVolatileRegs(guard); |
5020 | volatileRegs.takeUnchecked(scratch); |
5021 | |
5022 | Label bail; |
5023 | masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs, |
5024 | &bail); |
5025 | bailoutFrom(&bail, guard->snapshot()); |
5026 | } |
5027 | |
5028 | void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) { |
5029 | Register symbol = ToRegister(guard->symbol()); |
5030 | |
5031 | bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()), |
5032 | guard->snapshot()); |
5033 | } |
5034 | |
5035 | void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) { |
5036 | Register num = ToRegister(guard->num()); |
5037 | |
5038 | bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()), |
5039 | guard->snapshot()); |
5040 | } |
5041 | |
5042 | void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) { |
5043 | Register str = ToRegister(lir->string()); |
5044 | Register output = ToRegister(lir->output()); |
5045 | |
5046 | Label vmCall, done; |
5047 | masm.loadStringIndexValue(str, output, &vmCall); |
5048 | masm.jump(&done); |
5049 | |
5050 | { |
5051 | masm.bind(&vmCall); |
5052 | |
5053 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
5054 | volatileRegs.takeUnchecked(output); |
5055 | masm.PushRegsInMask(volatileRegs); |
5056 | |
5057 | using Fn = int32_t (*)(JSString* str); |
5058 | masm.setupAlignedABICall(); |
5059 | masm.passABIArg(str); |
5060 | masm.callWithABI<Fn, GetIndexFromString>(); |
5061 | masm.storeCallInt32Result(output); |
5062 | |
5063 | masm.PopRegsInMask(volatileRegs); |
5064 | |
5065 | // GetIndexFromString returns a negative value on failure. |
5066 | bailoutTest32(Assembler::Signed, output, output, lir->snapshot()); |
5067 | } |
5068 | |
5069 | masm.bind(&done); |
5070 | } |
5071 | |
5072 | void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) { |
5073 | Register str = ToRegister(lir->string()); |
5074 | Register output = ToRegister(lir->output()); |
5075 | Register temp = ToRegister(lir->temp0()); |
5076 | |
5077 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
5078 | |
5079 | Label bail; |
5080 | masm.guardStringToInt32(str, output, temp, volatileRegs, &bail); |
5081 | bailoutFrom(&bail, lir->snapshot()); |
5082 | } |
5083 | |
5084 | void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) { |
5085 | Register str = ToRegister(lir->string()); |
5086 | FloatRegister output = ToFloatRegister(lir->output()); |
5087 | Register temp0 = ToRegister(lir->temp0()); |
5088 | Register temp1 = ToRegister(lir->temp1()); |
5089 | |
5090 | Label vmCall, done; |
5091 | // Use indexed value as fast path if possible. |
5092 | masm.loadStringIndexValue(str, temp0, &vmCall); |
5093 | masm.convertInt32ToDouble(temp0, output); |
5094 | masm.jump(&done); |
5095 | { |
5096 | masm.bind(&vmCall); |
5097 | |
5098 | // Reserve stack for holding the result value of the call. |
5099 | masm.reserveStack(sizeof(double)); |
5100 | masm.moveStackPtrTo(temp0); |
5101 | |
5102 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
5103 | volatileRegs.takeUnchecked(temp0); |
5104 | volatileRegs.takeUnchecked(temp1); |
5105 | masm.PushRegsInMask(volatileRegs); |
5106 | |
5107 | using Fn = bool (*)(JSContext* cx, JSString* str, double* result); |
5108 | masm.setupAlignedABICall(); |
5109 | masm.loadJSContext(temp1); |
5110 | masm.passABIArg(temp1); |
5111 | masm.passABIArg(str); |
5112 | masm.passABIArg(temp0); |
5113 | masm.callWithABI<Fn, StringToNumberPure>(); |
5114 | masm.storeCallPointerResult(temp0); |
5115 | |
5116 | masm.PopRegsInMask(volatileRegs); |
5117 | |
5118 | Label ok; |
5119 | masm.branchIfTrueBool(temp0, &ok); |
5120 | { |
5121 | // OOM path, recovered by StringToNumberPure. |
5122 | // |
5123 | // Use addToStackPtr instead of freeStack as freeStack tracks stack height |
5124 | // flow-insensitively, and using it here would confuse the stack height |
5125 | // tracking. |
5126 | masm.addToStackPtr(Imm32(sizeof(double))); |
5127 | bailout(lir->snapshot()); |
5128 | } |
5129 | masm.bind(&ok); |
5130 | masm.Pop(output); |
5131 | } |
5132 | masm.bind(&done); |
5133 | } |
5134 | |
5135 | void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) { |
5136 | Register obj = ToRegister(guard->input()); |
5137 | Register temp = ToRegister(guard->temp0()); |
5138 | |
5139 | // Load obj->elements. |
5140 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp); |
5141 | |
5142 | // Make sure there are no dense elements. |
5143 | Address initLength(temp, ObjectElements::offsetOfInitializedLength()); |
5144 | bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot()); |
5145 | } |
5146 | |
5147 | void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) { |
5148 | Register input = ToRegister(lir->input()); |
5149 | Register64 output = ToOutRegister64(lir); |
5150 | |
5151 | masm.move32To64ZeroExtend(input, output); |
5152 | } |
5153 | |
5154 | void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input, |
5155 | Register64 output) { |
5156 | Register temp = output.scratchReg(); |
5157 | |
5158 | saveLive(lir); |
5159 | |
5160 | masm.reserveStack(sizeof(uint64_t)); |
5161 | masm.moveStackPtrTo(temp); |
5162 | pushArg(temp); |
5163 | pushArg(input); |
5164 | |
5165 | using Fn = bool (*)(JSContext*, HandleString, uint64_t*); |
5166 | callVM<Fn, DoStringToInt64>(lir); |
5167 | |
5168 | masm.load64(Address(masm.getStackPointer(), 0), output); |
5169 | masm.freeStack(sizeof(uint64_t)); |
5170 | |
5171 | restoreLiveIgnore(lir, StoreValueTo(output).clobbered()); |
5172 | } |
5173 | |
5174 | void CodeGenerator::visitStringToInt64(LStringToInt64* lir) { |
5175 | Register input = ToRegister(lir->input()); |
5176 | Register64 output = ToOutRegister64(lir); |
5177 | |
5178 | emitStringToInt64(lir, input, output); |
5179 | } |
5180 | |
5181 | void CodeGenerator::visitValueToInt64(LValueToInt64* lir) { |
5182 | ValueOperand input = ToValue(lir, LValueToInt64::InputIndex); |
5183 | Register temp = ToRegister(lir->temp0()); |
5184 | Register64 output = ToOutRegister64(lir); |
5185 | |
5186 | int checks = 3; |
5187 | |
5188 | Label fail, done; |
5189 | // Jump to fail if this is the last check and we fail it, |
5190 | // otherwise to the next test. |
5191 | auto emitTestAndUnbox = [&](auto testAndUnbox) { |
5192 | MOZ_ASSERT(checks > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(checks > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(checks > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("checks > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks > 0" ")"); do { *((volatile int*)__null) = 5192; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5193 | |
5194 | checks--; |
5195 | Label notType; |
5196 | Label* target = checks ? ¬Type : &fail; |
5197 | |
5198 | testAndUnbox(target); |
5199 | |
5200 | if (checks) { |
5201 | masm.jump(&done); |
5202 | masm.bind(¬Type); |
5203 | } |
5204 | }; |
5205 | |
5206 | Register tag = masm.extractTag(input, temp); |
5207 | |
5208 | // BigInt. |
5209 | emitTestAndUnbox([&](Label* target) { |
5210 | masm.branchTestBigInt(Assembler::NotEqual, tag, target); |
5211 | masm.unboxBigInt(input, temp); |
5212 | masm.loadBigInt64(temp, output); |
5213 | }); |
5214 | |
5215 | // Boolean |
5216 | emitTestAndUnbox([&](Label* target) { |
5217 | masm.branchTestBoolean(Assembler::NotEqual, tag, target); |
5218 | masm.unboxBoolean(input, temp); |
5219 | masm.move32To64ZeroExtend(temp, output); |
5220 | }); |
5221 | |
5222 | // String |
5223 | emitTestAndUnbox([&](Label* target) { |
5224 | masm.branchTestString(Assembler::NotEqual, tag, target); |
5225 | masm.unboxString(input, temp); |
5226 | emitStringToInt64(lir, temp, output); |
5227 | }); |
5228 | |
5229 | MOZ_ASSERT(checks == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(checks == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(checks == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("checks == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5229); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0" ")"); do { *((volatile int*)__null) = 5229; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5230 | |
5231 | bailoutFrom(&fail, lir->snapshot()); |
5232 | masm.bind(&done); |
5233 | } |
5234 | |
5235 | void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) { |
5236 | Register operand = ToRegister(lir->input()); |
5237 | Register64 output = ToOutRegister64(lir); |
5238 | |
5239 | masm.loadBigInt64(operand, output); |
5240 | } |
5241 | |
5242 | OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir, |
5243 | Scalar::Type type, |
5244 | Register64 input, |
5245 | Register output) { |
5246 | #if JS_BITS_PER_WORD64 == 32 |
5247 | using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t); |
5248 | auto args = ArgList(input.low, input.high); |
5249 | #else |
5250 | using Fn = BigInt* (*)(JSContext*, uint64_t); |
5251 | auto args = ArgList(input); |
5252 | #endif |
5253 | |
5254 | if (type == Scalar::BigInt64) { |
5255 | return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args, |
5256 | StoreRegisterTo(output)); |
5257 | } |
5258 | MOZ_ASSERT(type == Scalar::BigUint64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == Scalar::BigUint64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == Scalar::BigUint64))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == Scalar::BigUint64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5258); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == Scalar::BigUint64" ")"); do { *((volatile int*)__null) = 5258; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5259 | return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args, |
5260 | StoreRegisterTo(output)); |
5261 | } |
5262 | |
5263 | void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type, |
5264 | Register64 input, Register output, |
5265 | Register maybeTemp, |
5266 | Register64 maybeTemp64) { |
5267 | OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output); |
5268 | |
5269 | if (maybeTemp != InvalidReg) { |
5270 | masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry()); |
5271 | } else { |
5272 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
5273 | regs.take(input); |
5274 | regs.take(output); |
5275 | |
5276 | Register temp = regs.takeAny(); |
5277 | |
5278 | masm.push(temp); |
5279 | |
5280 | Label fail, ok; |
5281 | masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail); |
5282 | masm.pop(temp); |
5283 | masm.jump(&ok); |
5284 | masm.bind(&fail); |
5285 | masm.pop(temp); |
5286 | masm.jump(ool->entry()); |
5287 | masm.bind(&ok); |
5288 | } |
5289 | masm.initializeBigInt64(type, output, input, maybeTemp64); |
5290 | masm.bind(ool->rejoin()); |
5291 | } |
5292 | |
5293 | void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) { |
5294 | Register64 input = ToRegister64(lir->input()); |
5295 | Register64 temp = ToRegister64(lir->temp()); |
5296 | Register output = ToRegister(lir->output()); |
5297 | |
5298 | emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp.scratchReg(), |
5299 | temp); |
5300 | } |
5301 | |
5302 | void CodeGenerator::visitUint64ToBigInt(LUint64ToBigInt* lir) { |
5303 | Register64 input = ToRegister64(lir->input()); |
5304 | Register temp = ToRegister(lir->temp0()); |
5305 | Register output = ToRegister(lir->output()); |
5306 | |
5307 | emitCreateBigInt(lir, Scalar::BigUint64, input, output, temp); |
5308 | } |
5309 | |
5310 | void CodeGenerator::visitInt64ToIntPtr(LInt64ToIntPtr* lir) { |
5311 | Register64 input = ToRegister64(lir->input()); |
5312 | #ifdef JS_64BIT1 |
5313 | MOZ_ASSERT(input.reg == ToRegister(lir->output()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(input.reg == ToRegister(lir->output()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(input.reg == ToRegister(lir->output())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("input.reg == ToRegister(lir->output())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input.reg == ToRegister(lir->output())" ")"); do { *((volatile int*)__null) = 5313; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5314 | #else |
5315 | Register output = ToRegister(lir->output()); |
5316 | #endif |
5317 | |
5318 | Label bail; |
5319 | if (lir->mir()->isSigned()) { |
5320 | masm.branchInt64NotInPtrRange(input, &bail); |
5321 | } else { |
5322 | masm.branchUInt64NotInPtrRange(input, &bail); |
5323 | } |
5324 | bailoutFrom(&bail, lir->snapshot()); |
5325 | |
5326 | #ifndef JS_64BIT1 |
5327 | masm.move64To32(input, output); |
5328 | #endif |
5329 | } |
5330 | |
5331 | void CodeGenerator::visitIntPtrToInt64(LIntPtrToInt64* lir) { |
5332 | #ifdef JS_64BIT1 |
5333 | MOZ_CRASH("Not used on 64-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 64-bit platforms" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5333); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 64-bit platforms" ")"); do { *((volatile int*)__null) = 5333; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
5334 | #else |
5335 | Register input = ToRegister(lir->input()); |
5336 | Register64 output = ToOutRegister64(lir); |
5337 | |
5338 | masm.move32To64SignExtend(input, output); |
5339 | #endif |
5340 | } |
5341 | |
5342 | void CodeGenerator::visitGuardValue(LGuardValue* lir) { |
5343 | ValueOperand input = ToValue(lir, LGuardValue::InputIndex); |
5344 | Value expected = lir->mir()->expected(); |
5345 | Label bail; |
5346 | masm.branchTestValue(Assembler::NotEqual, input, expected, &bail); |
5347 | bailoutFrom(&bail, lir->snapshot()); |
5348 | } |
5349 | |
5350 | void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) { |
5351 | ValueOperand input = ToValue(lir, LGuardNullOrUndefined::InputIndex); |
5352 | |
5353 | ScratchTagScope tag(masm, input); |
5354 | masm.splitTagForTest(input, tag); |
5355 | |
5356 | Label done; |
5357 | masm.branchTestNull(Assembler::Equal, tag, &done); |
5358 | |
5359 | Label bail; |
5360 | masm.branchTestUndefined(Assembler::NotEqual, tag, &bail); |
5361 | bailoutFrom(&bail, lir->snapshot()); |
5362 | |
5363 | masm.bind(&done); |
5364 | } |
5365 | |
5366 | void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) { |
5367 | ValueOperand input = ToValue(lir, LGuardIsNotObject::InputIndex); |
5368 | |
5369 | Label bail; |
5370 | masm.branchTestObject(Assembler::Equal, input, &bail); |
5371 | bailoutFrom(&bail, lir->snapshot()); |
5372 | } |
5373 | |
5374 | void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) { |
5375 | Register function = ToRegister(lir->function()); |
5376 | |
5377 | Label bail; |
5378 | if (uint16_t flags = lir->mir()->expectedFlags()) { |
5379 | masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail); |
5380 | } |
5381 | if (uint16_t flags = lir->mir()->unexpectedFlags()) { |
5382 | masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail); |
5383 | } |
5384 | bailoutFrom(&bail, lir->snapshot()); |
5385 | } |
5386 | |
5387 | void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor( |
5388 | LGuardFunctionIsNonBuiltinCtor* lir) { |
5389 | Register function = ToRegister(lir->function()); |
5390 | Register temp = ToRegister(lir->temp0()); |
5391 | |
5392 | Label bail; |
5393 | masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail); |
5394 | bailoutFrom(&bail, lir->snapshot()); |
5395 | } |
5396 | |
5397 | void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) { |
5398 | Register function = ToRegister(lir->function()); |
5399 | Register temp = ToRegister(lir->temp0()); |
5400 | |
5401 | Assembler::Condition cond = |
5402 | lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
5403 | |
5404 | Label bail; |
5405 | masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail); |
5406 | bailoutFrom(&bail, lir->snapshot()); |
5407 | } |
5408 | |
5409 | void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) { |
5410 | Register function = ToRegister(lir->function()); |
5411 | |
5412 | Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript()); |
5413 | bailoutCmpPtr(Assembler::NotEqual, scriptAddr, |
5414 | ImmGCPtr(lir->mir()->expected()), lir->snapshot()); |
5415 | } |
5416 | |
5417 | // Out-of-line path to update the store buffer. |
5418 | class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> { |
5419 | LInstruction* lir_; |
5420 | const LAllocation* object_; |
5421 | |
5422 | public: |
5423 | OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object) |
5424 | : lir_(lir), object_(object) {} |
5425 | |
5426 | void accept(CodeGenerator* codegen) override { |
5427 | codegen->visitOutOfLineCallPostWriteBarrier(this); |
5428 | } |
5429 | |
5430 | LInstruction* lir() const { return lir_; } |
5431 | const LAllocation* object() const { return object_; } |
5432 | }; |
5433 | |
5434 | static void EmitStoreBufferCheckForConstant(MacroAssembler& masm, |
5435 | const gc::TenuredCell* cell, |
5436 | AllocatableGeneralRegisterSet& regs, |
5437 | Label* exit, Label* callVM) { |
5438 | Register temp = regs.takeAny(); |
5439 | |
5440 | gc::Arena* arena = cell->arena(); |
5441 | |
5442 | Register cells = temp; |
5443 | masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells); |
5444 | |
5445 | size_t index = gc::ArenaCellSet::getCellIndex(cell); |
5446 | size_t word; |
5447 | uint32_t mask; |
5448 | gc::ArenaCellSet::getWordIndexAndMask(index, &word, &mask); |
5449 | size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t); |
5450 | |
5451 | masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask), |
5452 | exit); |
5453 | |
5454 | // Check whether this is the sentinel set and if so call the VM to allocate |
5455 | // one for this arena. |
5456 | masm.branchPtr(Assembler::Equal, |
5457 | Address(cells, gc::ArenaCellSet::offsetOfArena()), |
5458 | ImmPtr(nullptr), callVM); |
5459 | |
5460 | // Add the cell to the set. |
5461 | masm.or32(Imm32(mask), Address(cells, offset)); |
5462 | masm.jump(exit); |
5463 | |
5464 | regs.add(temp); |
5465 | } |
5466 | |
5467 | static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime, |
5468 | Register objreg, JSObject* maybeConstant, |
5469 | bool isGlobal, |
5470 | AllocatableGeneralRegisterSet& regs) { |
5471 | MOZ_ASSERT_IF(isGlobal, maybeConstant)do { if (isGlobal) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(maybeConstant)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(maybeConstant))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("maybeConstant", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5471); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeConstant" ")"); do { *((volatile int*)__null) = 5471; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
5472 | |
5473 | Label callVM; |
5474 | Label exit; |
5475 | |
5476 | Register temp = regs.takeAny(); |
5477 | |
5478 | // We already have a fast path to check whether a global is in the store |
5479 | // buffer. |
5480 | if (!isGlobal) { |
5481 | if (maybeConstant) { |
5482 | // Check store buffer bitmap directly for known object. |
5483 | EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs, |
5484 | &exit, &callVM); |
5485 | } else { |
5486 | // Check one element cache to avoid VM call. |
5487 | masm.branchPtr(Assembler::Equal, |
5488 | AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()), |
5489 | objreg, &exit); |
5490 | } |
5491 | } |
5492 | |
5493 | // Call into the VM to barrier the write. |
5494 | masm.bind(&callVM); |
5495 | |
5496 | Register runtimereg = temp; |
5497 | masm.mov(ImmPtr(runtime), runtimereg); |
5498 | |
5499 | masm.setupAlignedABICall(); |
5500 | masm.passABIArg(runtimereg); |
5501 | masm.passABIArg(objreg); |
5502 | if (isGlobal) { |
5503 | using Fn = void (*)(JSRuntime* rt, GlobalObject* obj); |
5504 | masm.callWithABI<Fn, PostGlobalWriteBarrier>(); |
5505 | } else { |
5506 | using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj); |
5507 | masm.callWithABI<Fn, PostWriteBarrier>(); |
5508 | } |
5509 | |
5510 | masm.bind(&exit); |
5511 | } |
5512 | |
5513 | void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) { |
5514 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
5515 | |
5516 | Register objreg; |
5517 | JSObject* object = nullptr; |
5518 | bool isGlobal = false; |
5519 | if (obj->isConstant()) { |
5520 | object = &obj->toConstant()->toObject(); |
5521 | isGlobal = isGlobalObject(object); |
5522 | objreg = regs.takeAny(); |
5523 | masm.movePtr(ImmGCPtr(object), objreg); |
5524 | } else { |
5525 | objreg = ToRegister(obj); |
5526 | regs.takeUnchecked(objreg); |
5527 | } |
5528 | |
5529 | EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs); |
5530 | } |
5531 | |
5532 | // Returns true if `def` might be allocated in the nursery. |
5533 | static bool ValueNeedsPostBarrier(MDefinition* def) { |
5534 | if (def->isBox()) { |
5535 | def = def->toBox()->input(); |
5536 | } |
5537 | if (def->type() == MIRType::Value) { |
5538 | return true; |
5539 | } |
5540 | return NeedsPostBarrier(def->type()); |
5541 | } |
5542 | |
5543 | class OutOfLineElementPostWriteBarrier |
5544 | : public OutOfLineCodeBase<CodeGenerator> { |
5545 | LiveRegisterSet liveVolatileRegs_; |
5546 | const LAllocation* index_; |
5547 | int32_t indexDiff_; |
5548 | Register obj_; |
5549 | Register scratch_; |
5550 | |
5551 | public: |
5552 | OutOfLineElementPostWriteBarrier(const LiveRegisterSet& liveVolatileRegs, |
5553 | Register obj, const LAllocation* index, |
5554 | Register scratch, int32_t indexDiff) |
5555 | : liveVolatileRegs_(liveVolatileRegs), |
5556 | index_(index), |
5557 | indexDiff_(indexDiff), |
5558 | obj_(obj), |
5559 | scratch_(scratch) {} |
5560 | |
5561 | void accept(CodeGenerator* codegen) override { |
5562 | codegen->visitOutOfLineElementPostWriteBarrier(this); |
5563 | } |
5564 | |
5565 | const LiveRegisterSet& liveVolatileRegs() const { return liveVolatileRegs_; } |
5566 | const LAllocation* index() const { return index_; } |
5567 | int32_t indexDiff() const { return indexDiff_; } |
5568 | |
5569 | Register object() const { return obj_; } |
5570 | Register scratch() const { return scratch_; } |
5571 | }; |
5572 | |
5573 | void CodeGenerator::emitElementPostWriteBarrier( |
5574 | MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj, |
5575 | const LAllocation* index, Register scratch, const ConstantOrRegister& val, |
5576 | int32_t indexDiff) { |
5577 | if (val.constant()) { |
5578 | MOZ_ASSERT_IF(val.value().isGCThing(),do { if (val.value().isGCThing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!IsInsideNursery (val.value().toGCThing()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(val.value() .toGCThing())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!IsInsideNursery(val.value().toGCThing())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())" ")"); do { *((volatile int*)__null) = 5579; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
5579 | !IsInsideNursery(val.value().toGCThing()))do { if (val.value().isGCThing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!IsInsideNursery (val.value().toGCThing()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(val.value() .toGCThing())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!IsInsideNursery(val.value().toGCThing())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())" ")"); do { *((volatile int*)__null) = 5579; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
5580 | return; |
5581 | } |
5582 | |
5583 | TypedOrValueRegister reg = val.reg(); |
5584 | if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) { |
5585 | return; |
5586 | } |
5587 | |
5588 | auto* ool = new (alloc()) OutOfLineElementPostWriteBarrier( |
5589 | liveVolatileRegs, obj, index, scratch, indexDiff); |
5590 | addOutOfLineCode(ool, mir); |
5591 | |
5592 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, ool->rejoin()); |
5593 | |
5594 | if (reg.hasValue()) { |
5595 | masm.branchValueIsNurseryCell(Assembler::Equal, reg.valueReg(), scratch, |
5596 | ool->entry()); |
5597 | } else { |
5598 | masm.branchPtrInNurseryChunk(Assembler::Equal, reg.typedReg().gpr(), |
5599 | scratch, ool->entry()); |
5600 | } |
5601 | |
5602 | masm.bind(ool->rejoin()); |
5603 | } |
5604 | |
5605 | void CodeGenerator::visitOutOfLineElementPostWriteBarrier( |
5606 | OutOfLineElementPostWriteBarrier* ool) { |
5607 | Register obj = ool->object(); |
5608 | Register scratch = ool->scratch(); |
5609 | const LAllocation* index = ool->index(); |
5610 | int32_t indexDiff = ool->indexDiff(); |
5611 | |
5612 | masm.PushRegsInMask(ool->liveVolatileRegs()); |
5613 | |
5614 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
5615 | regs.takeUnchecked(obj); |
5616 | regs.takeUnchecked(scratch); |
5617 | |
5618 | Register indexReg; |
5619 | if (index->isConstant()) { |
5620 | indexReg = regs.takeAny(); |
5621 | masm.move32(Imm32(ToInt32(index) + indexDiff), indexReg); |
5622 | } else { |
5623 | indexReg = ToRegister(index); |
5624 | regs.takeUnchecked(indexReg); |
5625 | if (indexDiff != 0) { |
5626 | masm.add32(Imm32(indexDiff), indexReg); |
5627 | } |
5628 | } |
5629 | |
5630 | masm.setupUnalignedABICall(scratch); |
5631 | masm.movePtr(ImmPtr(gen->runtime), scratch); |
5632 | masm.passABIArg(scratch); |
5633 | masm.passABIArg(obj); |
5634 | masm.passABIArg(indexReg); |
5635 | using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index); |
5636 | masm.callWithABI<Fn, PostWriteElementBarrier>(); |
5637 | |
5638 | // We don't need a sub32 here because indexReg must be in liveVolatileRegs |
5639 | // if indexDiff is not zero, so it will be restored below. |
5640 | MOZ_ASSERT_IF(indexDiff != 0, ool->liveVolatileRegs().has(indexReg))do { if (indexDiff != 0) { do { static_assert( mozilla::detail ::AssertionConditionType<decltype(ool->liveVolatileRegs ().has(indexReg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ool->liveVolatileRegs().has (indexReg)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ool->liveVolatileRegs().has(indexReg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5640); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ool->liveVolatileRegs().has(indexReg)" ")"); do { *((volatile int*)__null) = 5640; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
5641 | |
5642 | masm.PopRegsInMask(ool->liveVolatileRegs()); |
5643 | |
5644 | masm.jump(ool->rejoin()); |
5645 | } |
5646 | |
5647 | void CodeGenerator::emitPostWriteBarrier(Register objreg) { |
5648 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
5649 | regs.takeUnchecked(objreg); |
5650 | EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs); |
5651 | } |
5652 | |
5653 | void CodeGenerator::visitOutOfLineCallPostWriteBarrier( |
5654 | OutOfLineCallPostWriteBarrier* ool) { |
5655 | saveLiveVolatile(ool->lir()); |
5656 | const LAllocation* obj = ool->object(); |
5657 | emitPostWriteBarrier(obj); |
5658 | restoreLiveVolatile(ool->lir()); |
5659 | |
5660 | masm.jump(ool->rejoin()); |
5661 | } |
5662 | |
5663 | void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal, |
5664 | OutOfLineCode* ool) { |
5665 | // Check whether an object is a global that we have already barriered before |
5666 | // calling into the VM. |
5667 | // |
5668 | // We only check for the script's global, not other globals within the same |
5669 | // compartment, because we bake in a pointer to realm->globalWriteBarriered |
5670 | // and doing that would be invalid for other realms because they could be |
5671 | // collected before the Ion code is discarded. |
5672 | |
5673 | if (!maybeGlobal->isConstant()) { |
5674 | return; |
5675 | } |
5676 | |
5677 | JSObject* obj = &maybeGlobal->toConstant()->toObject(); |
5678 | if (gen->realm->maybeGlobal() != obj) { |
5679 | return; |
5680 | } |
5681 | |
5682 | const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered(); |
5683 | masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0), |
5684 | ool->rejoin()); |
5685 | } |
5686 | |
5687 | template <class LPostBarrierType, MIRType nurseryType> |
5688 | void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir, |
5689 | OutOfLineCode* ool) { |
5690 | static_assert(NeedsPostBarrier(nurseryType)); |
5691 | |
5692 | addOutOfLineCode(ool, lir->mir()); |
5693 | |
5694 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
5695 | |
5696 | if (lir->object()->isConstant()) { |
5697 | // Constant nursery objects cannot appear here, see |
5698 | // LIRGenerator::visitPostWriteElementBarrier. |
5699 | MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsInsideNursery(&lir->object()->toConstant ()->toObject()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&lir-> object()->toConstant()->toObject())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsInsideNursery(&lir->object()->toConstant()->toObject())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5699); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())" ")"); do { *((volatile int*)__null) = 5699; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5700 | } else { |
5701 | masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()), |
5702 | temp, ool->rejoin()); |
5703 | } |
5704 | |
5705 | maybeEmitGlobalBarrierCheck(lir->object(), ool); |
5706 | |
5707 | Register value = ToRegister(lir->value()); |
5708 | if constexpr (nurseryType == MIRType::Object) { |
5709 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5709); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 5709; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5710 | } else if constexpr (nurseryType == MIRType::String) { |
5711 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::String )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 5711; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5712 | } else { |
5713 | static_assert(nurseryType == MIRType::BigInt); |
5714 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::BigInt )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::BigInt ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::BigInt", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5714); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 5714; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5715 | } |
5716 | masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry()); |
5717 | |
5718 | masm.bind(ool->rejoin()); |
5719 | } |
5720 | |
5721 | template <class LPostBarrierType> |
5722 | void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir, |
5723 | OutOfLineCode* ool) { |
5724 | addOutOfLineCode(ool, lir->mir()); |
5725 | |
5726 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
5727 | |
5728 | if (lir->object()->isConstant()) { |
5729 | // Constant nursery objects cannot appear here, see |
5730 | // LIRGenerator::visitPostWriteElementBarrier. |
5731 | MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsInsideNursery(&lir->object()->toConstant ()->toObject()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&lir-> object()->toConstant()->toObject())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsInsideNursery(&lir->object()->toConstant()->toObject())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5731); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())" ")"); do { *((volatile int*)__null) = 5731; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5732 | } else { |
5733 | masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()), |
5734 | temp, ool->rejoin()); |
5735 | } |
5736 | |
5737 | maybeEmitGlobalBarrierCheck(lir->object(), ool); |
5738 | |
5739 | ValueOperand value = ToValue(lir, LPostBarrierType::ValueIndex); |
5740 | masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry()); |
5741 | |
5742 | masm.bind(ool->rejoin()); |
5743 | } |
5744 | |
5745 | void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) { |
5746 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
5747 | visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool); |
5748 | } |
5749 | |
5750 | void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) { |
5751 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
5752 | visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool); |
5753 | } |
5754 | |
5755 | void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) { |
5756 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
5757 | visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool); |
5758 | } |
5759 | |
5760 | void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) { |
5761 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
5762 | visitPostWriteBarrierCommonV(lir, ool); |
5763 | } |
5764 | |
5765 | // Out-of-line path to update the store buffer. |
5766 | class OutOfLineCallPostWriteElementBarrier |
5767 | : public OutOfLineCodeBase<CodeGenerator> { |
5768 | LInstruction* lir_; |
5769 | const LAllocation* object_; |
5770 | const LAllocation* index_; |
5771 | |
5772 | public: |
5773 | OutOfLineCallPostWriteElementBarrier(LInstruction* lir, |
5774 | const LAllocation* object, |
5775 | const LAllocation* index) |
5776 | : lir_(lir), object_(object), index_(index) {} |
5777 | |
5778 | void accept(CodeGenerator* codegen) override { |
5779 | codegen->visitOutOfLineCallPostWriteElementBarrier(this); |
5780 | } |
5781 | |
5782 | LInstruction* lir() const { return lir_; } |
5783 | |
5784 | const LAllocation* object() const { return object_; } |
5785 | |
5786 | const LAllocation* index() const { return index_; } |
5787 | }; |
5788 | |
5789 | void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier( |
5790 | OutOfLineCallPostWriteElementBarrier* ool) { |
5791 | saveLiveVolatile(ool->lir()); |
5792 | |
5793 | const LAllocation* obj = ool->object(); |
5794 | const LAllocation* index = ool->index(); |
5795 | |
5796 | Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj); |
5797 | Register indexreg = ToRegister(index); |
5798 | |
5799 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
5800 | regs.takeUnchecked(indexreg); |
5801 | |
5802 | if (obj->isConstant()) { |
5803 | objreg = regs.takeAny(); |
5804 | masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg); |
5805 | } else { |
5806 | regs.takeUnchecked(objreg); |
5807 | } |
5808 | |
5809 | Register runtimereg = regs.takeAny(); |
5810 | using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index); |
5811 | masm.setupAlignedABICall(); |
5812 | masm.mov(ImmPtr(gen->runtime), runtimereg); |
5813 | masm.passABIArg(runtimereg); |
5814 | masm.passABIArg(objreg); |
5815 | masm.passABIArg(indexreg); |
5816 | masm.callWithABI<Fn, PostWriteElementBarrier>(); |
5817 | |
5818 | restoreLiveVolatile(ool->lir()); |
5819 | |
5820 | masm.jump(ool->rejoin()); |
5821 | } |
5822 | |
5823 | void CodeGenerator::visitPostWriteElementBarrierO( |
5824 | LPostWriteElementBarrierO* lir) { |
5825 | auto ool = new (alloc()) |
5826 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
5827 | visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir, |
5828 | ool); |
5829 | } |
5830 | |
5831 | void CodeGenerator::visitPostWriteElementBarrierS( |
5832 | LPostWriteElementBarrierS* lir) { |
5833 | auto ool = new (alloc()) |
5834 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
5835 | visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir, |
5836 | ool); |
5837 | } |
5838 | |
5839 | void CodeGenerator::visitPostWriteElementBarrierBI( |
5840 | LPostWriteElementBarrierBI* lir) { |
5841 | auto ool = new (alloc()) |
5842 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
5843 | visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir, |
5844 | ool); |
5845 | } |
5846 | |
5847 | void CodeGenerator::visitPostWriteElementBarrierV( |
5848 | LPostWriteElementBarrierV* lir) { |
5849 | auto ool = new (alloc()) |
5850 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
5851 | visitPostWriteBarrierCommonV(lir, ool); |
5852 | } |
5853 | |
5854 | void CodeGenerator::visitAssertCanElidePostWriteBarrier( |
5855 | LAssertCanElidePostWriteBarrier* lir) { |
5856 | Register object = ToRegister(lir->object()); |
5857 | ValueOperand value = |
5858 | ToValue(lir, LAssertCanElidePostWriteBarrier::ValueIndex); |
5859 | Register temp = ToRegister(lir->temp0()); |
5860 | |
5861 | Label ok; |
5862 | masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok); |
5863 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok); |
5864 | |
5865 | masm.assumeUnreachable("Unexpected missing post write barrier"); |
5866 | |
5867 | masm.bind(&ok); |
5868 | } |
5869 | |
5870 | template <typename LCallIns> |
5871 | void CodeGenerator::emitCallNative(LCallIns* call, JSNative native, |
5872 | Register argContextReg, Register argUintNReg, |
5873 | Register argVpReg, Register tempReg, |
5874 | uint32_t unusedStack) { |
5875 | masm.checkStackAlignment(); |
5876 | |
5877 | // Native functions have the signature: |
5878 | // bool (*)(JSContext*, unsigned, Value* vp) |
5879 | // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward |
5880 | // are the function arguments. |
5881 | |
5882 | // Allocate space for the outparam, moving the StackPointer to what will be |
5883 | // &vp[1]. |
5884 | masm.adjustStack(unusedStack); |
5885 | |
5886 | // Push a Value containing the callee object: natives are allowed to access |
5887 | // their callee before setting the return value. The StackPointer is moved |
5888 | // to &vp[0]. |
5889 | // |
5890 | // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|. |
5891 | if constexpr (std::is_same_v<LCallIns, LCallClassHook>) { |
5892 | Register calleeReg = ToRegister(call->getCallee()); |
5893 | masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg))); |
5894 | |
5895 | // Enter the callee realm. |
5896 | if (call->mir()->maybeCrossRealm()) { |
5897 | masm.switchToObjectRealm(calleeReg, tempReg); |
5898 | } |
5899 | } else { |
5900 | WrappedFunction* target = call->mir()->getSingleTarget(); |
5901 | masm.Push(ObjectValue(*target->rawNativeJSFunction())); |
5902 | |
5903 | // Enter the callee realm. |
5904 | if (call->mir()->maybeCrossRealm()) { |
5905 | masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg); |
5906 | masm.switchToObjectRealm(tempReg, tempReg); |
5907 | } |
5908 | } |
5909 | |
5910 | // Preload arguments into registers. |
5911 | masm.loadJSContext(argContextReg); |
5912 | masm.moveStackPtrTo(argVpReg); |
5913 | |
5914 | // Initialize |NativeExitFrameLayout::argc_|. |
5915 | masm.Push(argUintNReg); |
5916 | |
5917 | // Construct native exit frame. |
5918 | // |
5919 | // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and |
5920 | // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|. |
5921 | // |
5922 | // The NativeExitFrameLayout is now fully initialized. |
5923 | uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg); |
5924 | masm.enterFakeExitFrameForNative(argContextReg, tempReg, |
5925 | call->mir()->isConstructing()); |
5926 | |
5927 | markSafepointAt(safepointOffset, call); |
5928 | |
5929 | // Construct and execute call. |
5930 | masm.setupAlignedABICall(); |
5931 | masm.passABIArg(argContextReg); |
5932 | masm.passABIArg(argUintNReg); |
5933 | masm.passABIArg(argVpReg); |
5934 | |
5935 | ensureOsiSpace(); |
5936 | // If we're using a simulator build, `native` will already point to the |
5937 | // simulator's call-redirection code for LCallClassHook. Load the address in |
5938 | // a register first so that we don't try to redirect it a second time. |
5939 | bool emittedCall = false; |
5940 | #ifdef JS_SIMULATOR |
5941 | if constexpr (std::is_same_v<LCallIns, LCallClassHook>) { |
5942 | masm.movePtr(ImmPtr(native), tempReg); |
5943 | masm.callWithABI(tempReg); |
5944 | emittedCall = true; |
5945 | } |
5946 | #endif |
5947 | if (!emittedCall) { |
5948 | masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General, |
5949 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
5950 | } |
5951 | |
5952 | // Test for failure. |
5953 | masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); |
5954 | |
5955 | // Exit the callee realm. |
5956 | if (call->mir()->maybeCrossRealm()) { |
5957 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
5958 | } |
5959 | |
5960 | // Load the outparam vp[0] into output register(s). |
5961 | masm.loadValue( |
5962 | Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), |
5963 | JSReturnOperand); |
5964 | |
5965 | // Until C++ code is instrumented against Spectre, prevent speculative |
5966 | // execution from returning any private data. |
5967 | if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() && |
5968 | call->mir()->hasLiveDefUses()) { |
5969 | masm.speculationBarrier(); |
5970 | } |
5971 | |
5972 | #ifdef DEBUG1 |
5973 | // Native constructors are guaranteed to return an Object value. |
5974 | if (call->mir()->isConstructing()) { |
5975 | Label notPrimitive; |
5976 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
5977 | ¬Primitive); |
5978 | masm.assumeUnreachable("native constructors don't return primitives"); |
5979 | masm.bind(¬Primitive); |
5980 | } |
5981 | #endif |
5982 | } |
5983 | |
5984 | template <typename LCallIns> |
5985 | void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) { |
5986 | uint32_t unusedStack = |
5987 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
5988 | |
5989 | // Registers used for callWithABI() argument-passing. |
5990 | const Register argContextReg = ToRegister(call->getArgContextReg()); |
5991 | const Register argUintNReg = ToRegister(call->getArgUintNReg()); |
5992 | const Register argVpReg = ToRegister(call->getArgVpReg()); |
5993 | |
5994 | // Misc. temporary registers. |
5995 | const Register tempReg = ToRegister(call->getTempReg()); |
5996 | |
5997 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
5998 | |
5999 | // Initialize the argc register. |
6000 | masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg); |
6001 | |
6002 | // Create the exit frame and call the native. |
6003 | emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg, |
6004 | unusedStack); |
6005 | |
6006 | // The next instruction is removing the footer of the exit frame, so there |
6007 | // is no need for leaveFakeExitFrame. |
6008 | |
6009 | // Move the StackPointer back to its original location, unwinding the native |
6010 | // exit frame. |
6011 | masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack); |
6012 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6012); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 6012; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6013 | } |
6014 | |
6015 | void CodeGenerator::visitCallNative(LCallNative* call) { |
6016 | WrappedFunction* target = call->getSingleTarget(); |
6017 | MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6017); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")" ); do { *((volatile int*)__null) = 6017; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6018 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6018); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 6018; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6019 | |
6020 | JSNative native = target->native(); |
6021 | if (call->ignoresReturnValue() && target->hasJitInfo()) { |
6022 | const JSJitInfo* jitInfo = target->jitInfo(); |
6023 | if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) { |
6024 | native = jitInfo->ignoresReturnValueMethod; |
6025 | } |
6026 | } |
6027 | emitCallNative(call, native); |
6028 | } |
6029 | |
6030 | void CodeGenerator::visitCallClassHook(LCallClassHook* call) { |
6031 | emitCallNative(call, call->mir()->target()); |
6032 | } |
6033 | |
6034 | static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv, |
6035 | DOMObjectKind kind) { |
6036 | // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This |
6037 | // will be in the first slot but may be fixed or non-fixed. |
6038 | MOZ_ASSERT(obj != priv)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj != priv)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj != priv))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj != priv", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6038); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv" ")"); do { *((volatile int*)__null) = 6038; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6039 | |
6040 | switch (kind) { |
6041 | case DOMObjectKind::Native: |
6042 | // If it's a native object, the value must be in a fixed slot. |
6043 | // See CanAttachDOMCall in CacheIR.cpp. |
6044 | masm.debugAssertObjHasFixedSlots(obj, priv); |
6045 | masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv); |
6046 | break; |
6047 | case DOMObjectKind::Proxy: { |
6048 | #ifdef DEBUG1 |
6049 | // Sanity check: it must be a DOM proxy. |
6050 | Label isDOMProxy; |
6051 | masm.branchTestProxyHandlerFamily( |
6052 | Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy); |
6053 | masm.assumeUnreachable("Expected a DOM proxy"); |
6054 | masm.bind(&isDOMProxy); |
6055 | #endif |
6056 | masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), priv); |
6057 | masm.loadPrivate( |
6058 | Address(priv, js::detail::ProxyReservedSlots::offsetOfSlot(0)), priv); |
6059 | break; |
6060 | } |
6061 | } |
6062 | } |
6063 | |
6064 | void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) { |
6065 | WrappedFunction* target = call->getSingleTarget(); |
6066 | MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6066); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")" ); do { *((volatile int*)__null) = 6066; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6067 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6067); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 6067; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6068 | MOZ_ASSERT(target->hasJitInfo())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->hasJitInfo())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->hasJitInfo()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("target->hasJitInfo()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6068); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitInfo()" ")"); do { *((volatile int*)__null) = 6068; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6069 | MOZ_ASSERT(call->mir()->isCallDOMNative())do { static_assert( mozilla::detail::AssertionConditionType< decltype(call->mir()->isCallDOMNative())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(call->mir()->isCallDOMNative ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("call->mir()->isCallDOMNative()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()" ")"); do { *((volatile int*)__null) = 6069; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6070 | |
6071 | int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
6072 | |
6073 | // Registers used for callWithABI() argument-passing. |
6074 | const Register argJSContext = ToRegister(call->getArgJSContext()); |
6075 | const Register argObj = ToRegister(call->getArgObj()); |
6076 | const Register argPrivate = ToRegister(call->getArgPrivate()); |
6077 | const Register argArgs = ToRegister(call->getArgArgs()); |
6078 | |
6079 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
6080 | |
6081 | masm.checkStackAlignment(); |
6082 | |
6083 | // DOM methods have the signature: |
6084 | // bool (*)(JSContext*, HandleObject, void* private, const |
6085 | // JSJitMethodCallArgs& args) |
6086 | // Where args is initialized from an argc and a vp, vp[0] is space for an |
6087 | // outparam and the callee, vp[1] is |this|, and vp[2] onward are the |
6088 | // function arguments. Note that args stores the argv, not the vp, and |
6089 | // argv == vp + 2. |
6090 | |
6091 | // Nestle the stack up against the pushed arguments, leaving StackPointer at |
6092 | // &vp[1] |
6093 | masm.adjustStack(unusedStack); |
6094 | // argObj is filled with the extracted object, then returned. |
6095 | Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj); |
6096 | MOZ_ASSERT(obj == argObj)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj == argObj)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj == argObj))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj == argObj", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj == argObj" ")"); do { *((volatile int*)__null) = 6096; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6097 | |
6098 | // Push a Value containing the callee object: natives are allowed to access |
6099 | // their callee before setting the return value. After this the StackPointer |
6100 | // points to &vp[0]. |
6101 | masm.Push(ObjectValue(*target->rawNativeJSFunction())); |
6102 | |
6103 | // Now compute the argv value. Since StackPointer is pointing to &vp[0] and |
6104 | // argv is &vp[2] we just need to add 2*sizeof(Value) to the current |
6105 | // StackPointer. |
6106 | static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0); |
6107 | static_assert(JSJitMethodCallArgsTraits::offsetOfArgc == |
6108 | IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv); |
6109 | masm.computeEffectiveAddress( |
6110 | Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs); |
6111 | |
6112 | LoadDOMPrivate(masm, obj, argPrivate, |
6113 | static_cast<MCallDOMNative*>(call->mir())->objectKind()); |
6114 | |
6115 | // Push argc from the call instruction into what will become the IonExitFrame |
6116 | masm.Push(Imm32(call->numActualArgs())); |
6117 | |
6118 | // Push our argv onto the stack |
6119 | masm.Push(argArgs); |
6120 | // And store our JSJitMethodCallArgs* in argArgs. |
6121 | masm.moveStackPtrTo(argArgs); |
6122 | |
6123 | // Push |this| object for passing HandleObject. We push after argc to |
6124 | // maintain the same sp-relative location of the object pointer with other |
6125 | // DOMExitFrames. |
6126 | masm.Push(argObj); |
6127 | masm.moveStackPtrTo(argObj); |
6128 | |
6129 | if (call->mir()->maybeCrossRealm()) { |
6130 | // We use argJSContext as scratch register here. |
6131 | masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext); |
6132 | masm.switchToObjectRealm(argJSContext, argJSContext); |
6133 | } |
6134 | |
6135 | bool preTenureWrapperAllocation = |
6136 | call->mir()->to<MCallDOMNative>()->initialHeap() == gc::Heap::Tenured; |
6137 | if (preTenureWrapperAllocation) { |
6138 | auto ptr = ImmPtr(mirGen().realm->zone()->tenuringAllocSite()); |
6139 | masm.storeLocalAllocSite(ptr, argJSContext); |
6140 | } |
6141 | |
6142 | // Construct native exit frame. |
6143 | uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext); |
6144 | |
6145 | masm.loadJSContext(argJSContext); |
6146 | masm.enterFakeExitFrame(argJSContext, argJSContext, |
6147 | ExitFrameType::IonDOMMethod); |
6148 | |
6149 | markSafepointAt(safepointOffset, call); |
6150 | |
6151 | // Construct and execute call. |
6152 | masm.setupAlignedABICall(); |
6153 | masm.loadJSContext(argJSContext); |
6154 | masm.passABIArg(argJSContext); |
6155 | masm.passABIArg(argObj); |
6156 | masm.passABIArg(argPrivate); |
6157 | masm.passABIArg(argArgs); |
6158 | ensureOsiSpace(); |
6159 | masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method), |
6160 | ABIType::General, |
6161 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
6162 | |
6163 | if (target->jitInfo()->isInfallible) { |
6164 | masm.loadValue(Address(masm.getStackPointer(), |
6165 | IonDOMMethodExitFrameLayout::offsetOfResult()), |
6166 | JSReturnOperand); |
6167 | } else { |
6168 | // Test for failure. |
6169 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
6170 | |
6171 | // Load the outparam vp[0] into output register(s). |
6172 | masm.loadValue(Address(masm.getStackPointer(), |
6173 | IonDOMMethodExitFrameLayout::offsetOfResult()), |
6174 | JSReturnOperand); |
6175 | } |
6176 | |
6177 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
6178 | "Clobbering ReturnReg should not affect the return value"); |
6179 | |
6180 | // Switch back to the current realm if needed. Note: if the DOM method threw |
6181 | // an exception, the exception handler will do this. |
6182 | if (call->mir()->maybeCrossRealm()) { |
6183 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
6184 | } |
6185 | |
6186 | // Wipe out the preTenuring bit from the local alloc site |
6187 | // On exception we handle this in C++ |
6188 | if (preTenureWrapperAllocation) { |
6189 | masm.storeLocalAllocSite(ImmPtr(nullptr), ReturnReg); |
6190 | } |
6191 | |
6192 | // Until C++ code is instrumented against Spectre, prevent speculative |
6193 | // execution from returning any private data. |
6194 | if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) { |
6195 | masm.speculationBarrier(); |
6196 | } |
6197 | |
6198 | // The next instruction is removing the footer of the exit frame, so there |
6199 | // is no need for leaveFakeExitFrame. |
6200 | |
6201 | // Move the StackPointer back to its original location, unwinding the native |
6202 | // exit frame. |
6203 | masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack); |
6204 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6204); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 6204; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6205 | } |
6206 | |
6207 | void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) { |
6208 | pushArg(ImmGCPtr(lir->mir()->name())); |
6209 | |
6210 | using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue); |
6211 | callVM<Fn, GetIntrinsicValue>(lir); |
6212 | } |
6213 | |
6214 | void CodeGenerator::emitCallInvokeFunction( |
6215 | LInstruction* call, Register calleereg, bool constructing, |
6216 | bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) { |
6217 | // Nestle %esp up to the argument vector. |
6218 | // Each path must account for framePushed_ separately, for callVM to be valid. |
6219 | masm.freeStack(unusedStack); |
6220 | |
6221 | pushArg(masm.getStackPointer()); // argv. |
6222 | pushArg(Imm32(argc)); // argc. |
6223 | pushArg(Imm32(ignoresReturnValue)); |
6224 | pushArg(Imm32(constructing)); // constructing. |
6225 | pushArg(calleereg); // JSFunction*. |
6226 | |
6227 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
6228 | MutableHandleValue); |
6229 | callVM<Fn, jit::InvokeFunction>(call); |
6230 | |
6231 | // Un-nestle %esp from the argument vector. No prefix was pushed. |
6232 | masm.reserveStack(unusedStack); |
6233 | } |
6234 | |
6235 | void CodeGenerator::visitCallGeneric(LCallGeneric* call) { |
6236 | // The callee is passed straight through to the trampoline. |
6237 | MOZ_ASSERT(ToRegister(call->getCallee()) == IonGenericCallCalleeReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(call->getCallee()) == IonGenericCallCalleeReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(call->getCallee()) == IonGenericCallCalleeReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(call->getCallee()) == IonGenericCallCalleeReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg" ")"); do { *((volatile int*)__null) = 6237; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6238 | |
6239 | Register argcReg = ToRegister(call->getArgc()); |
6240 | uint32_t unusedStack = |
6241 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
6242 | |
6243 | // Known-target case is handled by LCallKnown. |
6244 | MOZ_ASSERT(!call->hasSingleTarget())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!call->hasSingleTarget())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!call->hasSingleTarget()) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!call->hasSingleTarget()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->hasSingleTarget()" ")"); do { *((volatile int*)__null) = 6244; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6245 | |
6246 | masm.checkStackAlignment(); |
6247 | |
6248 | masm.move32(Imm32(call->numActualArgs()), argcReg); |
6249 | |
6250 | // Nestle the StackPointer up to the argument vector. |
6251 | masm.freeStack(unusedStack); |
6252 | ensureOsiSpace(); |
6253 | |
6254 | auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct |
6255 | : IonGenericCallKind::Call; |
6256 | |
6257 | TrampolinePtr genericCallStub = |
6258 | gen->jitRuntime()->getIonGenericCallStub(kind); |
6259 | uint32_t callOffset = masm.callJit(genericCallStub); |
6260 | markSafepointAt(callOffset, call); |
6261 | |
6262 | if (call->mir()->maybeCrossRealm()) { |
6263 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
6264 | "ReturnReg available as scratch after scripted calls"); |
6265 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
6266 | } |
6267 | |
6268 | // Restore stack pointer. |
6269 | masm.setFramePushed(frameSize()); |
6270 | emitRestoreStackPointerFromFP(); |
6271 | |
6272 | // If the return value of the constructing function is Primitive, |
6273 | // replace the return value with the Object from CreateThis. |
6274 | if (call->mir()->isConstructing()) { |
6275 | Label notPrimitive; |
6276 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
6277 | ¬Primitive); |
6278 | masm.loadValue(Address(masm.getStackPointer(), unusedStack), |
6279 | JSReturnOperand); |
6280 | #ifdef DEBUG1 |
6281 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
6282 | ¬Primitive); |
6283 | masm.assumeUnreachable("CreateThis creates an object"); |
6284 | #endif |
6285 | masm.bind(¬Primitive); |
6286 | } |
6287 | } |
6288 | |
6289 | void JitRuntime::generateIonGenericCallArgumentsShift( |
6290 | MacroAssembler& masm, Register argc, Register curr, Register end, |
6291 | Register scratch, Label* done) { |
6292 | static_assert(sizeof(Value) == 8); |
6293 | // There are |argc| Values on the stack. Shift them all down by 8 bytes, |
6294 | // overwriting the first value. |
6295 | |
6296 | // Initialize `curr` to the destination of the first copy, and `end` to the |
6297 | // final value of curr. |
6298 | masm.moveStackPtrTo(curr); |
6299 | masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end); |
6300 | |
6301 | Label loop; |
6302 | masm.bind(&loop); |
6303 | masm.branchPtr(Assembler::Equal, curr, end, done); |
6304 | masm.loadPtr(Address(curr, 8), scratch); |
6305 | masm.storePtr(scratch, Address(curr, 0)); |
6306 | masm.addPtr(Imm32(sizeof(uintptr_t)), curr); |
6307 | masm.jump(&loop); |
6308 | } |
6309 | |
6310 | void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm, |
6311 | IonGenericCallKind kind) { |
6312 | AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub"); |
6313 | ionGenericCallStubOffset_[kind] = startTrampolineCode(masm); |
6314 | |
6315 | // This code is tightly coupled with visitCallGeneric. |
6316 | // |
6317 | // Upon entry: |
6318 | // IonGenericCallCalleeReg contains a pointer to the callee object. |
6319 | // IonGenericCallArgcReg contains the number of actual args. |
6320 | // The arguments have been pushed onto the stack: |
6321 | // [newTarget] (iff isConstructing) |
6322 | // [argN] |
6323 | // ... |
6324 | // [arg1] |
6325 | // [arg0] |
6326 | // [this] |
6327 | // <return address> (if not JS_USE_LINK_REGISTER) |
6328 | // |
6329 | // This trampoline is responsible for entering the callee's realm, |
6330 | // massaging the stack into the right shape, and then performing a |
6331 | // tail call. We will return directly to the Ion code from the |
6332 | // callee. |
6333 | // |
6334 | // To do a tail call, we keep the return address in a register, even |
6335 | // on platforms that don't normally use a link register, and push it |
6336 | // just before jumping to the callee, after we are done setting up |
6337 | // the stack. |
6338 | // |
6339 | // The caller is responsible for switching back to the caller's |
6340 | // realm and cleaning up the stack. |
6341 | |
6342 | Register calleeReg = IonGenericCallCalleeReg; |
6343 | Register argcReg = IonGenericCallArgcReg; |
6344 | Register scratch = IonGenericCallScratch; |
6345 | Register scratch2 = IonGenericCallScratch2; |
6346 | |
6347 | #ifndef JS_USE_LINK_REGISTER |
6348 | Register returnAddrReg = IonGenericCallReturnAddrReg; |
6349 | masm.pop(returnAddrReg); |
6350 | #endif |
6351 | |
6352 | #ifdef JS_CODEGEN_ARM |
6353 | // The default second scratch register on arm is lr, which we need |
6354 | // preserved for tail calls. |
6355 | AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg); |
6356 | #endif |
6357 | |
6358 | bool isConstructing = kind == IonGenericCallKind::Construct; |
6359 | |
6360 | Label entry, notFunction, noJitEntry, vmCall; |
6361 | masm.bind(&entry); |
6362 | |
6363 | // Guard that the callee is actually a function. |
6364 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch, |
6365 | calleeReg, ¬Function); |
6366 | |
6367 | // Guard that the callee supports the [[Call]] or [[Construct]] operation. |
6368 | // If these tests fail, we will call into the VM to throw an exception. |
6369 | if (isConstructing) { |
6370 | masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR, |
6371 | Assembler::Zero, &vmCall); |
6372 | } else { |
6373 | masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor, |
6374 | calleeReg, scratch, &vmCall); |
6375 | } |
6376 | |
6377 | if (isConstructing) { |
6378 | // Use the slow path if CreateThis was unable to create the |this| object. |
6379 | Address thisAddr(masm.getStackPointer(), 0); |
6380 | masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall); |
6381 | } |
6382 | |
6383 | masm.switchToObjectRealm(calleeReg, scratch); |
6384 | |
6385 | // Load jitCodeRaw for callee if it exists. |
6386 | masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry); |
6387 | |
6388 | // **************************** |
6389 | // * Functions with jit entry * |
6390 | // **************************** |
6391 | masm.loadJitCodeRaw(calleeReg, scratch2); |
6392 | |
6393 | // Construct the JitFrameLayout. |
6394 | masm.PushCalleeToken(calleeReg, isConstructing); |
6395 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch); |
6396 | #ifndef JS_USE_LINK_REGISTER |
6397 | masm.push(returnAddrReg); |
6398 | #endif |
6399 | |
6400 | // Check whether we need a rectifier frame. |
6401 | Label noRectifier; |
6402 | masm.loadFunctionArgCount(calleeReg, scratch); |
6403 | masm.branch32(Assembler::BelowOrEqual, scratch, argcReg, &noRectifier); |
6404 | { |
6405 | // Tail-call the arguments rectifier. |
6406 | // Because all trampolines are created at the same time, |
6407 | // we can't create a TrampolinePtr for the arguments rectifier, |
6408 | // because it hasn't been linked yet. We can, however, directly |
6409 | // encode its offset. |
6410 | Label rectifier; |
6411 | bindLabelToOffset(&rectifier, argumentsRectifierOffset_); |
6412 | |
6413 | masm.jump(&rectifier); |
6414 | } |
6415 | |
6416 | // Tail call the jit entry. |
6417 | masm.bind(&noRectifier); |
6418 | masm.jump(scratch2); |
6419 | |
6420 | // ******************** |
6421 | // * Native functions * |
6422 | // ******************** |
6423 | masm.bind(&noJitEntry); |
6424 | if (!isConstructing) { |
6425 | generateIonGenericCallFunCall(masm, &entry, &vmCall); |
6426 | } |
6427 | generateIonGenericCallNativeFunction(masm, isConstructing); |
6428 | |
6429 | // ******************* |
6430 | // * Bound functions * |
6431 | // ******************* |
6432 | // TODO: support class hooks? |
6433 | masm.bind(¬Function); |
6434 | if (!isConstructing) { |
6435 | // TODO: support generic bound constructors? |
6436 | generateIonGenericCallBoundFunction(masm, &entry, &vmCall); |
6437 | } |
6438 | |
6439 | // ******************** |
6440 | // * Fallback VM call * |
6441 | // ******************** |
6442 | masm.bind(&vmCall); |
6443 | |
6444 | masm.push(masm.getStackPointer()); // argv |
6445 | masm.push(argcReg); // argc |
6446 | masm.push(Imm32(false)); // ignores return value |
6447 | masm.push(Imm32(isConstructing)); // constructing |
6448 | masm.push(calleeReg); // callee |
6449 | |
6450 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
6451 | MutableHandleValue); |
6452 | VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id; |
6453 | uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)]; |
6454 | Label invokeFunctionVMEntry; |
6455 | bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset); |
6456 | |
6457 | masm.pushFrameDescriptor(FrameType::IonJS); |
6458 | #ifndef JS_USE_LINK_REGISTER |
6459 | masm.push(returnAddrReg); |
6460 | #endif |
6461 | masm.jump(&invokeFunctionVMEntry); |
6462 | } |
6463 | |
6464 | void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm, |
6465 | bool isConstructing) { |
6466 | Register calleeReg = IonGenericCallCalleeReg; |
6467 | Register argcReg = IonGenericCallArgcReg; |
6468 | Register scratch = IonGenericCallScratch; |
6469 | Register scratch2 = IonGenericCallScratch2; |
6470 | Register contextReg = IonGenericCallScratch3; |
6471 | #ifndef JS_USE_LINK_REGISTER |
6472 | Register returnAddrReg = IonGenericCallReturnAddrReg; |
6473 | #endif |
6474 | |
6475 | // Push a value containing the callee, which will become argv[0]. |
6476 | masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg); |
6477 | |
6478 | // Load the callee address into calleeReg. |
6479 | #ifdef JS_SIMULATOR |
6480 | masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg); |
6481 | #else |
6482 | masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()), |
6483 | calleeReg); |
6484 | #endif |
6485 | |
6486 | // Load argv into scratch2. |
6487 | masm.moveStackPtrTo(scratch2); |
6488 | |
6489 | // Push argc. |
6490 | masm.push(argcReg); |
6491 | |
6492 | masm.loadJSContext(contextReg); |
6493 | |
6494 | // Construct native exit frame. Note that unlike other cases in this |
6495 | // trampoline, this code does not use a tail call. |
6496 | masm.pushFrameDescriptor(FrameType::IonJS); |
6497 | #ifdef JS_USE_LINK_REGISTER |
6498 | masm.pushReturnAddress(); |
6499 | #else |
6500 | masm.push(returnAddrReg); |
6501 | #endif |
6502 | |
6503 | masm.push(FramePointer); |
6504 | masm.moveStackPtrTo(FramePointer); |
6505 | masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing); |
6506 | |
6507 | masm.setupUnalignedABICall(scratch); |
6508 | masm.passABIArg(contextReg); // cx |
6509 | masm.passABIArg(argcReg); // argc |
6510 | masm.passABIArg(scratch2); // argv |
6511 | |
6512 | masm.callWithABI(calleeReg); |
6513 | |
6514 | // Test for failure. |
6515 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
6516 | |
6517 | masm.loadValue( |
6518 | Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), |
6519 | JSReturnOperand); |
6520 | |
6521 | // Leave the exit frame. |
6522 | masm.moveToStackPtr(FramePointer); |
6523 | masm.pop(FramePointer); |
6524 | |
6525 | // Return. |
6526 | masm.ret(); |
6527 | } |
6528 | |
6529 | void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm, |
6530 | Label* entry, Label* vmCall) { |
6531 | Register calleeReg = IonGenericCallCalleeReg; |
6532 | Register argcReg = IonGenericCallArgcReg; |
6533 | Register scratch = IonGenericCallScratch; |
6534 | Register scratch2 = IonGenericCallScratch2; |
6535 | Register scratch3 = IonGenericCallScratch3; |
6536 | |
6537 | Label notFunCall; |
6538 | masm.branchPtr(Assembler::NotEqual, |
6539 | Address(calleeReg, JSFunction::offsetOfNativeOrEnv()), |
6540 | ImmPtr(js::fun_call), ¬FunCall); |
6541 | |
6542 | // In general, we can implement fun_call by replacing calleeReg with |
6543 | // |this|, sliding all the other arguments down, and decrementing argc. |
6544 | // |
6545 | // *BEFORE* *AFTER* |
6546 | // [argN] argc = N+1 <padding> |
6547 | // ... [argN] argc = N |
6548 | // [arg1] ... |
6549 | // [arg0] [arg1] <- now arg0 |
6550 | // [this] <- top of stack (aligned) [arg0] <- now this |
6551 | // |
6552 | // The only exception is when argc is already 0, in which case instead |
6553 | // of shifting arguments down we replace [this] with UndefinedValue(): |
6554 | // |
6555 | // *BEFORE* *AFTER* |
6556 | // [this] argc = 0 [undef] argc = 0 |
6557 | // |
6558 | // After making this transformation, we can jump back to the beginning |
6559 | // of this trampoline to handle the inner call. |
6560 | |
6561 | // Guard that |this| is an object. If it is, replace calleeReg. |
6562 | masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall); |
6563 | masm.movePtr(scratch, calleeReg); |
6564 | |
6565 | Label hasArgs; |
6566 | masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs); |
6567 | |
6568 | // No arguments. Replace |this| with |undefined| and start from the top. |
6569 | masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0)); |
6570 | masm.jump(entry); |
6571 | |
6572 | masm.bind(&hasArgs); |
6573 | |
6574 | Label doneSliding; |
6575 | generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2, |
6576 | scratch3, &doneSliding); |
6577 | masm.bind(&doneSliding); |
6578 | masm.sub32(Imm32(1), argcReg); |
6579 | |
6580 | masm.jump(entry); |
6581 | |
6582 | masm.bind(¬FunCall); |
6583 | } |
6584 | |
6585 | void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm, |
6586 | Label* entry, |
6587 | Label* vmCall) { |
6588 | Register calleeReg = IonGenericCallCalleeReg; |
6589 | Register argcReg = IonGenericCallArgcReg; |
6590 | Register scratch = IonGenericCallScratch; |
6591 | Register scratch2 = IonGenericCallScratch2; |
6592 | Register scratch3 = IonGenericCallScratch3; |
6593 | |
6594 | masm.branchTestObjClass(Assembler::NotEqual, calleeReg, |
6595 | &BoundFunctionObject::class_, scratch, calleeReg, |
6596 | vmCall); |
6597 | |
6598 | Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot()); |
6599 | Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot()); |
6600 | Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot()); |
6601 | Address firstInlineArgSlot( |
6602 | calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg()); |
6603 | |
6604 | // Check that we won't be pushing too many arguments. |
6605 | masm.load32(flagsSlot, scratch); |
6606 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch); |
6607 | masm.add32(argcReg, scratch); |
6608 | masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall); |
6609 | |
6610 | // The stack is currently correctly aligned for a jit call. We will |
6611 | // be updating the `this` value and potentially adding additional |
6612 | // arguments. On platforms with 16-byte alignment, if the number of |
6613 | // bound arguments is odd, we have to move the arguments that are |
6614 | // currently on the stack. For example, with one bound argument: |
6615 | // |
6616 | // *BEFORE* *AFTER* |
6617 | // [argN] <padding> |
6618 | // ... [argN] | |
6619 | // [arg1] ... | These arguments have been |
6620 | // [arg0] [arg1] | shifted down 8 bytes. |
6621 | // [this] <- top of stack (aligned) [arg0] v |
6622 | // [bound0] <- one bound argument (odd) |
6623 | // [boundThis] <- top of stack (aligned) |
6624 | // |
6625 | Label poppedThis; |
6626 | if (JitStackValueAlignment > 1) { |
6627 | Label alreadyAligned; |
6628 | masm.branchTest32(Assembler::Zero, flagsSlot, |
6629 | Imm32(1 << BoundFunctionObject::NumBoundArgsShift), |
6630 | &alreadyAligned); |
6631 | |
6632 | // We have an odd number of bound arguments. Shift the existing arguments |
6633 | // down by 8 bytes. |
6634 | generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2, |
6635 | scratch3, &poppedThis); |
6636 | masm.bind(&alreadyAligned); |
6637 | } |
6638 | |
6639 | // Pop the current `this`. It will be replaced with the bound `this`. |
6640 | masm.freeStack(sizeof(Value)); |
6641 | masm.bind(&poppedThis); |
6642 | |
6643 | // Load the number of bound arguments in scratch |
6644 | masm.load32(flagsSlot, scratch); |
6645 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch); |
6646 | |
6647 | Label donePushingBoundArguments; |
6648 | masm.branch32(Assembler::Equal, scratch, Imm32(0), |
6649 | &donePushingBoundArguments); |
6650 | |
6651 | // Update argc to include bound arguments. |
6652 | masm.add32(scratch, argcReg); |
6653 | |
6654 | // Load &boundArgs[0] in scratch2. |
6655 | Label outOfLineBoundArguments, haveBoundArguments; |
6656 | masm.branch32(Assembler::Above, scratch, |
6657 | Imm32(BoundFunctionObject::MaxInlineBoundArgs), |
6658 | &outOfLineBoundArguments); |
6659 | masm.computeEffectiveAddress(firstInlineArgSlot, scratch2); |
6660 | masm.jump(&haveBoundArguments); |
6661 | |
6662 | masm.bind(&outOfLineBoundArguments); |
6663 | masm.unboxObject(firstInlineArgSlot, scratch2); |
6664 | masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2); |
6665 | |
6666 | masm.bind(&haveBoundArguments); |
6667 | |
6668 | // Load &boundArgs[numBoundArgs] in scratch. |
6669 | BaseObjectElementIndex lastBoundArg(scratch2, scratch); |
6670 | masm.computeEffectiveAddress(lastBoundArg, scratch); |
6671 | |
6672 | // Push the bound arguments, starting with the last one. |
6673 | // Copying pre-decrements scratch until scratch2 is reached. |
6674 | Label boundArgumentsLoop; |
6675 | masm.bind(&boundArgumentsLoop); |
6676 | masm.subPtr(Imm32(sizeof(Value)), scratch); |
6677 | masm.pushValue(Address(scratch, 0)); |
6678 | masm.branchPtr(Assembler::Above, scratch, scratch2, &boundArgumentsLoop); |
6679 | masm.bind(&donePushingBoundArguments); |
6680 | |
6681 | // Push the bound `this`. |
6682 | masm.pushValue(thisSlot); |
6683 | |
6684 | // Load the target in calleeReg. |
6685 | masm.unboxObject(targetSlot, calleeReg); |
6686 | |
6687 | // At this point, all preconditions for entering the trampoline are met: |
6688 | // - calleeReg contains a pointer to the callee object |
6689 | // - argcReg contains the number of actual args (now including bound args) |
6690 | // - the arguments are on the stack with the correct alignment. |
6691 | // Instead of generating more code, we can jump back to the entry point |
6692 | // of the trampoline to call the bound target. |
6693 | masm.jump(entry); |
6694 | } |
6695 | |
6696 | void CodeGenerator::visitCallKnown(LCallKnown* call) { |
6697 | Register calleereg = ToRegister(call->getFunction()); |
6698 | Register objreg = ToRegister(call->getTempObject()); |
6699 | uint32_t unusedStack = |
6700 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
6701 | WrappedFunction* target = call->getSingleTarget(); |
6702 | |
6703 | // Native single targets (except Wasm and TrampolineNative functions) are |
6704 | // handled by LCallNative. |
6705 | MOZ_ASSERT(target->hasJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->hasJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->hasJitEntry()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->hasJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6705); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitEntry()" ")"); do { *((volatile int*)__null) = 6705; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6706 | |
6707 | // Missing arguments must have been explicitly appended by WarpBuilder. |
6708 | DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing(); |
6709 | MOZ_ASSERT(target->nargs() <=do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->nargs() <= call->mir()->numStackArgs () - numNonArgsOnStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->nargs() <= call ->mir()->numStackArgs() - numNonArgsOnStack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" ")"); do { *((volatile int*)__null) = 6710; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
6710 | call->mir()->numStackArgs() - numNonArgsOnStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->nargs() <= call->mir()->numStackArgs () - numNonArgsOnStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->nargs() <= call ->mir()->numStackArgs() - numNonArgsOnStack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" ")"); do { *((volatile int*)__null) = 6710; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6711 | |
6712 | MOZ_ASSERT_IF(call->isConstructing(), target->isConstructor())do { if (call->isConstructing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(target->isConstructor ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(target->isConstructor()))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("target->isConstructor()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6712); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isConstructor()" ")"); do { *((volatile int*)__null) = 6712; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
6713 | |
6714 | masm.checkStackAlignment(); |
6715 | |
6716 | if (target->isClassConstructor() && !call->isConstructing()) { |
6717 | emitCallInvokeFunction(call, calleereg, call->isConstructing(), |
6718 | call->ignoresReturnValue(), call->numActualArgs(), |
6719 | unusedStack); |
6720 | return; |
6721 | } |
6722 | |
6723 | MOZ_ASSERT_IF(target->isClassConstructor(), call->isConstructing())do { if (target->isClassConstructor()) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(call-> isConstructing())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(call->isConstructing()))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("call->isConstructing()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->isConstructing()" ")"); do { *((volatile int*)__null) = 6723; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
6724 | |
6725 | MOZ_ASSERT(!call->mir()->needsThisCheck())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!call->mir()->needsThisCheck())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!call->mir()->needsThisCheck ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!call->mir()->needsThisCheck()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6725); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->mir()->needsThisCheck()" ")"); do { *((volatile int*)__null) = 6725; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6726 | |
6727 | if (call->mir()->maybeCrossRealm()) { |
6728 | masm.switchToObjectRealm(calleereg, objreg); |
6729 | } |
6730 | |
6731 | masm.loadJitCodeRaw(calleereg, objreg); |
6732 | |
6733 | // Nestle the StackPointer up to the argument vector. |
6734 | masm.freeStack(unusedStack); |
6735 | |
6736 | // Construct the JitFrameLayout. |
6737 | masm.PushCalleeToken(calleereg, call->mir()->isConstructing()); |
6738 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, call->numActualArgs()); |
6739 | |
6740 | // Finally call the function in objreg. |
6741 | ensureOsiSpace(); |
6742 | uint32_t callOffset = masm.callJit(objreg); |
6743 | markSafepointAt(callOffset, call); |
6744 | |
6745 | if (call->mir()->maybeCrossRealm()) { |
6746 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
6747 | "ReturnReg available as scratch after scripted calls"); |
6748 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
6749 | } |
6750 | |
6751 | // Restore stack pointer: pop JitFrameLayout fields still left on the stack |
6752 | // and undo the earlier |freeStack(unusedStack)|. |
6753 | int prefixGarbage = |
6754 | sizeof(JitFrameLayout) - JitFrameLayout::bytesPoppedAfterCall(); |
6755 | masm.adjustStack(prefixGarbage - unusedStack); |
6756 | |
6757 | // If the return value of the constructing function is Primitive, |
6758 | // replace the return value with the Object from CreateThis. |
6759 | if (call->mir()->isConstructing()) { |
6760 | Label notPrimitive; |
6761 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
6762 | ¬Primitive); |
6763 | masm.loadValue(Address(masm.getStackPointer(), unusedStack), |
6764 | JSReturnOperand); |
6765 | #ifdef DEBUG1 |
6766 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
6767 | ¬Primitive); |
6768 | masm.assumeUnreachable("CreateThis creates an object"); |
6769 | #endif |
6770 | masm.bind(¬Primitive); |
6771 | } |
6772 | } |
6773 | |
6774 | template <typename T> |
6775 | void CodeGenerator::emitCallInvokeFunction(T* apply) { |
6776 | pushArg(masm.getStackPointer()); // argv. |
6777 | pushArg(ToRegister(apply->getArgc())); // argc. |
6778 | pushArg(Imm32(apply->mir()->ignoresReturnValue())); // ignoresReturnValue. |
6779 | pushArg(Imm32(apply->mir()->isConstructing())); // isConstructing. |
6780 | pushArg(ToRegister(apply->getFunction())); // JSFunction*. |
6781 | |
6782 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
6783 | MutableHandleValue); |
6784 | callVM<Fn, jit::InvokeFunction>(apply); |
6785 | } |
6786 | |
6787 | // Do not bailout after the execution of this function since the stack no longer |
6788 | // correspond to what is expected by the snapshots. |
6789 | void CodeGenerator::emitAllocateSpaceForApply(Register argcreg, |
6790 | Register scratch) { |
6791 | // Use scratch register to calculate stack space (including padding). |
6792 | masm.movePtr(argcreg, scratch); |
6793 | |
6794 | // Align the JitFrameLayout on the JitStackAlignment. |
6795 | if (JitStackValueAlignment > 1) { |
6796 | MOZ_ASSERT(frameSize() % JitStackAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6797; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
6797 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6797; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6798 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6798); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6798; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6799 | Label noPaddingNeeded; |
6800 | // If the number of arguments is odd, then we do not need any padding. |
6801 | // |
6802 | // Note: The |JitStackValueAlignment == 2| condition requires that the |
6803 | // overall number of values on the stack is even. When we have an odd number |
6804 | // of arguments, we don't need any padding, because the |thisValue| is |
6805 | // pushed after the arguments, so the overall number of values on the stack |
6806 | // is even. |
6807 | masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded); |
6808 | masm.addPtr(Imm32(1), scratch); |
6809 | masm.bind(&noPaddingNeeded); |
6810 | } |
6811 | |
6812 | // Reserve space for copying the arguments. |
6813 | NativeObject::elementsSizeMustNotOverflow(); |
6814 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
6815 | masm.subFromStackPtr(scratch); |
6816 | |
6817 | #ifdef DEBUG1 |
6818 | // Put a magic value in the space reserved for padding. Note, this code cannot |
6819 | // be merged with the previous test, as not all architectures can write below |
6820 | // their stack pointers. |
6821 | if (JitStackValueAlignment > 1) { |
6822 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6822; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6823 | Label noPaddingNeeded; |
6824 | // If the number of arguments is odd, then we do not need any padding. |
6825 | masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded); |
6826 | BaseValueIndex dstPtr(masm.getStackPointer(), argcreg); |
6827 | masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr); |
6828 | masm.bind(&noPaddingNeeded); |
6829 | } |
6830 | #endif |
6831 | } |
6832 | |
6833 | // Do not bailout after the execution of this function since the stack no longer |
6834 | // correspond to what is expected by the snapshots. |
6835 | void CodeGenerator::emitAllocateSpaceForConstructAndPushNewTarget( |
6836 | Register argcreg, Register newTargetAndScratch) { |
6837 | // Align the JitFrameLayout on the JitStackAlignment. Contrary to |
6838 | // |emitAllocateSpaceForApply()|, we're always pushing a magic value, because |
6839 | // we can't write to |newTargetAndScratch| before |new.target| has been pushed |
6840 | // onto the stack. |
6841 | if (JitStackValueAlignment > 1) { |
6842 | MOZ_ASSERT(frameSize() % JitStackAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6843); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6843; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
6843 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6843); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6843; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6844 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6844); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6844; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6845 | |
6846 | Label noPaddingNeeded; |
6847 | // If the number of arguments is even, then we do not need any padding. |
6848 | // |
6849 | // Note: The |JitStackValueAlignment == 2| condition requires that the |
6850 | // overall number of values on the stack is even. When we have an even |
6851 | // number of arguments, we don't need any padding, because |new.target| is |
6852 | // is pushed before the arguments and |thisValue| is pushed after all |
6853 | // arguments, so the overall number of values on the stack is even. |
6854 | masm.branchTestPtr(Assembler::Zero, argcreg, Imm32(1), &noPaddingNeeded); |
6855 | masm.pushValue(MagicValue(JS_ARG_POISON)); |
6856 | masm.bind(&noPaddingNeeded); |
6857 | } |
6858 | |
6859 | // Push |new.target| after the padding value, but before any arguments. |
6860 | masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch); |
6861 | |
6862 | // Use newTargetAndScratch to calculate stack space (including padding). |
6863 | masm.movePtr(argcreg, newTargetAndScratch); |
6864 | |
6865 | // Reserve space for copying the arguments. |
6866 | NativeObject::elementsSizeMustNotOverflow(); |
6867 | masm.lshiftPtr(Imm32(ValueShift), newTargetAndScratch); |
6868 | masm.subFromStackPtr(newTargetAndScratch); |
6869 | } |
6870 | |
6871 | // Destroys argvIndex and copyreg. |
6872 | void CodeGenerator::emitCopyValuesForApply(Register argvSrcBase, |
6873 | Register argvIndex, Register copyreg, |
6874 | size_t argvSrcOffset, |
6875 | size_t argvDstOffset) { |
6876 | Label loop; |
6877 | masm.bind(&loop); |
6878 | |
6879 | // As argvIndex is off by 1, and we use the decBranchPtr instruction to loop |
6880 | // back, we have to substract the size of the word which are copied. |
6881 | BaseValueIndex srcPtr(argvSrcBase, argvIndex, |
6882 | int32_t(argvSrcOffset) - sizeof(void*)); |
6883 | BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex, |
6884 | int32_t(argvDstOffset) - sizeof(void*)); |
6885 | masm.loadPtr(srcPtr, copyreg); |
6886 | masm.storePtr(copyreg, dstPtr); |
6887 | |
6888 | // Handle 32 bits architectures. |
6889 | if (sizeof(Value) == 2 * sizeof(void*)) { |
6890 | BaseValueIndex srcPtrLow(argvSrcBase, argvIndex, |
6891 | int32_t(argvSrcOffset) - 2 * sizeof(void*)); |
6892 | BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex, |
6893 | int32_t(argvDstOffset) - 2 * sizeof(void*)); |
6894 | masm.loadPtr(srcPtrLow, copyreg); |
6895 | masm.storePtr(copyreg, dstPtrLow); |
6896 | } |
6897 | |
6898 | masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop); |
6899 | } |
6900 | |
6901 | void CodeGenerator::emitRestoreStackPointerFromFP() { |
6902 | // This is used to restore the stack pointer after a call with a dynamic |
6903 | // number of arguments. |
6904 | |
6905 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6905); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 6905; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6906 | |
6907 | int32_t offset = -int32_t(frameSize()); |
6908 | masm.computeEffectiveAddress(Address(FramePointer, offset), |
6909 | masm.getStackPointer()); |
6910 | #if JS_CODEGEN_ARM64 |
6911 | masm.syncStackPtr(); |
6912 | #endif |
6913 | } |
6914 | |
6915 | void CodeGenerator::emitPushArguments(Register argcreg, Register scratch, |
6916 | Register copyreg, uint32_t extraFormals) { |
6917 | Label end; |
6918 | |
6919 | // Skip the copy of arguments if there are none. |
6920 | masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end); |
6921 | |
6922 | // clang-format off |
6923 | // |
6924 | // We are making a copy of the arguments which are above the JitFrameLayout |
6925 | // of the current Ion frame. |
6926 | // |
6927 | // [arg1] [arg0] <- src [this] [JitFrameLayout] [.. frameSize ..] [pad] [arg1] [arg0] <- dst |
6928 | // |
6929 | // clang-format on |
6930 | |
6931 | // Compute the source and destination offsets into the stack. |
6932 | // |
6933 | // The |extraFormals| parameter is used when copying rest-parameters and |
6934 | // allows to skip the initial parameters before the actual rest-parameters. |
6935 | Register argvSrcBase = FramePointer; |
6936 | size_t argvSrcOffset = |
6937 | JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value); |
6938 | size_t argvDstOffset = 0; |
6939 | |
6940 | Register argvIndex = scratch; |
6941 | masm.move32(argcreg, argvIndex); |
6942 | |
6943 | // Copy arguments. |
6944 | emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset, |
6945 | argvDstOffset); |
6946 | |
6947 | // Join with all arguments copied. |
6948 | masm.bind(&end); |
6949 | } |
6950 | |
6951 | void CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply) { |
6952 | // Holds the function nargs. |
6953 | Register argcreg = ToRegister(apply->getArgc()); |
6954 | Register copyreg = ToRegister(apply->getTempObject()); |
6955 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
6956 | uint32_t extraFormals = apply->numExtraFormals(); |
6957 | |
6958 | // Allocate space on the stack for arguments. |
6959 | emitAllocateSpaceForApply(argcreg, scratch); |
6960 | |
6961 | emitPushArguments(argcreg, scratch, copyreg, extraFormals); |
6962 | |
6963 | // Push |this|. |
6964 | masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex)); |
6965 | } |
6966 | |
6967 | void CodeGenerator::emitPushArguments(LApplyArgsObj* apply) { |
6968 | Register argsObj = ToRegister(apply->getArgsObj()); |
6969 | Register tmpArgc = ToRegister(apply->getTempObject()); |
6970 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
6971 | |
6972 | // argc and argsObj are mapped to the same calltemp register. |
6973 | MOZ_ASSERT(argsObj == ToRegister(apply->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(argsObj == ToRegister(apply->getArgc()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argsObj == ToRegister(apply->getArgc())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argsObj == ToRegister(apply->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6973); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argsObj == ToRegister(apply->getArgc())" ")"); do { *((volatile int*)__null) = 6973; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6974 | |
6975 | // Load argc into tmpArgc. |
6976 | masm.loadArgumentsObjectLength(argsObj, tmpArgc); |
6977 | |
6978 | // Allocate space on the stack for arguments. |
6979 | emitAllocateSpaceForApply(tmpArgc, scratch); |
6980 | |
6981 | // Load arguments data. |
6982 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
6983 | argsObj); |
6984 | size_t argsSrcOffset = ArgumentsData::offsetOfArgs(); |
6985 | |
6986 | // This is the end of the lifetime of argsObj. |
6987 | // After this call, the argsObj register holds the argument count instead. |
6988 | emitPushArrayAsArguments(tmpArgc, argsObj, scratch, argsSrcOffset); |
6989 | |
6990 | // Push |this|. |
6991 | masm.pushValue(ToValue(apply, LApplyArgsObj::ThisIndex)); |
6992 | } |
6993 | |
6994 | void CodeGenerator::emitPushArrayAsArguments(Register tmpArgc, |
6995 | Register srcBaseAndArgc, |
6996 | Register scratch, |
6997 | size_t argvSrcOffset) { |
6998 | // Preconditions: |
6999 | // 1. |tmpArgc| * sizeof(Value) bytes have been allocated at the top of |
7000 | // the stack to hold arguments. |
7001 | // 2. |srcBaseAndArgc| + |srcOffset| points to an array of |tmpArgc| values. |
7002 | // |
7003 | // Postconditions: |
7004 | // 1. The arguments at |srcBaseAndArgc| + |srcOffset| have been copied into |
7005 | // the allocated space. |
7006 | // 2. |srcBaseAndArgc| now contains the original value of |tmpArgc|. |
7007 | // |
7008 | // |scratch| is used as a temp register within this function and clobbered. |
7009 | |
7010 | Label noCopy, epilogue; |
7011 | |
7012 | // Skip the copy of arguments if there are none. |
7013 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
7014 | { |
7015 | // Copy the values. This code is skipped entirely if there are no values. |
7016 | size_t argvDstOffset = 0; |
7017 | |
7018 | Register argvSrcBase = srcBaseAndArgc; |
7019 | |
7020 | // Stash away |tmpArgc| and adjust argvDstOffset accordingly. |
7021 | masm.push(tmpArgc); |
7022 | Register argvIndex = tmpArgc; |
7023 | argvDstOffset += sizeof(void*); |
7024 | |
7025 | // Copy |
7026 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
7027 | argvDstOffset); |
7028 | |
7029 | // Restore. |
7030 | masm.pop(srcBaseAndArgc); // srcBaseAndArgc now contains argc. |
7031 | masm.jump(&epilogue); |
7032 | } |
7033 | masm.bind(&noCopy); |
7034 | { |
7035 | // Clear argc if we skipped the copy step. |
7036 | masm.movePtr(ImmWord(0), srcBaseAndArgc); |
7037 | } |
7038 | |
7039 | // Join with all arguments copied. |
7040 | // Note, "srcBase" has become "argc". |
7041 | masm.bind(&epilogue); |
7042 | } |
7043 | |
7044 | void CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply) { |
7045 | Register elements = ToRegister(apply->getElements()); |
7046 | Register tmpArgc = ToRegister(apply->getTempObject()); |
7047 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
7048 | |
7049 | // argc and elements are mapped to the same calltemp register. |
7050 | MOZ_ASSERT(elements == ToRegister(apply->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(elements == ToRegister(apply->getArgc()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(elements == ToRegister(apply->getArgc())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("elements == ToRegister(apply->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7050); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(apply->getArgc())" ")"); do { *((volatile int*)__null) = 7050; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7051 | |
7052 | // Invariants guarded in the caller: |
7053 | // - the array is not too long |
7054 | // - the array length equals its initialized length |
7055 | |
7056 | // The array length is our argc for the purposes of allocating space. |
7057 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
7058 | |
7059 | // Allocate space for the values. |
7060 | emitAllocateSpaceForApply(tmpArgc, scratch); |
7061 | |
7062 | // After this call "elements" has become "argc". |
7063 | size_t elementsOffset = 0; |
7064 | emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset); |
7065 | |
7066 | // Push |this|. |
7067 | masm.pushValue(ToValue(apply, LApplyArrayGeneric::ThisIndex)); |
7068 | } |
7069 | |
7070 | void CodeGenerator::emitPushArguments(LConstructArgsGeneric* construct) { |
7071 | // Holds the function nargs. |
7072 | Register argcreg = ToRegister(construct->getArgc()); |
7073 | Register copyreg = ToRegister(construct->getTempObject()); |
7074 | Register scratch = ToRegister(construct->getTempForArgCopy()); |
7075 | uint32_t extraFormals = construct->numExtraFormals(); |
7076 | |
7077 | // newTarget and scratch are mapped to the same calltemp register. |
7078 | MOZ_ASSERT(scratch == ToRegister(construct->getNewTarget()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch == ToRegister(construct->getNewTarget())) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(scratch == ToRegister(construct->getNewTarget())) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch == ToRegister(construct->getNewTarget())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())" ")"); do { *((volatile int*)__null) = 7078; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7079 | |
7080 | // Allocate space for the values. |
7081 | // After this call "newTarget" has become "scratch". |
7082 | emitAllocateSpaceForConstructAndPushNewTarget(argcreg, scratch); |
7083 | |
7084 | emitPushArguments(argcreg, scratch, copyreg, extraFormals); |
7085 | |
7086 | // Push |this|. |
7087 | masm.pushValue(ToValue(construct, LConstructArgsGeneric::ThisIndex)); |
7088 | } |
7089 | |
7090 | void CodeGenerator::emitPushArguments(LConstructArrayGeneric* construct) { |
7091 | Register elements = ToRegister(construct->getElements()); |
7092 | Register tmpArgc = ToRegister(construct->getTempObject()); |
7093 | Register scratch = ToRegister(construct->getTempForArgCopy()); |
7094 | |
7095 | // argc and elements are mapped to the same calltemp register. |
7096 | MOZ_ASSERT(elements == ToRegister(construct->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(elements == ToRegister(construct->getArgc()))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(elements == ToRegister(construct->getArgc())))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("elements == ToRegister(construct->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(construct->getArgc())" ")"); do { *((volatile int*)__null) = 7096; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7097 | |
7098 | // newTarget and scratch are mapped to the same calltemp register. |
7099 | MOZ_ASSERT(scratch == ToRegister(construct->getNewTarget()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch == ToRegister(construct->getNewTarget())) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(scratch == ToRegister(construct->getNewTarget())) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch == ToRegister(construct->getNewTarget())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7099); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())" ")"); do { *((volatile int*)__null) = 7099; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7100 | |
7101 | // Invariants guarded in the caller: |
7102 | // - the array is not too long |
7103 | // - the array length equals its initialized length |
7104 | |
7105 | // The array length is our argc for the purposes of allocating space. |
7106 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
7107 | |
7108 | // Allocate space for the values. |
7109 | // After this call "newTarget" has become "scratch". |
7110 | emitAllocateSpaceForConstructAndPushNewTarget(tmpArgc, scratch); |
7111 | |
7112 | // After this call "elements" has become "argc". |
7113 | size_t elementsOffset = 0; |
7114 | emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset); |
7115 | |
7116 | // Push |this|. |
7117 | masm.pushValue(ToValue(construct, LConstructArrayGeneric::ThisIndex)); |
7118 | } |
7119 | |
7120 | template <typename T> |
7121 | void CodeGenerator::emitApplyGeneric(T* apply) { |
7122 | // Holds the function object. |
7123 | Register calleereg = ToRegister(apply->getFunction()); |
7124 | |
7125 | // Temporary register for modifying the function object. |
7126 | Register objreg = ToRegister(apply->getTempObject()); |
7127 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
7128 | |
7129 | // Holds the function nargs, computed in the invoker or (for ApplyArray, |
7130 | // ConstructArray, or ApplyArgsObj) in the argument pusher. |
7131 | Register argcreg = ToRegister(apply->getArgc()); |
7132 | |
7133 | // Copy the arguments of the current function. |
7134 | // |
7135 | // In the case of ApplyArray, ConstructArray, or ApplyArgsObj, also compute |
7136 | // argc. The argc register and the elements/argsObj register are the same; |
7137 | // argc must not be referenced before the call to emitPushArguments() and |
7138 | // elements/argsObj must not be referenced after it returns. |
7139 | // |
7140 | // In the case of ConstructArray or ConstructArgs, also overwrite newTarget; |
7141 | // newTarget must not be referenced after this point. |
7142 | // |
7143 | // objreg is dead across this call. |
7144 | emitPushArguments(apply); |
7145 | |
7146 | masm.checkStackAlignment(); |
7147 | |
7148 | bool constructing = apply->mir()->isConstructing(); |
7149 | |
7150 | // If the function is native, the call is compiled through emitApplyNative. |
7151 | MOZ_ASSERT_IF(apply->hasSingleTarget(),do { if (apply->hasSingleTarget()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!apply->getSingleTarget ()->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!apply->getSingleTarget() ->isNativeWithoutJitEntry()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("!apply->getSingleTarget()->isNativeWithoutJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 7152; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
7152 | !apply->getSingleTarget()->isNativeWithoutJitEntry())do { if (apply->hasSingleTarget()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!apply->getSingleTarget ()->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!apply->getSingleTarget() ->isNativeWithoutJitEntry()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("!apply->getSingleTarget()->isNativeWithoutJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 7152; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
7153 | |
7154 | Label end, invoke; |
7155 | |
7156 | // Unless already known, guard that calleereg is actually a function object. |
7157 | if (!apply->hasSingleTarget()) { |
7158 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleereg, objreg, |
7159 | calleereg, &invoke); |
7160 | } |
7161 | |
7162 | // Guard that calleereg is an interpreted function with a JSScript. |
7163 | masm.branchIfFunctionHasNoJitEntry(calleereg, &invoke); |
7164 | |
7165 | // Guard that callee allows the [[Call]] or [[Construct]] operation required. |
7166 | if (constructing) { |
7167 | masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR, |
7168 | Assembler::Zero, &invoke); |
7169 | } else { |
7170 | masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor, |
7171 | calleereg, objreg, &invoke); |
7172 | } |
7173 | |
7174 | // Use the slow path if CreateThis was unable to create the |this| object. |
7175 | if (constructing) { |
7176 | Address thisAddr(masm.getStackPointer(), 0); |
7177 | masm.branchTestNull(Assembler::Equal, thisAddr, &invoke); |
7178 | } |
7179 | |
7180 | // Call with an Ion frame or a rectifier frame. |
7181 | { |
7182 | if (apply->mir()->maybeCrossRealm()) { |
7183 | masm.switchToObjectRealm(calleereg, objreg); |
7184 | } |
7185 | |
7186 | // Knowing that calleereg is a non-native function, load jitcode. |
7187 | masm.loadJitCodeRaw(calleereg, objreg); |
7188 | |
7189 | masm.PushCalleeToken(calleereg, constructing); |
7190 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcreg, scratch); |
7191 | |
7192 | Label underflow, rejoin; |
7193 | |
7194 | // Check whether the provided arguments satisfy target argc. |
7195 | if (!apply->hasSingleTarget()) { |
7196 | Register nformals = scratch; |
7197 | masm.loadFunctionArgCount(calleereg, nformals); |
7198 | masm.branch32(Assembler::Below, argcreg, nformals, &underflow); |
7199 | } else { |
7200 | masm.branch32(Assembler::Below, argcreg, |
7201 | Imm32(apply->getSingleTarget()->nargs()), &underflow); |
7202 | } |
7203 | |
7204 | // Skip the construction of the rectifier frame because we have no |
7205 | // underflow. |
7206 | masm.jump(&rejoin); |
7207 | |
7208 | // Argument fixup needed. Get ready to call the argumentsRectifier. |
7209 | { |
7210 | masm.bind(&underflow); |
7211 | |
7212 | // Hardcode the address of the argumentsRectifier code. |
7213 | TrampolinePtr argumentsRectifier = |
7214 | gen->jitRuntime()->getArgumentsRectifier(); |
7215 | masm.movePtr(argumentsRectifier, objreg); |
7216 | } |
7217 | |
7218 | masm.bind(&rejoin); |
7219 | |
7220 | // Finally call the function in objreg, as assigned by one of the paths |
7221 | // above. |
7222 | ensureOsiSpace(); |
7223 | uint32_t callOffset = masm.callJit(objreg); |
7224 | markSafepointAt(callOffset, apply); |
7225 | |
7226 | if (apply->mir()->maybeCrossRealm()) { |
7227 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
7228 | "ReturnReg available as scratch after scripted calls"); |
7229 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
7230 | } |
7231 | |
7232 | // Discard JitFrameLayout fields still left on the stack. |
7233 | masm.freeStack(sizeof(JitFrameLayout) - |
7234 | JitFrameLayout::bytesPoppedAfterCall()); |
7235 | masm.jump(&end); |
7236 | } |
7237 | |
7238 | // Handle uncompiled or native functions. |
7239 | { |
7240 | masm.bind(&invoke); |
7241 | emitCallInvokeFunction(apply); |
7242 | } |
7243 | |
7244 | masm.bind(&end); |
7245 | |
7246 | // If the return value of the constructing function is Primitive, replace the |
7247 | // return value with the Object from CreateThis. |
7248 | if (constructing) { |
7249 | Label notPrimitive; |
7250 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
7251 | ¬Primitive); |
7252 | masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand); |
7253 | |
7254 | #ifdef DEBUG1 |
7255 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
7256 | ¬Primitive); |
7257 | masm.assumeUnreachable("CreateThis creates an object"); |
7258 | #endif |
7259 | |
7260 | masm.bind(¬Primitive); |
7261 | } |
7262 | |
7263 | // Pop arguments and continue. |
7264 | emitRestoreStackPointerFromFP(); |
7265 | } |
7266 | |
7267 | template <typename T> |
7268 | void CodeGenerator::emitAlignStackForApplyNative(T* apply, Register argc) { |
7269 | static_assert(JitStackAlignment % ABIStackAlignment == 0, |
7270 | "aligning on JIT stack subsumes ABI alignment"); |
7271 | |
7272 | // Align the arguments on the JitStackAlignment. |
7273 | if (JitStackValueAlignment > 1) { |
7274 | MOZ_ASSERT(JitStackValueAlignment == 2,do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" " (" "Stack padding adds exactly one Value" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ") (" "Stack padding adds exactly one Value" ")"); do { *((volatile int*)__null) = 7275; __attribute__((nomerge)) ::abort(); } while (false); } } while (false) |
7275 | "Stack padding adds exactly one Value")do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" " (" "Stack padding adds exactly one Value" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ") (" "Stack padding adds exactly one Value" ")"); do { *((volatile int*)__null) = 7275; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
7276 | MOZ_ASSERT(frameSize() % JitStackValueAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackValueAlignment == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(frameSize() % JitStackValueAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("frameSize() % JitStackValueAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 7277; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
7277 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackValueAlignment == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(frameSize() % JitStackValueAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("frameSize() % JitStackValueAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 7277; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7278 | |
7279 | Assembler::Condition cond; |
7280 | if constexpr (T::isConstructing()) { |
7281 | // If the number of arguments is even, then we do not need any padding. |
7282 | // |
7283 | // Also see emitAllocateSpaceForApply(). |
7284 | cond = Assembler::Zero; |
7285 | } else { |
7286 | // If the number of arguments is odd, then we do not need any padding. |
7287 | // |
7288 | // Also see emitAllocateSpaceForConstructAndPushNewTarget(). |
7289 | cond = Assembler::NonZero; |
7290 | } |
7291 | |
7292 | Label noPaddingNeeded; |
7293 | masm.branchTestPtr(cond, argc, Imm32(1), &noPaddingNeeded); |
7294 | masm.pushValue(MagicValue(JS_ARG_POISON)); |
7295 | masm.bind(&noPaddingNeeded); |
7296 | } |
7297 | } |
7298 | |
7299 | template <typename T> |
7300 | void CodeGenerator::emitPushNativeArguments(T* apply) { |
7301 | Register argc = ToRegister(apply->getArgc()); |
7302 | Register tmpArgc = ToRegister(apply->getTempObject()); |
7303 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
7304 | uint32_t extraFormals = apply->numExtraFormals(); |
7305 | |
7306 | // Align stack. |
7307 | emitAlignStackForApplyNative(apply, argc); |
7308 | |
7309 | // Push newTarget. |
7310 | if constexpr (T::isConstructing()) { |
7311 | masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget())); |
7312 | } |
7313 | |
7314 | // Push arguments. |
7315 | Label noCopy; |
7316 | masm.branchTestPtr(Assembler::Zero, argc, argc, &noCopy); |
7317 | { |
7318 | // Use scratch register to calculate stack space. |
7319 | masm.movePtr(argc, scratch); |
7320 | |
7321 | // Reserve space for copying the arguments. |
7322 | NativeObject::elementsSizeMustNotOverflow(); |
7323 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
7324 | masm.subFromStackPtr(scratch); |
7325 | |
7326 | // Compute the source and destination offsets into the stack. |
7327 | Register argvSrcBase = FramePointer; |
7328 | size_t argvSrcOffset = |
7329 | JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value); |
7330 | size_t argvDstOffset = 0; |
7331 | |
7332 | Register argvIndex = tmpArgc; |
7333 | masm.move32(argc, argvIndex); |
7334 | |
7335 | // Copy arguments. |
7336 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
7337 | argvDstOffset); |
7338 | } |
7339 | masm.bind(&noCopy); |
7340 | |
7341 | // Push |this|. |
7342 | if constexpr (T::isConstructing()) { |
7343 | masm.pushValue(MagicValue(JS_IS_CONSTRUCTING)); |
7344 | } else { |
7345 | masm.pushValue(ToValue(apply, T::ThisIndex)); |
7346 | } |
7347 | } |
7348 | |
7349 | template <typename T> |
7350 | void CodeGenerator::emitPushArrayAsNativeArguments(T* apply) { |
7351 | Register argc = ToRegister(apply->getArgc()); |
7352 | Register elements = ToRegister(apply->getElements()); |
7353 | Register tmpArgc = ToRegister(apply->getTempObject()); |
7354 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
7355 | |
7356 | // NB: argc and elements are mapped to the same register. |
7357 | MOZ_ASSERT(argc == elements)do { static_assert( mozilla::detail::AssertionConditionType< decltype(argc == elements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(argc == elements))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argc == elements" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7357); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == elements" ")"); do { *((volatile int*)__null) = 7357; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7358 | |
7359 | // Invariants guarded in the caller: |
7360 | // - the array is not too long |
7361 | // - the array length equals its initialized length |
7362 | |
7363 | // The array length is our argc. |
7364 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
7365 | |
7366 | // Align stack. |
7367 | emitAlignStackForApplyNative(apply, tmpArgc); |
7368 | |
7369 | // Push newTarget. |
7370 | if constexpr (T::isConstructing()) { |
7371 | masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget())); |
7372 | } |
7373 | |
7374 | // Skip the copy of arguments if there are none. |
7375 | Label noCopy; |
7376 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
7377 | { |
7378 | // |tmpArgc| is off-by-one, so adjust the offset accordingly. |
7379 | BaseObjectElementIndex srcPtr(elements, tmpArgc, |
7380 | -int32_t(sizeof(JS::Value))); |
7381 | |
7382 | Label loop; |
7383 | masm.bind(&loop); |
7384 | masm.pushValue(srcPtr, scratch); |
7385 | masm.decBranchPtr(Assembler::NonZero, tmpArgc, Imm32(1), &loop); |
7386 | } |
7387 | masm.bind(&noCopy); |
7388 | |
7389 | // Set argc in preparation for calling the native function. |
7390 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), argc); |
7391 | |
7392 | // Push |this|. |
7393 | if constexpr (T::isConstructing()) { |
7394 | masm.pushValue(MagicValue(JS_IS_CONSTRUCTING)); |
7395 | } else { |
7396 | masm.pushValue(ToValue(apply, T::ThisIndex)); |
7397 | } |
7398 | } |
7399 | |
7400 | void CodeGenerator::emitPushArguments(LApplyArgsNative* apply) { |
7401 | emitPushNativeArguments(apply); |
7402 | } |
7403 | |
7404 | void CodeGenerator::emitPushArguments(LApplyArrayNative* apply) { |
7405 | emitPushArrayAsNativeArguments(apply); |
7406 | } |
7407 | |
7408 | void CodeGenerator::emitPushArguments(LConstructArgsNative* construct) { |
7409 | emitPushNativeArguments(construct); |
7410 | } |
7411 | |
7412 | void CodeGenerator::emitPushArguments(LConstructArrayNative* construct) { |
7413 | emitPushArrayAsNativeArguments(construct); |
7414 | } |
7415 | |
7416 | void CodeGenerator::emitPushArguments(LApplyArgsObjNative* apply) { |
7417 | Register argc = ToRegister(apply->getArgc()); |
7418 | Register argsObj = ToRegister(apply->getArgsObj()); |
7419 | Register tmpArgc = ToRegister(apply->getTempObject()); |
7420 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
7421 | Register scratch2 = ToRegister(apply->getTempExtra()); |
7422 | |
7423 | // NB: argc and argsObj are mapped to the same register. |
7424 | MOZ_ASSERT(argc == argsObj)do { static_assert( mozilla::detail::AssertionConditionType< decltype(argc == argsObj)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(argc == argsObj))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argc == argsObj" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == argsObj" ")"); do { *((volatile int*)__null) = 7424; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7425 | |
7426 | // Load argc into tmpArgc. |
7427 | masm.loadArgumentsObjectLength(argsObj, tmpArgc); |
7428 | |
7429 | // Align stack. |
7430 | emitAlignStackForApplyNative(apply, tmpArgc); |
7431 | |
7432 | // Push arguments. |
7433 | Label noCopy, epilogue; |
7434 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
7435 | { |
7436 | // Use scratch register to calculate stack space. |
7437 | masm.movePtr(tmpArgc, scratch); |
7438 | |
7439 | // Reserve space for copying the arguments. |
7440 | NativeObject::elementsSizeMustNotOverflow(); |
7441 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
7442 | masm.subFromStackPtr(scratch); |
7443 | |
7444 | // Load arguments data. |
7445 | Register argvSrcBase = argsObj; |
7446 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
7447 | argvSrcBase); |
7448 | size_t argvSrcOffset = ArgumentsData::offsetOfArgs(); |
7449 | size_t argvDstOffset = 0; |
7450 | |
7451 | Register argvIndex = scratch2; |
7452 | masm.move32(tmpArgc, argvIndex); |
7453 | |
7454 | // Copy the values. |
7455 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
7456 | argvDstOffset); |
7457 | } |
7458 | masm.bind(&noCopy); |
7459 | |
7460 | // Set argc in preparation for calling the native function. |
7461 | masm.movePtr(tmpArgc, argc); |
7462 | |
7463 | // Push |this|. |
7464 | masm.pushValue(ToValue(apply, LApplyArgsObjNative::ThisIndex)); |
7465 | } |
7466 | |
7467 | template <typename T> |
7468 | void CodeGenerator::emitApplyNative(T* apply) { |
7469 | MOZ_ASSERT(T::isConstructing() == apply->mir()->isConstructing(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(T::isConstructing() == apply->mir()->isConstructing ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(T::isConstructing() == apply->mir()->isConstructing ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("T::isConstructing() == apply->mir()->isConstructing()" " (" "isConstructing condition must be consistent" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()" ") (" "isConstructing condition must be consistent" ")"); do { *((volatile int*)__null) = 7470; __attribute__((nomerge)) :: abort(); } while (false); } } while (false) |
7470 | "isConstructing condition must be consistent")do { static_assert( mozilla::detail::AssertionConditionType< decltype(T::isConstructing() == apply->mir()->isConstructing ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(T::isConstructing() == apply->mir()->isConstructing ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("T::isConstructing() == apply->mir()->isConstructing()" " (" "isConstructing condition must be consistent" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()" ") (" "isConstructing condition must be consistent" ")"); do { *((volatile int*)__null) = 7470; __attribute__((nomerge)) :: abort(); } while (false); } } while (false); |
7471 | |
7472 | WrappedFunction* target = apply->mir()->getSingleTarget(); |
7473 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 7473; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7474 | |
7475 | JSNative native = target->native(); |
7476 | if (apply->mir()->ignoresReturnValue() && target->hasJitInfo()) { |
7477 | const JSJitInfo* jitInfo = target->jitInfo(); |
7478 | if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) { |
7479 | native = jitInfo->ignoresReturnValueMethod; |
7480 | } |
7481 | } |
7482 | |
7483 | // Push arguments, including newTarget and |this|. |
7484 | emitPushArguments(apply); |
7485 | |
7486 | // Registers used for callWithABI() argument-passing. |
7487 | Register argContextReg = ToRegister(apply->getTempObject()); |
7488 | Register argUintNReg = ToRegister(apply->getArgc()); |
7489 | Register argVpReg = ToRegister(apply->getTempForArgCopy()); |
7490 | Register tempReg = ToRegister(apply->getTempExtra()); |
7491 | |
7492 | // No unused stack for variadic calls. |
7493 | uint32_t unusedStack = 0; |
7494 | |
7495 | // Pushed arguments don't change the pushed frames amount. |
7496 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 7496; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7497 | |
7498 | // Create the exit frame and call the native. |
7499 | emitCallNative(apply, native, argContextReg, argUintNReg, argVpReg, tempReg, |
7500 | unusedStack); |
7501 | |
7502 | // The exit frame is still on the stack. |
7503 | MOZ_ASSERT(masm.framePushed() == frameSize() + NativeExitFrameLayout::Size())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize() + NativeExitFrameLayout ::Size())>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(masm.framePushed() == frameSize() + NativeExitFrameLayout ::Size()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7503); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()" ")"); do { *((volatile int*)__null) = 7503; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7504 | |
7505 | // The next instruction is removing the exit frame, so there is no need for |
7506 | // leaveFakeExitFrame. |
7507 | |
7508 | // Pop arguments and continue. |
7509 | masm.setFramePushed(frameSize()); |
7510 | emitRestoreStackPointerFromFP(); |
7511 | } |
7512 | |
7513 | template <typename T> |
7514 | void CodeGenerator::emitApplyArgsGuard(T* apply) { |
7515 | LSnapshot* snapshot = apply->snapshot(); |
7516 | Register argcreg = ToRegister(apply->getArgc()); |
7517 | |
7518 | // Ensure that we have a reasonable number of arguments. |
7519 | bailoutCmp32(Assembler::Above, argcreg, Imm32(JIT_ARGS_LENGTH_MAX), snapshot); |
7520 | } |
7521 | |
7522 | template <typename T> |
7523 | void CodeGenerator::emitApplyArgsObjGuard(T* apply) { |
7524 | Register argsObj = ToRegister(apply->getArgsObj()); |
7525 | Register temp = ToRegister(apply->getTempObject()); |
7526 | |
7527 | Label bail; |
7528 | masm.loadArgumentsObjectLength(argsObj, temp, &bail); |
7529 | masm.branch32(Assembler::Above, temp, Imm32(JIT_ARGS_LENGTH_MAX), &bail); |
7530 | bailoutFrom(&bail, apply->snapshot()); |
7531 | } |
7532 | |
7533 | template <typename T> |
7534 | void CodeGenerator::emitApplyArrayGuard(T* apply) { |
7535 | LSnapshot* snapshot = apply->snapshot(); |
7536 | Register elements = ToRegister(apply->getElements()); |
7537 | Register tmp = ToRegister(apply->getTempObject()); |
7538 | |
7539 | Address length(elements, ObjectElements::offsetOfLength()); |
7540 | masm.load32(length, tmp); |
7541 | |
7542 | // Ensure that we have a reasonable number of arguments. |
7543 | bailoutCmp32(Assembler::Above, tmp, Imm32(JIT_ARGS_LENGTH_MAX), snapshot); |
7544 | |
7545 | // Ensure that the array does not contain an uninitialized tail. |
7546 | |
7547 | Address initializedLength(elements, |
7548 | ObjectElements::offsetOfInitializedLength()); |
7549 | masm.sub32(initializedLength, tmp); |
7550 | bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot); |
7551 | } |
7552 | |
7553 | void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) { |
7554 | emitApplyArgsGuard(apply); |
7555 | emitApplyGeneric(apply); |
7556 | } |
7557 | |
7558 | void CodeGenerator::visitApplyArgsObj(LApplyArgsObj* apply) { |
7559 | emitApplyArgsObjGuard(apply); |
7560 | emitApplyGeneric(apply); |
7561 | } |
7562 | |
7563 | void CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) { |
7564 | emitApplyArrayGuard(apply); |
7565 | emitApplyGeneric(apply); |
7566 | } |
7567 | |
7568 | void CodeGenerator::visitConstructArgsGeneric(LConstructArgsGeneric* lir) { |
7569 | emitApplyArgsGuard(lir); |
7570 | emitApplyGeneric(lir); |
7571 | } |
7572 | |
7573 | void CodeGenerator::visitConstructArrayGeneric(LConstructArrayGeneric* lir) { |
7574 | emitApplyArrayGuard(lir); |
7575 | emitApplyGeneric(lir); |
7576 | } |
7577 | |
7578 | void CodeGenerator::visitApplyArgsNative(LApplyArgsNative* lir) { |
7579 | emitApplyArgsGuard(lir); |
7580 | emitApplyNative(lir); |
7581 | } |
7582 | |
7583 | void CodeGenerator::visitApplyArgsObjNative(LApplyArgsObjNative* lir) { |
7584 | emitApplyArgsObjGuard(lir); |
7585 | emitApplyNative(lir); |
7586 | } |
7587 | |
7588 | void CodeGenerator::visitApplyArrayNative(LApplyArrayNative* lir) { |
7589 | emitApplyArrayGuard(lir); |
7590 | emitApplyNative(lir); |
7591 | } |
7592 | |
7593 | void CodeGenerator::visitConstructArgsNative(LConstructArgsNative* lir) { |
7594 | emitApplyArgsGuard(lir); |
7595 | emitApplyNative(lir); |
7596 | } |
7597 | |
7598 | void CodeGenerator::visitConstructArrayNative(LConstructArrayNative* lir) { |
7599 | emitApplyArrayGuard(lir); |
7600 | emitApplyNative(lir); |
7601 | } |
7602 | |
7603 | void CodeGenerator::visitBail(LBail* lir) { bailout(lir->snapshot()); } |
7604 | |
7605 | void CodeGenerator::visitUnreachable(LUnreachable* lir) { |
7606 | masm.assumeUnreachable("end-of-block assumed unreachable"); |
7607 | } |
7608 | |
7609 | void CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot* lir) { |
7610 | encode(lir->snapshot()); |
7611 | } |
7612 | |
7613 | void CodeGenerator::visitUnreachableResultV(LUnreachableResultV* lir) { |
7614 | masm.assumeUnreachable("must be unreachable"); |
7615 | } |
7616 | |
7617 | void CodeGenerator::visitUnreachableResultT(LUnreachableResultT* lir) { |
7618 | masm.assumeUnreachable("must be unreachable"); |
7619 | } |
7620 | |
7621 | // Out-of-line path to report over-recursed error and fail. |
7622 | class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator> { |
7623 | LInstruction* lir_; |
7624 | |
7625 | public: |
7626 | explicit CheckOverRecursedFailure(LInstruction* lir) : lir_(lir) {} |
7627 | |
7628 | void accept(CodeGenerator* codegen) override { |
7629 | codegen->visitCheckOverRecursedFailure(this); |
7630 | } |
7631 | |
7632 | LInstruction* lir() const { return lir_; } |
7633 | }; |
7634 | |
7635 | void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) { |
7636 | // If we don't push anything on the stack, skip the check. |
7637 | if (omitOverRecursedCheck()) { |
7638 | return; |
7639 | } |
7640 | |
7641 | // Ensure that this frame will not cross the stack limit. |
7642 | // This is a weak check, justified by Ion using the C stack: we must always |
7643 | // be some distance away from the actual limit, since if the limit is |
7644 | // crossed, an error must be thrown, which requires more frames. |
7645 | // |
7646 | // It must always be possible to trespass past the stack limit. |
7647 | // Ion may legally place frames very close to the limit. Calling additional |
7648 | // C functions may then violate the limit without any checking. |
7649 | // |
7650 | // Since Ion frames exist on the C stack, the stack limit may be |
7651 | // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota(). |
7652 | |
7653 | CheckOverRecursedFailure* ool = new (alloc()) CheckOverRecursedFailure(lir); |
7654 | addOutOfLineCode(ool, lir->mir()); |
7655 | |
7656 | // Conditional forward (unlikely) branch to failure. |
7657 | const void* limitAddr = gen->runtime->addressOfJitStackLimit(); |
7658 | masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), |
7659 | ool->entry()); |
7660 | masm.bind(ool->rejoin()); |
7661 | } |
7662 | |
7663 | void CodeGenerator::visitCheckOverRecursedFailure( |
7664 | CheckOverRecursedFailure* ool) { |
7665 | // The OOL path is hit if the recursion depth has been exceeded. |
7666 | // Throw an InternalError for over-recursion. |
7667 | |
7668 | // LFunctionEnvironment can appear before LCheckOverRecursed, so we have |
7669 | // to save all live registers to avoid crashes if CheckOverRecursed triggers |
7670 | // a GC. |
7671 | saveLive(ool->lir()); |
7672 | |
7673 | using Fn = bool (*)(JSContext*); |
7674 | callVM<Fn, CheckOverRecursed>(ool->lir()); |
7675 | |
7676 | restoreLive(ool->lir()); |
7677 | masm.jump(ool->rejoin()); |
7678 | } |
7679 | |
7680 | IonScriptCounts* CodeGenerator::maybeCreateScriptCounts() { |
7681 | // If scripts are being profiled, create a new IonScriptCounts for the |
7682 | // profiling data, which will be attached to the associated JSScript or |
7683 | // wasm module after code generation finishes. |
7684 | if (!gen->hasProfilingScripts()) { |
7685 | return nullptr; |
7686 | } |
7687 | |
7688 | // This test inhibits IonScriptCount creation for wasm code which is |
7689 | // currently incompatible with wasm codegen for two reasons: (1) wasm code |
7690 | // must be serializable and script count codegen bakes in absolute |
7691 | // addresses, (2) wasm code does not have a JSScript with which to associate |
7692 | // code coverage data. |
7693 | JSScript* script = gen->outerInfo().script(); |
7694 | if (!script) { |
7695 | return nullptr; |
7696 | } |
7697 | |
7698 | auto counts = MakeUnique<IonScriptCounts>(); |
7699 | if (!counts || !counts->init(graph.numBlocks())) { |
7700 | return nullptr; |
7701 | } |
7702 | |
7703 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
7704 | MBasicBlock* block = graph.getBlock(i)->mir(); |
7705 | |
7706 | uint32_t offset = 0; |
7707 | char* description = nullptr; |
7708 | if (MResumePoint* resume = block->entryResumePoint()) { |
7709 | // Find a PC offset in the outermost script to use. If this |
7710 | // block is from an inlined script, find a location in the |
7711 | // outer script to associate information about the inlining |
7712 | // with. |
7713 | while (resume->caller()) { |
7714 | resume = resume->caller(); |
7715 | } |
7716 | offset = script->pcToOffset(resume->pc()); |
7717 | |
7718 | if (block->entryResumePoint()->caller()) { |
7719 | // Get the filename and line number of the inner script. |
7720 | JSScript* innerScript = block->info().script(); |
7721 | description = js_pod_calloc<char>(200); |
7722 | if (description) { |
7723 | snprintf(description, 200, "%s:%u", innerScript->filename(), |
7724 | innerScript->lineno()); |
7725 | } |
7726 | } |
7727 | } |
7728 | |
7729 | if (!counts->block(i).init(block->id(), offset, description, |
7730 | block->numSuccessors())) { |
7731 | return nullptr; |
7732 | } |
7733 | |
7734 | for (size_t j = 0; j < block->numSuccessors(); j++) { |
7735 | counts->block(i).setSuccessor( |
7736 | j, skipTrivialBlocks(block->getSuccessor(j))->id()); |
7737 | } |
7738 | } |
7739 | |
7740 | scriptCounts_ = counts.release(); |
7741 | return scriptCounts_; |
7742 | } |
7743 | |
7744 | // Structure for managing the state tracked for a block by script counters. |
7745 | struct ScriptCountBlockState { |
7746 | IonBlockCounts& block; |
7747 | MacroAssembler& masm; |
7748 | |
7749 | Sprinter printer; |
7750 | |
7751 | public: |
7752 | ScriptCountBlockState(IonBlockCounts* block, MacroAssembler* masm) |
7753 | : block(*block), masm(*masm), printer(GetJitContext()->cx, false) {} |
7754 | |
7755 | bool init() { |
7756 | if (!printer.init()) { |
7757 | return false; |
7758 | } |
7759 | |
7760 | // Bump the hit count for the block at the start. This code is not |
7761 | // included in either the text for the block or the instruction byte |
7762 | // counts. |
7763 | masm.inc64(AbsoluteAddress(block.addressOfHitCount())); |
7764 | |
7765 | // Collect human readable assembly for the code generated in the block. |
7766 | masm.setPrinter(&printer); |
7767 | |
7768 | return true; |
7769 | } |
7770 | |
7771 | void visitInstruction(LInstruction* ins) { |
7772 | #ifdef JS_JITSPEW1 |
7773 | // Prefix stream of assembly instructions with their LIR instruction |
7774 | // name and any associated high level info. |
7775 | if (const char* extra = ins->getExtraName()) { |
7776 | printer.printf("[%s:%s]\n", ins->opName(), extra); |
7777 | } else { |
7778 | printer.printf("[%s]\n", ins->opName()); |
7779 | } |
7780 | #endif |
7781 | } |
7782 | |
7783 | ~ScriptCountBlockState() { |
7784 | masm.setPrinter(nullptr); |
7785 | |
7786 | if (JS::UniqueChars str = printer.release()) { |
7787 | block.setCode(str.get()); |
7788 | } |
7789 | } |
7790 | }; |
7791 | |
7792 | void CodeGenerator::branchIfInvalidated(Register temp, Label* invalidated) { |
7793 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp); |
7794 | masm.propagateOOM(ionScriptLabels_.append(label)); |
7795 | |
7796 | // If IonScript::invalidationCount_ != 0, the script has been invalidated. |
7797 | masm.branch32(Assembler::NotEqual, |
7798 | Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0), |
7799 | invalidated); |
7800 | } |
7801 | |
7802 | #ifdef DEBUG1 |
7803 | void CodeGenerator::emitAssertGCThingResult(Register input, |
7804 | const MDefinition* mir) { |
7805 | MIRType type = mir->type(); |
7806 | MOZ_ASSERT(type == MIRType::Object || type == MIRType::String ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7807); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 7807; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
7807 | type == MIRType::Symbol || type == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7807); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 7807; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7808 | |
7809 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
7810 | regs.take(input); |
7811 | |
7812 | Register temp = regs.takeAny(); |
7813 | masm.push(temp); |
7814 | |
7815 | // Don't check if the script has been invalidated. In that case invalid |
7816 | // types are expected (until we reach the OsiPoint and bailout). |
7817 | Label done; |
7818 | branchIfInvalidated(temp, &done); |
7819 | |
7820 | # ifndef JS_SIMULATOR |
7821 | // Check that we have a valid GC pointer. |
7822 | // Disable for wasm because we don't have a context on wasm compilation |
7823 | // threads and this needs a context. |
7824 | // Also disable for simulator builds because the C++ call is a lot slower |
7825 | // there than on actual hardware. |
7826 | if (JitOptions.fullDebugChecks && !IsCompilingWasm()) { |
7827 | saveVolatile(); |
7828 | masm.setupUnalignedABICall(temp); |
7829 | masm.loadJSContext(temp); |
7830 | masm.passABIArg(temp); |
7831 | masm.passABIArg(input); |
7832 | |
7833 | switch (type) { |
7834 | case MIRType::Object: { |
7835 | using Fn = void (*)(JSContext* cx, JSObject* obj); |
7836 | masm.callWithABI<Fn, AssertValidObjectPtr>(); |
7837 | break; |
7838 | } |
7839 | case MIRType::String: { |
7840 | using Fn = void (*)(JSContext* cx, JSString* str); |
7841 | masm.callWithABI<Fn, AssertValidStringPtr>(); |
7842 | break; |
7843 | } |
7844 | case MIRType::Symbol: { |
7845 | using Fn = void (*)(JSContext* cx, JS::Symbol* sym); |
7846 | masm.callWithABI<Fn, AssertValidSymbolPtr>(); |
7847 | break; |
7848 | } |
7849 | case MIRType::BigInt: { |
7850 | using Fn = void (*)(JSContext* cx, JS::BigInt* bi); |
7851 | masm.callWithABI<Fn, AssertValidBigIntPtr>(); |
7852 | break; |
7853 | } |
7854 | default: |
7855 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7855); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 7855; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
7856 | } |
7857 | |
7858 | restoreVolatile(); |
7859 | } |
7860 | # endif |
7861 | |
7862 | masm.bind(&done); |
7863 | masm.pop(temp); |
7864 | } |
7865 | |
7866 | void CodeGenerator::emitAssertResultV(const ValueOperand input, |
7867 | const MDefinition* mir) { |
7868 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
7869 | regs.take(input); |
7870 | |
7871 | Register temp1 = regs.takeAny(); |
7872 | Register temp2 = regs.takeAny(); |
7873 | masm.push(temp1); |
7874 | masm.push(temp2); |
7875 | |
7876 | // Don't check if the script has been invalidated. In that case invalid |
7877 | // types are expected (until we reach the OsiPoint and bailout). |
7878 | Label done; |
7879 | branchIfInvalidated(temp1, &done); |
7880 | |
7881 | // Check that we have a valid GC pointer. |
7882 | if (JitOptions.fullDebugChecks) { |
7883 | saveVolatile(); |
7884 | |
7885 | masm.pushValue(input); |
7886 | masm.moveStackPtrTo(temp1); |
7887 | |
7888 | using Fn = void (*)(JSContext* cx, Value* v); |
7889 | masm.setupUnalignedABICall(temp2); |
7890 | masm.loadJSContext(temp2); |
7891 | masm.passABIArg(temp2); |
7892 | masm.passABIArg(temp1); |
7893 | masm.callWithABI<Fn, AssertValidValue>(); |
7894 | masm.popValue(input); |
7895 | restoreVolatile(); |
7896 | } |
7897 | |
7898 | masm.bind(&done); |
7899 | masm.pop(temp2); |
7900 | masm.pop(temp1); |
7901 | } |
7902 | |
7903 | void CodeGenerator::emitGCThingResultChecks(LInstruction* lir, |
7904 | MDefinition* mir) { |
7905 | if (lir->numDefs() == 0) { |
7906 | return; |
7907 | } |
7908 | |
7909 | MOZ_ASSERT(lir->numDefs() == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->numDefs() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->numDefs() == 1))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("lir->numDefs() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7909); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { *((volatile int*)__null) = 7909; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7910 | if (lir->getDef(0)->isBogusTemp()) { |
7911 | return; |
7912 | } |
7913 | |
7914 | Register output = ToRegister(lir->getDef(0)); |
7915 | emitAssertGCThingResult(output, mir); |
7916 | } |
7917 | |
7918 | void CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir) { |
7919 | if (lir->numDefs() == 0) { |
7920 | return; |
7921 | } |
7922 | |
7923 | MOZ_ASSERT(lir->numDefs() == BOX_PIECES)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->numDefs() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->numDefs() == 1))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("lir->numDefs() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { *((volatile int*)__null) = 7923; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7924 | if (!lir->getDef(0)->output()->isRegister()) { |
7925 | return; |
7926 | } |
7927 | |
7928 | ValueOperand output = ToOutValue(lir); |
7929 | |
7930 | emitAssertResultV(output, mir); |
7931 | } |
7932 | |
7933 | void CodeGenerator::emitDebugResultChecks(LInstruction* ins) { |
7934 | // In debug builds, check that LIR instructions return valid values. |
7935 | |
7936 | MDefinition* mir = ins->mirRaw(); |
7937 | if (!mir) { |
7938 | return; |
7939 | } |
7940 | |
7941 | switch (mir->type()) { |
7942 | case MIRType::Object: |
7943 | case MIRType::String: |
7944 | case MIRType::Symbol: |
7945 | case MIRType::BigInt: |
7946 | emitGCThingResultChecks(ins, mir); |
7947 | break; |
7948 | case MIRType::Value: |
7949 | emitValueResultChecks(ins, mir); |
7950 | break; |
7951 | default: |
7952 | break; |
7953 | } |
7954 | } |
7955 | |
7956 | void CodeGenerator::emitDebugForceBailing(LInstruction* lir) { |
7957 | if (MOZ_LIKELY(!gen->options.ionBailAfterEnabled())(__builtin_expect(!!(!gen->options.ionBailAfterEnabled()), 1))) { |
7958 | return; |
7959 | } |
7960 | if (!lir->snapshot()) { |
7961 | return; |
7962 | } |
7963 | if (lir->isOsiPoint()) { |
7964 | return; |
7965 | } |
7966 | |
7967 | masm.comment("emitDebugForceBailing"); |
7968 | const void* bailAfterCounterAddr = |
7969 | gen->runtime->addressOfIonBailAfterCounter(); |
7970 | |
7971 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
7972 | |
7973 | Label done, notBail; |
7974 | masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterCounterAddr), |
7975 | Imm32(0), &done); |
7976 | { |
7977 | Register temp = regs.takeAny(); |
7978 | |
7979 | masm.push(temp); |
7980 | masm.load32(AbsoluteAddress(bailAfterCounterAddr), temp); |
7981 | masm.sub32(Imm32(1), temp); |
7982 | masm.store32(temp, AbsoluteAddress(bailAfterCounterAddr)); |
7983 | |
7984 | masm.branch32(Assembler::NotEqual, temp, Imm32(0), ¬Bail); |
7985 | { |
7986 | masm.pop(temp); |
7987 | bailout(lir->snapshot()); |
7988 | } |
7989 | masm.bind(¬Bail); |
7990 | masm.pop(temp); |
7991 | } |
7992 | masm.bind(&done); |
7993 | } |
7994 | #endif |
7995 | |
7996 | bool CodeGenerator::generateBody() { |
7997 | JitSpewCont(JitSpew_Codegen, "\n"); |
7998 | AutoCreatedBy acb(masm, "CodeGenerator::generateBody"); |
7999 | |
8000 | JitSpew(JitSpew_Codegen, "==== BEGIN CodeGenerator::generateBody ===="); |
8001 | IonScriptCounts* counts = maybeCreateScriptCounts(); |
8002 | |
8003 | const bool compilingWasm = gen->compilingWasm(); |
8004 | |
8005 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
8006 | current = graph.getBlock(i); |
8007 | |
8008 | // Don't emit any code for trivial blocks, containing just a goto. Such |
8009 | // blocks are created to split critical edges, and if we didn't end up |
8010 | // putting any instructions in them, we can skip them. |
8011 | if (current->isTrivial()) { |
8012 | continue; |
8013 | } |
8014 | |
8015 | #ifdef JS_JITSPEW1 |
8016 | const char* filename = nullptr; |
8017 | size_t lineNumber = 0; |
8018 | JS::LimitedColumnNumberOneOrigin columnNumber; |
8019 | if (current->mir()->info().script()) { |
8020 | filename = current->mir()->info().script()->filename(); |
8021 | if (current->mir()->pc()) { |
8022 | lineNumber = PCToLineNumber(current->mir()->info().script(), |
8023 | current->mir()->pc(), &columnNumber); |
8024 | } |
8025 | } |
8026 | JitSpew(JitSpew_Codegen, "--------------------------------"); |
8027 | JitSpew(JitSpew_Codegen, "# block%zu %s:%zu:%u%s:", i, |
8028 | filename ? filename : "?", lineNumber, |
8029 | columnNumber.oneOriginValue(), |
8030 | current->mir()->isLoopHeader() ? " (loop header)" : ""); |
8031 | #endif |
8032 | |
8033 | if (current->mir()->isLoopHeader() && compilingWasm) { |
8034 | masm.nopAlign(CodeAlignment); |
8035 | } |
8036 | |
8037 | masm.bind(current->label()); |
8038 | |
8039 | mozilla::Maybe<ScriptCountBlockState> blockCounts; |
8040 | if (counts) { |
8041 | blockCounts.emplace(&counts->block(i), &masm); |
8042 | if (!blockCounts->init()) { |
8043 | return false; |
8044 | } |
8045 | } |
8046 | |
8047 | for (LInstructionIterator iter = current->begin(); iter != current->end(); |
8048 | iter++) { |
8049 | if (!alloc().ensureBallast()) { |
8050 | return false; |
8051 | } |
8052 | |
8053 | perfSpewer_.recordInstruction(masm, *iter); |
8054 | #ifdef JS_JITSPEW1 |
8055 | JitSpewStart(JitSpew_Codegen, " # LIR=%s", |
8056 | iter->opName()); |
8057 | if (const char* extra = iter->getExtraName()) { |
8058 | JitSpewCont(JitSpew_Codegen, ":%s", extra); |
8059 | } |
8060 | JitSpewFin(JitSpew_Codegen); |
8061 | #endif |
8062 | |
8063 | if (counts) { |
8064 | blockCounts->visitInstruction(*iter); |
8065 | } |
8066 | |
8067 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
8068 | if (iter->safepoint() && !compilingWasm) { |
8069 | resetOsiPointRegs(iter->safepoint()); |
8070 | } |
8071 | #endif |
8072 | |
8073 | if (!compilingWasm) { |
8074 | if (MDefinition* mir = iter->mirRaw()) { |
8075 | if (!addNativeToBytecodeEntry(mir->trackedSite())) { |
8076 | return false; |
8077 | } |
8078 | } |
8079 | } |
8080 | |
8081 | setElement(*iter); // needed to encode correct snapshot location. |
8082 | |
8083 | #ifdef DEBUG1 |
8084 | emitDebugForceBailing(*iter); |
8085 | #endif |
8086 | |
8087 | switch (iter->op()) { |
8088 | #ifndef JS_CODEGEN_NONE |
8089 | # define LIROP(op) \ |
8090 | case LNode::Opcode::op: \ |
8091 | visit##op(iter->to##op()); \ |
8092 | break; |
8093 | LIR_OPCODE_LIST(LIROP)LIROP(Phi)LIROP(Box)LIROP(OsiPoint)LIROP(MoveGroup)LIROP(Integer )LIROP(Integer64)LIROP(Pointer)LIROP(Double)LIROP(Float32)LIROP (Value)LIROP(NurseryObject)LIROP(Parameter)LIROP(Callee)LIROP (IsConstructing)LIROP(Goto)LIROP(NewArray)LIROP(NewArrayDynamicLength )LIROP(NewIterator)LIROP(NewTypedArray)LIROP(NewTypedArrayDynamicLength )LIROP(NewTypedArrayFromArray)LIROP(NewTypedArrayFromArrayBuffer )LIROP(BindFunction)LIROP(NewBoundFunction)LIROP(NewObject)LIROP (NewPlainObject)LIROP(NewArrayObject)LIROP(NewNamedLambdaObject )LIROP(NewCallObject)LIROP(NewMapObject)LIROP(NewSetObject)LIROP (NewMapObjectFromIterable)LIROP(NewSetObjectFromIterable)LIROP (NewStringObject)LIROP(InitElemGetterSetter)LIROP(MutateProto )LIROP(InitPropGetterSetter)LIROP(CheckOverRecursed)LIROP(WasmTrap )LIROP(WasmTrapIfNull)LIROP(WasmRefIsSubtypeOfConcrete)LIROP( WasmRefIsSubtypeOfAbstract)LIROP(WasmRefIsSubtypeOfConcreteAndBranch )LIROP(WasmRefIsSubtypeOfAbstractAndBranch)LIROP(WasmNewStructObject )LIROP(WasmNewArrayObject)LIROP(ReinterpretCast)LIROP(ReinterpretCastFromI64 )LIROP(ReinterpretCastToI64)LIROP(Rotate)LIROP(RotateI64)LIROP (InterruptCheck)LIROP(WasmStackSwitchToMain)LIROP(WasmStackSwitchToSuspendable )LIROP(WasmStackContinueOnSuspendable)LIROP(WasmInterruptCheck )LIROP(TypeOfV)LIROP(TypeOfO)LIROP(TypeOfName)LIROP(TypeOfIsNonPrimitiveV )LIROP(TypeOfIsNonPrimitiveO)LIROP(TypeOfIsPrimitive)LIROP(ToAsyncIter )LIROP(ToPropertyKeyCache)LIROP(CreateThis)LIROP(CreateArgumentsObject )LIROP(CreateInlinedArgumentsObject)LIROP(GetInlinedArgument) LIROP(GetInlinedArgumentHole)LIROP(GetArgumentsObjectArg)LIROP (SetArgumentsObjectArg)LIROP(LoadArgumentsObjectArg)LIROP(LoadArgumentsObjectArgHole )LIROP(InArgumentsObjectArg)LIROP(ArgumentsObjectLength)LIROP (ArrayFromArgumentsObject)LIROP(GuardArgumentsObjectFlags)LIROP (BoundFunctionNumArgs)LIROP(GuardBoundFunctionIsConstructor)LIROP (ReturnFromCtor)LIROP(BoxNonStrictThis)LIROP(ImplicitThis)LIROP (StackArgT)LIROP(StackArgV)LIROP(CallGeneric)LIROP(CallKnown) LIROP(CallNative)LIROP(CallDOMNative)LIROP(CallClassHook)LIROP (Bail)LIROP(Unreachable)LIROP(EncodeSnapshot)LIROP(UnreachableResultV )LIROP(UnreachableResultT)LIROP(GetDOMProperty)LIROP(GetDOMMemberV )LIROP(GetDOMMemberT)LIROP(SetDOMProperty)LIROP(LoadDOMExpandoValue )LIROP(LoadDOMExpandoValueGuardGeneration)LIROP(LoadDOMExpandoValueIgnoreGeneration )LIROP(GuardDOMExpandoMissingOrGuardShape)LIROP(ApplyArgsGeneric )LIROP(ApplyArgsObj)LIROP(ApplyArrayGeneric)LIROP(ConstructArgsGeneric )LIROP(ConstructArrayGeneric)LIROP(ApplyArgsNative)LIROP(ApplyArgsObjNative )LIROP(ApplyArrayNative)LIROP(ConstructArgsNative)LIROP(ConstructArrayNative )LIROP(TestIAndBranch)LIROP(TestIPtrAndBranch)LIROP(TestI64AndBranch )LIROP(TestDAndBranch)LIROP(TestFAndBranch)LIROP(TestBIAndBranch )LIROP(TestOAndBranch)LIROP(TestVAndBranch)LIROP(Compare)LIROP (CompareI64)LIROP(CompareI64AndBranch)LIROP(CompareAndBranch) LIROP(CompareD)LIROP(CompareF)LIROP(CompareDAndBranch)LIROP(CompareFAndBranch )LIROP(CompareS)LIROP(CompareSInline)LIROP(CompareSSingle)LIROP (CompareBigInt)LIROP(CompareBigIntInt32)LIROP(CompareBigIntDouble )LIROP(CompareBigIntString)LIROP(CompareBigIntInt32AndBranch) LIROP(BitAndAndBranch)LIROP(BitAnd64AndBranch)LIROP(IsNullOrLikeUndefinedV )LIROP(IsNullOrLikeUndefinedT)LIROP(IsNull)LIROP(IsUndefined) LIROP(IsNullOrLikeUndefinedAndBranchV)LIROP(IsNullOrLikeUndefinedAndBranchT )LIROP(IsNullAndBranch)LIROP(IsUndefinedAndBranch)LIROP(SameValueDouble )LIROP(SameValue)LIROP(NotI)LIROP(NotIPtr)LIROP(NotI64)LIROP( NotD)LIROP(NotF)LIROP(NotBI)LIROP(NotO)LIROP(NotV)LIROP(BitNotI )LIROP(BitNotI64)LIROP(BitOpI)LIROP(BitOpI64)LIROP(ShiftI)LIROP (ShiftI64)LIROP(SignExtendInt32)LIROP(SignExtendIntPtr)LIROP( SignExtendInt64)LIROP(UrshD)LIROP(Return)LIROP(Throw)LIROP(ThrowWithStack )LIROP(MinMaxI)LIROP(MinMaxD)LIROP(MinMaxF)LIROP(MinMaxArrayI )LIROP(MinMaxArrayD)LIROP(NegI)LIROP(NegI64)LIROP(NegD)LIROP( NegF)LIROP(AbsI)LIROP(AbsD)LIROP(AbsF)LIROP(CopySignD)LIROP(CopySignF )LIROP(ClzI)LIROP(ClzI64)LIROP(CtzI)LIROP(CtzI64)LIROP(PopcntI )LIROP(PopcntI64)LIROP(SqrtD)LIROP(SqrtF)LIROP(Atan2D)LIROP(Hypot )LIROP(PowI)LIROP(PowII)LIROP(PowD)LIROP(PowOfTwoI)LIROP(SignI )LIROP(SignD)LIROP(SignDI)LIROP(MathFunctionD)LIROP(MathFunctionF )LIROP(AddI)LIROP(AddI64)LIROP(SubI)LIROP(SubI64)LIROP(MulI64 )LIROP(MathD)LIROP(MathF)LIROP(ModD)LIROP(ModPowTwoD)LIROP(WasmBuiltinModD )LIROP(BigIntAdd)LIROP(BigIntSub)LIROP(BigIntMul)LIROP(BigIntDiv )LIROP(BigIntMod)LIROP(BigIntPow)LIROP(BigIntBitAnd)LIROP(BigIntBitOr )LIROP(BigIntBitXor)LIROP(BigIntLsh)LIROP(BigIntRsh)LIROP(BigIntIncrement )LIROP(BigIntDecrement)LIROP(BigIntNegate)LIROP(BigIntBitNot) LIROP(BigIntToIntPtr)LIROP(IntPtrToBigInt)LIROP(BigIntPtrAdd) LIROP(BigIntPtrSub)LIROP(BigIntPtrMul)LIROP(BigIntPtrDiv)LIROP (BigIntPtrDivPowTwo)LIROP(BigIntPtrMod)LIROP(BigIntPtrModPowTwo )LIROP(BigIntPtrPow)LIROP(BigIntPtrBitAnd)LIROP(BigIntPtrBitOr )LIROP(BigIntPtrBitXor)LIROP(BigIntPtrLsh)LIROP(BigIntPtrRsh) LIROP(BigIntPtrBitNot)LIROP(Int32ToStringWithBase)LIROP(NumberParseInt )LIROP(DoubleParseInt)LIROP(Concat)LIROP(LinearizeString)LIROP (LinearizeForCharAccess)LIROP(LinearizeForCodePointAccess)LIROP (ToRelativeStringIndex)LIROP(CharCodeAt)LIROP(CharCodeAtOrNegative )LIROP(CodePointAt)LIROP(CodePointAtOrNegative)LIROP(NegativeToNaN )LIROP(NegativeToUndefined)LIROP(FromCharCode)LIROP(FromCharCodeEmptyIfNegative )LIROP(FromCharCodeUndefinedIfNegative)LIROP(FromCodePoint)LIROP (StringIncludes)LIROP(StringIncludesSIMD)LIROP(StringIndexOf) LIROP(StringIndexOfSIMD)LIROP(StringLastIndexOf)LIROP(StringStartsWith )LIROP(StringStartsWithInline)LIROP(StringEndsWith)LIROP(StringEndsWithInline )LIROP(StringToLowerCase)LIROP(CharCodeToLowerCase)LIROP(StringToUpperCase )LIROP(CharCodeToUpperCase)LIROP(StringTrimStartIndex)LIROP(StringTrimEndIndex )LIROP(StringSplit)LIROP(Substr)LIROP(Int32ToDouble)LIROP(Float32ToDouble )LIROP(DoubleToFloat32)LIROP(Int32ToFloat32)LIROP(DoubleToFloat16 )LIROP(DoubleToFloat32ToFloat16)LIROP(Float32ToFloat16)LIROP( Int32ToFloat16)LIROP(ValueToDouble)LIROP(ValueToFloat32)LIROP (ValueToFloat16)LIROP(ValueToNumberInt32)LIROP(ValueTruncateToInt32 )LIROP(ValueToBigInt)LIROP(DoubleToInt32)LIROP(Float32ToInt32 )LIROP(TruncateDToInt32)LIROP(WasmBuiltinTruncateDToInt32)LIROP (TruncateFToInt32)LIROP(WasmBuiltinTruncateFToInt32)LIROP(WasmTruncateToInt32 )LIROP(WrapInt64ToInt32)LIROP(ExtendInt32ToInt64)LIROP(BooleanToString )LIROP(IntToString)LIROP(DoubleToString)LIROP(ValueToString)LIROP (PowHalfD)LIROP(NaNToZero)LIROP(OsrEntry)LIROP(OsrValue)LIROP (OsrEnvironmentChain)LIROP(OsrReturnValue)LIROP(OsrArgumentsObject )LIROP(RegExp)LIROP(RegExpMatcher)LIROP(RegExpSearcher)LIROP( RegExpSearcherLastLimit)LIROP(RegExpExecMatch)LIROP(RegExpExecTest )LIROP(RegExpHasCaptureGroups)LIROP(RegExpPrototypeOptimizable )LIROP(RegExpInstanceOptimizable)LIROP(GetFirstDollarIndex)LIROP (StringReplace)LIROP(BinaryValueCache)LIROP(BinaryBoolCache)LIROP (UnaryCache)LIROP(ModuleMetadata)LIROP(DynamicImport)LIROP(Lambda )LIROP(FunctionWithProto)LIROP(SetFunName)LIROP(KeepAliveObject )LIROP(DebugEnterGCUnsafeRegion)LIROP(DebugLeaveGCUnsafeRegion )LIROP(Slots)LIROP(Elements)LIROP(InitializedLength)LIROP(SetInitializedLength )LIROP(ArrayLength)LIROP(SetArrayLength)LIROP(FunctionLength) LIROP(FunctionName)LIROP(GetNextEntryForIterator)LIROP(ArrayBufferByteLength )LIROP(ArrayBufferViewLength)LIROP(ArrayBufferViewByteOffset) LIROP(ArrayBufferViewElements)LIROP(TypedArrayElementSize)LIROP (ResizableTypedArrayLength)LIROP(ResizableTypedArrayByteOffsetMaybeOutOfBounds )LIROP(ResizableDataViewByteLength)LIROP(GrowableSharedArrayBufferByteLength )LIROP(GuardResizableArrayBufferViewInBounds)LIROP(GuardResizableArrayBufferViewInBoundsOrDetached )LIROP(GuardHasAttachedArrayBuffer)LIROP(GuardNumberToIntPtrIndex )LIROP(BoundsCheck)LIROP(BoundsCheckRange)LIROP(BoundsCheckLower )LIROP(SpectreMaskIndex)LIROP(LoadElementV)LIROP(InArray)LIROP (GuardElementNotHole)LIROP(LoadElementHole)LIROP(StoreElementV )LIROP(StoreElementT)LIROP(StoreHoleValueElement)LIROP(StoreElementHoleV )LIROP(StoreElementHoleT)LIROP(ArrayPopShift)LIROP(ArrayPush) LIROP(ArraySlice)LIROP(ArgumentsSlice)LIROP(FrameArgumentsSlice )LIROP(InlineArgumentsSlice)LIROP(NormalizeSliceTerm)LIROP(ArrayJoin )LIROP(ObjectKeys)LIROP(ObjectKeysLength)LIROP(LoadUnboxedScalar )LIROP(LoadUnboxedInt64)LIROP(LoadDataViewElement)LIROP(LoadDataViewElement64 )LIROP(LoadTypedArrayElementHole)LIROP(LoadTypedArrayElementHoleBigInt )LIROP(StoreUnboxedScalar)LIROP(StoreUnboxedInt64)LIROP(StoreDataViewElement )LIROP(StoreDataViewElement64)LIROP(StoreTypedArrayElementHole )LIROP(StoreTypedArrayElementHoleInt64)LIROP(AtomicIsLockFree )LIROP(CompareExchangeTypedArrayElement)LIROP(AtomicExchangeTypedArrayElement )LIROP(AtomicTypedArrayElementBinop)LIROP(AtomicTypedArrayElementBinopForEffect )LIROP(AtomicLoad64)LIROP(AtomicStore64)LIROP(CompareExchangeTypedArrayElement64 )LIROP(AtomicExchangeTypedArrayElement64)LIROP(AtomicTypedArrayElementBinop64 )LIROP(AtomicTypedArrayElementBinopForEffect64)LIROP(EffectiveAddress )LIROP(ClampIToUint8)LIROP(ClampDToUint8)LIROP(ClampVToUint8) LIROP(LoadScriptedProxyHandler)LIROP(CheckScriptedProxyGetResult )LIROP(IdToStringOrSymbol)LIROP(LoadFixedSlotV)LIROP(LoadFixedSlotAndAtomize )LIROP(LoadFixedSlotT)LIROP(LoadFixedSlotAndUnbox)LIROP(LoadDynamicSlotAndUnbox )LIROP(LoadElementAndUnbox)LIROP(LoadFixedSlotUnboxAndAtomize )LIROP(LoadDynamicSlotUnboxAndAtomize)LIROP(AddAndStoreSlot)LIROP (AllocateAndStoreSlot)LIROP(AddSlotAndCallAddPropHook)LIROP(StoreFixedSlotV )LIROP(StoreFixedSlotT)LIROP(GetNameCache)LIROP(CallGetIntrinsicValue )LIROP(GetPropSuperCache)LIROP(GetPropertyCache)LIROP(BindNameCache )LIROP(CallBindVar)LIROP(LoadDynamicSlotV)LIROP(LoadDynamicSlotAndAtomize )LIROP(StoreDynamicSlotV)LIROP(StoreDynamicSlotT)LIROP(StringLength )LIROP(Floor)LIROP(FloorF)LIROP(Ceil)LIROP(CeilF)LIROP(Round) LIROP(RoundF)LIROP(Trunc)LIROP(TruncF)LIROP(NearbyInt)LIROP(NearbyIntF )LIROP(FunctionEnvironment)LIROP(HomeObject)LIROP(HomeObjectSuperBase )LIROP(NewLexicalEnvironmentObject)LIROP(NewClassBodyEnvironmentObject )LIROP(NewVarEnvironmentObject)LIROP(MegamorphicSetElement)LIROP (CallDeleteProperty)LIROP(CallDeleteElement)LIROP(ObjectToIterator )LIROP(ValueToIterator)LIROP(IteratorHasIndicesAndBranch)LIROP (LoadSlotByIteratorIndex)LIROP(StoreSlotByIteratorIndex)LIROP (SetPropertyCache)LIROP(GetIteratorCache)LIROP(OptimizeSpreadCallCache )LIROP(IteratorMore)LIROP(IsNoIterAndBranch)LIROP(IteratorEnd )LIROP(CloseIterCache)LIROP(OptimizeGetIteratorCache)LIROP(ArgumentsLength )LIROP(GetFrameArgument)LIROP(GetFrameArgumentHole)LIROP(Rest )LIROP(Int32ToIntPtr)LIROP(NonNegativeIntPtrToInt32)LIROP(IntPtrToDouble )LIROP(AdjustDataViewLength)LIROP(BooleanToInt64)LIROP(StringToInt64 )LIROP(ValueToInt64)LIROP(TruncateBigIntToInt64)LIROP(Int64ToBigInt )LIROP(Uint64ToBigInt)LIROP(Int64ToIntPtr)LIROP(IntPtrToInt64 )LIROP(PostWriteBarrierO)LIROP(PostWriteBarrierS)LIROP(PostWriteBarrierBI )LIROP(PostWriteBarrierV)LIROP(PostWriteElementBarrierO)LIROP (PostWriteElementBarrierS)LIROP(PostWriteElementBarrierBI)LIROP (PostWriteElementBarrierV)LIROP(AssertCanElidePostWriteBarrier )LIROP(GuardObjectIdentity)LIROP(GuardSpecificFunction)LIROP( GuardSpecificAtom)LIROP(GuardSpecificSymbol)LIROP(GuardSpecificInt32 )LIROP(GuardStringToIndex)LIROP(GuardStringToInt32)LIROP(GuardStringToDouble )LIROP(GuardShape)LIROP(GuardMultipleShapes)LIROP(GuardProto) LIROP(GuardNullProto)LIROP(GuardIsNativeObject)LIROP(GuardGlobalGeneration )LIROP(GuardFuse)LIROP(GuardIsProxy)LIROP(GuardIsNotProxy)LIROP (GuardIsNotDOMProxy)LIROP(ProxyGet)LIROP(ProxyGetByValue)LIROP (ProxyHasProp)LIROP(ProxySet)LIROP(ProxySetByValue)LIROP(CallSetArrayLength )LIROP(MegamorphicLoadSlot)LIROP(MegamorphicLoadSlotByValue)LIROP (MegamorphicLoadSlotPermissive)LIROP(MegamorphicLoadSlotByValuePermissive )LIROP(MegamorphicStoreSlot)LIROP(MegamorphicHasProp)LIROP(SmallObjectVariableKeyHasProp )LIROP(GuardIsNotArrayBufferMaybeShared)LIROP(GuardIsTypedArray )LIROP(GuardIsFixedLengthTypedArray)LIROP(GuardIsResizableTypedArray )LIROP(GuardHasProxyHandler)LIROP(GuardNoDenseElements)LIROP( InCache)LIROP(HasOwnCache)LIROP(CheckPrivateFieldCache)LIROP( NewPrivateName)LIROP(InstanceOfO)LIROP(InstanceOfV)LIROP(InstanceOfCache )LIROP(IsCallableO)LIROP(IsCallableV)LIROP(IsConstructor)LIROP (IsCrossRealmArrayConstructor)LIROP(IsArrayO)LIROP(IsArrayV)LIROP (IsTypedArray)LIROP(IsObject)LIROP(IsObjectAndBranch)LIROP(IsNullOrUndefined )LIROP(IsNullOrUndefinedAndBranch)LIROP(HasClass)LIROP(GuardToClass )LIROP(GuardToEitherClass)LIROP(GuardToFunction)LIROP(ObjectClassToString )LIROP(WasmSelect)LIROP(WasmSelectI64)LIROP(WasmCompareAndSelect )LIROP(WasmAddOffset)LIROP(WasmAddOffset64)LIROP(WasmBoundsCheck )LIROP(WasmBoundsCheck64)LIROP(WasmBoundsCheckRange32)LIROP(WasmExtendU32Index )LIROP(WasmWrapU32Index)LIROP(WasmClampTable64Address)LIROP(WasmAlignmentCheck )LIROP(WasmAlignmentCheck64)LIROP(WasmLoadInstance)LIROP(WasmLoadInstance64 )LIROP(WasmHeapReg)LIROP(WasmLoad)LIROP(WasmLoadI64)LIROP(WasmStore )LIROP(WasmStoreI64)LIROP(AsmJSLoadHeap)LIROP(AsmJSStoreHeap) LIROP(WasmCompareExchangeHeap)LIROP(WasmFence)LIROP(WasmAtomicExchangeHeap )LIROP(WasmAtomicBinopHeap)LIROP(WasmAtomicBinopHeapForEffect )LIROP(WasmLoadSlot)LIROP(WasmLoadElement)LIROP(WasmLoadSlotI64 )LIROP(WasmLoadElementI64)LIROP(WasmStoreSlot)LIROP(WasmStoreSlotI64 )LIROP(WasmStoreElement)LIROP(WasmStoreElementI64)LIROP(WasmStoreElementRef )LIROP(WasmLoadTableElement)LIROP(WasmDerivedPointer)LIROP(WasmDerivedIndexPointer )LIROP(WasmStoreRef)LIROP(WasmPostWriteBarrierImmediate)LIROP (WasmPostWriteBarrierIndex)LIROP(WasmParameter)LIROP(WasmParameterI64 )LIROP(WasmReturn)LIROP(WasmReturnI64)LIROP(WasmReturnVoid)LIROP (WasmStackArg)LIROP(WasmStackArgI64)LIROP(WasmNullConstant)LIROP (WasmCallIndirectAdjunctSafepoint)LIROP(WasmCall)LIROP(WasmCallLandingPrePad )LIROP(WasmRegisterResult)LIROP(WasmRegisterPairResult)LIROP( WasmStackResultArea)LIROP(WasmStackResult)LIROP(WasmStackResult64 )LIROP(AssertRangeI)LIROP(AssertRangeD)LIROP(AssertRangeF)LIROP (AssertRangeV)LIROP(AssertClass)LIROP(AssertShape)LIROP(GuardValue )LIROP(GuardNullOrUndefined)LIROP(GuardIsNotObject)LIROP(GuardFunctionFlags )LIROP(GuardFunctionIsNonBuiltinCtor)LIROP(GuardFunctionKind) LIROP(GuardFunctionScript)LIROP(IncrementWarmUpCounter)LIROP( LexicalCheck)LIROP(ThrowRuntimeLexicalError)LIROP(ThrowMsg)LIROP (GlobalDeclInstantiation)LIROP(MemoryBarrier)LIROP(Debugger)LIROP (NewTarget)LIROP(Random)LIROP(CheckReturn)LIROP(CheckIsObj)LIROP (CheckObjCoercible)LIROP(CheckClassHeritage)LIROP(CheckThis)LIROP (CheckThisReinit)LIROP(Generator)LIROP(AsyncResolve)LIROP(AsyncReject )LIROP(AsyncAwait)LIROP(CanSkipAwait)LIROP(MaybeExtractAwaitValue )LIROP(DebugCheckSelfHosted)LIROP(IsPackedArray)LIROP(GuardArrayIsPacked )LIROP(GetPrototypeOf)LIROP(ObjectWithProto)LIROP(ObjectStaticProto )LIROP(BuiltinObject)LIROP(SuperFunction)LIROP(InitHomeObject )LIROP(IsTypedArrayConstructor)LIROP(LoadValueTag)LIROP(GuardTagNotEqual )LIROP(LoadWrapperTarget)LIROP(GuardHasGetterSetter)LIROP(GuardIsExtensible )LIROP(GuardInt32IsNonNegative)LIROP(GuardInt32Range)LIROP(GuardIndexIsNotDenseElement )LIROP(GuardIndexIsValidUpdateOrAdd)LIROP(CallAddOrUpdateSparseElement )LIROP(CallGetSparseElement)LIROP(CallNativeGetElement)LIROP( CallNativeGetElementSuper)LIROP(CallObjectHasSparseElement)LIROP (BigIntAsIntN)LIROP(GuardNonGCThing)LIROP(ToHashableNonGCThing )LIROP(ToHashableString)LIROP(ToHashableValue)LIROP(HashNonGCThing )LIROP(HashString)LIROP(HashSymbol)LIROP(HashBigInt)LIROP(HashObject )LIROP(HashValue)LIROP(SetObjectHasNonBigInt)LIROP(SetObjectHasBigInt )LIROP(SetObjectHasValue)LIROP(SetObjectHasValueVMCall)LIROP( SetObjectSize)LIROP(MapObjectHasNonBigInt)LIROP(MapObjectHasBigInt )LIROP(MapObjectHasValue)LIROP(MapObjectHasValueVMCall)LIROP( MapObjectGetNonBigInt)LIROP(MapObjectGetBigInt)LIROP(MapObjectGetValueVMCall )LIROP(MapObjectSize)LIROP(BigIntAsUintN)LIROP(IonToWasmCall) LIROP(IonToWasmCallV)LIROP(IonToWasmCallI64)LIROP(WasmAnyRefFromJSValue )LIROP(WasmAnyRefFromJSObject)LIROP(WasmAnyRefFromJSString)LIROP (WasmAnyRefIsJSString)LIROP(WasmTrapIfAnyRefIsNotJSString)LIROP (WasmAnyRefJSStringLength)LIROP(WasmNewI31Ref)LIROP(WasmI31RefGet )LIROP(Simd128)LIROP(WasmTernarySimd128)LIROP(WasmBinarySimd128 )LIROP(WasmBinarySimd128WithConstant)LIROP(WasmVariableShiftSimd128 )LIROP(WasmConstantShiftSimd128)LIROP(WasmSignReplicationSimd128 )LIROP(WasmShuffleSimd128)LIROP(WasmPermuteSimd128)LIROP(WasmReplaceLaneSimd128 )LIROP(WasmReplaceInt64LaneSimd128)LIROP(WasmScalarToSimd128) LIROP(WasmInt64ToSimd128)LIROP(WasmUnarySimd128)LIROP(WasmReduceSimd128 )LIROP(WasmReduceAndBranchSimd128)LIROP(WasmReduceSimd128ToInt64 )LIROP(WasmLoadLaneSimd128)LIROP(WasmStoreLaneSimd128)LIROP(Unbox )LIROP(UnboxFloatingPoint)LIROP(WasmUint32ToDouble)LIROP(WasmUint32ToFloat32 )LIROP(DivI)LIROP(ModI)LIROP(DivPowTwoI)LIROP(ModPowTwoI)LIROP (TableSwitch)LIROP(TableSwitchV)LIROP(MulI)LIROP(DivOrModI64) LIROP(UDivOrModI64)LIROP(DivOrModConstantI)LIROP(UDivOrMod)LIROP (UDivOrModConstant)LIROP(WasmTruncateToInt64)LIROP(Int64ToFloatingPoint )LIROP(AddDisposableResource)LIROP(TakeDisposeCapability)LIROP (CreateSuppressedError)LIROP(AtomicPause)LIROP(SetObjectDelete )LIROP(SetObjectAdd)LIROP(MapObjectGetValue)LIROP(MapObjectDelete )LIROP(MapObjectSet)LIROP(DateFillLocalTimeSlots)LIROP(DateHoursFromSecondsIntoYear )LIROP(DateMinutesFromSecondsIntoYear)LIROP(DateSecondsFromSecondsIntoYear ) |
8094 | # undef LIROP |
8095 | #endif |
8096 | case LNode::Opcode::Invalid: |
8097 | default: |
8098 | MOZ_CRASH("Invalid LIR op")do { do { } while (false); MOZ_ReportCrash("" "Invalid LIR op" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8098); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid LIR op" ")"); do { *((volatile int*)__null) = 8098; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
8099 | } |
8100 | |
8101 | #ifdef DEBUG1 |
8102 | if (!counts) { |
8103 | emitDebugResultChecks(*iter); |
8104 | } |
8105 | #endif |
8106 | } |
8107 | if (masm.oom()) { |
8108 | return false; |
8109 | } |
8110 | } |
8111 | |
8112 | JitSpew(JitSpew_Codegen, "==== END CodeGenerator::generateBody ====\n"); |
8113 | return true; |
8114 | } |
8115 | |
8116 | // Out-of-line object allocation for LNewArray. |
8117 | class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator> { |
8118 | LNewArray* lir_; |
8119 | |
8120 | public: |
8121 | explicit OutOfLineNewArray(LNewArray* lir) : lir_(lir) {} |
8122 | |
8123 | void accept(CodeGenerator* codegen) override { |
8124 | codegen->visitOutOfLineNewArray(this); |
8125 | } |
8126 | |
8127 | LNewArray* lir() const { return lir_; } |
8128 | }; |
8129 | |
8130 | void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) { |
8131 | Register objReg = ToRegister(lir->output()); |
8132 | |
8133 | MOZ_ASSERT(!lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8133); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { *((volatile int*)__null) = 8133; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8134 | saveLive(lir); |
8135 | |
8136 | JSObject* templateObject = lir->mir()->templateObject(); |
8137 | |
8138 | if (templateObject) { |
8139 | pushArg(ImmGCPtr(templateObject->shape())); |
8140 | pushArg(Imm32(lir->mir()->length())); |
8141 | |
8142 | using Fn = ArrayObject* (*)(JSContext*, uint32_t, Handle<Shape*>); |
8143 | callVM<Fn, NewArrayWithShape>(lir); |
8144 | } else { |
8145 | pushArg(Imm32(GenericObject)); |
8146 | pushArg(Imm32(lir->mir()->length())); |
8147 | |
8148 | using Fn = ArrayObject* (*)(JSContext*, uint32_t, NewObjectKind); |
8149 | callVM<Fn, NewArrayOperation>(lir); |
8150 | } |
8151 | |
8152 | masm.storeCallPointerResult(objReg); |
8153 | |
8154 | MOZ_ASSERT(!lir->safepoint()->liveRegs().has(objReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->safepoint()->liveRegs().has(objReg))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(!lir->safepoint()->liveRegs().has(objReg)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->safepoint()->liveRegs().has(objReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8154); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)" ")"); do { *((volatile int*)__null) = 8154; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8155 | restoreLive(lir); |
8156 | } |
8157 | |
8158 | void CodeGenerator::visitAtan2D(LAtan2D* lir) { |
8159 | FloatRegister y = ToFloatRegister(lir->y()); |
8160 | FloatRegister x = ToFloatRegister(lir->x()); |
8161 | |
8162 | using Fn = double (*)(double x, double y); |
8163 | masm.setupAlignedABICall(); |
8164 | masm.passABIArg(y, ABIType::Float64); |
8165 | masm.passABIArg(x, ABIType::Float64); |
8166 | masm.callWithABI<Fn, ecmaAtan2>(ABIType::Float64); |
8167 | |
8168 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8168); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 8168; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8169 | } |
8170 | |
8171 | void CodeGenerator::visitHypot(LHypot* lir) { |
8172 | uint32_t numArgs = lir->numArgs(); |
8173 | masm.setupAlignedABICall(); |
8174 | |
8175 | for (uint32_t i = 0; i < numArgs; ++i) { |
8176 | masm.passABIArg(ToFloatRegister(lir->getOperand(i)), ABIType::Float64); |
8177 | } |
8178 | |
8179 | switch (numArgs) { |
8180 | case 2: { |
8181 | using Fn = double (*)(double x, double y); |
8182 | masm.callWithABI<Fn, ecmaHypot>(ABIType::Float64); |
8183 | break; |
8184 | } |
8185 | case 3: { |
8186 | using Fn = double (*)(double x, double y, double z); |
8187 | masm.callWithABI<Fn, hypot3>(ABIType::Float64); |
8188 | break; |
8189 | } |
8190 | case 4: { |
8191 | using Fn = double (*)(double x, double y, double z, double w); |
8192 | masm.callWithABI<Fn, hypot4>(ABIType::Float64); |
8193 | break; |
8194 | } |
8195 | default: |
8196 | MOZ_CRASH("Unexpected number of arguments to hypot function.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected number of arguments to hypot function." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8196); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected number of arguments to hypot function." ")"); do { *((volatile int*)__null) = 8196; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
8197 | } |
8198 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 8198; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8199 | } |
8200 | |
8201 | void CodeGenerator::visitNewArray(LNewArray* lir) { |
8202 | Register objReg = ToRegister(lir->output()); |
8203 | Register tempReg = ToRegister(lir->temp()); |
8204 | DebugOnly<uint32_t> length = lir->mir()->length(); |
8205 | |
8206 | MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8206); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT" ")"); do { *((volatile int*)__null) = 8206; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8207 | |
8208 | if (lir->mir()->isVMCall()) { |
8209 | visitNewArrayCallVM(lir); |
8210 | return; |
8211 | } |
8212 | |
8213 | OutOfLineNewArray* ool = new (alloc()) OutOfLineNewArray(lir); |
8214 | addOutOfLineCode(ool, lir->mir()); |
8215 | TemplateObject templateObject(lir->mir()->templateObject()); |
8216 | #ifdef DEBUG1 |
8217 | size_t numInlineElements = gc::GetGCKindSlots(templateObject.getAllocKind()) - |
8218 | ObjectElements::VALUES_PER_HEADER; |
8219 | MOZ_ASSERT(length <= numInlineElements,do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= numInlineElements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length <= numInlineElements ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "length <= numInlineElements" " (" "Inline allocation only supports inline elements" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8220); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements" ")") ; do { *((volatile int*)__null) = 8220; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
8220 | "Inline allocation only supports inline elements")do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= numInlineElements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length <= numInlineElements ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "length <= numInlineElements" " (" "Inline allocation only supports inline elements" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8220); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements" ")") ; do { *((volatile int*)__null) = 8220; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8221 | #endif |
8222 | masm.createGCObject(objReg, tempReg, templateObject, |
8223 | lir->mir()->initialHeap(), ool->entry()); |
8224 | |
8225 | masm.bind(ool->rejoin()); |
8226 | } |
8227 | |
8228 | void CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray* ool) { |
8229 | visitNewArrayCallVM(ool->lir()); |
8230 | masm.jump(ool->rejoin()); |
8231 | } |
8232 | |
8233 | void CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) { |
8234 | Register lengthReg = ToRegister(lir->length()); |
8235 | Register objReg = ToRegister(lir->output()); |
8236 | Register tempReg = ToRegister(lir->temp0()); |
8237 | |
8238 | JSObject* templateObject = lir->mir()->templateObject(); |
8239 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
8240 | |
8241 | using Fn = ArrayObject* (*)(JSContext*, Handle<ArrayObject*>, int32_t length, |
8242 | gc::AllocSite*); |
8243 | OutOfLineCode* ool = oolCallVM<Fn, ArrayConstructorOneArg>( |
8244 | lir, ArgList(ImmGCPtr(templateObject), lengthReg, ImmPtr(nullptr)), |
8245 | StoreRegisterTo(objReg)); |
8246 | |
8247 | bool canInline = true; |
8248 | size_t inlineLength = 0; |
8249 | if (templateObject->as<ArrayObject>().hasFixedElements()) { |
8250 | size_t numSlots = |
8251 | gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); |
8252 | inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; |
8253 | } else { |
8254 | canInline = false; |
8255 | } |
8256 | |
8257 | if (canInline) { |
8258 | // Try to do the allocation inline if the template object is big enough |
8259 | // for the length in lengthReg. If the length is bigger we could still |
8260 | // use the template object and not allocate the elements, but it's more |
8261 | // efficient to do a single big allocation than (repeatedly) reallocating |
8262 | // the array later on when filling it. |
8263 | masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength), |
8264 | ool->entry()); |
8265 | |
8266 | TemplateObject templateObj(templateObject); |
8267 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, |
8268 | ool->entry()); |
8269 | |
8270 | size_t lengthOffset = NativeObject::offsetOfFixedElements() + |
8271 | ObjectElements::offsetOfLength(); |
8272 | masm.store32(lengthReg, Address(objReg, lengthOffset)); |
8273 | } else { |
8274 | masm.jump(ool->entry()); |
8275 | } |
8276 | |
8277 | masm.bind(ool->rejoin()); |
8278 | } |
8279 | |
8280 | void CodeGenerator::visitNewIterator(LNewIterator* lir) { |
8281 | Register objReg = ToRegister(lir->output()); |
8282 | Register tempReg = ToRegister(lir->temp0()); |
8283 | |
8284 | OutOfLineCode* ool; |
8285 | switch (lir->mir()->type()) { |
8286 | case MNewIterator::ArrayIterator: { |
8287 | using Fn = ArrayIteratorObject* (*)(JSContext*); |
8288 | ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(), |
8289 | StoreRegisterTo(objReg)); |
8290 | break; |
8291 | } |
8292 | case MNewIterator::StringIterator: { |
8293 | using Fn = StringIteratorObject* (*)(JSContext*); |
8294 | ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(), |
8295 | StoreRegisterTo(objReg)); |
8296 | break; |
8297 | } |
8298 | case MNewIterator::RegExpStringIterator: { |
8299 | using Fn = RegExpStringIteratorObject* (*)(JSContext*); |
8300 | ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(), |
8301 | StoreRegisterTo(objReg)); |
8302 | break; |
8303 | } |
8304 | default: |
8305 | MOZ_CRASH("unexpected iterator type")do { do { } while (false); MOZ_ReportCrash("" "unexpected iterator type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8305); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected iterator type" ")"); do { *((volatile int*)__null) = 8305; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
8306 | } |
8307 | |
8308 | TemplateObject templateObject(lir->mir()->templateObject()); |
8309 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
8310 | ool->entry()); |
8311 | |
8312 | masm.bind(ool->rejoin()); |
8313 | } |
8314 | |
8315 | void CodeGenerator::visitNewTypedArray(LNewTypedArray* lir) { |
8316 | Register objReg = ToRegister(lir->output()); |
8317 | Register tempReg = ToRegister(lir->temp0()); |
8318 | Register lengthReg = ToRegister(lir->temp1()); |
8319 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
8320 | |
8321 | JSObject* templateObject = lir->mir()->templateObject(); |
8322 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
8323 | |
8324 | auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>(); |
8325 | |
8326 | size_t n = ttemplate->length(); |
8327 | MOZ_ASSERT(n <= INT32_MAX,do { static_assert( mozilla::detail::AssertionConditionType< decltype(n <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)" " (" "Template objects are only created for int32 lengths" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")" ); do { *((volatile int*)__null) = 8328; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
8328 | "Template objects are only created for int32 lengths")do { static_assert( mozilla::detail::AssertionConditionType< decltype(n <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)" " (" "Template objects are only created for int32 lengths" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")" ); do { *((volatile int*)__null) = 8328; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8329 | |
8330 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length); |
8331 | OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
8332 | lir, ArgList(ImmGCPtr(templateObject), Imm32(n)), |
8333 | StoreRegisterTo(objReg)); |
8334 | |
8335 | TemplateObject templateObj(templateObject); |
8336 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry()); |
8337 | |
8338 | masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(), |
8339 | ttemplate, MacroAssembler::TypedArrayLength::Fixed); |
8340 | |
8341 | masm.bind(ool->rejoin()); |
8342 | } |
8343 | |
8344 | void CodeGenerator::visitNewTypedArrayDynamicLength( |
8345 | LNewTypedArrayDynamicLength* lir) { |
8346 | Register lengthReg = ToRegister(lir->length()); |
8347 | Register objReg = ToRegister(lir->output()); |
8348 | Register tempReg = ToRegister(lir->temp0()); |
8349 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
8350 | |
8351 | JSObject* templateObject = lir->mir()->templateObject(); |
8352 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
8353 | |
8354 | auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>(); |
8355 | |
8356 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length); |
8357 | OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
8358 | lir, ArgList(ImmGCPtr(templateObject), lengthReg), |
8359 | StoreRegisterTo(objReg)); |
8360 | |
8361 | // Volatile |lengthReg| is saved across the ABI call in |initTypedArraySlots|. |
8362 | MOZ_ASSERT_IF(lengthReg.volatile_(), liveRegs.has(lengthReg))do { if (lengthReg.volatile_()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(liveRegs.has(lengthReg ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(liveRegs.has(lengthReg)))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("liveRegs.has(lengthReg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "liveRegs.has(lengthReg)" ")"); do { *((volatile int*)__null) = 8362; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
8363 | |
8364 | TemplateObject templateObj(templateObject); |
8365 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry()); |
8366 | |
8367 | masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(), |
8368 | ttemplate, |
8369 | MacroAssembler::TypedArrayLength::Dynamic); |
8370 | |
8371 | masm.bind(ool->rejoin()); |
8372 | } |
8373 | |
8374 | void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) { |
8375 | pushArg(ToRegister(lir->array())); |
8376 | pushArg(ImmGCPtr(lir->mir()->templateObject())); |
8377 | |
8378 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject); |
8379 | callVM<Fn, js::NewTypedArrayWithTemplateAndArray>(lir); |
8380 | } |
8381 | |
8382 | void CodeGenerator::visitNewTypedArrayFromArrayBuffer( |
8383 | LNewTypedArrayFromArrayBuffer* lir) { |
8384 | pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::LengthIndex)); |
8385 | pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::ByteOffsetIndex)); |
8386 | pushArg(ToRegister(lir->arrayBuffer())); |
8387 | pushArg(ImmGCPtr(lir->mir()->templateObject())); |
8388 | |
8389 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject, |
8390 | HandleValue, HandleValue); |
8391 | callVM<Fn, js::NewTypedArrayWithTemplateAndBuffer>(lir); |
8392 | } |
8393 | |
8394 | void CodeGenerator::visitBindFunction(LBindFunction* lir) { |
8395 | Register target = ToRegister(lir->target()); |
8396 | Register temp1 = ToRegister(lir->temp0()); |
8397 | Register temp2 = ToRegister(lir->temp1()); |
8398 | |
8399 | // Try to allocate a new BoundFunctionObject we can pass to the VM function. |
8400 | // If this fails, we set temp1 to nullptr so we do the allocation in C++. |
8401 | TemplateObject templateObject(lir->mir()->templateObject()); |
8402 | Label allocOk, allocFailed; |
8403 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
8404 | &allocFailed); |
8405 | masm.jump(&allocOk); |
8406 | |
8407 | masm.bind(&allocFailed); |
8408 | masm.movePtr(ImmWord(0), temp1); |
8409 | |
8410 | masm.bind(&allocOk); |
8411 | |
8412 | // Set temp2 to the address of the first argument on the stack. |
8413 | // Note that the Value slots used for arguments are currently aligned for a |
8414 | // JIT call, even though that's not strictly necessary for calling into C++. |
8415 | uint32_t argc = lir->mir()->numStackArgs(); |
8416 | if (JitStackValueAlignment > 1) { |
8417 | argc = AlignBytes(argc, JitStackValueAlignment); |
8418 | } |
8419 | uint32_t unusedStack = UnusedStackBytesForCall(argc); |
8420 | masm.computeEffectiveAddress(Address(masm.getStackPointer(), unusedStack), |
8421 | temp2); |
8422 | |
8423 | pushArg(temp1); |
8424 | pushArg(Imm32(lir->mir()->numStackArgs())); |
8425 | pushArg(temp2); |
8426 | pushArg(target); |
8427 | |
8428 | using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*, |
8429 | uint32_t, Handle<BoundFunctionObject*>); |
8430 | callVM<Fn, js::BoundFunctionObject::functionBindImpl>(lir); |
8431 | } |
8432 | |
8433 | void CodeGenerator::visitNewBoundFunction(LNewBoundFunction* lir) { |
8434 | Register output = ToRegister(lir->output()); |
8435 | Register temp = ToRegister(lir->temp0()); |
8436 | |
8437 | JSObject* templateObj = lir->mir()->templateObj(); |
8438 | |
8439 | using Fn = BoundFunctionObject* (*)(JSContext*, Handle<BoundFunctionObject*>); |
8440 | OutOfLineCode* ool = oolCallVM<Fn, BoundFunctionObject::createWithTemplate>( |
8441 | lir, ArgList(ImmGCPtr(templateObj)), StoreRegisterTo(output)); |
8442 | |
8443 | TemplateObject templateObject(templateObj); |
8444 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
8445 | ool->entry()); |
8446 | |
8447 | masm.bind(ool->rejoin()); |
8448 | } |
8449 | |
8450 | // Out-of-line object allocation for JSOp::NewObject. |
8451 | class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator> { |
8452 | LNewObject* lir_; |
8453 | |
8454 | public: |
8455 | explicit OutOfLineNewObject(LNewObject* lir) : lir_(lir) {} |
8456 | |
8457 | void accept(CodeGenerator* codegen) override { |
8458 | codegen->visitOutOfLineNewObject(this); |
8459 | } |
8460 | |
8461 | LNewObject* lir() const { return lir_; } |
8462 | }; |
8463 | |
8464 | void CodeGenerator::visitNewObjectVMCall(LNewObject* lir) { |
8465 | Register objReg = ToRegister(lir->output()); |
8466 | |
8467 | MOZ_ASSERT(!lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8467); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { *((volatile int*)__null) = 8467; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8468 | saveLive(lir); |
8469 | |
8470 | JSObject* templateObject = lir->mir()->templateObject(); |
8471 | |
8472 | // If we're making a new object with a class prototype (that is, an object |
8473 | // that derives its class from its prototype instead of being |
8474 | // PlainObject::class_'d) from self-hosted code, we need a different init |
8475 | // function. |
8476 | switch (lir->mir()->mode()) { |
8477 | case MNewObject::ObjectLiteral: { |
8478 | MOZ_ASSERT(!templateObject)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!templateObject)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!templateObject))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!templateObject" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8478); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateObject" ")"); do { *((volatile int*)__null) = 8478; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8479 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
8480 | pushArg(ImmGCPtr(lir->mir()->block()->info().script())); |
8481 | |
8482 | using Fn = JSObject* (*)(JSContext*, HandleScript, const jsbytecode* pc); |
8483 | callVM<Fn, NewObjectOperation>(lir); |
8484 | break; |
8485 | } |
8486 | case MNewObject::ObjectCreate: { |
8487 | pushArg(ImmGCPtr(templateObject)); |
8488 | |
8489 | using Fn = PlainObject* (*)(JSContext*, Handle<PlainObject*>); |
8490 | callVM<Fn, ObjectCreateWithTemplate>(lir); |
8491 | break; |
8492 | } |
8493 | } |
8494 | |
8495 | masm.storeCallPointerResult(objReg); |
8496 | |
8497 | MOZ_ASSERT(!lir->safepoint()->liveRegs().has(objReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->safepoint()->liveRegs().has(objReg))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(!lir->safepoint()->liveRegs().has(objReg)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->safepoint()->liveRegs().has(objReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8497); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)" ")"); do { *((volatile int*)__null) = 8497; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8498 | restoreLive(lir); |
8499 | } |
8500 | |
8501 | static bool ShouldInitFixedSlots(MIRGenerator* gen, LNewPlainObject* lir, |
8502 | const Shape* shape, uint32_t nfixed) { |
8503 | // Look for StoreFixedSlot instructions following an object allocation |
8504 | // that write to this object before a GC is triggered or this object is |
8505 | // passed to a VM call. If all fixed slots will be initialized, the |
8506 | // allocation code doesn't need to set the slots to |undefined|. |
8507 | |
8508 | if (nfixed == 0) { |
8509 | return false; |
8510 | } |
8511 | |
8512 | #ifdef DEBUG1 |
8513 | // The bailAfter testing function can trigger a bailout between allocating the |
8514 | // object and initializing the slots. |
8515 | if (gen->options.ionBailAfterEnabled()) { |
8516 | return true; |
8517 | } |
8518 | #endif |
8519 | |
8520 | // Keep track of the fixed slots that are initialized. initializedSlots is |
8521 | // a bit mask with a bit for each slot. |
8522 | MOZ_ASSERT(nfixed <= NativeObject::MAX_FIXED_SLOTS)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nfixed <= NativeObject::MAX_FIXED_SLOTS)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nfixed <= NativeObject::MAX_FIXED_SLOTS))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nfixed <= NativeObject::MAX_FIXED_SLOTS" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8522); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nfixed <= NativeObject::MAX_FIXED_SLOTS" ")"); do { *((volatile int*)__null) = 8522; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8523 | static_assert(NativeObject::MAX_FIXED_SLOTS <= 32, |
8524 | "Slot bits must fit in 32 bits"); |
8525 | uint32_t initializedSlots = 0; |
8526 | uint32_t numInitialized = 0; |
8527 | |
8528 | MInstruction* allocMir = lir->mir(); |
8529 | MBasicBlock* block = allocMir->block(); |
8530 | |
8531 | // Skip the allocation instruction. |
8532 | MInstructionIterator iter = block->begin(allocMir); |
8533 | MOZ_ASSERT(*iter == allocMir)do { static_assert( mozilla::detail::AssertionConditionType< decltype(*iter == allocMir)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*iter == allocMir))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*iter == allocMir" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "*iter == allocMir" ")"); do { *((volatile int*)__null) = 8533; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8534 | iter++; |
8535 | |
8536 | // Handle the leading shape guard, if present. |
8537 | for (; iter != block->end(); iter++) { |
8538 | if (iter->isConstant()) { |
8539 | // This instruction won't trigger a GC or read object slots. |
8540 | continue; |
8541 | } |
8542 | if (iter->isGuardShape()) { |
8543 | auto* guard = iter->toGuardShape(); |
8544 | if (guard->object() != allocMir || guard->shape() != shape) { |
8545 | return true; |
8546 | } |
8547 | allocMir = guard; |
8548 | iter++; |
8549 | } |
8550 | break; |
8551 | } |
8552 | |
8553 | for (; iter != block->end(); iter++) { |
8554 | if (iter->isConstant() || iter->isPostWriteBarrier()) { |
8555 | // These instructions won't trigger a GC or read object slots. |
8556 | continue; |
8557 | } |
8558 | |
8559 | if (iter->isStoreFixedSlot()) { |
8560 | MStoreFixedSlot* store = iter->toStoreFixedSlot(); |
8561 | if (store->object() != allocMir) { |
8562 | return true; |
8563 | } |
8564 | |
8565 | // We may not initialize this object slot on allocation, so the |
8566 | // pre-barrier could read uninitialized memory. Simply disable |
8567 | // the barrier for this store: the object was just initialized |
8568 | // so the barrier is not necessary. |
8569 | store->setNeedsBarrier(false); |
8570 | |
8571 | uint32_t slot = store->slot(); |
8572 | MOZ_ASSERT(slot < nfixed)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot < nfixed)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot < nfixed))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot < nfixed" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot < nfixed" ")"); do { *((volatile int*)__null) = 8572; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8573 | if ((initializedSlots & (1 << slot)) == 0) { |
8574 | numInitialized++; |
8575 | initializedSlots |= (1 << slot); |
8576 | |
8577 | if (numInitialized == nfixed) { |
8578 | // All fixed slots will be initialized. |
8579 | MOZ_ASSERT(mozilla::CountPopulation32(initializedSlots) == nfixed)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::CountPopulation32(initializedSlots) == nfixed )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mozilla::CountPopulation32(initializedSlots) == nfixed ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mozilla::CountPopulation32(initializedSlots) == nfixed", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::CountPopulation32(initializedSlots) == nfixed" ")"); do { *((volatile int*)__null) = 8579; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8580 | return false; |
8581 | } |
8582 | } |
8583 | continue; |
8584 | } |
8585 | |
8586 | // Unhandled instruction, assume it bails or reads object slots. |
8587 | return true; |
8588 | } |
8589 | |
8590 | MOZ_CRASH("Shouldn't get here")do { do { } while (false); MOZ_ReportCrash("" "Shouldn't get here" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8590); AnnotateMozCrashReason("MOZ_CRASH(" "Shouldn't get here" ")"); do { *((volatile int*)__null) = 8590; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
8591 | } |
8592 | |
8593 | void CodeGenerator::visitNewObject(LNewObject* lir) { |
8594 | Register objReg = ToRegister(lir->output()); |
8595 | Register tempReg = ToRegister(lir->temp()); |
8596 | |
8597 | if (lir->mir()->isVMCall()) { |
8598 | visitNewObjectVMCall(lir); |
8599 | return; |
8600 | } |
8601 | |
8602 | OutOfLineNewObject* ool = new (alloc()) OutOfLineNewObject(lir); |
8603 | addOutOfLineCode(ool, lir->mir()); |
8604 | |
8605 | TemplateObject templateObject(lir->mir()->templateObject()); |
8606 | |
8607 | masm.createGCObject(objReg, tempReg, templateObject, |
8608 | lir->mir()->initialHeap(), ool->entry()); |
8609 | |
8610 | masm.bind(ool->rejoin()); |
8611 | } |
8612 | |
8613 | void CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject* ool) { |
8614 | visitNewObjectVMCall(ool->lir()); |
8615 | masm.jump(ool->rejoin()); |
8616 | } |
8617 | |
8618 | void CodeGenerator::visitNewPlainObject(LNewPlainObject* lir) { |
8619 | Register objReg = ToRegister(lir->output()); |
8620 | Register temp0Reg = ToRegister(lir->temp0()); |
8621 | Register temp1Reg = ToRegister(lir->temp1()); |
8622 | Register shapeReg = ToRegister(lir->temp2()); |
8623 | |
8624 | auto* mir = lir->mir(); |
8625 | const Shape* shape = mir->shape(); |
8626 | gc::Heap initialHeap = mir->initialHeap(); |
8627 | gc::AllocKind allocKind = mir->allocKind(); |
8628 | |
8629 | using Fn = |
8630 | JSObject* (*)(JSContext*, Handle<SharedShape*>, gc::AllocKind, gc::Heap); |
8631 | OutOfLineCode* ool = oolCallVM<Fn, NewPlainObjectOptimizedFallback>( |
8632 | lir, |
8633 | ArgList(ImmGCPtr(shape), Imm32(int32_t(allocKind)), |
8634 | Imm32(int32_t(initialHeap))), |
8635 | StoreRegisterTo(objReg)); |
8636 | |
8637 | bool initContents = |
8638 | ShouldInitFixedSlots(gen, lir, shape, mir->numFixedSlots()); |
8639 | |
8640 | masm.movePtr(ImmGCPtr(shape), shapeReg); |
8641 | masm.createPlainGCObject( |
8642 | objReg, shapeReg, temp0Reg, temp1Reg, mir->numFixedSlots(), |
8643 | mir->numDynamicSlots(), allocKind, initialHeap, ool->entry(), |
8644 | AllocSiteInput(gc::CatchAllAllocSite::Optimized), initContents); |
8645 | |
8646 | #ifdef DEBUG1 |
8647 | // ShouldInitFixedSlots expects that the leading GuardShape will never fail, |
8648 | // so ensure the newly created object has the correct shape. Should the guard |
8649 | // ever fail, we may end up with uninitialized fixed slots, which can confuse |
8650 | // the GC. |
8651 | Label ok; |
8652 | masm.branchTestObjShape(Assembler::Equal, objReg, shape, temp0Reg, objReg, |
8653 | &ok); |
8654 | masm.assumeUnreachable("Newly created object has the correct shape"); |
8655 | masm.bind(&ok); |
8656 | #endif |
8657 | |
8658 | masm.bind(ool->rejoin()); |
8659 | } |
8660 | |
8661 | void CodeGenerator::visitNewArrayObject(LNewArrayObject* lir) { |
8662 | Register objReg = ToRegister(lir->output()); |
8663 | Register temp0Reg = ToRegister(lir->temp0()); |
8664 | Register shapeReg = ToRegister(lir->temp1()); |
8665 | |
8666 | auto* mir = lir->mir(); |
8667 | uint32_t arrayLength = mir->length(); |
8668 | |
8669 | gc::AllocKind allocKind = GuessArrayGCKind(arrayLength); |
8670 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(allocKind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8670); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 8670; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8671 | allocKind = ForegroundToBackgroundAllocKind(allocKind); |
8672 | |
8673 | uint32_t slotCount = GetGCKindSlots(allocKind); |
8674 | MOZ_ASSERT(slotCount >= ObjectElements::VALUES_PER_HEADER)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotCount >= ObjectElements::VALUES_PER_HEADER)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(slotCount >= ObjectElements::VALUES_PER_HEADER))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("slotCount >= ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotCount >= ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 8674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8675 | uint32_t arrayCapacity = slotCount - ObjectElements::VALUES_PER_HEADER; |
8676 | |
8677 | const Shape* shape = mir->shape(); |
8678 | |
8679 | NewObjectKind objectKind = |
8680 | mir->initialHeap() == gc::Heap::Tenured ? TenuredObject : GenericObject; |
8681 | |
8682 | using Fn = |
8683 | ArrayObject* (*)(JSContext*, uint32_t, gc::AllocKind, NewObjectKind); |
8684 | OutOfLineCode* ool = oolCallVM<Fn, NewArrayObjectOptimizedFallback>( |
8685 | lir, |
8686 | ArgList(Imm32(arrayLength), Imm32(int32_t(allocKind)), Imm32(objectKind)), |
8687 | StoreRegisterTo(objReg)); |
8688 | |
8689 | masm.movePtr(ImmPtr(shape), shapeReg); |
8690 | masm.createArrayWithFixedElements( |
8691 | objReg, shapeReg, temp0Reg, InvalidReg, arrayLength, arrayCapacity, 0, 0, |
8692 | allocKind, mir->initialHeap(), ool->entry(), |
8693 | AllocSiteInput(gc::CatchAllAllocSite::Optimized)); |
8694 | masm.bind(ool->rejoin()); |
8695 | } |
8696 | |
8697 | void CodeGenerator::visitNewNamedLambdaObject(LNewNamedLambdaObject* lir) { |
8698 | Register objReg = ToRegister(lir->output()); |
8699 | Register tempReg = ToRegister(lir->temp0()); |
8700 | const CompileInfo& info = lir->mir()->block()->info(); |
8701 | |
8702 | using Fn = js::NamedLambdaObject* (*)(JSContext*, HandleFunction); |
8703 | OutOfLineCode* ool = oolCallVM<Fn, NamedLambdaObject::createWithoutEnclosing>( |
8704 | lir, ArgList(info.funMaybeLazy()), StoreRegisterTo(objReg)); |
8705 | |
8706 | TemplateObject templateObject(lir->mir()->templateObj()); |
8707 | |
8708 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
8709 | ool->entry()); |
8710 | |
8711 | masm.bind(ool->rejoin()); |
8712 | } |
8713 | |
8714 | void CodeGenerator::visitNewCallObject(LNewCallObject* lir) { |
8715 | Register objReg = ToRegister(lir->output()); |
8716 | Register tempReg = ToRegister(lir->temp0()); |
8717 | |
8718 | CallObject* templateObj = lir->mir()->templateObject(); |
8719 | |
8720 | using Fn = CallObject* (*)(JSContext*, Handle<SharedShape*>); |
8721 | OutOfLineCode* ool = oolCallVM<Fn, CallObject::createWithShape>( |
8722 | lir, ArgList(ImmGCPtr(templateObj->sharedShape())), |
8723 | StoreRegisterTo(objReg)); |
8724 | |
8725 | // Inline call object creation, using the OOL path only for tricky cases. |
8726 | TemplateObject templateObject(templateObj); |
8727 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
8728 | ool->entry()); |
8729 | |
8730 | masm.bind(ool->rejoin()); |
8731 | } |
8732 | |
8733 | void CodeGenerator::visitNewMapObject(LNewMapObject* lir) { |
8734 | Register output = ToRegister(lir->output()); |
8735 | Register temp = ToRegister(lir->temp0()); |
8736 | |
8737 | // Note: pass nullptr for |proto| to use |Map.prototype|. |
8738 | using Fn = MapObject* (*)(JSContext*, HandleObject); |
8739 | auto* ool = oolCallVM<Fn, MapObject::create>(lir, ArgList(ImmPtr(nullptr)), |
8740 | StoreRegisterTo(output)); |
8741 | |
8742 | TemplateObject templateObject(lir->mir()->templateObject()); |
8743 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
8744 | ool->entry()); |
8745 | masm.bind(ool->rejoin()); |
8746 | } |
8747 | |
8748 | void CodeGenerator::visitNewSetObject(LNewSetObject* lir) { |
8749 | Register output = ToRegister(lir->output()); |
8750 | Register temp = ToRegister(lir->temp0()); |
8751 | |
8752 | // Note: pass nullptr for |proto| to use |Set.prototype|. |
8753 | using Fn = SetObject* (*)(JSContext*, HandleObject); |
8754 | auto* ool = oolCallVM<Fn, SetObject::create>(lir, ArgList(ImmPtr(nullptr)), |
8755 | StoreRegisterTo(output)); |
8756 | |
8757 | TemplateObject templateObject(lir->mir()->templateObject()); |
8758 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
8759 | ool->entry()); |
8760 | masm.bind(ool->rejoin()); |
8761 | } |
8762 | |
8763 | void CodeGenerator::visitNewMapObjectFromIterable( |
8764 | LNewMapObjectFromIterable* lir) { |
8765 | ValueOperand iterable = |
8766 | ToValue(lir, LNewMapObjectFromIterable::IterableIndex); |
8767 | Register output = ToRegister(lir->output()); |
8768 | Register temp1 = ToRegister(lir->temp0()); |
8769 | Register temp2 = ToRegister(lir->temp1()); |
8770 | |
8771 | // Allocate a new MapObject. If this fails we pass nullptr for |
8772 | // allocatedFromJit. |
8773 | Label failedAlloc, vmCall, done; |
8774 | TemplateObject templateObject(lir->mir()->templateObject()); |
8775 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
8776 | &failedAlloc); |
8777 | |
8778 | // We're done if |iterable| is null or undefined. |
8779 | masm.branchIfNotNullOrUndefined(iterable, &vmCall); |
8780 | masm.movePtr(temp1, output); |
8781 | masm.jump(&done); |
8782 | |
8783 | masm.bind(&failedAlloc); |
8784 | masm.movePtr(ImmPtr(nullptr), temp1); |
8785 | |
8786 | masm.bind(&vmCall); |
8787 | |
8788 | pushArg(temp1); // allocatedFromJit |
8789 | pushArg(iterable); |
8790 | pushArg(ImmPtr(nullptr)); // proto |
8791 | |
8792 | using Fn = MapObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>, |
8793 | Handle<MapObject*>); |
8794 | callVM<Fn, MapObject::createFromIterable>(lir); |
8795 | |
8796 | masm.bind(&done); |
8797 | } |
8798 | |
8799 | void CodeGenerator::visitNewSetObjectFromIterable( |
8800 | LNewSetObjectFromIterable* lir) { |
8801 | ValueOperand iterable = |
8802 | ToValue(lir, LNewSetObjectFromIterable::IterableIndex); |
8803 | Register output = ToRegister(lir->output()); |
8804 | Register temp1 = ToRegister(lir->temp0()); |
8805 | Register temp2 = ToRegister(lir->temp1()); |
8806 | |
8807 | // Allocate a new SetObject. If this fails we pass nullptr for |
8808 | // allocatedFromJit. |
8809 | Label failedAlloc, vmCall, done; |
8810 | TemplateObject templateObject(lir->mir()->templateObject()); |
8811 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
8812 | &failedAlloc); |
8813 | |
8814 | // We're done if |iterable| is null or undefined. |
8815 | masm.branchIfNotNullOrUndefined(iterable, &vmCall); |
8816 | masm.movePtr(temp1, output); |
8817 | masm.jump(&done); |
8818 | |
8819 | masm.bind(&failedAlloc); |
8820 | masm.movePtr(ImmPtr(nullptr), temp1); |
8821 | |
8822 | masm.bind(&vmCall); |
8823 | |
8824 | pushArg(temp1); // allocatedFromJit |
8825 | pushArg(iterable); |
8826 | pushArg(ImmPtr(nullptr)); // proto |
8827 | |
8828 | using Fn = SetObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>, |
8829 | Handle<SetObject*>); |
8830 | callVM<Fn, SetObject::createFromIterable>(lir); |
8831 | |
8832 | masm.bind(&done); |
8833 | } |
8834 | |
8835 | void CodeGenerator::visitNewStringObject(LNewStringObject* lir) { |
8836 | Register input = ToRegister(lir->input()); |
8837 | Register output = ToRegister(lir->output()); |
8838 | Register temp = ToRegister(lir->temp0()); |
8839 | |
8840 | StringObject* templateObj = lir->mir()->templateObj(); |
8841 | |
8842 | using Fn = JSObject* (*)(JSContext*, HandleString); |
8843 | OutOfLineCode* ool = oolCallVM<Fn, NewStringObject>(lir, ArgList(input), |
8844 | StoreRegisterTo(output)); |
8845 | |
8846 | TemplateObject templateObject(templateObj); |
8847 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
8848 | ool->entry()); |
8849 | |
8850 | masm.loadStringLength(input, temp); |
8851 | |
8852 | masm.storeValue(JSVAL_TYPE_STRING, input, |
8853 | Address(output, StringObject::offsetOfPrimitiveValue())); |
8854 | masm.storeValue(JSVAL_TYPE_INT32, temp, |
8855 | Address(output, StringObject::offsetOfLength())); |
8856 | |
8857 | masm.bind(ool->rejoin()); |
8858 | } |
8859 | |
8860 | void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) { |
8861 | Register obj = ToRegister(lir->object()); |
8862 | Register value = ToRegister(lir->value()); |
8863 | |
8864 | pushArg(value); |
8865 | pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex)); |
8866 | pushArg(obj); |
8867 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
8868 | |
8869 | using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue, |
8870 | HandleObject); |
8871 | callVM<Fn, InitElemGetterSetterOperation>(lir); |
8872 | } |
8873 | |
8874 | void CodeGenerator::visitMutateProto(LMutateProto* lir) { |
8875 | Register objReg = ToRegister(lir->object()); |
8876 | |
8877 | pushArg(ToValue(lir, LMutateProto::ValueIndex)); |
8878 | pushArg(objReg); |
8879 | |
8880 | using Fn = |
8881 | bool (*)(JSContext* cx, Handle<PlainObject*> obj, HandleValue value); |
8882 | callVM<Fn, MutatePrototype>(lir); |
8883 | } |
8884 | |
8885 | void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) { |
8886 | Register obj = ToRegister(lir->object()); |
8887 | Register value = ToRegister(lir->value()); |
8888 | |
8889 | pushArg(value); |
8890 | pushArg(ImmGCPtr(lir->mir()->name())); |
8891 | pushArg(obj); |
8892 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
8893 | |
8894 | using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, |
8895 | Handle<PropertyName*>, HandleObject); |
8896 | callVM<Fn, InitPropGetterSetterOperation>(lir); |
8897 | } |
8898 | |
8899 | void CodeGenerator::visitCreateThis(LCreateThis* lir) { |
8900 | const LAllocation* callee = lir->callee(); |
8901 | const LAllocation* newTarget = lir->newTarget(); |
8902 | |
8903 | if (newTarget->isConstant()) { |
8904 | pushArg(ImmGCPtr(&newTarget->toConstant()->toObject())); |
8905 | } else { |
8906 | pushArg(ToRegister(newTarget)); |
8907 | } |
8908 | |
8909 | if (callee->isConstant()) { |
8910 | pushArg(ImmGCPtr(&callee->toConstant()->toObject())); |
8911 | } else { |
8912 | pushArg(ToRegister(callee)); |
8913 | } |
8914 | |
8915 | using Fn = bool (*)(JSContext* cx, HandleObject callee, |
8916 | HandleObject newTarget, MutableHandleValue rval); |
8917 | callVM<Fn, jit::CreateThisFromIon>(lir); |
8918 | } |
8919 | |
8920 | void CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) { |
8921 | // This should be getting constructed in the first block only, and not any OSR |
8922 | // entry blocks. |
8923 | MOZ_ASSERT(lir->mir()->block()->id() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->block()->id() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->block()->id() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->block()->id() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->block()->id() == 0" ")"); do { *((volatile int*)__null) = 8923; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8924 | |
8925 | Register callObj = ToRegister(lir->callObject()); |
8926 | Register temp0 = ToRegister(lir->temp0()); |
8927 | Label done; |
8928 | |
8929 | if (ArgumentsObject* templateObj = lir->mir()->templateObject()) { |
8930 | Register objTemp = ToRegister(lir->temp1()); |
8931 | Register cxTemp = ToRegister(lir->temp2()); |
8932 | |
8933 | masm.Push(callObj); |
8934 | |
8935 | // Try to allocate an arguments object. This will leave the reserved |
8936 | // slots uninitialized, so it's important we don't GC until we |
8937 | // initialize these slots in ArgumentsObject::finishForIonPure. |
8938 | Label failure; |
8939 | TemplateObject templateObject(templateObj); |
8940 | masm.createGCObject(objTemp, temp0, templateObject, gc::Heap::Default, |
8941 | &failure, |
8942 | /* initContents = */ false); |
8943 | |
8944 | masm.moveStackPtrTo(temp0); |
8945 | masm.addPtr(Imm32(masm.framePushed()), temp0); |
8946 | |
8947 | using Fn = ArgumentsObject* (*)(JSContext* cx, jit::JitFrameLayout* frame, |
8948 | JSObject* scopeChain, ArgumentsObject* obj); |
8949 | masm.setupAlignedABICall(); |
8950 | masm.loadJSContext(cxTemp); |
8951 | masm.passABIArg(cxTemp); |
8952 | masm.passABIArg(temp0); |
8953 | masm.passABIArg(callObj); |
8954 | masm.passABIArg(objTemp); |
8955 | |
8956 | masm.callWithABI<Fn, ArgumentsObject::finishForIonPure>(); |
8957 | masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure); |
8958 | |
8959 | // Discard saved callObj on the stack. |
8960 | masm.addToStackPtr(Imm32(sizeof(uintptr_t))); |
8961 | masm.jump(&done); |
8962 | |
8963 | masm.bind(&failure); |
8964 | masm.Pop(callObj); |
8965 | } |
8966 | |
8967 | masm.moveStackPtrTo(temp0); |
8968 | masm.addPtr(Imm32(frameSize()), temp0); |
8969 | |
8970 | pushArg(callObj); |
8971 | pushArg(temp0); |
8972 | |
8973 | using Fn = ArgumentsObject* (*)(JSContext*, JitFrameLayout*, HandleObject); |
8974 | callVM<Fn, ArgumentsObject::createForIon>(lir); |
8975 | |
8976 | masm.bind(&done); |
8977 | } |
8978 | |
8979 | void CodeGenerator::visitCreateInlinedArgumentsObject( |
8980 | LCreateInlinedArgumentsObject* lir) { |
8981 | Register callObj = ToRegister(lir->getCallObject()); |
8982 | Register callee = ToRegister(lir->getCallee()); |
8983 | Register argsAddress = ToRegister(lir->temp1()); |
8984 | Register argsObj = ToRegister(lir->temp2()); |
8985 | |
8986 | // TODO: Do we have to worry about alignment here? |
8987 | |
8988 | // Create a contiguous array of values for ArgumentsObject::create |
8989 | // by pushing the arguments onto the stack in reverse order. |
8990 | uint32_t argc = lir->mir()->numActuals(); |
8991 | for (uint32_t i = 0; i < argc; i++) { |
8992 | uint32_t argNum = argc - i - 1; |
8993 | uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(argNum); |
8994 | ConstantOrRegister arg = |
8995 | toConstantOrRegister(lir, index, lir->mir()->getArg(argNum)->type()); |
8996 | masm.Push(arg); |
8997 | } |
8998 | masm.moveStackPtrTo(argsAddress); |
8999 | |
9000 | Label done; |
9001 | if (ArgumentsObject* templateObj = lir->mir()->templateObject()) { |
9002 | LiveRegisterSet liveRegs; |
9003 | liveRegs.add(callObj); |
9004 | liveRegs.add(callee); |
9005 | |
9006 | masm.PushRegsInMask(liveRegs); |
9007 | |
9008 | // We are free to clobber all registers, as LCreateInlinedArgumentsObject is |
9009 | // a call instruction. |
9010 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
9011 | allRegs.take(callObj); |
9012 | allRegs.take(callee); |
9013 | allRegs.take(argsObj); |
9014 | allRegs.take(argsAddress); |
9015 | |
9016 | Register temp3 = allRegs.takeAny(); |
9017 | Register temp4 = allRegs.takeAny(); |
9018 | |
9019 | // Try to allocate an arguments object. This will leave the reserved slots |
9020 | // uninitialized, so it's important we don't GC until we initialize these |
9021 | // slots in ArgumentsObject::finishForIonPure. |
9022 | Label failure; |
9023 | TemplateObject templateObject(templateObj); |
9024 | masm.createGCObject(argsObj, temp3, templateObject, gc::Heap::Default, |
9025 | &failure, |
9026 | /* initContents = */ false); |
9027 | |
9028 | Register numActuals = temp3; |
9029 | masm.move32(Imm32(argc), numActuals); |
9030 | |
9031 | using Fn = ArgumentsObject* (*)(JSContext*, JSObject*, JSFunction*, Value*, |
9032 | uint32_t, ArgumentsObject*); |
9033 | masm.setupAlignedABICall(); |
9034 | masm.loadJSContext(temp4); |
9035 | masm.passABIArg(temp4); |
9036 | masm.passABIArg(callObj); |
9037 | masm.passABIArg(callee); |
9038 | masm.passABIArg(argsAddress); |
9039 | masm.passABIArg(numActuals); |
9040 | masm.passABIArg(argsObj); |
9041 | |
9042 | masm.callWithABI<Fn, ArgumentsObject::finishInlineForIonPure>(); |
9043 | masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure); |
9044 | |
9045 | // Discard saved callObj, callee, and values array on the stack. |
9046 | masm.addToStackPtr( |
9047 | Imm32(MacroAssembler::PushRegsInMaskSizeInBytes(liveRegs) + |
9048 | argc * sizeof(Value))); |
9049 | masm.jump(&done); |
9050 | |
9051 | masm.bind(&failure); |
9052 | masm.PopRegsInMask(liveRegs); |
9053 | |
9054 | // Reload argsAddress because it may have been overridden. |
9055 | masm.moveStackPtrTo(argsAddress); |
9056 | } |
9057 | |
9058 | pushArg(Imm32(argc)); |
9059 | pushArg(callObj); |
9060 | pushArg(callee); |
9061 | pushArg(argsAddress); |
9062 | |
9063 | using Fn = ArgumentsObject* (*)(JSContext*, Value*, HandleFunction, |
9064 | HandleObject, uint32_t); |
9065 | callVM<Fn, ArgumentsObject::createForInlinedIon>(lir); |
9066 | |
9067 | // Discard the array of values. |
9068 | masm.freeStack(argc * sizeof(Value)); |
9069 | |
9070 | masm.bind(&done); |
9071 | } |
9072 | |
9073 | template <class GetInlinedArgument> |
9074 | void CodeGenerator::emitGetInlinedArgument(GetInlinedArgument* lir, |
9075 | Register index, |
9076 | ValueOperand output) { |
9077 | uint32_t numActuals = lir->mir()->numActuals(); |
9078 | MOZ_ASSERT(numActuals <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(numActuals <= ArgumentsObject::MaxInlinedArgs)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(numActuals <= ArgumentsObject::MaxInlinedArgs))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("numActuals <= ArgumentsObject::MaxInlinedArgs" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numActuals <= ArgumentsObject::MaxInlinedArgs" ")"); do { *((volatile int*)__null) = 9078; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9079 | |
9080 | // The index has already been bounds-checked, so the code we |
9081 | // generate here should be unreachable. We can end up in this |
9082 | // situation in self-hosted code using GetArgument(), or in a |
9083 | // monomorphically inlined function if we've inlined some CacheIR |
9084 | // that was created for a different caller. |
9085 | if (numActuals == 0) { |
9086 | masm.assumeUnreachable("LGetInlinedArgument: invalid index"); |
9087 | return; |
9088 | } |
9089 | |
9090 | // Check the first n-1 possible indices. |
9091 | Label done; |
9092 | for (uint32_t i = 0; i < numActuals - 1; i++) { |
9093 | Label skip; |
9094 | ConstantOrRegister arg = toConstantOrRegister( |
9095 | lir, GetInlinedArgument::ArgIndex(i), lir->mir()->getArg(i)->type()); |
9096 | masm.branch32(Assembler::NotEqual, index, Imm32(i), &skip); |
9097 | masm.moveValue(arg, output); |
9098 | |
9099 | masm.jump(&done); |
9100 | masm.bind(&skip); |
9101 | } |
9102 | |
9103 | #ifdef DEBUG1 |
9104 | Label skip; |
9105 | masm.branch32(Assembler::Equal, index, Imm32(numActuals - 1), &skip); |
9106 | masm.assumeUnreachable("LGetInlinedArgument: invalid index"); |
9107 | masm.bind(&skip); |
9108 | #endif |
9109 | |
9110 | // The index has already been bounds-checked, so load the last argument. |
9111 | uint32_t lastIdx = numActuals - 1; |
9112 | ConstantOrRegister arg = |
9113 | toConstantOrRegister(lir, GetInlinedArgument::ArgIndex(lastIdx), |
9114 | lir->mir()->getArg(lastIdx)->type()); |
9115 | masm.moveValue(arg, output); |
9116 | masm.bind(&done); |
9117 | } |
9118 | |
9119 | void CodeGenerator::visitGetInlinedArgument(LGetInlinedArgument* lir) { |
9120 | Register index = ToRegister(lir->getIndex()); |
9121 | ValueOperand output = ToOutValue(lir); |
9122 | |
9123 | emitGetInlinedArgument(lir, index, output); |
9124 | } |
9125 | |
9126 | void CodeGenerator::visitGetInlinedArgumentHole(LGetInlinedArgumentHole* lir) { |
9127 | Register index = ToRegister(lir->getIndex()); |
9128 | ValueOperand output = ToOutValue(lir); |
9129 | |
9130 | uint32_t numActuals = lir->mir()->numActuals(); |
9131 | |
9132 | if (numActuals == 0) { |
9133 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
9134 | masm.moveValue(UndefinedValue(), output); |
9135 | return; |
9136 | } |
9137 | |
9138 | Label outOfBounds, done; |
9139 | masm.branch32(Assembler::AboveOrEqual, index, Imm32(numActuals), |
9140 | &outOfBounds); |
9141 | |
9142 | emitGetInlinedArgument(lir, index, output); |
9143 | masm.jump(&done); |
9144 | |
9145 | masm.bind(&outOfBounds); |
9146 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
9147 | masm.moveValue(UndefinedValue(), output); |
9148 | |
9149 | masm.bind(&done); |
9150 | } |
9151 | |
9152 | void CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir) { |
9153 | Register temp = ToRegister(lir->temp0()); |
9154 | Register argsObj = ToRegister(lir->argsObject()); |
9155 | ValueOperand out = ToOutValue(lir); |
9156 | |
9157 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
9158 | temp); |
9159 | Address argAddr(temp, ArgumentsData::offsetOfArgs() + |
9160 | lir->mir()->argno() * sizeof(Value)); |
9161 | masm.loadValue(argAddr, out); |
9162 | #ifdef DEBUG1 |
9163 | Label success; |
9164 | masm.branchTestMagic(Assembler::NotEqual, out, &success); |
9165 | masm.assumeUnreachable( |
9166 | "Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); |
9167 | masm.bind(&success); |
9168 | #endif |
9169 | } |
9170 | |
9171 | void CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir) { |
9172 | Register temp = ToRegister(lir->getTemp(0)); |
9173 | Register argsObj = ToRegister(lir->argsObject()); |
9174 | ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex); |
9175 | |
9176 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
9177 | temp); |
9178 | Address argAddr(temp, ArgumentsData::offsetOfArgs() + |
9179 | lir->mir()->argno() * sizeof(Value)); |
9180 | emitPreBarrier(argAddr); |
9181 | #ifdef DEBUG1 |
9182 | Label success; |
9183 | masm.branchTestMagic(Assembler::NotEqual, argAddr, &success); |
9184 | masm.assumeUnreachable( |
9185 | "Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); |
9186 | masm.bind(&success); |
9187 | #endif |
9188 | masm.storeValue(value, argAddr); |
9189 | } |
9190 | |
9191 | void CodeGenerator::visitLoadArgumentsObjectArg(LLoadArgumentsObjectArg* lir) { |
9192 | Register temp = ToRegister(lir->temp0()); |
9193 | Register argsObj = ToRegister(lir->argsObject()); |
9194 | Register index = ToRegister(lir->index()); |
9195 | ValueOperand out = ToOutValue(lir); |
9196 | |
9197 | Label bail; |
9198 | masm.loadArgumentsObjectElement(argsObj, index, out, temp, &bail); |
9199 | bailoutFrom(&bail, lir->snapshot()); |
9200 | } |
9201 | |
9202 | void CodeGenerator::visitLoadArgumentsObjectArgHole( |
9203 | LLoadArgumentsObjectArgHole* lir) { |
9204 | Register temp = ToRegister(lir->temp0()); |
9205 | Register argsObj = ToRegister(lir->argsObject()); |
9206 | Register index = ToRegister(lir->index()); |
9207 | ValueOperand out = ToOutValue(lir); |
9208 | |
9209 | Label bail; |
9210 | masm.loadArgumentsObjectElementHole(argsObj, index, out, temp, &bail); |
9211 | bailoutFrom(&bail, lir->snapshot()); |
9212 | } |
9213 | |
9214 | void CodeGenerator::visitInArgumentsObjectArg(LInArgumentsObjectArg* lir) { |
9215 | Register temp = ToRegister(lir->temp0()); |
9216 | Register argsObj = ToRegister(lir->argsObject()); |
9217 | Register index = ToRegister(lir->index()); |
9218 | Register out = ToRegister(lir->output()); |
9219 | |
9220 | Label bail; |
9221 | masm.loadArgumentsObjectElementExists(argsObj, index, out, temp, &bail); |
9222 | bailoutFrom(&bail, lir->snapshot()); |
9223 | } |
9224 | |
9225 | void CodeGenerator::visitArgumentsObjectLength(LArgumentsObjectLength* lir) { |
9226 | Register argsObj = ToRegister(lir->argsObject()); |
9227 | Register out = ToRegister(lir->output()); |
9228 | |
9229 | Label bail; |
9230 | masm.loadArgumentsObjectLength(argsObj, out, &bail); |
9231 | bailoutFrom(&bail, lir->snapshot()); |
9232 | } |
9233 | |
9234 | void CodeGenerator::visitArrayFromArgumentsObject( |
9235 | LArrayFromArgumentsObject* lir) { |
9236 | pushArg(ToRegister(lir->argsObject())); |
9237 | |
9238 | using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>); |
9239 | callVM<Fn, js::ArrayFromArgumentsObject>(lir); |
9240 | } |
9241 | |
9242 | void CodeGenerator::visitGuardArgumentsObjectFlags( |
9243 | LGuardArgumentsObjectFlags* lir) { |
9244 | Register argsObj = ToRegister(lir->argsObject()); |
9245 | Register temp = ToRegister(lir->temp0()); |
9246 | |
9247 | Label bail; |
9248 | masm.branchTestArgumentsObjectFlags(argsObj, temp, lir->mir()->flags(), |
9249 | Assembler::NonZero, &bail); |
9250 | bailoutFrom(&bail, lir->snapshot()); |
9251 | } |
9252 | |
9253 | void CodeGenerator::visitBoundFunctionNumArgs(LBoundFunctionNumArgs* lir) { |
9254 | Register obj = ToRegister(lir->object()); |
9255 | Register output = ToRegister(lir->output()); |
9256 | |
9257 | masm.unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()), |
9258 | output); |
9259 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), output); |
9260 | } |
9261 | |
9262 | void CodeGenerator::visitGuardBoundFunctionIsConstructor( |
9263 | LGuardBoundFunctionIsConstructor* lir) { |
9264 | Register obj = ToRegister(lir->object()); |
9265 | |
9266 | Label bail; |
9267 | Address flagsSlot(obj, BoundFunctionObject::offsetOfFlagsSlot()); |
9268 | masm.branchTest32(Assembler::Zero, flagsSlot, |
9269 | Imm32(BoundFunctionObject::IsConstructorFlag), &bail); |
9270 | bailoutFrom(&bail, lir->snapshot()); |
9271 | } |
9272 | |
9273 | void CodeGenerator::visitReturnFromCtor(LReturnFromCtor* lir) { |
9274 | ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex); |
9275 | Register obj = ToRegister(lir->object()); |
9276 | Register output = ToRegister(lir->output()); |
9277 | |
9278 | Label valueIsObject, end; |
9279 | |
9280 | masm.branchTestObject(Assembler::Equal, value, &valueIsObject); |
9281 | |
9282 | // Value is not an object. Return that other object. |
9283 | masm.movePtr(obj, output); |
9284 | masm.jump(&end); |
9285 | |
9286 | // Value is an object. Return unbox(Value). |
9287 | masm.bind(&valueIsObject); |
9288 | Register payload = masm.extractObject(value, output); |
9289 | if (payload != output) { |
9290 | masm.movePtr(payload, output); |
9291 | } |
9292 | |
9293 | masm.bind(&end); |
9294 | } |
9295 | |
9296 | class OutOfLineBoxNonStrictThis : public OutOfLineCodeBase<CodeGenerator> { |
9297 | LBoxNonStrictThis* ins_; |
9298 | |
9299 | public: |
9300 | explicit OutOfLineBoxNonStrictThis(LBoxNonStrictThis* ins) : ins_(ins) {} |
9301 | void accept(CodeGenerator* codegen) override { |
9302 | codegen->visitOutOfLineBoxNonStrictThis(this); |
9303 | } |
9304 | LBoxNonStrictThis* ins() const { return ins_; } |
9305 | }; |
9306 | |
9307 | void CodeGenerator::visitBoxNonStrictThis(LBoxNonStrictThis* lir) { |
9308 | ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex); |
9309 | Register output = ToRegister(lir->output()); |
9310 | |
9311 | auto* ool = new (alloc()) OutOfLineBoxNonStrictThis(lir); |
9312 | addOutOfLineCode(ool, lir->mir()); |
9313 | |
9314 | masm.fallibleUnboxObject(value, output, ool->entry()); |
9315 | masm.bind(ool->rejoin()); |
9316 | } |
9317 | |
9318 | void CodeGenerator::visitOutOfLineBoxNonStrictThis( |
9319 | OutOfLineBoxNonStrictThis* ool) { |
9320 | LBoxNonStrictThis* lir = ool->ins(); |
9321 | |
9322 | ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex); |
9323 | Register output = ToRegister(lir->output()); |
9324 | |
9325 | Label notNullOrUndefined; |
9326 | { |
9327 | Label isNullOrUndefined; |
9328 | ScratchTagScope tag(masm, value); |
9329 | masm.splitTagForTest(value, tag); |
9330 | masm.branchTestUndefined(Assembler::Equal, tag, &isNullOrUndefined); |
9331 | masm.branchTestNull(Assembler::NotEqual, tag, ¬NullOrUndefined); |
9332 | masm.bind(&isNullOrUndefined); |
9333 | masm.movePtr(ImmGCPtr(lir->mir()->globalThis()), output); |
9334 | masm.jump(ool->rejoin()); |
9335 | } |
9336 | |
9337 | masm.bind(¬NullOrUndefined); |
9338 | |
9339 | saveLive(lir); |
9340 | |
9341 | pushArg(value); |
9342 | using Fn = JSObject* (*)(JSContext*, HandleValue); |
9343 | callVM<Fn, BoxNonStrictThis>(lir); |
9344 | |
9345 | StoreRegisterTo(output).generate(this); |
9346 | restoreLiveIgnore(lir, StoreRegisterTo(output).clobbered()); |
9347 | |
9348 | masm.jump(ool->rejoin()); |
9349 | } |
9350 | |
9351 | void CodeGenerator::visitImplicitThis(LImplicitThis* lir) { |
9352 | Register env = ToRegister(lir->env()); |
9353 | ValueOperand output = ToOutValue(lir); |
9354 | |
9355 | using Fn = void (*)(JSContext*, HandleObject, MutableHandleValue); |
9356 | auto* ool = oolCallVM<Fn, ImplicitThisOperation>(lir, ArgList(env), |
9357 | StoreValueTo(output)); |
9358 | |
9359 | masm.computeImplicitThis(env, output, ool->entry()); |
9360 | masm.bind(ool->rejoin()); |
9361 | } |
9362 | |
9363 | void CodeGenerator::visitArrayLength(LArrayLength* lir) { |
9364 | Register elements = ToRegister(lir->elements()); |
9365 | Register output = ToRegister(lir->output()); |
9366 | |
9367 | Address length(elements, ObjectElements::offsetOfLength()); |
9368 | masm.load32(length, output); |
9369 | |
9370 | // Bail out if the length doesn't fit in int32. |
9371 | bailoutTest32(Assembler::Signed, output, output, lir->snapshot()); |
9372 | } |
9373 | |
9374 | static void SetLengthFromIndex(MacroAssembler& masm, const LAllocation* index, |
9375 | const Address& length) { |
9376 | if (index->isConstant()) { |
9377 | masm.store32(Imm32(ToInt32(index) + 1), length); |
9378 | } else { |
9379 | Register newLength = ToRegister(index); |
9380 | masm.add32(Imm32(1), newLength); |
9381 | masm.store32(newLength, length); |
9382 | masm.sub32(Imm32(1), newLength); |
9383 | } |
9384 | } |
9385 | |
9386 | void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) { |
9387 | Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); |
9388 | SetLengthFromIndex(masm, lir->index(), length); |
9389 | } |
9390 | |
9391 | void CodeGenerator::visitFunctionLength(LFunctionLength* lir) { |
9392 | Register function = ToRegister(lir->function()); |
9393 | Register output = ToRegister(lir->output()); |
9394 | |
9395 | Label bail; |
9396 | |
9397 | // Get the JSFunction flags. |
9398 | masm.load32(Address(function, JSFunction::offsetOfFlagsAndArgCount()), |
9399 | output); |
9400 | |
9401 | // Functions with a SelfHostedLazyScript must be compiled with the slow-path |
9402 | // before the function length is known. If the length was previously resolved, |
9403 | // the length property may be shadowed. |
9404 | masm.branchTest32( |
9405 | Assembler::NonZero, output, |
9406 | Imm32(FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH), |
9407 | &bail); |
9408 | |
9409 | masm.loadFunctionLength(function, output, output, &bail); |
9410 | |
9411 | bailoutFrom(&bail, lir->snapshot()); |
9412 | } |
9413 | |
9414 | void CodeGenerator::visitFunctionName(LFunctionName* lir) { |
9415 | Register function = ToRegister(lir->function()); |
9416 | Register output = ToRegister(lir->output()); |
9417 | |
9418 | Label bail; |
9419 | |
9420 | const JSAtomState& names = gen->runtime->names(); |
9421 | masm.loadFunctionName(function, output, ImmGCPtr(names.empty_), &bail); |
9422 | |
9423 | bailoutFrom(&bail, lir->snapshot()); |
9424 | } |
9425 | |
9426 | template <class TableObject> |
9427 | static void TableIteratorLoadEntry(MacroAssembler&, Register, Register, |
9428 | Register); |
9429 | |
9430 | template <> |
9431 | void TableIteratorLoadEntry<MapObject>(MacroAssembler& masm, Register iter, |
9432 | Register i, Register front) { |
9433 | masm.unboxObject(Address(iter, MapIteratorObject::offsetOfTarget()), front); |
9434 | masm.loadPrivate(Address(front, MapObject::offsetOfData()), front); |
9435 | |
9436 | static_assert(MapObject::Table::offsetOfImplDataElement() == 0, |
9437 | "offsetof(Data, element) is 0"); |
9438 | static_assert(MapObject::Table::sizeofImplData() == 24, "sizeof(Data) is 24"); |
9439 | masm.mulBy3(i, i); |
9440 | masm.lshiftPtr(Imm32(3), i); |
9441 | masm.addPtr(i, front); |
9442 | } |
9443 | |
9444 | template <> |
9445 | void TableIteratorLoadEntry<SetObject>(MacroAssembler& masm, Register iter, |
9446 | Register i, Register front) { |
9447 | masm.unboxObject(Address(iter, SetIteratorObject::offsetOfTarget()), front); |
9448 | masm.loadPrivate(Address(front, SetObject::offsetOfData()), front); |
9449 | |
9450 | static_assert(SetObject::Table::offsetOfImplDataElement() == 0, |
9451 | "offsetof(Data, element) is 0"); |
9452 | static_assert(SetObject::Table::sizeofImplData() == 16, "sizeof(Data) is 16"); |
9453 | masm.lshiftPtr(Imm32(4), i); |
9454 | masm.addPtr(i, front); |
9455 | } |
9456 | |
9457 | template <class TableObject> |
9458 | static void TableIteratorAdvance(MacroAssembler& masm, Register iter, |
9459 | Register front, Register dataLength, |
9460 | Register temp) { |
9461 | Register i = temp; |
9462 | |
9463 | // Note: |count| and |index| are stored as PrivateUint32Value. We use add32 |
9464 | // and store32 to change the payload. |
9465 | masm.add32(Imm32(1), Address(iter, TableIteratorObject::offsetOfCount())); |
9466 | |
9467 | masm.unboxInt32(Address(iter, TableIteratorObject::offsetOfIndex()), i); |
9468 | |
9469 | Label done, seek; |
9470 | masm.bind(&seek); |
9471 | masm.add32(Imm32(1), i); |
9472 | masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done); |
9473 | |
9474 | // We can add sizeof(Data) to |front| to select the next element, because |
9475 | // |front| and |mapOrSetObject.data[i]| point to the same location. |
9476 | static_assert(TableObject::Table::offsetOfImplDataElement() == 0, |
9477 | "offsetof(Data, element) is 0"); |
9478 | masm.addPtr(Imm32(TableObject::Table::sizeofImplData()), front); |
9479 | |
9480 | masm.branchTestMagic(Assembler::Equal, |
9481 | Address(front, TableObject::Table::offsetOfEntryKey()), |
9482 | JS_HASH_KEY_EMPTY, &seek); |
9483 | |
9484 | masm.bind(&done); |
9485 | masm.store32(i, Address(iter, TableIteratorObject::offsetOfIndex())); |
9486 | } |
9487 | |
9488 | // Corresponds to TableIteratorObject::finish. |
9489 | static void TableIteratorFinish(MacroAssembler& masm, Register iter, |
9490 | Register temp0, Register temp1) { |
9491 | Register next = temp0; |
9492 | Register prevp = temp1; |
9493 | masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfNext()), next); |
9494 | masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfPrevPtr()), |
9495 | prevp); |
9496 | masm.storePtr(next, Address(prevp, 0)); |
9497 | |
9498 | Label hasNoNext; |
9499 | masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext); |
9500 | masm.storePrivateValue(prevp, |
9501 | Address(next, TableIteratorObject::offsetOfPrevPtr())); |
9502 | masm.bind(&hasNoNext); |
9503 | |
9504 | // Mark iterator inactive. |
9505 | Address targetAddr(iter, TableIteratorObject::offsetOfTarget()); |
9506 | masm.guardedCallPreBarrier(targetAddr, MIRType::Value); |
9507 | masm.storeValue(UndefinedValue(), targetAddr); |
9508 | } |
9509 | |
9510 | template <> |
9511 | void CodeGenerator::emitLoadIteratorValues<MapObject>(Register result, |
9512 | Register temp, |
9513 | Register front) { |
9514 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
9515 | |
9516 | Address keyAddress(front, MapObject::Table::Entry::offsetOfKey()); |
9517 | Address valueAddress(front, MapObject::Table::Entry::offsetOfValue()); |
9518 | Address keyElemAddress(result, elementsOffset); |
9519 | Address valueElemAddress(result, elementsOffset + sizeof(Value)); |
9520 | masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value); |
9521 | masm.guardedCallPreBarrier(valueElemAddress, MIRType::Value); |
9522 | masm.storeValue(keyAddress, keyElemAddress, temp); |
9523 | masm.storeValue(valueAddress, valueElemAddress, temp); |
9524 | |
9525 | Label emitBarrier, skipBarrier; |
9526 | masm.branchValueIsNurseryCell(Assembler::Equal, keyAddress, temp, |
9527 | &emitBarrier); |
9528 | masm.branchValueIsNurseryCell(Assembler::NotEqual, valueAddress, temp, |
9529 | &skipBarrier); |
9530 | { |
9531 | masm.bind(&emitBarrier); |
9532 | saveVolatile(temp); |
9533 | emitPostWriteBarrier(result); |
9534 | restoreVolatile(temp); |
9535 | } |
9536 | masm.bind(&skipBarrier); |
9537 | } |
9538 | |
9539 | template <> |
9540 | void CodeGenerator::emitLoadIteratorValues<SetObject>(Register result, |
9541 | Register temp, |
9542 | Register front) { |
9543 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
9544 | |
9545 | Address keyAddress(front, SetObject::Table::offsetOfEntryKey()); |
9546 | Address keyElemAddress(result, elementsOffset); |
9547 | masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value); |
9548 | masm.storeValue(keyAddress, keyElemAddress, temp); |
9549 | |
9550 | Label skipBarrier; |
9551 | masm.branchValueIsNurseryCell(Assembler::NotEqual, keyAddress, temp, |
9552 | &skipBarrier); |
9553 | { |
9554 | saveVolatile(temp); |
9555 | emitPostWriteBarrier(result); |
9556 | restoreVolatile(temp); |
9557 | } |
9558 | masm.bind(&skipBarrier); |
9559 | } |
9560 | |
9561 | template <class IteratorObject, class TableObject> |
9562 | void CodeGenerator::emitGetNextEntryForIterator(LGetNextEntryForIterator* lir) { |
9563 | Register iter = ToRegister(lir->iter()); |
9564 | Register result = ToRegister(lir->result()); |
9565 | Register temp = ToRegister(lir->temp0()); |
9566 | Register dataLength = ToRegister(lir->temp1()); |
9567 | Register front = ToRegister(lir->temp2()); |
9568 | Register output = ToRegister(lir->output()); |
9569 | |
9570 | #ifdef DEBUG1 |
9571 | // Self-hosted code is responsible for ensuring GetNextEntryForIterator is |
9572 | // only called with the correct iterator class. Assert here all self- |
9573 | // hosted callers of GetNextEntryForIterator perform this class check. |
9574 | // No Spectre mitigations are needed because this is DEBUG-only code. |
9575 | Label success; |
9576 | masm.branchTestObjClassNoSpectreMitigations( |
9577 | Assembler::Equal, iter, &IteratorObject::class_, temp, &success); |
9578 | masm.assumeUnreachable("Iterator object should have the correct class."); |
9579 | masm.bind(&success); |
9580 | #endif |
9581 | |
9582 | // If the iterator has no target, it's already done. |
9583 | // See TableIteratorObject::isActive. |
9584 | Label iterAlreadyDone, iterDone, done; |
9585 | masm.branchTestUndefined(Assembler::Equal, |
9586 | Address(iter, IteratorObject::offsetOfTarget()), |
9587 | &iterAlreadyDone); |
9588 | |
9589 | // Load |iter->index| in |temp| and |iter->target->dataLength| in |
9590 | // |dataLength|. Both values are stored as PrivateUint32Value. |
9591 | masm.unboxInt32(Address(iter, IteratorObject::offsetOfIndex()), temp); |
9592 | masm.unboxObject(Address(iter, IteratorObject::offsetOfTarget()), dataLength); |
9593 | masm.unboxInt32(Address(dataLength, TableObject::offsetOfDataLength()), |
9594 | dataLength); |
9595 | masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone); |
9596 | { |
9597 | TableIteratorLoadEntry<TableObject>(masm, iter, temp, front); |
9598 | |
9599 | emitLoadIteratorValues<TableObject>(result, temp, front); |
9600 | |
9601 | TableIteratorAdvance<TableObject>(masm, iter, front, dataLength, temp); |
9602 | |
9603 | masm.move32(Imm32(0), output); |
9604 | masm.jump(&done); |
9605 | } |
9606 | { |
9607 | masm.bind(&iterDone); |
9608 | TableIteratorFinish(masm, iter, temp, dataLength); |
9609 | |
9610 | masm.bind(&iterAlreadyDone); |
9611 | masm.move32(Imm32(1), output); |
9612 | } |
9613 | masm.bind(&done); |
9614 | } |
9615 | |
9616 | void CodeGenerator::visitGetNextEntryForIterator( |
9617 | LGetNextEntryForIterator* lir) { |
9618 | if (lir->mir()->mode() == MGetNextEntryForIterator::Map) { |
9619 | emitGetNextEntryForIterator<MapIteratorObject, MapObject>(lir); |
9620 | } else { |
9621 | MOZ_ASSERT(lir->mir()->mode() == MGetNextEntryForIterator::Set)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->mode() == MGetNextEntryForIterator ::Set)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->mode() == MGetNextEntryForIterator ::Set))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->mode() == MGetNextEntryForIterator::Set", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9621); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MGetNextEntryForIterator::Set" ")"); do { *((volatile int*)__null) = 9621; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9622 | emitGetNextEntryForIterator<SetIteratorObject, SetObject>(lir); |
9623 | } |
9624 | } |
9625 | |
9626 | // The point of these is to inform Ion of where these values already are; they |
9627 | // don't normally generate (much) code. |
9628 | void CodeGenerator::visitWasmRegisterPairResult(LWasmRegisterPairResult* lir) {} |
9629 | void CodeGenerator::visitWasmStackResult(LWasmStackResult* lir) {} |
9630 | void CodeGenerator::visitWasmStackResult64(LWasmStackResult64* lir) {} |
9631 | |
9632 | void CodeGenerator::visitWasmStackResultArea(LWasmStackResultArea* lir) { |
9633 | LAllocation* output = lir->getDef(0)->output(); |
9634 | MOZ_ASSERT(output->isStackArea())do { static_assert( mozilla::detail::AssertionConditionType< decltype(output->isStackArea())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output->isStackArea()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output->isStackArea()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9634); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output->isStackArea()" ")"); do { *((volatile int*)__null) = 9634; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9635 | bool tempInit = false; |
9636 | for (auto iter = output->toStackArea()->results(); iter; iter.next()) { |
9637 | // Zero out ref stack results. |
9638 | if (iter.isWasmAnyRef()) { |
9639 | Register temp = ToRegister(lir->temp0()); |
9640 | if (!tempInit) { |
9641 | masm.xorPtr(temp, temp); |
9642 | tempInit = true; |
9643 | } |
9644 | masm.storePtr(temp, ToAddress(iter.alloc())); |
9645 | } |
9646 | } |
9647 | } |
9648 | |
9649 | void CodeGenerator::visitWasmRegisterResult(LWasmRegisterResult* lir) { |
9650 | #ifdef JS_64BIT1 |
9651 | if (MWasmRegisterResult* mir = lir->mir()) { |
9652 | if (mir->type() == MIRType::Int32) { |
9653 | masm.widenInt32(ToRegister(lir->output())); |
9654 | } |
9655 | } |
9656 | #endif |
9657 | } |
9658 | |
9659 | void CodeGenerator::visitWasmCall(LWasmCall* lir) { |
9660 | const MWasmCallBase* callBase = lir->callBase(); |
9661 | bool isReturnCall = lir->isReturnCall(); |
9662 | |
9663 | // If this call is in Wasm try code block, initialise a wasm::TryNote for this |
9664 | // call. |
9665 | bool inTry = callBase->inTry(); |
9666 | if (inTry) { |
9667 | size_t tryNoteIndex = callBase->tryNoteIndex(); |
9668 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
9669 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
9670 | tryNote.setTryBodyBegin(masm.currentOffset()); |
9671 | } |
9672 | |
9673 | MOZ_ASSERT((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment ==do { static_assert( mozilla::detail::AssertionConditionType< decltype((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" ")"); do { *((volatile int*)__null) = 9674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9674 | 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" ")"); do { *((volatile int*)__null) = 9674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9675 | static_assert( |
9676 | WasmStackAlignment >= ABIStackAlignment && |
9677 | WasmStackAlignment % ABIStackAlignment == 0, |
9678 | "The wasm stack alignment should subsume the ABI-required alignment"); |
9679 | |
9680 | #ifdef DEBUG1 |
9681 | Label ok; |
9682 | masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok); |
9683 | masm.breakpoint(); |
9684 | masm.bind(&ok); |
9685 | #endif |
9686 | |
9687 | // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the |
9688 | // instance and pinned regs. The only case where where we don't have to |
9689 | // reload the instance and pinned regs is when the callee preserves them. |
9690 | bool reloadRegs = true; |
9691 | bool switchRealm = true; |
9692 | |
9693 | const wasm::CallSiteDesc& desc = callBase->desc(); |
9694 | const wasm::CalleeDesc& callee = callBase->callee(); |
9695 | CodeOffset retOffset; |
9696 | CodeOffset secondRetOffset; |
9697 | switch (callee.which()) { |
9698 | case wasm::CalleeDesc::Func: |
9699 | if (isReturnCall) { |
9700 | ReturnCallAdjustmentInfo retCallInfo( |
9701 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
9702 | masm.wasmReturnCall(desc, callee.funcIndex(), retCallInfo); |
9703 | // The rest of the method is unnecessary for a return call. |
9704 | return; |
9705 | } |
9706 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9706); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9706; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9707 | retOffset = masm.call(desc, callee.funcIndex()); |
9708 | reloadRegs = false; |
9709 | switchRealm = false; |
9710 | break; |
9711 | case wasm::CalleeDesc::Import: |
9712 | if (isReturnCall) { |
9713 | ReturnCallAdjustmentInfo retCallInfo( |
9714 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
9715 | masm.wasmReturnCallImport(desc, callee, retCallInfo); |
9716 | // The rest of the method is unnecessary for a return call. |
9717 | return; |
9718 | } |
9719 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9719); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9719; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9720 | retOffset = masm.wasmCallImport(desc, callee); |
9721 | break; |
9722 | case wasm::CalleeDesc::AsmJSTable: |
9723 | retOffset = masm.asmCallIndirect(desc, callee); |
9724 | break; |
9725 | case wasm::CalleeDesc::WasmTable: { |
9726 | Label* boundsCheckFailed = nullptr; |
9727 | if (lir->needsBoundsCheck()) { |
9728 | OutOfLineAbortingWasmTrap* ool = |
9729 | new (alloc()) OutOfLineAbortingWasmTrap(desc.toTrapSiteDesc(), |
9730 | wasm::Trap::OutOfBounds); |
9731 | if (lir->isCatchable()) { |
9732 | addOutOfLineCode(ool, lir->mirCatchable()); |
9733 | } else if (isReturnCall) { |
9734 | addOutOfLineCode(ool, lir->mirReturnCall()); |
9735 | } else { |
9736 | addOutOfLineCode(ool, lir->mirUncatchable()); |
9737 | } |
9738 | boundsCheckFailed = ool->entry(); |
9739 | } |
9740 | Label* nullCheckFailed = nullptr; |
9741 | #ifndef WASM_HAS_HEAPREG1 |
9742 | { |
9743 | OutOfLineAbortingWasmTrap* ool = |
9744 | new (alloc()) OutOfLineAbortingWasmTrap( |
9745 | desc.toTrapSiteDesc(), wasm::Trap::IndirectCallToNull); |
9746 | if (lir->isCatchable()) { |
9747 | addOutOfLineCode(ool, lir->mirCatchable()); |
9748 | } else if (isReturnCall) { |
9749 | addOutOfLineCode(ool, lir->mirReturnCall()); |
9750 | } else { |
9751 | addOutOfLineCode(ool, lir->mirUncatchable()); |
9752 | } |
9753 | nullCheckFailed = ool->entry(); |
9754 | } |
9755 | #endif |
9756 | if (isReturnCall) { |
9757 | ReturnCallAdjustmentInfo retCallInfo( |
9758 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
9759 | masm.wasmReturnCallIndirect(desc, callee, boundsCheckFailed, |
9760 | nullCheckFailed, mozilla::Nothing(), |
9761 | retCallInfo); |
9762 | // The rest of the method is unnecessary for a return call. |
9763 | return; |
9764 | } |
9765 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9765); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9765; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9766 | masm.wasmCallIndirect(desc, callee, boundsCheckFailed, nullCheckFailed, |
9767 | lir->tableSize(), &retOffset, &secondRetOffset); |
9768 | // Register reloading and realm switching are handled dynamically inside |
9769 | // wasmCallIndirect. There are two return offsets, one for each call |
9770 | // instruction (fast path and slow path). |
9771 | reloadRegs = false; |
9772 | switchRealm = false; |
9773 | break; |
9774 | } |
9775 | case wasm::CalleeDesc::Builtin: |
9776 | retOffset = masm.call(desc, callee.builtin()); |
9777 | reloadRegs = false; |
9778 | switchRealm = false; |
9779 | break; |
9780 | case wasm::CalleeDesc::BuiltinInstanceMethod: |
9781 | retOffset = masm.wasmCallBuiltinInstanceMethod( |
9782 | desc, callBase->instanceArg(), callee.builtin(), |
9783 | callBase->builtinMethodFailureMode()); |
9784 | switchRealm = false; |
9785 | break; |
9786 | case wasm::CalleeDesc::FuncRef: |
9787 | if (isReturnCall) { |
9788 | ReturnCallAdjustmentInfo retCallInfo( |
9789 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
9790 | masm.wasmReturnCallRef(desc, callee, retCallInfo); |
9791 | // The rest of the method is unnecessary for a return call. |
9792 | return; |
9793 | } |
9794 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9794); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9794; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9795 | // Register reloading and realm switching are handled dynamically inside |
9796 | // wasmCallRef. There are two return offsets, one for each call |
9797 | // instruction (fast path and slow path). |
9798 | masm.wasmCallRef(desc, callee, &retOffset, &secondRetOffset); |
9799 | reloadRegs = false; |
9800 | switchRealm = false; |
9801 | break; |
9802 | } |
9803 | |
9804 | // Note the assembler offset for the associated LSafePoint. |
9805 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9805); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9805; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9806 | markSafepointAt(retOffset.offset(), lir); |
9807 | |
9808 | // Now that all the outbound in-memory args are on the stack, note the |
9809 | // required lower boundary point of the associated StackMap. |
9810 | uint32_t framePushedAtStackMapBase = |
9811 | masm.framePushed() - |
9812 | wasm::AlignStackArgAreaSize(callBase->stackArgAreaSizeUnaligned()); |
9813 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAtStackMapBase); |
9814 | MOZ_ASSERT(lir->safepoint()->wasmSafepointKind() ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->safepoint()->wasmSafepointKind() == WasmSafepointKind ::LirCall)>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(lir->safepoint()->wasmSafepointKind () == WasmSafepointKind::LirCall))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9815); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" ")"); do { *((volatile int*)__null) = 9815; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9815 | WasmSafepointKind::LirCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->safepoint()->wasmSafepointKind() == WasmSafepointKind ::LirCall)>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(lir->safepoint()->wasmSafepointKind () == WasmSafepointKind::LirCall))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9815); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" ")"); do { *((volatile int*)__null) = 9815; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9816 | |
9817 | // Note the assembler offset and framePushed for use by the adjunct |
9818 | // LSafePoint, see visitor for LWasmCallIndirectAdjunctSafepoint below. |
9819 | if (callee.which() == wasm::CalleeDesc::WasmTable || |
9820 | callee.which() == wasm::CalleeDesc::FuncRef) { |
9821 | lir->adjunctSafepoint()->recordSafepointInfo(secondRetOffset, |
9822 | framePushedAtStackMapBase); |
9823 | } |
9824 | |
9825 | if (reloadRegs) { |
9826 | masm.loadPtr( |
9827 | Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall), |
9828 | InstanceReg); |
9829 | masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); |
9830 | if (switchRealm) { |
9831 | masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1); |
9832 | } |
9833 | } else { |
9834 | MOZ_ASSERT(!switchRealm)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!switchRealm)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!switchRealm))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!switchRealm", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9834); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!switchRealm" ")"); do { *((volatile int*)__null) = 9834; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9835 | } |
9836 | |
9837 | switch (callee.which()) { |
9838 | case wasm::CalleeDesc::Func: |
9839 | case wasm::CalleeDesc::Import: |
9840 | case wasm::CalleeDesc::WasmTable: |
9841 | case wasm::CalleeDesc::FuncRef: |
9842 | // Stack allocation could change during Wasm (return) calls, |
9843 | // recover pre-call state. |
9844 | masm.freeStackTo(masm.framePushed()); |
9845 | break; |
9846 | default: |
9847 | break; |
9848 | } |
9849 | |
9850 | if (inTry) { |
9851 | // Set the end of the try note range |
9852 | size_t tryNoteIndex = callBase->tryNoteIndex(); |
9853 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
9854 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
9855 | |
9856 | // Don't set the end of the try note if we've OOM'ed, as the above |
9857 | // instructions may not have been emitted, which will trigger an assert |
9858 | // about zero-length try-notes. This is okay as this compilation will be |
9859 | // thrown away. |
9860 | if (!masm.oom()) { |
9861 | tryNote.setTryBodyEnd(masm.currentOffset()); |
9862 | } |
9863 | |
9864 | // This instruction or the adjunct safepoint must be the last instruction |
9865 | // in the block. No other instructions may be inserted. |
9866 | LBlock* block = lir->block(); |
9867 | MOZ_RELEASE_ASSERT(*block->rbegin() == lir ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9868 | (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9869 | *(++block->rbegin()) == lir))do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9870 | |
9871 | // Jump to the fallthrough block |
9872 | jumpToBlock(lir->mirCatchable()->getSuccessor( |
9873 | MWasmCallCatchable::FallthroughBranchIndex)); |
9874 | } |
9875 | } |
9876 | |
9877 | #ifdef ENABLE_WASM_JSPI1 |
9878 | void CodeGenerator::callWasmUpdateSuspenderState( |
9879 | wasm::UpdateSuspenderStateAction kind, Register suspender, Register temp) { |
9880 | masm.Push(InstanceReg); |
9881 | int32_t framePushedAfterInstance = masm.framePushed(); |
9882 | |
9883 | masm.move32(Imm32(uint32_t(kind)), temp); |
9884 | |
9885 | masm.setupWasmABICall(); |
9886 | masm.passABIArg(InstanceReg); |
9887 | masm.passABIArg(suspender); |
9888 | masm.passABIArg(temp); |
9889 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
9890 | masm.callWithABI(wasm::BytecodeOffset(0), |
9891 | wasm::SymbolicAddress::UpdateSuspenderState, |
9892 | mozilla::Some(instanceOffset)); |
9893 | |
9894 | masm.Pop(InstanceReg); |
9895 | } |
9896 | |
9897 | void CodeGenerator::prepareWasmStackSwitchTrampolineCall(Register suspender, |
9898 | Register data) { |
9899 | // Reserve stack space for the wasm call. |
9900 | unsigned argDecrement; |
9901 | { |
9902 | WasmABIArgGenerator abi; |
9903 | ABIArg arg; |
9904 | arg = abi.next(MIRType::Pointer); |
9905 | arg = abi.next(MIRType::Pointer); |
9906 | argDecrement = StackDecrementForCall(WasmStackAlignment, 0, |
9907 | abi.stackBytesConsumedSoFar()); |
9908 | } |
9909 | masm.reserveStack(argDecrement); |
9910 | |
9911 | // Pass the suspender and data params through the wasm function ABI registers. |
9912 | WasmABIArgGenerator abi; |
9913 | ABIArg arg; |
9914 | arg = abi.next(MIRType::Pointer); |
9915 | if (arg.kind() == ABIArg::GPR) { |
9916 | masm.movePtr(suspender, arg.gpr()); |
9917 | } else { |
9918 | MOZ_ASSERT(arg.kind() == ABIArg::Stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(arg.kind() == ABIArg::Stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(arg.kind() == ABIArg::Stack) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arg.kind() == ABIArg::Stack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9918); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack" ")"); do { *((volatile int*)__null) = 9918; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9919 | masm.storePtr(suspender, |
9920 | Address(masm.getStackPointer(), arg.offsetFromArgBase())); |
9921 | } |
9922 | arg = abi.next(MIRType::Pointer); |
9923 | if (arg.kind() == ABIArg::GPR) { |
9924 | masm.movePtr(data, arg.gpr()); |
9925 | } else { |
9926 | MOZ_ASSERT(arg.kind() == ABIArg::Stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(arg.kind() == ABIArg::Stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(arg.kind() == ABIArg::Stack) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arg.kind() == ABIArg::Stack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack" ")"); do { *((volatile int*)__null) = 9926; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9927 | masm.storePtr(data, |
9928 | Address(masm.getStackPointer(), arg.offsetFromArgBase())); |
9929 | } |
9930 | |
9931 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
9932 | WasmCallerInstanceOffsetBeforeCall)); |
9933 | } |
9934 | #endif // ENABLE_WASM_JSPI |
9935 | |
9936 | void CodeGenerator::visitWasmStackSwitchToSuspendable( |
9937 | LWasmStackSwitchToSuspendable* lir) { |
9938 | #ifdef ENABLE_WASM_JSPI1 |
9939 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
9940 | const Register FnReg = lir->fn()->toRegister().gpr(); |
9941 | const Register DataReg = lir->data()->toRegister().gpr(); |
9942 | const Register SuspenderDataReg = ABINonArgReg3; |
9943 | |
9944 | # ifdef JS_CODEGEN_ARM64 |
9945 | vixl::UseScratchRegisterScope temps(&masm); |
9946 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
9947 | # elif defined(JS_CODEGEN_X86) |
9948 | const Register ScratchReg1 = ABINonArgReg3; |
9949 | # elif defined(JS_CODEGEN_X641) |
9950 | const Register ScratchReg1 = ScratchReg; |
9951 | # elif defined(JS_CODEGEN_ARM) |
9952 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
9953 | # elif defined(JS_CODEGEN_LOONG64) |
9954 | SecondScratchRegisterScope scratch2(masm); |
9955 | const Register ScratchReg1 = scratch2; |
9956 | # else |
9957 | # error "NYI: scratch register" |
9958 | # endif |
9959 | |
9960 | masm.Push(SuspenderReg); |
9961 | masm.Push(FnReg); |
9962 | masm.Push(DataReg); |
9963 | |
9964 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Enter, |
9965 | SuspenderReg, ScratchReg1); |
9966 | masm.Pop(DataReg); |
9967 | masm.Pop(FnReg); |
9968 | masm.Pop(SuspenderReg); |
9969 | |
9970 | masm.Push(SuspenderReg); |
9971 | int32_t framePushedAtSuspender = masm.framePushed(); |
9972 | masm.Push(InstanceReg); |
9973 | |
9974 | wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch); |
9975 | CodeLabel returnCallsite; |
9976 | |
9977 | // Aligning stack before trampoline call. |
9978 | uint32_t reserve = ComputeByteAlignment( |
9979 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
9980 | masm.reserveStack(reserve); |
9981 | |
9982 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
9983 | wasm::SuspenderObjectDataSlot)), |
9984 | SuspenderDataReg); |
9985 | |
9986 | // Switch stacks to suspendable, keep original FP to maintain |
9987 | // frames chain between main and suspendable stack segments. |
9988 | masm.storeStackPtr( |
9989 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
9990 | masm.storePtr( |
9991 | FramePointer, |
9992 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP())); |
9993 | |
9994 | masm.loadStackPtr(Address( |
9995 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
9996 | |
9997 | masm.assertStackAlignment(WasmStackAlignment); |
9998 | |
9999 | // The FramePointer is not changed for SwitchToSuspendable. |
10000 | uint32_t framePushed = masm.framePushed(); |
10001 | |
10002 | // On different stack, reset framePushed. FramePointer is not valid here. |
10003 | masm.setFramePushed(0); |
10004 | |
10005 | prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg); |
10006 | |
10007 | // Get wasm instance pointer for callee. |
10008 | size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot( |
10009 | FunctionExtended::WASM_INSTANCE_SLOT); |
10010 | masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg); |
10011 | |
10012 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
10013 | WasmCalleeInstanceOffsetBeforeCall)); |
10014 | masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); |
10015 | |
10016 | masm.assertStackAlignment(WasmStackAlignment); |
10017 | |
10018 | const Register ReturnAddressReg = ScratchReg1; |
10019 | |
10020 | // DataReg is not needed anymore, using it as a scratch register. |
10021 | const Register ScratchReg2 = DataReg; |
10022 | |
10023 | // Save future of suspendable stack exit frame pointer. |
10024 | masm.computeEffectiveAddress( |
10025 | Address(masm.getStackPointer(), -int32_t(sizeof(wasm::Frame))), |
10026 | ScratchReg2); |
10027 | masm.storePtr( |
10028 | ScratchReg2, |
10029 | Address(SuspenderDataReg, |
10030 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP())); |
10031 | |
10032 | masm.mov(&returnCallsite, ReturnAddressReg); |
10033 | |
10034 | // Call wasm function fast. |
10035 | # ifdef JS_USE_LINK_REGISTER |
10036 | # if defined(JS_CODEGEN_LOONG64) |
10037 | masm.mov(ReturnAddressReg, ra); |
10038 | # else |
10039 | masm.mov(ReturnAddressReg, lr); |
10040 | # endif |
10041 | # else |
10042 | masm.Push(ReturnAddressReg); |
10043 | # endif |
10044 | // Get funcUncheckedCallEntry() from the function's |
10045 | // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot. |
10046 | size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot( |
10047 | FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT); |
10048 | masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2); |
10049 | masm.jump(ScratchReg2); |
10050 | |
10051 | // About to use valid FramePointer -- restore framePushed. |
10052 | masm.setFramePushed(framePushed); |
10053 | |
10054 | // For IsPlausibleStackMapKey check for the following callsite. |
10055 | masm.wasmTrapInstruction(); |
10056 | |
10057 | // Callsite for return from main stack. |
10058 | masm.bind(&returnCallsite); |
10059 | masm.append(desc, *returnCallsite.target()); |
10060 | masm.addCodeLabel(returnCallsite); |
10061 | |
10062 | masm.assertStackAlignment(WasmStackAlignment); |
10063 | |
10064 | markSafepointAt(returnCallsite.target()->offset(), lir); |
10065 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
10066 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
10067 | // Rooting SuspenderReg. |
10068 | masm.propagateOOM( |
10069 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
10070 | |
10071 | masm.freeStackTo(framePushed); |
10072 | |
10073 | masm.freeStack(reserve); |
10074 | masm.Pop(InstanceReg); |
10075 | masm.Pop(SuspenderReg); |
10076 | |
10077 | masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2); |
10078 | |
10079 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave, |
10080 | SuspenderReg, ScratchReg1); |
10081 | #else |
10082 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10082); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 10082; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
10083 | #endif // ENABLE_WASM_JSPI |
10084 | } |
10085 | |
10086 | void CodeGenerator::visitWasmStackSwitchToMain(LWasmStackSwitchToMain* lir) { |
10087 | #ifdef ENABLE_WASM_JSPI1 |
10088 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
10089 | const Register FnReg = lir->fn()->toRegister().gpr(); |
10090 | const Register DataReg = lir->data()->toRegister().gpr(); |
10091 | const Register SuspenderDataReg = ABINonArgReg3; |
10092 | |
10093 | # ifdef JS_CODEGEN_ARM64 |
10094 | vixl::UseScratchRegisterScope temps(&masm); |
10095 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
10096 | # elif defined(JS_CODEGEN_X86) |
10097 | const Register ScratchReg1 = ABINonArgReg3; |
10098 | # elif defined(JS_CODEGEN_X641) |
10099 | const Register ScratchReg1 = ScratchReg; |
10100 | # elif defined(JS_CODEGEN_ARM) |
10101 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
10102 | # elif defined(JS_CODEGEN_LOONG64) |
10103 | SecondScratchRegisterScope scratch2(masm); |
10104 | const Register ScratchReg1 = scratch2; |
10105 | # else |
10106 | # error "NYI: scratch register" |
10107 | # endif |
10108 | |
10109 | masm.Push(SuspenderReg); |
10110 | masm.Push(FnReg); |
10111 | masm.Push(DataReg); |
10112 | |
10113 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Suspend, |
10114 | SuspenderReg, ScratchReg1); |
10115 | |
10116 | masm.Pop(DataReg); |
10117 | masm.Pop(FnReg); |
10118 | masm.Pop(SuspenderReg); |
10119 | |
10120 | masm.Push(SuspenderReg); |
10121 | int32_t framePushedAtSuspender = masm.framePushed(); |
10122 | masm.Push(InstanceReg); |
10123 | |
10124 | wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch); |
10125 | CodeLabel returnCallsite; |
10126 | |
10127 | // Aligning stack before trampoline call. |
10128 | uint32_t reserve = ComputeByteAlignment( |
10129 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
10130 | masm.reserveStack(reserve); |
10131 | |
10132 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
10133 | wasm::SuspenderObjectDataSlot)), |
10134 | SuspenderDataReg); |
10135 | |
10136 | // Switch stacks to main. |
10137 | masm.storeStackPtr(Address( |
10138 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
10139 | masm.storePtr(FramePointer, |
10140 | Address(SuspenderDataReg, |
10141 | wasm::SuspenderObjectData::offsetOfSuspendableFP())); |
10142 | |
10143 | masm.loadStackPtr( |
10144 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
10145 | masm.loadPtr( |
10146 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()), |
10147 | FramePointer); |
10148 | |
10149 | // Set main_ra field to returnCallsite. |
10150 | # ifdef JS_CODEGEN_X86 |
10151 | // SuspenderDataReg is also ScratchReg1, use DataReg as a scratch register. |
10152 | MOZ_ASSERT(ScratchReg1 == SuspenderDataReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ScratchReg1 == SuspenderDataReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ScratchReg1 == SuspenderDataReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ScratchReg1 == SuspenderDataReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 == SuspenderDataReg" ")"); do { *((volatile int*)__null) = 10152; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10153 | masm.push(DataReg); |
10154 | masm.mov(&returnCallsite, DataReg); |
10155 | masm.storePtr( |
10156 | DataReg, |
10157 | Address(SuspenderDataReg, |
10158 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress())); |
10159 | masm.pop(DataReg); |
10160 | # else |
10161 | MOZ_ASSERT(ScratchReg1 != SuspenderDataReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ScratchReg1 != SuspenderDataReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ScratchReg1 != SuspenderDataReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ScratchReg1 != SuspenderDataReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 != SuspenderDataReg" ")"); do { *((volatile int*)__null) = 10161; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10162 | masm.mov(&returnCallsite, ScratchReg1); |
10163 | masm.storePtr( |
10164 | ScratchReg1, |
10165 | Address(SuspenderDataReg, |
10166 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress())); |
10167 | # endif |
10168 | |
10169 | masm.assertStackAlignment(WasmStackAlignment); |
10170 | |
10171 | // The FramePointer is pointing to the same |
10172 | // place as before switch happened. |
10173 | uint32_t framePushed = masm.framePushed(); |
10174 | |
10175 | // On different stack, reset framePushed. FramePointer is not valid here. |
10176 | masm.setFramePushed(0); |
10177 | |
10178 | prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg); |
10179 | |
10180 | // Get wasm instance pointer for callee. |
10181 | size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot( |
10182 | FunctionExtended::WASM_INSTANCE_SLOT); |
10183 | masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg); |
10184 | |
10185 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
10186 | WasmCalleeInstanceOffsetBeforeCall)); |
10187 | masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); |
10188 | |
10189 | masm.assertStackAlignment(WasmStackAlignment); |
10190 | |
10191 | const Register ReturnAddressReg = ScratchReg1; |
10192 | // DataReg is not needed anymore, using it as a scratch register. |
10193 | const Register ScratchReg2 = DataReg; |
10194 | |
10195 | // Save future of main stack exit frame pointer. |
10196 | masm.computeEffectiveAddress( |
10197 | Address(masm.getStackPointer(), -int32_t(sizeof(wasm::Frame))), |
10198 | ScratchReg2); |
10199 | masm.storePtr(ScratchReg2, |
10200 | Address(SuspenderDataReg, |
10201 | wasm::SuspenderObjectData::offsetOfMainExitFP())); |
10202 | |
10203 | // Load InstanceReg from suspendable stack exit frame. |
10204 | masm.loadPtr(Address(SuspenderDataReg, |
10205 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
10206 | ScratchReg2); |
10207 | masm.loadPtr( |
10208 | Address(ScratchReg2, wasm::FrameWithInstances::callerInstanceOffset()), |
10209 | ScratchReg2); |
10210 | masm.storePtr(ScratchReg2, Address(masm.getStackPointer(), |
10211 | WasmCallerInstanceOffsetBeforeCall)); |
10212 | |
10213 | // Load RA from suspendable stack exit frame. |
10214 | masm.loadPtr(Address(SuspenderDataReg, |
10215 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
10216 | ScratchReg1); |
10217 | masm.loadPtr(Address(ScratchReg1, wasm::Frame::returnAddressOffset()), |
10218 | ReturnAddressReg); |
10219 | |
10220 | // Call wasm function fast. |
10221 | # ifdef JS_USE_LINK_REGISTER |
10222 | # if defined(JS_CODEGEN_LOONG64) |
10223 | masm.mov(ReturnAddressReg, ra); |
10224 | # else |
10225 | masm.mov(ReturnAddressReg, lr); |
10226 | # endif |
10227 | # else |
10228 | masm.Push(ReturnAddressReg); |
10229 | # endif |
10230 | // Get funcUncheckedCallEntry() from the function's |
10231 | // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot. |
10232 | size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot( |
10233 | FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT); |
10234 | masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2); |
10235 | masm.jump(ScratchReg2); |
10236 | |
10237 | // About to use valid FramePointer -- restore framePushed. |
10238 | masm.setFramePushed(framePushed); |
10239 | |
10240 | // For IsPlausibleStackMapKey check for the following callsite. |
10241 | masm.wasmTrapInstruction(); |
10242 | |
10243 | // Callsite for return from suspendable stack. |
10244 | masm.bind(&returnCallsite); |
10245 | masm.append(desc, *returnCallsite.target()); |
10246 | masm.addCodeLabel(returnCallsite); |
10247 | |
10248 | masm.assertStackAlignment(WasmStackAlignment); |
10249 | |
10250 | markSafepointAt(returnCallsite.target()->offset(), lir); |
10251 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
10252 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
10253 | // Rooting SuspenderReg. |
10254 | masm.propagateOOM( |
10255 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
10256 | |
10257 | masm.freeStackTo(framePushed); |
10258 | |
10259 | // Push ReturnReg that is passed from ContinueOnSuspended on the stack after, |
10260 | // the SuspenderReg has been restored (see ScratchReg1 push below). |
10261 | // (On some platforms SuspenderReg == ReturnReg) |
10262 | masm.mov(ReturnReg, ScratchReg1); |
10263 | |
10264 | masm.freeStack(reserve); |
10265 | masm.Pop(InstanceReg); |
10266 | masm.Pop(SuspenderReg); |
10267 | |
10268 | masm.Push(ScratchReg1); |
10269 | |
10270 | masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2); |
10271 | |
10272 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Resume, |
10273 | SuspenderReg, ScratchReg1); |
10274 | |
10275 | masm.Pop(ToRegister(lir->output())); |
10276 | |
10277 | #else |
10278 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10278); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 10278; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
10279 | #endif // ENABLE_WASM_JSPI |
10280 | } |
10281 | |
10282 | void CodeGenerator::visitWasmStackContinueOnSuspendable( |
10283 | LWasmStackContinueOnSuspendable* lir) { |
10284 | #ifdef ENABLE_WASM_JSPI1 |
10285 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
10286 | const Register ResultReg = lir->result()->toRegister().gpr(); |
10287 | const Register SuspenderDataReg = ABINonArgReg3; |
10288 | |
10289 | # ifdef JS_CODEGEN_ARM64 |
10290 | vixl::UseScratchRegisterScope temps(&masm); |
10291 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
10292 | # elif defined(JS_CODEGEN_X86) |
10293 | const Register ScratchReg1 = ABINonArgReturnReg1; |
10294 | # elif defined(JS_CODEGEN_X641) |
10295 | const Register ScratchReg1 = ScratchReg; |
10296 | # elif defined(JS_CODEGEN_ARM) |
10297 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
10298 | # elif defined(JS_CODEGEN_LOONG64) |
10299 | SecondScratchRegisterScope scratch2(masm); |
10300 | const Register ScratchReg1 = scratch2; |
10301 | # else |
10302 | # error "NYI: scratch register" |
10303 | # endif |
10304 | const Register ScratchReg2 = ABINonArgReg1; |
10305 | |
10306 | masm.Push(SuspenderReg); |
10307 | int32_t framePushedAtSuspender = masm.framePushed(); |
10308 | masm.Push(InstanceReg); |
10309 | |
10310 | wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch); |
10311 | CodeLabel returnCallsite; |
10312 | |
10313 | // Aligning stack before trampoline call. |
10314 | uint32_t reserve = ComputeByteAlignment( |
10315 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
10316 | masm.reserveStack(reserve); |
10317 | |
10318 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
10319 | wasm::SuspenderObjectDataSlot)), |
10320 | SuspenderDataReg); |
10321 | masm.storeStackPtr( |
10322 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
10323 | masm.storePtr( |
10324 | FramePointer, |
10325 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP())); |
10326 | |
10327 | // Adjust exit frame FP. |
10328 | masm.loadPtr(Address(SuspenderDataReg, |
10329 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
10330 | ScratchReg1); |
10331 | masm.storePtr(FramePointer, |
10332 | Address(ScratchReg1, wasm::Frame::callerFPOffset())); |
10333 | |
10334 | // Adjust exit frame RA. |
10335 | masm.mov(&returnCallsite, ScratchReg2); |
10336 | |
10337 | masm.storePtr(ScratchReg2, |
10338 | Address(ScratchReg1, wasm::Frame::returnAddressOffset())); |
10339 | // Adjust exit frame caller instance slot. |
10340 | masm.storePtr( |
10341 | InstanceReg, |
10342 | Address(ScratchReg1, wasm::FrameWithInstances::callerInstanceOffset())); |
10343 | |
10344 | // Switch stacks to suspendable. |
10345 | masm.loadStackPtr(Address( |
10346 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
10347 | masm.loadPtr(Address(SuspenderDataReg, |
10348 | wasm::SuspenderObjectData::offsetOfSuspendableFP()), |
10349 | FramePointer); |
10350 | |
10351 | masm.assertStackAlignment(WasmStackAlignment); |
10352 | |
10353 | // The FramePointer is pointing to the same |
10354 | // place as before switch happened. |
10355 | uint32_t framePushed = masm.framePushed(); |
10356 | |
10357 | // On different stack, reset framePushed. FramePointer is not valid here. |
10358 | masm.setFramePushed(0); |
10359 | |
10360 | // Restore shadow stack area and instance slots. |
10361 | WasmABIArgGenerator abi; |
10362 | unsigned reserveBeforeCall = abi.stackBytesConsumedSoFar(); |
10363 | MOZ_ASSERT(masm.framePushed() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("masm.framePushed() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10363); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { *((volatile int*)__null) = 10363; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10364 | unsigned argDecrement = |
10365 | StackDecrementForCall(WasmStackAlignment, 0, reserveBeforeCall); |
10366 | masm.reserveStack(argDecrement); |
10367 | |
10368 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
10369 | WasmCallerInstanceOffsetBeforeCall)); |
10370 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
10371 | WasmCalleeInstanceOffsetBeforeCall)); |
10372 | |
10373 | masm.assertStackAlignment(WasmStackAlignment); |
10374 | |
10375 | // Transfer results to ReturnReg so it will appear at SwitchToMain return. |
10376 | masm.mov(ResultReg, ReturnReg); |
10377 | |
10378 | const Register ReturnAddressReg = ScratchReg1; |
10379 | |
10380 | // Pretend we just returned from the function. |
10381 | masm.loadPtr( |
10382 | Address(SuspenderDataReg, |
10383 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()), |
10384 | ReturnAddressReg); |
10385 | masm.jump(ReturnAddressReg); |
10386 | |
10387 | // About to use valid FramePointer -- restore framePushed. |
10388 | masm.setFramePushed(framePushed); |
10389 | |
10390 | // For IsPlausibleStackMapKey check for the following callsite. |
10391 | masm.wasmTrapInstruction(); |
10392 | |
10393 | // Callsite for return from suspendable stack. |
10394 | masm.bind(&returnCallsite); |
10395 | masm.append(desc, *returnCallsite.target()); |
10396 | masm.addCodeLabel(returnCallsite); |
10397 | |
10398 | masm.assertStackAlignment(WasmStackAlignment); |
10399 | |
10400 | markSafepointAt(returnCallsite.target()->offset(), lir); |
10401 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
10402 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
10403 | // Rooting SuspenderReg. |
10404 | masm.propagateOOM( |
10405 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
10406 | |
10407 | masm.freeStackTo(framePushed); |
10408 | |
10409 | masm.freeStack(reserve); |
10410 | masm.Pop(InstanceReg); |
10411 | masm.Pop(SuspenderReg); |
10412 | |
10413 | // Using SuspenderDataReg and ABINonArgReg2 as temps. |
10414 | masm.switchToWasmInstanceRealm(SuspenderDataReg, ABINonArgReg2); |
10415 | |
10416 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave, |
10417 | SuspenderReg, ScratchReg1); |
10418 | #else |
10419 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10419); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 10419; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
10420 | #endif // ENABLE_WASM_JSPI |
10421 | } |
10422 | |
10423 | void CodeGenerator::visitWasmCallLandingPrePad(LWasmCallLandingPrePad* lir) { |
10424 | LBlock* block = lir->block(); |
10425 | MWasmCallLandingPrePad* mir = lir->mir(); |
10426 | MBasicBlock* mirBlock = mir->block(); |
10427 | MBasicBlock* callMirBlock = mir->callBlock(); |
10428 | |
10429 | // This block must be the pre-pad successor of the call block. No blocks may |
10430 | // be inserted between us, such as for critical edge splitting. |
10431 | MOZ_RELEASE_ASSERT(mirBlock == callMirBlock->getSuccessor(do { static_assert( mozilla::detail::AssertionConditionType< decltype(mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable ::PrePadBranchIndex))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mirBlock == callMirBlock-> getSuccessor( MWasmCallCatchable::PrePadBranchIndex)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10432); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" ")"); do { *((volatile int*)__null) = 10432; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
10432 | MWasmCallCatchable::PrePadBranchIndex))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable ::PrePadBranchIndex))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mirBlock == callMirBlock-> getSuccessor( MWasmCallCatchable::PrePadBranchIndex)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10432); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" ")"); do { *((volatile int*)__null) = 10432; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10433 | |
10434 | // This instruction or a move group must be the first instruction in the |
10435 | // block. No other instructions may be inserted. |
10436 | MOZ_RELEASE_ASSERT(*block->begin() == lir || (block->begin()->isMoveGroup() &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir))>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10437); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" ")"); do { *((volatile int*)__null) = 10437; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
10437 | *(++block->begin()) == lir))do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir))>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10437); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" ")"); do { *((volatile int*)__null) = 10437; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10438 | |
10439 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
10440 | wasm::TryNote& tryNote = tryNotes[mir->tryNoteIndex()]; |
10441 | // Set the entry point for the call try note to be the beginning of this |
10442 | // block. The above assertions (and assertions in visitWasmCall) guarantee |
10443 | // that we are not skipping over instructions that should be executed. |
10444 | tryNote.setLandingPad(block->label()->offset(), masm.framePushed()); |
10445 | } |
10446 | |
10447 | void CodeGenerator::visitWasmCallIndirectAdjunctSafepoint( |
10448 | LWasmCallIndirectAdjunctSafepoint* lir) { |
10449 | markSafepointAt(lir->safepointLocation().offset(), lir); |
10450 | lir->safepoint()->setFramePushedAtStackMapBase( |
10451 | lir->framePushedAtStackMapBase()); |
10452 | } |
10453 | |
10454 | template <typename InstructionWithMaybeTrapSite> |
10455 | void EmitSignalNullCheckTrapSite(MacroAssembler& masm, |
10456 | InstructionWithMaybeTrapSite* ins, |
10457 | FaultingCodeOffset fco, |
10458 | wasm::TrapMachineInsn tmi) { |
10459 | if (!ins->maybeTrap()) { |
10460 | return; |
10461 | } |
10462 | masm.append(wasm::Trap::NullPointerDereference, |
10463 | wasm::TrapSite(tmi, fco, *ins->maybeTrap())); |
10464 | } |
10465 | |
10466 | template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex> |
10467 | void CodeGenerator::emitWasmValueLoad(InstructionWithMaybeTrapSite* ins, |
10468 | MIRType type, MWideningOp wideningOp, |
10469 | AddressOrBaseIndex addr, |
10470 | AnyRegister dst) { |
10471 | FaultingCodeOffset fco; |
10472 | switch (type) { |
10473 | case MIRType::Int32: |
10474 | switch (wideningOp) { |
10475 | case MWideningOp::None: |
10476 | fco = masm.load32(addr, dst.gpr()); |
10477 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10478 | wasm::TrapMachineInsn::Load32); |
10479 | break; |
10480 | case MWideningOp::FromU16: |
10481 | fco = masm.load16ZeroExtend(addr, dst.gpr()); |
10482 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10483 | wasm::TrapMachineInsn::Load16); |
10484 | break; |
10485 | case MWideningOp::FromS16: |
10486 | fco = masm.load16SignExtend(addr, dst.gpr()); |
10487 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10488 | wasm::TrapMachineInsn::Load16); |
10489 | break; |
10490 | case MWideningOp::FromU8: |
10491 | fco = masm.load8ZeroExtend(addr, dst.gpr()); |
10492 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10493 | wasm::TrapMachineInsn::Load8); |
10494 | break; |
10495 | case MWideningOp::FromS8: |
10496 | fco = masm.load8SignExtend(addr, dst.gpr()); |
10497 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10498 | wasm::TrapMachineInsn::Load8); |
10499 | break; |
10500 | default: |
10501 | MOZ_CRASH("unexpected widening op in ::visitWasmLoadElement")do { do { } while (false); MOZ_ReportCrash("" "unexpected widening op in ::visitWasmLoadElement" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10501); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected widening op in ::visitWasmLoadElement" ")"); do { *((volatile int*)__null) = 10501; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10502 | } |
10503 | break; |
10504 | case MIRType::Float32: |
10505 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10505; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10506 | fco = masm.loadFloat32(addr, dst.fpu()); |
10507 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10508 | wasm::TrapMachineInsn::Load32); |
10509 | break; |
10510 | case MIRType::Double: |
10511 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10511); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10511; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10512 | fco = masm.loadDouble(addr, dst.fpu()); |
10513 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10514 | wasm::TrapMachineInsn::Load64); |
10515 | break; |
10516 | case MIRType::Pointer: |
10517 | case MIRType::WasmAnyRef: |
10518 | case MIRType::WasmArrayData: |
10519 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10519); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10519; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10520 | fco = masm.loadPtr(addr, dst.gpr()); |
10521 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10522 | wasm::TrapMachineInsnForLoadWord()); |
10523 | break; |
10524 | default: |
10525 | MOZ_CRASH("unexpected type in ::emitWasmValueLoad")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueLoad" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10525); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueLoad" ")"); do { *((volatile int*)__null) = 10525; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10526 | } |
10527 | } |
10528 | |
10529 | template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex> |
10530 | void CodeGenerator::emitWasmValueStore(InstructionWithMaybeTrapSite* ins, |
10531 | MIRType type, MNarrowingOp narrowingOp, |
10532 | AnyRegister src, |
10533 | AddressOrBaseIndex addr) { |
10534 | FaultingCodeOffset fco; |
10535 | switch (type) { |
10536 | case MIRType::Int32: |
10537 | switch (narrowingOp) { |
10538 | case MNarrowingOp::None: |
10539 | fco = masm.store32(src.gpr(), addr); |
10540 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10541 | wasm::TrapMachineInsn::Store32); |
10542 | break; |
10543 | case MNarrowingOp::To16: |
10544 | fco = masm.store16(src.gpr(), addr); |
10545 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10546 | wasm::TrapMachineInsn::Store16); |
10547 | break; |
10548 | case MNarrowingOp::To8: |
10549 | fco = masm.store8(src.gpr(), addr); |
10550 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10551 | wasm::TrapMachineInsn::Store8); |
10552 | break; |
10553 | default: |
10554 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10554); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 10554; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
10555 | } |
10556 | break; |
10557 | case MIRType::Float32: |
10558 | fco = masm.storeFloat32(src.fpu(), addr); |
10559 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10560 | wasm::TrapMachineInsn::Store32); |
10561 | break; |
10562 | case MIRType::Double: |
10563 | fco = masm.storeDouble(src.fpu(), addr); |
10564 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10565 | wasm::TrapMachineInsn::Store64); |
10566 | break; |
10567 | case MIRType::Pointer: |
10568 | // This could be correct, but it would be a new usage, so check carefully. |
10569 | MOZ_CRASH("Unexpected type in ::emitWasmValueStore.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type in ::emitWasmValueStore." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10569); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected type in ::emitWasmValueStore." ")"); do { *((volatile int*)__null) = 10569; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10570 | case MIRType::WasmAnyRef: |
10571 | MOZ_CRASH("Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef.")do { do { } while (false); MOZ_ReportCrash("" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10571); AnnotateMozCrashReason("MOZ_CRASH(" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef." ")"); do { *((volatile int*)__null) = 10571; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10572 | default: |
10573 | MOZ_CRASH("unexpected type in ::emitWasmValueStore")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueStore" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10573); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueStore" ")"); do { *((volatile int*)__null) = 10573; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10574 | } |
10575 | } |
10576 | |
10577 | void CodeGenerator::visitWasmLoadSlot(LWasmLoadSlot* ins) { |
10578 | MIRType type = ins->type(); |
10579 | MWideningOp wideningOp = ins->wideningOp(); |
10580 | Register container = ToRegister(ins->containerRef()); |
10581 | Address addr(container, ins->offset()); |
10582 | AnyRegister dst = ToAnyRegister(ins->output()); |
10583 | |
10584 | #ifdef ENABLE_WASM_SIMD1 |
10585 | if (type == MIRType::Simd128) { |
10586 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10586; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10587 | FaultingCodeOffset fco = masm.loadUnalignedSimd128(addr, dst.fpu()); |
10588 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128); |
10589 | return; |
10590 | } |
10591 | #endif |
10592 | emitWasmValueLoad(ins, type, wideningOp, addr, dst); |
10593 | } |
10594 | |
10595 | void CodeGenerator::visitWasmLoadElement(LWasmLoadElement* ins) { |
10596 | MIRType type = ins->type(); |
10597 | MWideningOp wideningOp = ins->wideningOp(); |
10598 | Scale scale = ins->scale(); |
10599 | Register base = ToRegister(ins->base()); |
10600 | Register index = ToRegister(ins->index()); |
10601 | AnyRegister dst = ToAnyRegister(ins->output()); |
10602 | |
10603 | #ifdef ENABLE_WASM_SIMD1 |
10604 | if (type == MIRType::Simd128) { |
10605 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10605; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10606 | FaultingCodeOffset fco; |
10607 | Register temp = ToRegister(ins->temp0()); |
10608 | masm.movePtr(index, temp); |
10609 | masm.lshiftPtr(Imm32(4), temp); |
10610 | fco = masm.loadUnalignedSimd128(BaseIndex(base, temp, Scale::TimesOne), |
10611 | dst.fpu()); |
10612 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128); |
10613 | return; |
10614 | } |
10615 | #endif |
10616 | emitWasmValueLoad(ins, type, wideningOp, BaseIndex(base, index, scale), dst); |
10617 | } |
10618 | |
10619 | void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) { |
10620 | MIRType type = ins->type(); |
10621 | MNarrowingOp narrowingOp = ins->narrowingOp(); |
10622 | Register container = ToRegister(ins->containerRef()); |
10623 | Address addr(container, ins->offset()); |
10624 | AnyRegister src = ToAnyRegister(ins->value()); |
10625 | if (type != MIRType::Int32) { |
10626 | MOZ_RELEASE_ASSERT(narrowingOp == MNarrowingOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(narrowingOp == MNarrowingOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(narrowingOp == MNarrowingOp:: None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("narrowingOp == MNarrowingOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10626); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None" ")"); do { *((volatile int*)__null) = 10626; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10627 | } |
10628 | |
10629 | #ifdef ENABLE_WASM_SIMD1 |
10630 | if (type == MIRType::Simd128) { |
10631 | FaultingCodeOffset fco = masm.storeUnalignedSimd128(src.fpu(), addr); |
10632 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10633 | wasm::TrapMachineInsn::Store128); |
10634 | return; |
10635 | } |
10636 | #endif |
10637 | emitWasmValueStore(ins, type, narrowingOp, src, addr); |
10638 | } |
10639 | |
10640 | void CodeGenerator::visitWasmStoreElement(LWasmStoreElement* ins) { |
10641 | MIRType type = ins->type(); |
10642 | MNarrowingOp narrowingOp = ins->narrowingOp(); |
10643 | Scale scale = ins->scale(); |
10644 | Register base = ToRegister(ins->base()); |
10645 | Register index = ToRegister(ins->index()); |
10646 | AnyRegister src = ToAnyRegister(ins->value()); |
10647 | if (type != MIRType::Int32) { |
10648 | MOZ_RELEASE_ASSERT(narrowingOp == MNarrowingOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(narrowingOp == MNarrowingOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(narrowingOp == MNarrowingOp:: None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("narrowingOp == MNarrowingOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10648); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None" ")"); do { *((volatile int*)__null) = 10648; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10649 | } |
10650 | |
10651 | #ifdef ENABLE_WASM_SIMD1 |
10652 | if (type == MIRType::Simd128) { |
10653 | Register temp = ToRegister(ins->temp0()); |
10654 | masm.movePtr(index, temp); |
10655 | masm.lshiftPtr(Imm32(4), temp); |
10656 | FaultingCodeOffset fco = masm.storeUnalignedSimd128( |
10657 | src.fpu(), BaseIndex(base, temp, Scale::TimesOne)); |
10658 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10659 | wasm::TrapMachineInsn::Store128); |
10660 | return; |
10661 | } |
10662 | #endif |
10663 | emitWasmValueStore(ins, type, narrowingOp, src, |
10664 | BaseIndex(base, index, scale)); |
10665 | } |
10666 | |
10667 | void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) { |
10668 | Register elements = ToRegister(ins->elements()); |
10669 | Register index = ToRegister(ins->index()); |
10670 | Register output = ToRegister(ins->output()); |
10671 | masm.loadPtr(BaseIndex(elements, index, ScalePointer), output); |
10672 | } |
10673 | |
10674 | void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) { |
10675 | masm.movePtr(ToRegister(ins->base()), ToRegister(ins->output())); |
10676 | masm.addPtr(Imm32(int32_t(ins->offset())), ToRegister(ins->output())); |
10677 | } |
10678 | |
10679 | void CodeGenerator::visitWasmDerivedIndexPointer( |
10680 | LWasmDerivedIndexPointer* ins) { |
10681 | Register base = ToRegister(ins->base()); |
10682 | Register index = ToRegister(ins->index()); |
10683 | Register output = ToRegister(ins->output()); |
10684 | masm.computeEffectiveAddress(BaseIndex(base, index, ins->scale()), output); |
10685 | } |
10686 | |
10687 | void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) { |
10688 | Register instance = ToRegister(ins->instance()); |
10689 | Register valueBase = ToRegister(ins->valueBase()); |
10690 | size_t offset = ins->offset(); |
10691 | Register value = ToRegister(ins->value()); |
10692 | Register temp = ToRegister(ins->temp0()); |
10693 | |
10694 | if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) { |
10695 | Label skipPreBarrier; |
10696 | wasm::EmitWasmPreBarrierGuard(masm, instance, temp, |
10697 | Address(valueBase, offset), &skipPreBarrier, |
10698 | ins->maybeTrap()); |
10699 | wasm::EmitWasmPreBarrierCallImmediate(masm, instance, temp, valueBase, |
10700 | offset); |
10701 | masm.bind(&skipPreBarrier); |
10702 | } |
10703 | |
10704 | FaultingCodeOffset fco = masm.storePtr(value, Address(valueBase, offset)); |
10705 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10706 | wasm::TrapMachineInsnForStoreWord()); |
10707 | // The postbarrier is handled separately. |
10708 | } |
10709 | |
10710 | void CodeGenerator::visitWasmStoreElementRef(LWasmStoreElementRef* ins) { |
10711 | Register instance = ToRegister(ins->instance()); |
10712 | Register base = ToRegister(ins->base()); |
10713 | Register index = ToRegister(ins->index()); |
10714 | Register value = ToRegister(ins->value()); |
10715 | Register temp0 = ToTempRegisterOrInvalid(ins->temp0()); |
10716 | Register temp1 = ToTempRegisterOrInvalid(ins->temp1()); |
10717 | |
10718 | BaseIndex addr(base, index, ScalePointer); |
10719 | |
10720 | if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) { |
10721 | Label skipPreBarrier; |
10722 | wasm::EmitWasmPreBarrierGuard(masm, instance, temp0, addr, &skipPreBarrier, |
10723 | ins->maybeTrap()); |
10724 | wasm::EmitWasmPreBarrierCallIndex(masm, instance, temp0, temp1, addr); |
10725 | masm.bind(&skipPreBarrier); |
10726 | } |
10727 | |
10728 | FaultingCodeOffset fco = masm.storePtr(value, addr); |
10729 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10730 | wasm::TrapMachineInsnForStoreWord()); |
10731 | // The postbarrier is handled separately. |
10732 | } |
10733 | |
10734 | // Out-of-line path to update the store buffer for wasm references. |
10735 | class OutOfLineWasmCallPostWriteBarrierImmediate |
10736 | : public OutOfLineCodeBase<CodeGenerator> { |
10737 | LInstruction* lir_; |
10738 | Register valueBase_; |
10739 | Register temp_; |
10740 | uint32_t valueOffset_; |
10741 | |
10742 | public: |
10743 | OutOfLineWasmCallPostWriteBarrierImmediate(LInstruction* lir, |
10744 | Register valueBase, Register temp, |
10745 | uint32_t valueOffset) |
10746 | : lir_(lir), |
10747 | valueBase_(valueBase), |
10748 | temp_(temp), |
10749 | valueOffset_(valueOffset) {} |
10750 | |
10751 | void accept(CodeGenerator* codegen) override { |
10752 | codegen->visitOutOfLineWasmCallPostWriteBarrierImmediate(this); |
10753 | } |
10754 | |
10755 | LInstruction* lir() const { return lir_; } |
10756 | Register valueBase() const { return valueBase_; } |
10757 | Register temp() const { return temp_; } |
10758 | uint32_t valueOffset() const { return valueOffset_; } |
10759 | }; |
10760 | |
10761 | void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierImmediate( |
10762 | OutOfLineWasmCallPostWriteBarrierImmediate* ool) { |
10763 | saveLiveVolatile(ool->lir()); |
10764 | masm.Push(InstanceReg); |
10765 | int32_t framePushedAfterInstance = masm.framePushed(); |
10766 | |
10767 | // Fold the value offset into the value base |
10768 | Register valueAddr = ool->valueBase(); |
10769 | Register temp = ool->temp(); |
10770 | masm.computeEffectiveAddress(Address(valueAddr, ool->valueOffset()), temp); |
10771 | |
10772 | // Call Instance::postBarrier |
10773 | masm.setupWasmABICall(); |
10774 | masm.passABIArg(InstanceReg); |
10775 | masm.passABIArg(temp); |
10776 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
10777 | masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier, |
10778 | mozilla::Some(instanceOffset), ABIType::General); |
10779 | |
10780 | masm.Pop(InstanceReg); |
10781 | restoreLiveVolatile(ool->lir()); |
10782 | |
10783 | masm.jump(ool->rejoin()); |
10784 | } |
10785 | |
10786 | void CodeGenerator::visitWasmPostWriteBarrierImmediate( |
10787 | LWasmPostWriteBarrierImmediate* lir) { |
10788 | Register object = ToRegister(lir->object()); |
10789 | Register value = ToRegister(lir->value()); |
10790 | Register valueBase = ToRegister(lir->valueBase()); |
10791 | Register temp = ToRegister(lir->temp0()); |
10792 | MOZ_ASSERT(ToRegister(lir->instance()) == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->instance()) == InstanceReg)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->instance()) == InstanceReg))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->instance()) == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10792); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg" ")"); do { *((volatile int*)__null) = 10792; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10793 | auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierImmediate( |
10794 | lir, valueBase, temp, lir->valueOffset()); |
10795 | addOutOfLineCode(ool, lir->mir()); |
10796 | |
10797 | wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, |
10798 | ool->rejoin()); |
10799 | masm.jump(ool->entry()); |
10800 | masm.bind(ool->rejoin()); |
10801 | } |
10802 | |
10803 | // Out-of-line path to update the store buffer for wasm references. |
10804 | class OutOfLineWasmCallPostWriteBarrierIndex |
10805 | : public OutOfLineCodeBase<CodeGenerator> { |
10806 | LInstruction* lir_; |
10807 | Register valueBase_; |
10808 | Register index_; |
10809 | Register temp_; |
10810 | uint32_t elemSize_; |
10811 | |
10812 | public: |
10813 | OutOfLineWasmCallPostWriteBarrierIndex(LInstruction* lir, Register valueBase, |
10814 | Register index, Register temp, |
10815 | uint32_t elemSize) |
10816 | : lir_(lir), |
10817 | valueBase_(valueBase), |
10818 | index_(index), |
10819 | temp_(temp), |
10820 | elemSize_(elemSize) { |
10821 | MOZ_ASSERT(elemSize == 1 || elemSize == 2 || elemSize == 4 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" ")"); do { *((volatile int*)__null) = 10822; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
10822 | elemSize == 8 || elemSize == 16)do { static_assert( mozilla::detail::AssertionConditionType< decltype(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" ")"); do { *((volatile int*)__null) = 10822; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10823 | } |
10824 | |
10825 | void accept(CodeGenerator* codegen) override { |
10826 | codegen->visitOutOfLineWasmCallPostWriteBarrierIndex(this); |
10827 | } |
10828 | |
10829 | LInstruction* lir() const { return lir_; } |
10830 | Register valueBase() const { return valueBase_; } |
10831 | Register index() const { return index_; } |
10832 | Register temp() const { return temp_; } |
10833 | uint32_t elemSize() const { return elemSize_; } |
10834 | }; |
10835 | |
10836 | void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierIndex( |
10837 | OutOfLineWasmCallPostWriteBarrierIndex* ool) { |
10838 | saveLiveVolatile(ool->lir()); |
10839 | masm.Push(InstanceReg); |
10840 | int32_t framePushedAfterInstance = masm.framePushed(); |
10841 | |
10842 | // Fold the value offset into the value base |
10843 | Register temp = ool->temp(); |
10844 | if (ool->elemSize() == 16) { |
10845 | masm.movePtr(ool->index(), temp); |
10846 | masm.lshiftPtr(Imm32(4), temp); |
10847 | masm.addPtr(ool->valueBase(), temp); |
10848 | } else { |
10849 | masm.computeEffectiveAddress(BaseIndex(ool->valueBase(), ool->index(), |
10850 | ScaleFromElemWidth(ool->elemSize())), |
10851 | temp); |
10852 | } |
10853 | |
10854 | // Call Instance::postBarrier |
10855 | masm.setupWasmABICall(); |
10856 | masm.passABIArg(InstanceReg); |
10857 | masm.passABIArg(temp); |
10858 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
10859 | masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier, |
10860 | mozilla::Some(instanceOffset), ABIType::General); |
10861 | |
10862 | masm.Pop(InstanceReg); |
10863 | restoreLiveVolatile(ool->lir()); |
10864 | |
10865 | masm.jump(ool->rejoin()); |
10866 | } |
10867 | |
10868 | void CodeGenerator::visitWasmPostWriteBarrierIndex( |
10869 | LWasmPostWriteBarrierIndex* lir) { |
10870 | Register object = ToRegister(lir->object()); |
10871 | Register value = ToRegister(lir->value()); |
10872 | Register valueBase = ToRegister(lir->valueBase()); |
10873 | Register index = ToRegister(lir->index()); |
10874 | Register temp = ToRegister(lir->temp0()); |
10875 | MOZ_ASSERT(ToRegister(lir->instance()) == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->instance()) == InstanceReg)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->instance()) == InstanceReg))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->instance()) == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10875); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg" ")"); do { *((volatile int*)__null) = 10875; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10876 | auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierIndex( |
10877 | lir, valueBase, index, temp, lir->elemSize()); |
10878 | addOutOfLineCode(ool, lir->mir()); |
10879 | |
10880 | wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, |
10881 | ool->rejoin()); |
10882 | masm.jump(ool->entry()); |
10883 | masm.bind(ool->rejoin()); |
10884 | } |
10885 | |
10886 | void CodeGenerator::visitWasmLoadSlotI64(LWasmLoadSlotI64* ins) { |
10887 | Register container = ToRegister(ins->containerRef()); |
10888 | Address addr(container, ins->offset()); |
10889 | Register64 output = ToOutRegister64(ins); |
10890 | // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one |
10891 | // transaction will always trap before the other, so it seems safest to |
10892 | // register both of them as potentially trapping. |
10893 | #ifdef JS_64BIT1 |
10894 | FaultingCodeOffset fco = masm.load64(addr, output); |
10895 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64); |
10896 | #else |
10897 | FaultingCodeOffsetPair fcop = masm.load64(addr, output); |
10898 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
10899 | wasm::TrapMachineInsn::Load32); |
10900 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
10901 | wasm::TrapMachineInsn::Load32); |
10902 | #endif |
10903 | } |
10904 | |
10905 | void CodeGenerator::visitWasmLoadElementI64(LWasmLoadElementI64* ins) { |
10906 | Register base = ToRegister(ins->base()); |
10907 | Register index = ToRegister(ins->index()); |
10908 | BaseIndex addr(base, index, Scale::TimesEight); |
10909 | Register64 output = ToOutRegister64(ins); |
10910 | // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one |
10911 | // transaction will always trap before the other, so it seems safest to |
10912 | // register both of them as potentially trapping. |
10913 | #ifdef JS_64BIT1 |
10914 | FaultingCodeOffset fco = masm.load64(addr, output); |
10915 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64); |
10916 | #else |
10917 | FaultingCodeOffsetPair fcop = masm.load64(addr, output); |
10918 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
10919 | wasm::TrapMachineInsn::Load32); |
10920 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
10921 | wasm::TrapMachineInsn::Load32); |
10922 | #endif |
10923 | } |
10924 | |
10925 | void CodeGenerator::visitWasmStoreSlotI64(LWasmStoreSlotI64* ins) { |
10926 | Register container = ToRegister(ins->containerRef()); |
10927 | Address addr(container, ins->offset()); |
10928 | Register64 value = ToRegister64(ins->value()); |
10929 | // Either 1 or 2 words. As above we register both transactions in the |
10930 | // 2-word case. |
10931 | #ifdef JS_64BIT1 |
10932 | FaultingCodeOffset fco = masm.store64(value, addr); |
10933 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64); |
10934 | #else |
10935 | FaultingCodeOffsetPair fcop = masm.store64(value, addr); |
10936 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
10937 | wasm::TrapMachineInsn::Store32); |
10938 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
10939 | wasm::TrapMachineInsn::Store32); |
10940 | #endif |
10941 | } |
10942 | |
10943 | void CodeGenerator::visitWasmStoreElementI64(LWasmStoreElementI64* ins) { |
10944 | Register base = ToRegister(ins->base()); |
10945 | Register index = ToRegister(ins->index()); |
10946 | BaseIndex addr(base, index, Scale::TimesEight); |
10947 | Register64 value = ToRegister64(ins->value()); |
10948 | // Either 1 or 2 words. As above we register both transactions in the |
10949 | // 2-word case. |
10950 | #ifdef JS_64BIT1 |
10951 | FaultingCodeOffset fco = masm.store64(value, addr); |
10952 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64); |
10953 | #else |
10954 | FaultingCodeOffsetPair fcop = masm.store64(value, addr); |
10955 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
10956 | wasm::TrapMachineInsn::Store32); |
10957 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
10958 | wasm::TrapMachineInsn::Store32); |
10959 | #endif |
10960 | } |
10961 | |
10962 | void CodeGenerator::visitWasmClampTable64Address( |
10963 | LWasmClampTable64Address* lir) { |
10964 | #ifdef ENABLE_WASM_MEMORY641 |
10965 | Register64 address = ToRegister64(lir->address()); |
10966 | Register out = ToRegister(lir->output()); |
10967 | masm.wasmClampTable64Address(address, out); |
10968 | #else |
10969 | MOZ_CRASH("table64 addresses should not be valid without memory64")do { do { } while (false); MOZ_ReportCrash("" "table64 addresses should not be valid without memory64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10969); AnnotateMozCrashReason("MOZ_CRASH(" "table64 addresses should not be valid without memory64" ")"); do { *((volatile int*)__null) = 10969; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10970 | #endif |
10971 | } |
10972 | |
10973 | void CodeGenerator::visitArrayBufferByteLength(LArrayBufferByteLength* lir) { |
10974 | Register obj = ToRegister(lir->object()); |
10975 | Register out = ToRegister(lir->output()); |
10976 | masm.loadArrayBufferByteLengthIntPtr(obj, out); |
10977 | } |
10978 | |
10979 | void CodeGenerator::visitArrayBufferViewLength(LArrayBufferViewLength* lir) { |
10980 | Register obj = ToRegister(lir->object()); |
10981 | Register out = ToRegister(lir->output()); |
10982 | masm.loadArrayBufferViewLengthIntPtr(obj, out); |
10983 | } |
10984 | |
10985 | void CodeGenerator::visitArrayBufferViewByteOffset( |
10986 | LArrayBufferViewByteOffset* lir) { |
10987 | Register obj = ToRegister(lir->object()); |
10988 | Register out = ToRegister(lir->output()); |
10989 | masm.loadArrayBufferViewByteOffsetIntPtr(obj, out); |
10990 | } |
10991 | |
10992 | void CodeGenerator::visitArrayBufferViewElements( |
10993 | LArrayBufferViewElements* lir) { |
10994 | Register obj = ToRegister(lir->object()); |
10995 | Register out = ToRegister(lir->output()); |
10996 | masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), out); |
10997 | } |
10998 | |
10999 | void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) { |
11000 | Register obj = ToRegister(lir->object()); |
11001 | Register out = ToRegister(lir->output()); |
11002 | |
11003 | masm.typedArrayElementSize(obj, out); |
11004 | } |
11005 | |
11006 | void CodeGenerator::visitResizableTypedArrayByteOffsetMaybeOutOfBounds( |
11007 | LResizableTypedArrayByteOffsetMaybeOutOfBounds* lir) { |
11008 | Register obj = ToRegister(lir->object()); |
11009 | Register out = ToRegister(lir->output()); |
11010 | Register temp = ToRegister(lir->temp0()); |
11011 | |
11012 | masm.loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr(obj, out, temp); |
11013 | } |
11014 | |
11015 | void CodeGenerator::visitResizableTypedArrayLength( |
11016 | LResizableTypedArrayLength* lir) { |
11017 | Register obj = ToRegister(lir->object()); |
11018 | Register out = ToRegister(lir->output()); |
11019 | Register temp = ToRegister(lir->temp0()); |
11020 | |
11021 | masm.loadResizableTypedArrayLengthIntPtr(lir->synchronization(), obj, out, |
11022 | temp); |
11023 | } |
11024 | |
11025 | void CodeGenerator::visitResizableDataViewByteLength( |
11026 | LResizableDataViewByteLength* lir) { |
11027 | Register obj = ToRegister(lir->object()); |
11028 | Register out = ToRegister(lir->output()); |
11029 | Register temp = ToRegister(lir->temp0()); |
11030 | |
11031 | masm.loadResizableDataViewByteLengthIntPtr(lir->synchronization(), obj, out, |
11032 | temp); |
11033 | } |
11034 | |
11035 | void CodeGenerator::visitGrowableSharedArrayBufferByteLength( |
11036 | LGrowableSharedArrayBufferByteLength* lir) { |
11037 | Register obj = ToRegister(lir->object()); |
11038 | Register out = ToRegister(lir->output()); |
11039 | |
11040 | // Explicit |byteLength| accesses are seq-consistent atomic loads. |
11041 | auto sync = Synchronization::Load(); |
11042 | |
11043 | masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out); |
11044 | } |
11045 | |
11046 | void CodeGenerator::visitGuardResizableArrayBufferViewInBounds( |
11047 | LGuardResizableArrayBufferViewInBounds* lir) { |
11048 | Register obj = ToRegister(lir->object()); |
11049 | Register temp = ToRegister(lir->temp0()); |
11050 | |
11051 | Label bail; |
11052 | masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail); |
11053 | bailoutFrom(&bail, lir->snapshot()); |
11054 | } |
11055 | |
11056 | void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached( |
11057 | LGuardResizableArrayBufferViewInBoundsOrDetached* lir) { |
11058 | Register obj = ToRegister(lir->object()); |
11059 | Register temp = ToRegister(lir->temp0()); |
11060 | |
11061 | Label done, bail; |
11062 | masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done); |
11063 | masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail); |
11064 | masm.bind(&done); |
11065 | bailoutFrom(&bail, lir->snapshot()); |
11066 | } |
11067 | |
11068 | void CodeGenerator::visitGuardHasAttachedArrayBuffer( |
11069 | LGuardHasAttachedArrayBuffer* lir) { |
11070 | Register obj = ToRegister(lir->object()); |
11071 | Register temp = ToRegister(lir->temp0()); |
11072 | |
11073 | Label bail; |
11074 | masm.branchIfHasDetachedArrayBuffer(obj, temp, &bail); |
11075 | bailoutFrom(&bail, lir->snapshot()); |
11076 | } |
11077 | |
11078 | class OutOfLineGuardNumberToIntPtrIndex |
11079 | : public OutOfLineCodeBase<CodeGenerator> { |
11080 | LGuardNumberToIntPtrIndex* lir_; |
11081 | |
11082 | public: |
11083 | explicit OutOfLineGuardNumberToIntPtrIndex(LGuardNumberToIntPtrIndex* lir) |
11084 | : lir_(lir) {} |
11085 | |
11086 | void accept(CodeGenerator* codegen) override { |
11087 | codegen->visitOutOfLineGuardNumberToIntPtrIndex(this); |
11088 | } |
11089 | LGuardNumberToIntPtrIndex* lir() const { return lir_; } |
11090 | }; |
11091 | |
11092 | void CodeGenerator::visitGuardNumberToIntPtrIndex( |
11093 | LGuardNumberToIntPtrIndex* lir) { |
11094 | FloatRegister input = ToFloatRegister(lir->input()); |
11095 | Register output = ToRegister(lir->output()); |
11096 | |
11097 | if (!lir->mir()->supportOOB()) { |
11098 | Label bail; |
11099 | masm.convertDoubleToPtr(input, output, &bail, false); |
11100 | bailoutFrom(&bail, lir->snapshot()); |
11101 | return; |
11102 | } |
11103 | |
11104 | auto* ool = new (alloc()) OutOfLineGuardNumberToIntPtrIndex(lir); |
11105 | addOutOfLineCode(ool, lir->mir()); |
11106 | |
11107 | masm.convertDoubleToPtr(input, output, ool->entry(), false); |
11108 | masm.bind(ool->rejoin()); |
11109 | } |
11110 | |
11111 | void CodeGenerator::visitOutOfLineGuardNumberToIntPtrIndex( |
11112 | OutOfLineGuardNumberToIntPtrIndex* ool) { |
11113 | // Substitute the invalid index with an arbitrary out-of-bounds index. |
11114 | masm.movePtr(ImmWord(-1), ToRegister(ool->lir()->output())); |
11115 | masm.jump(ool->rejoin()); |
11116 | } |
11117 | |
11118 | void CodeGenerator::visitStringLength(LStringLength* lir) { |
11119 | Register input = ToRegister(lir->string()); |
11120 | Register output = ToRegister(lir->output()); |
11121 | |
11122 | masm.loadStringLength(input, output); |
11123 | } |
11124 | |
11125 | void CodeGenerator::visitMinMaxI(LMinMaxI* ins) { |
11126 | Register first = ToRegister(ins->first()); |
11127 | Register output = ToRegister(ins->output()); |
11128 | |
11129 | MOZ_ASSERT(first == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(first == output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(first == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("first == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "first == output" ")"); do { *((volatile int*)__null) = 11129; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11130 | |
11131 | Assembler::Condition cond = |
11132 | ins->mir()->isMax() ? Assembler::GreaterThan : Assembler::LessThan; |
11133 | |
11134 | if (ins->second()->isConstant()) { |
11135 | Label done; |
11136 | masm.branch32(cond, first, Imm32(ToInt32(ins->second())), &done); |
11137 | masm.move32(Imm32(ToInt32(ins->second())), output); |
11138 | masm.bind(&done); |
11139 | } else { |
11140 | Register second = ToRegister(ins->second()); |
11141 | masm.cmp32Move32(cond, second, first, second, output); |
11142 | } |
11143 | } |
11144 | |
11145 | void CodeGenerator::visitMinMaxArrayI(LMinMaxArrayI* ins) { |
11146 | Register array = ToRegister(ins->array()); |
11147 | Register output = ToRegister(ins->output()); |
11148 | Register temp1 = ToRegister(ins->temp1()); |
11149 | Register temp2 = ToRegister(ins->temp2()); |
11150 | Register temp3 = ToRegister(ins->temp3()); |
11151 | bool isMax = ins->isMax(); |
11152 | |
11153 | Label bail; |
11154 | masm.minMaxArrayInt32(array, output, temp1, temp2, temp3, isMax, &bail); |
11155 | bailoutFrom(&bail, ins->snapshot()); |
11156 | } |
11157 | |
11158 | void CodeGenerator::visitMinMaxArrayD(LMinMaxArrayD* ins) { |
11159 | Register array = ToRegister(ins->array()); |
11160 | FloatRegister output = ToFloatRegister(ins->output()); |
11161 | Register temp1 = ToRegister(ins->temp1()); |
11162 | Register temp2 = ToRegister(ins->temp2()); |
11163 | FloatRegister floatTemp = ToFloatRegister(ins->floatTemp()); |
11164 | bool isMax = ins->isMax(); |
11165 | |
11166 | Label bail; |
11167 | masm.minMaxArrayNumber(array, output, floatTemp, temp1, temp2, isMax, &bail); |
11168 | bailoutFrom(&bail, ins->snapshot()); |
11169 | } |
11170 | |
11171 | // For Abs*, lowering will have tied input to output on platforms where that is |
11172 | // sensible, and otherwise left them untied. |
11173 | |
11174 | void CodeGenerator::visitAbsI(LAbsI* ins) { |
11175 | Register input = ToRegister(ins->input()); |
11176 | Register output = ToRegister(ins->output()); |
11177 | |
11178 | if (ins->mir()->fallible()) { |
11179 | Label positive; |
11180 | if (input != output) { |
11181 | masm.move32(input, output); |
11182 | } |
11183 | masm.branchTest32(Assembler::NotSigned, output, output, &positive); |
11184 | Label bail; |
11185 | masm.branchNeg32(Assembler::Overflow, output, &bail); |
11186 | bailoutFrom(&bail, ins->snapshot()); |
11187 | masm.bind(&positive); |
11188 | } else { |
11189 | masm.abs32(input, output); |
11190 | } |
11191 | } |
11192 | |
11193 | void CodeGenerator::visitAbsD(LAbsD* ins) { |
11194 | masm.absDouble(ToFloatRegister(ins->input()), ToFloatRegister(ins->output())); |
11195 | } |
11196 | |
11197 | void CodeGenerator::visitAbsF(LAbsF* ins) { |
11198 | masm.absFloat32(ToFloatRegister(ins->input()), |
11199 | ToFloatRegister(ins->output())); |
11200 | } |
11201 | |
11202 | void CodeGenerator::visitPowII(LPowII* ins) { |
11203 | Register value = ToRegister(ins->value()); |
11204 | Register power = ToRegister(ins->power()); |
11205 | Register output = ToRegister(ins->output()); |
11206 | Register temp0 = ToRegister(ins->temp0()); |
11207 | Register temp1 = ToRegister(ins->temp1()); |
11208 | |
11209 | Label bailout; |
11210 | masm.pow32(value, power, output, temp0, temp1, &bailout); |
11211 | bailoutFrom(&bailout, ins->snapshot()); |
11212 | } |
11213 | |
11214 | void CodeGenerator::visitPowI(LPowI* ins) { |
11215 | FloatRegister value = ToFloatRegister(ins->value()); |
11216 | Register power = ToRegister(ins->power()); |
11217 | |
11218 | using Fn = double (*)(double x, int32_t y); |
11219 | masm.setupAlignedABICall(); |
11220 | masm.passABIArg(value, ABIType::Float64); |
11221 | masm.passABIArg(power); |
11222 | |
11223 | masm.callWithABI<Fn, js::powi>(ABIType::Float64); |
11224 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11224); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11224; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11225 | } |
11226 | |
11227 | void CodeGenerator::visitPowD(LPowD* ins) { |
11228 | FloatRegister value = ToFloatRegister(ins->value()); |
11229 | FloatRegister power = ToFloatRegister(ins->power()); |
11230 | |
11231 | using Fn = double (*)(double x, double y); |
11232 | masm.setupAlignedABICall(); |
11233 | masm.passABIArg(value, ABIType::Float64); |
11234 | masm.passABIArg(power, ABIType::Float64); |
11235 | masm.callWithABI<Fn, ecmaPow>(ABIType::Float64); |
11236 | |
11237 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11237; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11238 | } |
11239 | |
11240 | void CodeGenerator::visitPowOfTwoI(LPowOfTwoI* ins) { |
11241 | Register power = ToRegister(ins->power()); |
11242 | Register output = ToRegister(ins->output()); |
11243 | |
11244 | uint32_t base = ins->base(); |
11245 | MOZ_ASSERT(mozilla::IsPowerOfTwo(base))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::IsPowerOfTwo(base))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mozilla::IsPowerOfTwo(base)) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mozilla::IsPowerOfTwo(base)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(base)" ")"); do { *((volatile int*)__null) = 11245; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11246 | |
11247 | uint32_t n = mozilla::FloorLog2(base); |
11248 | MOZ_ASSERT(n != 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(n != 0)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(n != 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("n != 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11248); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n != 0" ")" ); do { *((volatile int*)__null) = 11248; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
11249 | |
11250 | // Hacker's Delight, 2nd edition, theorem D2. |
11251 | auto ceilingDiv = [](uint32_t x, uint32_t y) { return (x + y - 1) / y; }; |
11252 | |
11253 | // Take bailout if |power| is greater-or-equals |log_y(2^31)| or is negative. |
11254 | // |2^(n*y) < 2^31| must hold, hence |n*y < 31| resp. |y < 31/n|. |
11255 | // |
11256 | // Note: it's important for this condition to match the code in CacheIR.cpp |
11257 | // (CanAttachInt32Pow) to prevent failure loops. |
11258 | bailoutCmp32(Assembler::AboveOrEqual, power, Imm32(ceilingDiv(31, n)), |
11259 | ins->snapshot()); |
11260 | |
11261 | // Compute (2^n)^y as 2^(n*y) using repeated shifts. We could directly scale |
11262 | // |power| and perform a single shift, but due to the lack of necessary |
11263 | // MacroAssembler functionality, like multiplying a register with an |
11264 | // immediate, we restrict the number of generated shift instructions when |
11265 | // lowering this operation. |
11266 | masm.move32(Imm32(1), output); |
11267 | do { |
11268 | masm.lshift32(power, output); |
11269 | n--; |
11270 | } while (n > 0); |
11271 | } |
11272 | |
11273 | void CodeGenerator::visitSqrtD(LSqrtD* ins) { |
11274 | FloatRegister input = ToFloatRegister(ins->input()); |
11275 | FloatRegister output = ToFloatRegister(ins->output()); |
11276 | masm.sqrtDouble(input, output); |
11277 | } |
11278 | |
11279 | void CodeGenerator::visitSqrtF(LSqrtF* ins) { |
11280 | FloatRegister input = ToFloatRegister(ins->input()); |
11281 | FloatRegister output = ToFloatRegister(ins->output()); |
11282 | masm.sqrtFloat32(input, output); |
11283 | } |
11284 | |
11285 | void CodeGenerator::visitSignI(LSignI* ins) { |
11286 | Register input = ToRegister(ins->input()); |
11287 | Register output = ToRegister(ins->output()); |
11288 | masm.signInt32(input, output); |
11289 | } |
11290 | |
11291 | void CodeGenerator::visitSignD(LSignD* ins) { |
11292 | FloatRegister input = ToFloatRegister(ins->input()); |
11293 | FloatRegister output = ToFloatRegister(ins->output()); |
11294 | masm.signDouble(input, output); |
11295 | } |
11296 | |
11297 | void CodeGenerator::visitSignDI(LSignDI* ins) { |
11298 | FloatRegister input = ToFloatRegister(ins->input()); |
11299 | FloatRegister temp = ToFloatRegister(ins->temp0()); |
11300 | Register output = ToRegister(ins->output()); |
11301 | |
11302 | Label bail; |
11303 | masm.signDoubleToInt32(input, output, temp, &bail); |
11304 | bailoutFrom(&bail, ins->snapshot()); |
11305 | } |
11306 | |
11307 | void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) { |
11308 | FloatRegister input = ToFloatRegister(ins->input()); |
11309 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11309; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11310 | |
11311 | UnaryMathFunction fun = ins->mir()->function(); |
11312 | UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(fun); |
11313 | |
11314 | masm.setupAlignedABICall(); |
11315 | |
11316 | masm.passABIArg(input, ABIType::Float64); |
11317 | masm.callWithABI(DynamicFunction<UnaryMathFunctionType>(funPtr), |
11318 | ABIType::Float64); |
11319 | } |
11320 | |
11321 | void CodeGenerator::visitMathFunctionF(LMathFunctionF* ins) { |
11322 | FloatRegister input = ToFloatRegister(ins->input()); |
11323 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnFloat32Reg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnFloat32Reg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnFloat32Reg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnFloat32Reg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnFloat32Reg" ")"); do { *((volatile int*)__null) = 11323; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11324 | |
11325 | masm.setupAlignedABICall(); |
11326 | masm.passABIArg(input, ABIType::Float32); |
11327 | |
11328 | using Fn = float (*)(float x); |
11329 | Fn funptr = nullptr; |
11330 | CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check; |
11331 | switch (ins->mir()->function()) { |
11332 | case UnaryMathFunction::Floor: |
11333 | funptr = floorf; |
11334 | check = CheckUnsafeCallWithABI::DontCheckOther; |
11335 | break; |
11336 | case UnaryMathFunction::Round: |
11337 | funptr = math_roundf_impl; |
11338 | break; |
11339 | case UnaryMathFunction::Trunc: |
11340 | funptr = math_truncf_impl; |
11341 | break; |
11342 | case UnaryMathFunction::Ceil: |
11343 | funptr = ceilf; |
11344 | check = CheckUnsafeCallWithABI::DontCheckOther; |
11345 | break; |
11346 | default: |
11347 | MOZ_CRASH("Unknown or unsupported float32 math function")do { do { } while (false); MOZ_ReportCrash("" "Unknown or unsupported float32 math function" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11347); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown or unsupported float32 math function" ")"); do { *((volatile int*)__null) = 11347; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
11348 | } |
11349 | |
11350 | masm.callWithABI(DynamicFunction<Fn>(funptr), ABIType::Float32, check); |
11351 | } |
11352 | |
11353 | void CodeGenerator::visitModD(LModD* ins) { |
11354 | MOZ_ASSERT(!gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!gen->compilingWasm()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 11354; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11355 | |
11356 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
11357 | FloatRegister rhs = ToFloatRegister(ins->rhs()); |
11358 | |
11359 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11359); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11359; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11360 | |
11361 | using Fn = double (*)(double a, double b); |
11362 | masm.setupAlignedABICall(); |
11363 | masm.passABIArg(lhs, ABIType::Float64); |
11364 | masm.passABIArg(rhs, ABIType::Float64); |
11365 | masm.callWithABI<Fn, NumberMod>(ABIType::Float64); |
11366 | } |
11367 | |
11368 | void CodeGenerator::visitModPowTwoD(LModPowTwoD* ins) { |
11369 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
11370 | uint32_t divisor = ins->divisor(); |
11371 | MOZ_ASSERT(mozilla::IsPowerOfTwo(divisor))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::IsPowerOfTwo(divisor))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mozilla::IsPowerOfTwo(divisor )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mozilla::IsPowerOfTwo(divisor)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11371); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(divisor)" ")"); do { *((volatile int*)__null) = 11371; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11372 | |
11373 | FloatRegister output = ToFloatRegister(ins->output()); |
11374 | |
11375 | // Compute |n % d| using |copysign(n - (d * trunc(n / d)), n)|. |
11376 | // |
11377 | // This doesn't work if |d| isn't a power of two, because we may lose too much |
11378 | // precision. For example |Number.MAX_VALUE % 3 == 2|, but |
11379 | // |3 * trunc(Number.MAX_VALUE / 3) == Infinity|. |
11380 | |
11381 | Label done; |
11382 | { |
11383 | ScratchDoubleScope scratch(masm); |
11384 | |
11385 | // Subnormals can lead to performance degradation, which can make calling |
11386 | // |fmod| faster than this inline implementation. Work around this issue by |
11387 | // directly returning the input for any value in the interval ]-1, +1[. |
11388 | Label notSubnormal; |
11389 | masm.loadConstantDouble(1.0, scratch); |
11390 | masm.loadConstantDouble(-1.0, output); |
11391 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, lhs, scratch, |
11392 | ¬Subnormal); |
11393 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, lhs, output, |
11394 | ¬Subnormal); |
11395 | |
11396 | masm.moveDouble(lhs, output); |
11397 | masm.jump(&done); |
11398 | |
11399 | masm.bind(¬Subnormal); |
11400 | |
11401 | if (divisor == 1) { |
11402 | // The pattern |n % 1 == 0| is used to detect integer numbers. We can skip |
11403 | // the multiplication by one in this case. |
11404 | masm.moveDouble(lhs, output); |
11405 | masm.nearbyIntDouble(RoundingMode::TowardsZero, output, scratch); |
11406 | masm.subDouble(scratch, output); |
11407 | } else { |
11408 | masm.loadConstantDouble(1.0 / double(divisor), scratch); |
11409 | masm.loadConstantDouble(double(divisor), output); |
11410 | |
11411 | masm.mulDouble(lhs, scratch); |
11412 | masm.nearbyIntDouble(RoundingMode::TowardsZero, scratch, scratch); |
11413 | masm.mulDouble(output, scratch); |
11414 | |
11415 | masm.moveDouble(lhs, output); |
11416 | masm.subDouble(scratch, output); |
11417 | } |
11418 | } |
11419 | |
11420 | masm.copySignDouble(output, lhs, output); |
11421 | masm.bind(&done); |
11422 | } |
11423 | |
11424 | void CodeGenerator::visitWasmBuiltinModD(LWasmBuiltinModD* ins) { |
11425 | masm.Push(InstanceReg); |
11426 | int32_t framePushedAfterInstance = masm.framePushed(); |
11427 | |
11428 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
11429 | FloatRegister rhs = ToFloatRegister(ins->rhs()); |
11430 | |
11431 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11431); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11431; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11432 | |
11433 | masm.setupWasmABICall(); |
11434 | masm.passABIArg(lhs, ABIType::Float64); |
11435 | masm.passABIArg(rhs, ABIType::Float64); |
11436 | |
11437 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
11438 | masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD, |
11439 | mozilla::Some(instanceOffset), ABIType::Float64); |
11440 | |
11441 | masm.Pop(InstanceReg); |
11442 | } |
11443 | |
11444 | void CodeGenerator::visitClzI(LClzI* ins) { |
11445 | Register input = ToRegister(ins->input()); |
11446 | Register output = ToRegister(ins->output()); |
11447 | bool knownNotZero = ins->mir()->operandIsNeverZero(); |
11448 | |
11449 | masm.clz32(input, output, knownNotZero); |
11450 | } |
11451 | |
11452 | void CodeGenerator::visitCtzI(LCtzI* ins) { |
11453 | Register input = ToRegister(ins->input()); |
11454 | Register output = ToRegister(ins->output()); |
11455 | bool knownNotZero = ins->mir()->operandIsNeverZero(); |
11456 | |
11457 | masm.ctz32(input, output, knownNotZero); |
11458 | } |
11459 | |
11460 | void CodeGenerator::visitPopcntI(LPopcntI* ins) { |
11461 | Register input = ToRegister(ins->input()); |
11462 | Register output = ToRegister(ins->output()); |
11463 | Register temp = ToRegister(ins->temp0()); |
11464 | |
11465 | masm.popcnt32(input, output, temp); |
11466 | } |
11467 | |
11468 | void CodeGenerator::visitClzI64(LClzI64* ins) { |
11469 | Register64 input = ToRegister64(ins->num()); |
11470 | Register64 output = ToOutRegister64(ins); |
11471 | |
11472 | masm.clz64(input, output); |
11473 | } |
11474 | |
11475 | void CodeGenerator::visitCtzI64(LCtzI64* ins) { |
11476 | Register64 input = ToRegister64(ins->num()); |
11477 | Register64 output = ToOutRegister64(ins); |
11478 | |
11479 | masm.ctz64(input, output); |
11480 | } |
11481 | |
11482 | void CodeGenerator::visitPopcntI64(LPopcntI64* ins) { |
11483 | Register64 input = ToRegister64(ins->num()); |
11484 | Register64 output = ToOutRegister64(ins); |
11485 | Register temp = ToRegister(ins->temp0()); |
11486 | |
11487 | masm.popcnt64(input, output, temp); |
11488 | } |
11489 | |
11490 | void CodeGenerator::visitBigIntAdd(LBigIntAdd* ins) { |
11491 | pushArg(ToRegister(ins->rhs())); |
11492 | pushArg(ToRegister(ins->lhs())); |
11493 | |
11494 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11495 | callVM<Fn, BigInt::add>(ins); |
11496 | } |
11497 | |
11498 | void CodeGenerator::visitBigIntSub(LBigIntSub* ins) { |
11499 | pushArg(ToRegister(ins->rhs())); |
11500 | pushArg(ToRegister(ins->lhs())); |
11501 | |
11502 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11503 | callVM<Fn, BigInt::sub>(ins); |
11504 | } |
11505 | |
11506 | void CodeGenerator::visitBigIntMul(LBigIntMul* ins) { |
11507 | pushArg(ToRegister(ins->rhs())); |
11508 | pushArg(ToRegister(ins->lhs())); |
11509 | |
11510 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11511 | callVM<Fn, BigInt::mul>(ins); |
11512 | } |
11513 | |
11514 | void CodeGenerator::visitBigIntDiv(LBigIntDiv* ins) { |
11515 | pushArg(ToRegister(ins->rhs())); |
11516 | pushArg(ToRegister(ins->lhs())); |
11517 | |
11518 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11519 | callVM<Fn, BigInt::div>(ins); |
11520 | } |
11521 | |
11522 | void CodeGenerator::visitBigIntMod(LBigIntMod* ins) { |
11523 | pushArg(ToRegister(ins->rhs())); |
11524 | pushArg(ToRegister(ins->lhs())); |
11525 | |
11526 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11527 | callVM<Fn, BigInt::mod>(ins); |
11528 | } |
11529 | |
11530 | void CodeGenerator::visitBigIntPow(LBigIntPow* ins) { |
11531 | pushArg(ToRegister(ins->rhs())); |
11532 | pushArg(ToRegister(ins->lhs())); |
11533 | |
11534 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11535 | callVM<Fn, BigInt::pow>(ins); |
11536 | } |
11537 | |
11538 | void CodeGenerator::visitBigIntBitAnd(LBigIntBitAnd* ins) { |
11539 | pushArg(ToRegister(ins->rhs())); |
11540 | pushArg(ToRegister(ins->lhs())); |
11541 | |
11542 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11543 | callVM<Fn, BigInt::bitAnd>(ins); |
11544 | } |
11545 | |
11546 | void CodeGenerator::visitBigIntBitOr(LBigIntBitOr* ins) { |
11547 | pushArg(ToRegister(ins->rhs())); |
11548 | pushArg(ToRegister(ins->lhs())); |
11549 | |
11550 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11551 | callVM<Fn, BigInt::bitOr>(ins); |
11552 | } |
11553 | |
11554 | void CodeGenerator::visitBigIntBitXor(LBigIntBitXor* ins) { |
11555 | pushArg(ToRegister(ins->rhs())); |
11556 | pushArg(ToRegister(ins->lhs())); |
11557 | |
11558 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11559 | callVM<Fn, BigInt::bitXor>(ins); |
11560 | } |
11561 | |
11562 | void CodeGenerator::visitBigIntLsh(LBigIntLsh* ins) { |
11563 | pushArg(ToRegister(ins->rhs())); |
11564 | pushArg(ToRegister(ins->lhs())); |
11565 | |
11566 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11567 | callVM<Fn, BigInt::lsh>(ins); |
11568 | } |
11569 | |
11570 | void CodeGenerator::visitBigIntRsh(LBigIntRsh* ins) { |
11571 | pushArg(ToRegister(ins->rhs())); |
11572 | pushArg(ToRegister(ins->lhs())); |
11573 | |
11574 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11575 | callVM<Fn, BigInt::rsh>(ins); |
11576 | } |
11577 | |
11578 | void CodeGenerator::visitBigIntIncrement(LBigIntIncrement* ins) { |
11579 | pushArg(ToRegister(ins->input())); |
11580 | |
11581 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
11582 | callVM<Fn, BigInt::inc>(ins); |
11583 | } |
11584 | |
11585 | void CodeGenerator::visitBigIntDecrement(LBigIntDecrement* ins) { |
11586 | pushArg(ToRegister(ins->input())); |
11587 | |
11588 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
11589 | callVM<Fn, BigInt::dec>(ins); |
11590 | } |
11591 | |
11592 | void CodeGenerator::visitBigIntNegate(LBigIntNegate* ins) { |
11593 | Register input = ToRegister(ins->input()); |
11594 | Register temp = ToRegister(ins->temp0()); |
11595 | Register output = ToRegister(ins->output()); |
11596 | |
11597 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
11598 | auto* ool = |
11599 | oolCallVM<Fn, BigInt::neg>(ins, ArgList(input), StoreRegisterTo(output)); |
11600 | |
11601 | // -0n == 0n |
11602 | Label lhsNonZero; |
11603 | masm.branchIfBigIntIsNonZero(input, &lhsNonZero); |
11604 | masm.movePtr(input, output); |
11605 | masm.jump(ool->rejoin()); |
11606 | masm.bind(&lhsNonZero); |
11607 | |
11608 | // Call into the VM when the input uses heap digits. |
11609 | masm.copyBigIntWithInlineDigits(input, output, temp, initialBigIntHeap(), |
11610 | ool->entry()); |
11611 | |
11612 | // Flip the sign bit. |
11613 | masm.xor32(Imm32(BigInt::signBitMask()), |
11614 | Address(output, BigInt::offsetOfFlags())); |
11615 | |
11616 | masm.bind(ool->rejoin()); |
11617 | } |
11618 | |
11619 | void CodeGenerator::visitBigIntBitNot(LBigIntBitNot* ins) { |
11620 | pushArg(ToRegister(ins->input())); |
11621 | |
11622 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
11623 | callVM<Fn, BigInt::bitNot>(ins); |
11624 | } |
11625 | |
11626 | void CodeGenerator::visitBigIntToIntPtr(LBigIntToIntPtr* ins) { |
11627 | Register input = ToRegister(ins->input()); |
11628 | Register output = ToRegister(ins->output()); |
11629 | |
11630 | Label bail; |
11631 | masm.loadBigIntPtr(input, output, &bail); |
11632 | bailoutFrom(&bail, ins->snapshot()); |
11633 | } |
11634 | |
11635 | void CodeGenerator::visitIntPtrToBigInt(LIntPtrToBigInt* ins) { |
11636 | Register input = ToRegister(ins->input()); |
11637 | Register temp = ToRegister(ins->temp0()); |
11638 | Register output = ToRegister(ins->output()); |
11639 | |
11640 | using Fn = BigInt* (*)(JSContext*, intptr_t); |
11641 | auto* ool = oolCallVM<Fn, JS::BigInt::createFromIntPtr>( |
11642 | ins, ArgList(input), StoreRegisterTo(output)); |
11643 | |
11644 | masm.newGCBigInt(output, temp, initialBigIntHeap(), ool->entry()); |
11645 | masm.movePtr(input, temp); |
11646 | masm.initializeBigIntPtr(output, temp); |
11647 | |
11648 | masm.bind(ool->rejoin()); |
11649 | } |
11650 | |
11651 | void CodeGenerator::visitBigIntPtrAdd(LBigIntPtrAdd* ins) { |
11652 | Register lhs = ToRegister(ins->lhs()); |
11653 | const LAllocation* rhs = ins->rhs(); |
11654 | Register output = ToRegister(ins->output()); |
11655 | |
11656 | if (rhs->isConstant()) { |
11657 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
11658 | } else { |
11659 | masm.movePtr(ToRegister(rhs), output); |
11660 | } |
11661 | |
11662 | Label bail; |
11663 | masm.branchAddPtr(Assembler::Overflow, lhs, output, &bail); |
11664 | bailoutFrom(&bail, ins->snapshot()); |
11665 | } |
11666 | |
11667 | void CodeGenerator::visitBigIntPtrSub(LBigIntPtrSub* ins) { |
11668 | Register lhs = ToRegister(ins->lhs()); |
11669 | Register rhs = ToRegister(ins->rhs()); |
11670 | Register output = ToRegister(ins->output()); |
11671 | |
11672 | Label bail; |
11673 | masm.movePtr(lhs, output); |
11674 | masm.branchSubPtr(Assembler::Overflow, rhs, output, &bail); |
11675 | bailoutFrom(&bail, ins->snapshot()); |
11676 | } |
11677 | |
11678 | void CodeGenerator::visitBigIntPtrMul(LBigIntPtrMul* ins) { |
11679 | Register lhs = ToRegister(ins->lhs()); |
11680 | const LAllocation* rhs = ins->rhs(); |
11681 | Register output = ToRegister(ins->output()); |
11682 | |
11683 | if (rhs->isConstant()) { |
11684 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
11685 | } else { |
11686 | masm.movePtr(ToRegister(rhs), output); |
11687 | } |
11688 | |
11689 | Label bail; |
11690 | masm.branchMulPtr(Assembler::Overflow, lhs, output, &bail); |
11691 | bailoutFrom(&bail, ins->snapshot()); |
11692 | } |
11693 | |
11694 | void CodeGenerator::visitBigIntPtrDiv(LBigIntPtrDiv* ins) { |
11695 | Register lhs = ToRegister(ins->lhs()); |
11696 | Register rhs = ToRegister(ins->rhs()); |
11697 | Register output = ToRegister(ins->output()); |
11698 | |
11699 | // x / 0 throws an error. |
11700 | Label bail; |
11701 | if (ins->mir()->canBeDivideByZero()) { |
11702 | masm.branchPtr(Assembler::Equal, rhs, Imm32(0), &bail); |
11703 | } |
11704 | |
11705 | static constexpr auto DigitMin = std::numeric_limits< |
11706 | mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min(); |
11707 | |
11708 | // Handle an integer overflow from INT{32,64}_MIN / -1. |
11709 | Label notOverflow; |
11710 | masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), ¬Overflow); |
11711 | masm.branchPtr(Assembler::Equal, rhs, Imm32(-1), &bail); |
11712 | masm.bind(¬Overflow); |
11713 | |
11714 | emitBigIntPtrDiv(ins, lhs, rhs, output); |
11715 | |
11716 | bailoutFrom(&bail, ins->snapshot()); |
11717 | } |
11718 | |
11719 | void CodeGenerator::visitBigIntPtrDivPowTwo(LBigIntPtrDivPowTwo* ins) { |
11720 | Register lhs = ToRegister(ins->lhs()); |
11721 | Register output = ToRegister(ins->output()); |
11722 | int32_t shift = ins->shift(); |
11723 | bool negativeDivisor = ins->negativeDivisor(); |
11724 | |
11725 | masm.movePtr(lhs, output); |
11726 | |
11727 | if (shift) { |
11728 | // Adjust the value so that shifting produces a correctly rounded result |
11729 | // when the numerator is negative. |
11730 | // See 10-1 "Signed Division by a Known Power of 2" in Henry S. Warren, |
11731 | // Jr.'s Hacker's Delight. |
11732 | |
11733 | constexpr size_t bits = BigInt::DigitBits; |
11734 | |
11735 | if (shift > 1) { |
11736 | // Copy the sign bit of the numerator. (= (2^bits - 1) or 0) |
11737 | masm.rshiftPtrArithmetic(Imm32(bits - 1), output); |
11738 | } |
11739 | |
11740 | // Divide by 2^(bits - shift) |
11741 | // i.e. (= (2^bits - 1) / 2^(bits - shift) or 0) |
11742 | // i.e. (= (2^shift - 1) or 0) |
11743 | masm.rshiftPtr(Imm32(bits - shift), output); |
11744 | |
11745 | // If signed, make any 1 bit below the shifted bits to bubble up, such that |
11746 | // once shifted the value would be rounded towards 0. |
11747 | masm.addPtr(lhs, output); |
11748 | |
11749 | masm.rshiftPtrArithmetic(Imm32(shift), output); |
11750 | |
11751 | if (negativeDivisor) { |
11752 | masm.negPtr(output); |
11753 | } |
11754 | } else if (negativeDivisor) { |
11755 | Label bail; |
11756 | masm.branchNegPtr(Assembler::Overflow, output, &bail); |
11757 | bailoutFrom(&bail, ins->snapshot()); |
11758 | } |
11759 | } |
11760 | |
11761 | void CodeGenerator::visitBigIntPtrMod(LBigIntPtrMod* ins) { |
11762 | Register lhs = ToRegister(ins->lhs()); |
11763 | Register rhs = ToRegister(ins->rhs()); |
11764 | Register output = ToRegister(ins->output()); |
11765 | Register temp = ToRegister(ins->temp0()); |
11766 | |
11767 | // x % 0 throws an error. |
11768 | if (ins->mir()->canBeDivideByZero()) { |
11769 | bailoutCmpPtr(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); |
11770 | } |
11771 | |
11772 | static constexpr auto DigitMin = std::numeric_limits< |
11773 | mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min(); |
11774 | |
11775 | masm.movePtr(lhs, temp); |
11776 | |
11777 | // Handle an integer overflow from INT{32,64}_MIN / -1. |
11778 | Label notOverflow; |
11779 | masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), ¬Overflow); |
11780 | masm.branchPtr(Assembler::NotEqual, rhs, Imm32(-1), ¬Overflow); |
11781 | masm.movePtr(ImmWord(0), temp); |
11782 | masm.bind(¬Overflow); |
11783 | |
11784 | emitBigIntPtrMod(ins, temp, rhs, output); |
11785 | } |
11786 | |
11787 | void CodeGenerator::visitBigIntPtrModPowTwo(LBigIntPtrModPowTwo* ins) { |
11788 | Register lhs = ToRegister(ins->lhs()); |
11789 | Register output = ToRegister(ins->output()); |
11790 | Register temp = ToRegister(ins->temp0()); |
11791 | int32_t shift = ins->shift(); |
11792 | |
11793 | masm.movePtr(lhs, output); |
11794 | masm.movePtr(ImmWord((uintptr_t(1) << shift) - uintptr_t(1)), temp); |
11795 | |
11796 | // Switch based on sign of the lhs. |
11797 | |
11798 | // Positive numbers are just a bitmask. |
11799 | Label negative; |
11800 | masm.branchTestPtr(Assembler::Signed, lhs, lhs, &negative); |
11801 | |
11802 | masm.andPtr(temp, output); |
11803 | |
11804 | Label done; |
11805 | masm.jump(&done); |
11806 | |
11807 | // Negative numbers need a negate, bitmask, negate |
11808 | masm.bind(&negative); |
11809 | |
11810 | masm.negPtr(output); |
11811 | masm.andPtr(temp, output); |
11812 | masm.negPtr(output); |
11813 | |
11814 | masm.bind(&done); |
11815 | } |
11816 | |
11817 | void CodeGenerator::visitBigIntPtrPow(LBigIntPtrPow* ins) { |
11818 | Register lhs = ToRegister(ins->lhs()); |
11819 | Register rhs = ToRegister(ins->rhs()); |
11820 | Register output = ToRegister(ins->output()); |
11821 | Register temp0 = ToRegister(ins->temp0()); |
11822 | Register temp1 = ToRegister(ins->temp1()); |
11823 | |
11824 | Label bail; |
11825 | masm.powPtr(lhs, rhs, output, temp0, temp1, &bail); |
11826 | bailoutFrom(&bail, ins->snapshot()); |
11827 | } |
11828 | |
11829 | void CodeGenerator::visitBigIntPtrBitAnd(LBigIntPtrBitAnd* ins) { |
11830 | Register lhs = ToRegister(ins->lhs()); |
11831 | const LAllocation* rhs = ins->rhs(); |
11832 | Register output = ToRegister(ins->output()); |
11833 | |
11834 | if (rhs->isConstant()) { |
11835 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
11836 | } else { |
11837 | masm.movePtr(ToRegister(rhs), output); |
11838 | } |
11839 | masm.andPtr(lhs, output); |
11840 | } |
11841 | |
11842 | void CodeGenerator::visitBigIntPtrBitOr(LBigIntPtrBitOr* ins) { |
11843 | Register lhs = ToRegister(ins->lhs()); |
11844 | const LAllocation* rhs = ins->rhs(); |
11845 | Register output = ToRegister(ins->output()); |
11846 | |
11847 | if (rhs->isConstant()) { |
11848 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
11849 | } else { |
11850 | masm.movePtr(ToRegister(rhs), output); |
11851 | } |
11852 | masm.orPtr(lhs, output); |
11853 | } |
11854 | |
11855 | void CodeGenerator::visitBigIntPtrBitXor(LBigIntPtrBitXor* ins) { |
11856 | Register lhs = ToRegister(ins->lhs()); |
11857 | const LAllocation* rhs = ins->rhs(); |
11858 | Register output = ToRegister(ins->output()); |
11859 | |
11860 | if (rhs->isConstant()) { |
11861 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
11862 | } else { |
11863 | masm.movePtr(ToRegister(rhs), output); |
11864 | } |
11865 | masm.xorPtr(lhs, output); |
11866 | } |
11867 | |
11868 | void CodeGenerator::visitBigIntPtrLsh(LBigIntPtrLsh* ins) { |
11869 | Register lhs = ToRegister(ins->lhs()); |
11870 | Register output = ToRegister(ins->output()); |
11871 | Register temp = ToTempRegisterOrInvalid(ins->temp0()); |
11872 | Register tempShift = ToTempRegisterOrInvalid(ins->temp1()); |
11873 | |
11874 | if (ins->rhs()->isConstant()) { |
11875 | intptr_t rhs = ToIntPtr(ins->rhs()); |
11876 | |
11877 | if (rhs >= intptr_t(BigInt::DigitBits)) { |
11878 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11878); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11878; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11879 | |
11880 | // x << DigitBits with x != 0n always exceeds pointer-sized storage. |
11881 | masm.movePtr(ImmWord(0), output); |
11882 | bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot()); |
11883 | } else if (rhs <= -intptr_t(BigInt::DigitBits)) { |
11884 | MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11884); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11884; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11885 | |
11886 | // x << -DigitBits == x >> DigitBits, which is either 0n or -1n. |
11887 | masm.movePtr(lhs, output); |
11888 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
11889 | } else if (rhs <= 0) { |
11890 | MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11890); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11890; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11891 | |
11892 | // |x << -y| is computed as |x >> y|. |
11893 | masm.movePtr(lhs, output); |
11894 | masm.rshiftPtrArithmetic(Imm32(-rhs), output); |
11895 | } else { |
11896 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11896); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11896; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11897 | |
11898 | masm.movePtr(lhs, output); |
11899 | masm.lshiftPtr(Imm32(rhs), output); |
11900 | |
11901 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
11902 | masm.movePtr(output, temp); |
11903 | masm.rshiftPtrArithmetic(Imm32(rhs), temp); |
11904 | bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot()); |
11905 | } |
11906 | } else { |
11907 | Register rhs = ToRegister(ins->rhs()); |
11908 | |
11909 | Label done, bail; |
11910 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11910); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11910; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11911 | |
11912 | masm.movePtr(lhs, output); |
11913 | |
11914 | // 0n << x == 0n |
11915 | masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done); |
11916 | |
11917 | // x << DigitBits with x != 0n always exceeds pointer-sized storage. |
11918 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(BigInt::DigitBits), |
11919 | &bail); |
11920 | |
11921 | // x << -DigitBits == x >> DigitBits, which is either 0n or -1n. |
11922 | Label shift; |
11923 | masm.branchPtr(Assembler::GreaterThan, rhs, |
11924 | Imm32(-int32_t(BigInt::DigitBits)), &shift); |
11925 | { |
11926 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
11927 | masm.jump(&done); |
11928 | } |
11929 | masm.bind(&shift); |
11930 | |
11931 | // Move |rhs| into the designated shift register. |
11932 | masm.movePtr(rhs, tempShift); |
11933 | |
11934 | // |x << -y| is computed as |x >> y|. |
11935 | Label leftShift; |
11936 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &leftShift); |
11937 | { |
11938 | masm.negPtr(tempShift); |
11939 | masm.rshiftPtrArithmetic(tempShift, output); |
11940 | masm.jump(&done); |
11941 | } |
11942 | masm.bind(&leftShift); |
11943 | |
11944 | masm.lshiftPtr(tempShift, output); |
11945 | |
11946 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
11947 | masm.movePtr(output, temp); |
11948 | masm.rshiftPtrArithmetic(tempShift, temp); |
11949 | masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail); |
11950 | |
11951 | masm.bind(&done); |
11952 | bailoutFrom(&bail, ins->snapshot()); |
11953 | } |
11954 | } |
11955 | |
11956 | void CodeGenerator::visitBigIntPtrRsh(LBigIntPtrRsh* ins) { |
11957 | Register lhs = ToRegister(ins->lhs()); |
11958 | Register output = ToRegister(ins->output()); |
11959 | Register temp = ToTempRegisterOrInvalid(ins->temp0()); |
11960 | Register tempShift = ToTempRegisterOrInvalid(ins->temp1()); |
11961 | |
11962 | if (ins->rhs()->isConstant()) { |
11963 | intptr_t rhs = ToIntPtr(ins->rhs()); |
11964 | |
11965 | if (rhs <= -intptr_t(BigInt::DigitBits)) { |
11966 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11966; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11967 | |
11968 | // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage. |
11969 | masm.movePtr(ImmWord(0), output); |
11970 | bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot()); |
11971 | } else if (rhs >= intptr_t(BigInt::DigitBits)) { |
11972 | MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11972; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11973 | |
11974 | // x >> DigitBits is either 0n or -1n. |
11975 | masm.movePtr(lhs, output); |
11976 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
11977 | } else if (rhs < 0) { |
11978 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11978); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11978; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11979 | |
11980 | // |x >> -y| is computed as |x << y|. |
11981 | masm.movePtr(lhs, output); |
11982 | masm.lshiftPtr(Imm32(-rhs), output); |
11983 | |
11984 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
11985 | masm.movePtr(output, temp); |
11986 | masm.rshiftPtrArithmetic(Imm32(-rhs), temp); |
11987 | bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot()); |
11988 | } else { |
11989 | MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11989); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11989; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11990 | |
11991 | masm.movePtr(lhs, output); |
11992 | masm.rshiftPtrArithmetic(Imm32(rhs), output); |
11993 | } |
11994 | } else { |
11995 | Register rhs = ToRegister(ins->rhs()); |
11996 | |
11997 | Label done, bail; |
11998 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11998); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11998; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11999 | |
12000 | masm.movePtr(lhs, output); |
12001 | |
12002 | // 0n >> x == 0n |
12003 | masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done); |
12004 | |
12005 | // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage. |
12006 | masm.branchPtr(Assembler::LessThanOrEqual, rhs, |
12007 | Imm32(-int32_t(BigInt::DigitBits)), &bail); |
12008 | |
12009 | // x >> DigitBits is either 0n or -1n. |
12010 | Label shift; |
12011 | masm.branchPtr(Assembler::LessThan, rhs, Imm32(BigInt::DigitBits), &shift); |
12012 | { |
12013 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
12014 | masm.jump(&done); |
12015 | } |
12016 | masm.bind(&shift); |
12017 | |
12018 | // Move |rhs| into the designated shift register. |
12019 | masm.movePtr(rhs, tempShift); |
12020 | |
12021 | // |x >> -y| is computed as |x << y|. |
12022 | Label rightShift; |
12023 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &rightShift); |
12024 | { |
12025 | masm.negPtr(tempShift); |
12026 | masm.lshiftPtr(tempShift, output); |
12027 | |
12028 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
12029 | masm.movePtr(output, temp); |
12030 | masm.rshiftPtrArithmetic(tempShift, temp); |
12031 | masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail); |
12032 | |
12033 | masm.jump(&done); |
12034 | } |
12035 | masm.bind(&rightShift); |
12036 | |
12037 | masm.rshiftPtrArithmetic(tempShift, output); |
12038 | |
12039 | masm.bind(&done); |
12040 | bailoutFrom(&bail, ins->snapshot()); |
12041 | } |
12042 | } |
12043 | |
12044 | void CodeGenerator::visitBigIntPtrBitNot(LBigIntPtrBitNot* ins) { |
12045 | Register input = ToRegister(ins->input()); |
12046 | Register output = ToRegister(ins->output()); |
12047 | |
12048 | masm.movePtr(input, output); |
12049 | masm.notPtr(output); |
12050 | } |
12051 | |
12052 | void CodeGenerator::visitInt32ToStringWithBase(LInt32ToStringWithBase* lir) { |
12053 | Register input = ToRegister(lir->input()); |
12054 | RegisterOrInt32 base = ToRegisterOrInt32(lir->base()); |
12055 | Register output = ToRegister(lir->output()); |
12056 | Register temp0 = ToRegister(lir->temp0()); |
12057 | Register temp1 = ToRegister(lir->temp1()); |
12058 | |
12059 | bool lowerCase = lir->mir()->lowerCase(); |
12060 | |
12061 | using Fn = JSLinearString* (*)(JSContext*, int32_t, int32_t, bool); |
12062 | if (base.is<Register>()) { |
12063 | auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>( |
12064 | lir, ArgList(input, base.as<Register>(), Imm32(lowerCase)), |
12065 | StoreRegisterTo(output)); |
12066 | |
12067 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
12068 | masm.loadInt32ToStringWithBase(input, base.as<Register>(), output, temp0, |
12069 | temp1, gen->runtime->staticStrings(), |
12070 | liveRegs, lowerCase, ool->entry()); |
12071 | masm.bind(ool->rejoin()); |
12072 | } else { |
12073 | auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>( |
12074 | lir, ArgList(input, Imm32(base.as<int32_t>()), Imm32(lowerCase)), |
12075 | StoreRegisterTo(output)); |
12076 | |
12077 | masm.loadInt32ToStringWithBase(input, base.as<int32_t>(), output, temp0, |
12078 | temp1, gen->runtime->staticStrings(), |
12079 | lowerCase, ool->entry()); |
12080 | masm.bind(ool->rejoin()); |
12081 | } |
12082 | } |
12083 | |
12084 | void CodeGenerator::visitNumberParseInt(LNumberParseInt* lir) { |
12085 | Register string = ToRegister(lir->string()); |
12086 | Register radix = ToRegister(lir->radix()); |
12087 | ValueOperand output = ToOutValue(lir); |
12088 | Register temp = ToRegister(lir->temp0()); |
12089 | |
12090 | #ifdef DEBUG1 |
12091 | Label ok; |
12092 | masm.branch32(Assembler::Equal, radix, Imm32(0), &ok); |
12093 | masm.branch32(Assembler::Equal, radix, Imm32(10), &ok); |
12094 | masm.assumeUnreachable("radix must be 0 or 10 for indexed value fast path"); |
12095 | masm.bind(&ok); |
12096 | #endif |
12097 | |
12098 | // Use indexed value as fast path if possible. |
12099 | Label vmCall, done; |
12100 | masm.loadStringIndexValue(string, temp, &vmCall); |
12101 | masm.tagValue(JSVAL_TYPE_INT32, temp, output); |
12102 | masm.jump(&done); |
12103 | { |
12104 | masm.bind(&vmCall); |
12105 | |
12106 | pushArg(radix); |
12107 | pushArg(string); |
12108 | |
12109 | using Fn = bool (*)(JSContext*, HandleString, int32_t, MutableHandleValue); |
12110 | callVM<Fn, js::NumberParseInt>(lir); |
12111 | } |
12112 | masm.bind(&done); |
12113 | } |
12114 | |
12115 | void CodeGenerator::visitDoubleParseInt(LDoubleParseInt* lir) { |
12116 | FloatRegister number = ToFloatRegister(lir->number()); |
12117 | Register output = ToRegister(lir->output()); |
12118 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
12119 | |
12120 | Label bail; |
12121 | masm.branchDouble(Assembler::DoubleUnordered, number, number, &bail); |
12122 | masm.branchTruncateDoubleToInt32(number, output, &bail); |
12123 | |
12124 | Label ok; |
12125 | masm.branch32(Assembler::NotEqual, output, Imm32(0), &ok); |
12126 | { |
12127 | // Accept both +0 and -0 and return 0. |
12128 | masm.loadConstantDouble(0.0, temp); |
12129 | masm.branchDouble(Assembler::DoubleEqual, number, temp, &ok); |
12130 | |
12131 | // Fail if a non-zero input is in the exclusive range (-1, 1.0e-6). |
12132 | masm.loadConstantDouble(DOUBLE_DECIMAL_IN_SHORTEST_LOW, temp); |
12133 | masm.branchDouble(Assembler::DoubleLessThan, number, temp, &bail); |
12134 | } |
12135 | masm.bind(&ok); |
12136 | |
12137 | bailoutFrom(&bail, lir->snapshot()); |
12138 | } |
12139 | |
12140 | void CodeGenerator::visitFloor(LFloor* lir) { |
12141 | FloatRegister input = ToFloatRegister(lir->input()); |
12142 | Register output = ToRegister(lir->output()); |
12143 | |
12144 | Label bail; |
12145 | masm.floorDoubleToInt32(input, output, &bail); |
12146 | bailoutFrom(&bail, lir->snapshot()); |
12147 | } |
12148 | |
12149 | void CodeGenerator::visitFloorF(LFloorF* lir) { |
12150 | FloatRegister input = ToFloatRegister(lir->input()); |
12151 | Register output = ToRegister(lir->output()); |
12152 | |
12153 | Label bail; |
12154 | masm.floorFloat32ToInt32(input, output, &bail); |
12155 | bailoutFrom(&bail, lir->snapshot()); |
12156 | } |
12157 | |
12158 | void CodeGenerator::visitCeil(LCeil* lir) { |
12159 | FloatRegister input = ToFloatRegister(lir->input()); |
12160 | Register output = ToRegister(lir->output()); |
12161 | |
12162 | Label bail; |
12163 | masm.ceilDoubleToInt32(input, output, &bail); |
12164 | bailoutFrom(&bail, lir->snapshot()); |
12165 | } |
12166 | |
12167 | void CodeGenerator::visitCeilF(LCeilF* lir) { |
12168 | FloatRegister input = ToFloatRegister(lir->input()); |
12169 | Register output = ToRegister(lir->output()); |
12170 | |
12171 | Label bail; |
12172 | masm.ceilFloat32ToInt32(input, output, &bail); |
12173 | bailoutFrom(&bail, lir->snapshot()); |
12174 | } |
12175 | |
12176 | void CodeGenerator::visitRound(LRound* lir) { |
12177 | FloatRegister input = ToFloatRegister(lir->input()); |
12178 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
12179 | Register output = ToRegister(lir->output()); |
12180 | |
12181 | Label bail; |
12182 | masm.roundDoubleToInt32(input, output, temp, &bail); |
12183 | bailoutFrom(&bail, lir->snapshot()); |
12184 | } |
12185 | |
12186 | void CodeGenerator::visitRoundF(LRoundF* lir) { |
12187 | FloatRegister input = ToFloatRegister(lir->input()); |
12188 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
12189 | Register output = ToRegister(lir->output()); |
12190 | |
12191 | Label bail; |
12192 | masm.roundFloat32ToInt32(input, output, temp, &bail); |
12193 | bailoutFrom(&bail, lir->snapshot()); |
12194 | } |
12195 | |
12196 | void CodeGenerator::visitTrunc(LTrunc* lir) { |
12197 | FloatRegister input = ToFloatRegister(lir->input()); |
12198 | Register output = ToRegister(lir->output()); |
12199 | |
12200 | Label bail; |
12201 | masm.truncDoubleToInt32(input, output, &bail); |
12202 | bailoutFrom(&bail, lir->snapshot()); |
12203 | } |
12204 | |
12205 | void CodeGenerator::visitTruncF(LTruncF* lir) { |
12206 | FloatRegister input = ToFloatRegister(lir->input()); |
12207 | Register output = ToRegister(lir->output()); |
12208 | |
12209 | Label bail; |
12210 | masm.truncFloat32ToInt32(input, output, &bail); |
12211 | bailoutFrom(&bail, lir->snapshot()); |
12212 | } |
12213 | |
12214 | void CodeGenerator::visitCompareS(LCompareS* lir) { |
12215 | JSOp op = lir->mir()->jsop(); |
12216 | Register left = ToRegister(lir->left()); |
12217 | Register right = ToRegister(lir->right()); |
12218 | Register output = ToRegister(lir->output()); |
12219 | |
12220 | OutOfLineCode* ool = nullptr; |
12221 | |
12222 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
12223 | if (op == JSOp::Eq || op == JSOp::StrictEq) { |
12224 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
12225 | lir, ArgList(left, right), StoreRegisterTo(output)); |
12226 | } else if (op == JSOp::Ne || op == JSOp::StrictNe) { |
12227 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
12228 | lir, ArgList(left, right), StoreRegisterTo(output)); |
12229 | } else if (op == JSOp::Lt) { |
12230 | ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>( |
12231 | lir, ArgList(left, right), StoreRegisterTo(output)); |
12232 | } else if (op == JSOp::Le) { |
12233 | // Push the operands in reverse order for JSOp::Le: |
12234 | // - |left <= right| is implemented as |right >= left|. |
12235 | ool = |
12236 | oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>( |
12237 | lir, ArgList(right, left), StoreRegisterTo(output)); |
12238 | } else if (op == JSOp::Gt) { |
12239 | // Push the operands in reverse order for JSOp::Gt: |
12240 | // - |left > right| is implemented as |right < left|. |
12241 | ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>( |
12242 | lir, ArgList(right, left), StoreRegisterTo(output)); |
12243 | } else { |
12244 | MOZ_ASSERT(op == JSOp::Ge)do { static_assert( mozilla::detail::AssertionConditionType< decltype(op == JSOp::Ge)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(op == JSOp::Ge))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("op == JSOp::Ge" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ge" ")"); do { *((volatile int*)__null) = 12244; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12245 | ool = |
12246 | oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>( |
12247 | lir, ArgList(left, right), StoreRegisterTo(output)); |
12248 | } |
12249 | |
12250 | masm.compareStrings(op, left, right, output, ool->entry()); |
12251 | |
12252 | masm.bind(ool->rejoin()); |
12253 | } |
12254 | |
12255 | void CodeGenerator::visitCompareSInline(LCompareSInline* lir) { |
12256 | JSOp op = lir->mir()->jsop(); |
12257 | MOZ_ASSERT(IsEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsEqualityOp(op)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12257); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12257; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12258 | |
12259 | Register input = ToRegister(lir->input()); |
12260 | Register output = ToRegister(lir->output()); |
12261 | |
12262 | const JSLinearString* str = lir->constant(); |
12263 | MOZ_ASSERT(str->length() > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(str->length() > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(str->length() > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("str->length() > 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12263); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() > 0" ")"); do { *((volatile int*)__null) = 12263; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12264 | |
12265 | OutOfLineCode* ool = nullptr; |
12266 | |
12267 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
12268 | if (op == JSOp::Eq || op == JSOp::StrictEq) { |
12269 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
12270 | lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output)); |
12271 | } else { |
12272 | MOZ_ASSERT(op == JSOp::Ne || op == JSOp::StrictNe)do { static_assert( mozilla::detail::AssertionConditionType< decltype(op == JSOp::Ne || op == JSOp::StrictNe)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(op == JSOp::Ne || op == JSOp::StrictNe))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("op == JSOp::Ne || op == JSOp::StrictNe" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ne || op == JSOp::StrictNe" ")"); do { *((volatile int*)__null) = 12272; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12273 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
12274 | lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output)); |
12275 | } |
12276 | |
12277 | Label compareChars; |
12278 | { |
12279 | Label notPointerEqual; |
12280 | |
12281 | // If operands point to the same instance, the strings are trivially equal. |
12282 | masm.branchPtr(Assembler::NotEqual, input, ImmGCPtr(str), ¬PointerEqual); |
12283 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
12284 | masm.jump(ool->rejoin()); |
12285 | |
12286 | masm.bind(¬PointerEqual); |
12287 | |
12288 | Label setNotEqualResult; |
12289 | |
12290 | if (str->isAtom()) { |
12291 | // Atoms cannot be equal to each other if they point to different strings. |
12292 | Imm32 atomBit(JSString::ATOM_BIT); |
12293 | masm.branchTest32(Assembler::NonZero, |
12294 | Address(input, JSString::offsetOfFlags()), atomBit, |
12295 | &setNotEqualResult); |
12296 | } |
12297 | |
12298 | if (str->hasTwoByteChars()) { |
12299 | // Pure two-byte strings can't be equal to Latin-1 strings. |
12300 | JS::AutoCheckCannotGC nogc; |
12301 | if (!mozilla::IsUtf16Latin1(str->twoByteRange(nogc))) { |
12302 | masm.branchLatin1String(input, &setNotEqualResult); |
12303 | } |
12304 | } |
12305 | |
12306 | // Strings of different length can never be equal. |
12307 | masm.branch32(Assembler::NotEqual, |
12308 | Address(input, JSString::offsetOfLength()), |
12309 | Imm32(str->length()), &setNotEqualResult); |
12310 | |
12311 | if (str->isAtom()) { |
12312 | Label forwardedPtrEqual; |
12313 | masm.tryFastAtomize(input, output, output, &compareChars); |
12314 | |
12315 | // We now have two atoms. Just check pointer equality. |
12316 | masm.branchPtr(Assembler::Equal, output, ImmGCPtr(str), |
12317 | &forwardedPtrEqual); |
12318 | |
12319 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
12320 | masm.jump(ool->rejoin()); |
12321 | |
12322 | masm.bind(&forwardedPtrEqual); |
12323 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
12324 | masm.jump(ool->rejoin()); |
12325 | } else { |
12326 | masm.jump(&compareChars); |
12327 | } |
12328 | |
12329 | masm.bind(&setNotEqualResult); |
12330 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
12331 | masm.jump(ool->rejoin()); |
12332 | } |
12333 | |
12334 | masm.bind(&compareChars); |
12335 | |
12336 | // Load the input string's characters. |
12337 | Register stringChars = output; |
12338 | masm.loadStringCharsForCompare(input, str, stringChars, ool->entry()); |
12339 | |
12340 | // Start comparing character by character. |
12341 | masm.compareStringChars(op, stringChars, str, output); |
12342 | |
12343 | masm.bind(ool->rejoin()); |
12344 | } |
12345 | |
12346 | void CodeGenerator::visitCompareSSingle(LCompareSSingle* lir) { |
12347 | JSOp op = lir->jsop(); |
12348 | MOZ_ASSERT(IsRelationalOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsRelationalOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsRelationalOp(op)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsRelationalOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12348); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsRelationalOp(op)" ")"); do { *((volatile int*)__null) = 12348; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12349 | |
12350 | Register input = ToRegister(lir->input()); |
12351 | Register output = ToRegister(lir->output()); |
12352 | Register temp = ToRegister(lir->temp0()); |
12353 | |
12354 | const JSLinearString* str = lir->constant(); |
12355 | MOZ_ASSERT(str->length() == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(str->length() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(str->length() == 1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("str->length() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12355); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() == 1" ")"); do { *((volatile int*)__null) = 12355; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12356 | |
12357 | char16_t ch = str->latin1OrTwoByteChar(0); |
12358 | |
12359 | masm.movePtr(input, temp); |
12360 | |
12361 | // Check if the string is empty. |
12362 | Label compareLength; |
12363 | masm.branch32(Assembler::Equal, Address(temp, JSString::offsetOfLength()), |
12364 | Imm32(0), &compareLength); |
12365 | |
12366 | // The first character is in the left-most rope child. |
12367 | Label notRope; |
12368 | masm.branchIfNotRope(temp, ¬Rope); |
12369 | { |
12370 | // Unwind ropes at the start if possible. |
12371 | Label unwindRope; |
12372 | masm.bind(&unwindRope); |
12373 | masm.loadRopeLeftChild(temp, output); |
12374 | masm.movePtr(output, temp); |
12375 | |
12376 | #ifdef DEBUG1 |
12377 | Label notEmpty; |
12378 | masm.branch32(Assembler::NotEqual, |
12379 | Address(temp, JSString::offsetOfLength()), Imm32(0), |
12380 | ¬Empty); |
12381 | masm.assumeUnreachable("rope children are non-empty"); |
12382 | masm.bind(¬Empty); |
12383 | #endif |
12384 | |
12385 | // Otherwise keep unwinding ropes. |
12386 | masm.branchIfRope(temp, &unwindRope); |
12387 | } |
12388 | masm.bind(¬Rope); |
12389 | |
12390 | // Load the first character into |output|. |
12391 | auto loadFirstChar = [&](auto encoding) { |
12392 | masm.loadStringChars(temp, output, encoding); |
12393 | masm.loadChar(Address(output, 0), output, encoding); |
12394 | }; |
12395 | |
12396 | Label done; |
12397 | if (ch <= JSString::MAX_LATIN1_CHAR) { |
12398 | // Handle both encodings when the search character is Latin-1. |
12399 | Label twoByte, compare; |
12400 | masm.branchTwoByteString(temp, &twoByte); |
12401 | |
12402 | loadFirstChar(CharEncoding::Latin1); |
12403 | masm.jump(&compare); |
12404 | |
12405 | masm.bind(&twoByte); |
12406 | loadFirstChar(CharEncoding::TwoByte); |
12407 | |
12408 | masm.bind(&compare); |
12409 | } else { |
12410 | // The search character is a two-byte character, so it can't be equal to any |
12411 | // character of a Latin-1 string. |
12412 | masm.move32(Imm32(int32_t(op == JSOp::Lt || op == JSOp::Le)), output); |
12413 | masm.branchLatin1String(temp, &done); |
12414 | |
12415 | loadFirstChar(CharEncoding::TwoByte); |
12416 | } |
12417 | |
12418 | // Compare the string length when the search character is equal to the |
12419 | // input's first character. |
12420 | masm.branch32(Assembler::Equal, output, Imm32(ch), &compareLength); |
12421 | |
12422 | // Otherwise compute the result and jump to the end. |
12423 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), output, Imm32(ch), |
12424 | output); |
12425 | masm.jump(&done); |
12426 | |
12427 | // Compare the string length to compute the overall result. |
12428 | masm.bind(&compareLength); |
12429 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), |
12430 | Address(temp, JSString::offsetOfLength()), Imm32(1), output); |
12431 | |
12432 | masm.bind(&done); |
12433 | } |
12434 | |
12435 | void CodeGenerator::visitCompareBigInt(LCompareBigInt* lir) { |
12436 | JSOp op = lir->mir()->jsop(); |
12437 | Register left = ToRegister(lir->left()); |
12438 | Register right = ToRegister(lir->right()); |
12439 | Register temp0 = ToRegister(lir->temp0()); |
12440 | Register temp1 = ToRegister(lir->temp1()); |
12441 | Register temp2 = ToRegister(lir->temp2()); |
12442 | Register output = ToRegister(lir->output()); |
12443 | |
12444 | Label notSame; |
12445 | Label compareSign; |
12446 | Label compareLength; |
12447 | Label compareDigit; |
12448 | |
12449 | Label* notSameSign; |
12450 | Label* notSameLength; |
12451 | Label* notSameDigit; |
12452 | if (IsEqualityOp(op)) { |
12453 | notSameSign = ¬Same; |
12454 | notSameLength = ¬Same; |
12455 | notSameDigit = ¬Same; |
12456 | } else { |
12457 | notSameSign = &compareSign; |
12458 | notSameLength = &compareLength; |
12459 | notSameDigit = &compareDigit; |
12460 | } |
12461 | |
12462 | masm.equalBigInts(left, right, temp0, temp1, temp2, output, notSameSign, |
12463 | notSameLength, notSameDigit); |
12464 | |
12465 | Label done; |
12466 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le || |
12467 | op == JSOp::Ge), |
12468 | output); |
12469 | masm.jump(&done); |
12470 | |
12471 | if (IsEqualityOp(op)) { |
12472 | masm.bind(¬Same); |
12473 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
12474 | } else { |
12475 | Label invertWhenNegative; |
12476 | |
12477 | // There are two cases when sign(left) != sign(right): |
12478 | // 1. sign(left) = positive and sign(right) = negative, |
12479 | // 2. or the dual case with reversed signs. |
12480 | // |
12481 | // For case 1, |left| <cmp> |right| is true for cmp=Gt or cmp=Ge and false |
12482 | // for cmp=Lt or cmp=Le. Initialize the result for case 1 and handle case 2 |
12483 | // with |invertWhenNegative|. |
12484 | masm.bind(&compareSign); |
12485 | masm.move32(Imm32(op == JSOp::Gt || op == JSOp::Ge), output); |
12486 | masm.jump(&invertWhenNegative); |
12487 | |
12488 | // For sign(left) = sign(right) and len(digits(left)) != len(digits(right)), |
12489 | // we have to consider the two cases: |
12490 | // 1. len(digits(left)) < len(digits(right)) |
12491 | // 2. len(digits(left)) > len(digits(right)) |
12492 | // |
12493 | // For |left| <cmp> |right| with cmp=Lt: |
12494 | // Assume both BigInts are positive, then |left < right| is true for case 1 |
12495 | // and false for case 2. When both are negative, the result is reversed. |
12496 | // |
12497 | // The other comparison operators can be handled similarly. |
12498 | // |
12499 | // |temp0| holds the digits length of the right-hand side operand. |
12500 | masm.bind(&compareLength); |
12501 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), |
12502 | Address(left, BigInt::offsetOfLength()), temp0, output); |
12503 | masm.jump(&invertWhenNegative); |
12504 | |
12505 | // Similar to the case above, compare the current digit to determine the |
12506 | // overall comparison result. |
12507 | // |
12508 | // |temp1| points to the current digit of the left-hand side operand. |
12509 | // |output| holds the current digit of the right-hand side operand. |
12510 | masm.bind(&compareDigit); |
12511 | masm.cmpPtrSet(JSOpToCondition(op, /* isSigned = */ false), |
12512 | Address(temp1, 0), output, output); |
12513 | |
12514 | Label nonNegative; |
12515 | masm.bind(&invertWhenNegative); |
12516 | masm.branchIfBigIntIsNonNegative(left, &nonNegative); |
12517 | masm.xor32(Imm32(1), output); |
12518 | masm.bind(&nonNegative); |
12519 | } |
12520 | |
12521 | masm.bind(&done); |
12522 | } |
12523 | |
12524 | void CodeGenerator::visitCompareBigIntInt32(LCompareBigIntInt32* lir) { |
12525 | JSOp op = lir->mir()->jsop(); |
12526 | Register left = ToRegister(lir->left()); |
12527 | Register temp0 = ToRegister(lir->temp0()); |
12528 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
12529 | Register output = ToRegister(lir->output()); |
12530 | |
12531 | Label ifTrue, ifFalse; |
12532 | if (lir->right()->isConstant()) { |
12533 | MOZ_ASSERT(temp1 == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 == InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 == InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp1 == InvalidReg" ")"); do { *((volatile int*)__null) = 12533; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12534 | |
12535 | Imm32 right = Imm32(ToInt32(lir->right())); |
12536 | masm.compareBigIntAndInt32(op, left, right, temp0, &ifTrue, &ifFalse); |
12537 | } else { |
12538 | MOZ_ASSERT(temp1 != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 != InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 != InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp1 != InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12538); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp1 != InvalidReg" ")"); do { *((volatile int*)__null) = 12538; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12539 | |
12540 | Register right = ToRegister(lir->right()); |
12541 | masm.compareBigIntAndInt32(op, left, right, temp0, temp1, &ifTrue, |
12542 | &ifFalse); |
12543 | } |
12544 | |
12545 | Label done; |
12546 | masm.bind(&ifFalse); |
12547 | masm.move32(Imm32(0), output); |
12548 | masm.jump(&done); |
12549 | masm.bind(&ifTrue); |
12550 | masm.move32(Imm32(1), output); |
12551 | masm.bind(&done); |
12552 | } |
12553 | |
12554 | void CodeGenerator::visitCompareBigIntInt32AndBranch( |
12555 | LCompareBigIntInt32AndBranch* lir) { |
12556 | JSOp op = lir->cmpMir()->jsop(); |
12557 | Register left = ToRegister(lir->left()); |
12558 | Register temp1 = ToRegister(lir->temp1()); |
12559 | Register temp2 = ToTempRegisterOrInvalid(lir->temp2()); |
12560 | |
12561 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
12562 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
12563 | |
12564 | // compareBigIntAndInt32 falls through to the false case. If the next block |
12565 | // is the true case, negate the comparison so we can fall through. |
12566 | if (isNextBlock(lir->ifTrue()->lir())) { |
12567 | op = NegateCompareOp(op); |
12568 | std::swap(ifTrue, ifFalse); |
12569 | } |
12570 | |
12571 | if (lir->right()->isConstant()) { |
12572 | MOZ_ASSERT(temp2 == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 == InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 == InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp2 == InvalidReg" ")"); do { *((volatile int*)__null) = 12572; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12573 | |
12574 | Imm32 right = Imm32(ToInt32(lir->right())); |
12575 | masm.compareBigIntAndInt32(op, left, right, temp1, ifTrue, ifFalse); |
12576 | } else { |
12577 | MOZ_ASSERT(temp2 != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 != InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 != InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp2 != InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12577); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp2 != InvalidReg" ")"); do { *((volatile int*)__null) = 12577; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12578 | |
12579 | Register right = ToRegister(lir->right()); |
12580 | masm.compareBigIntAndInt32(op, left, right, temp1, temp2, ifTrue, ifFalse); |
12581 | } |
12582 | |
12583 | if (!isNextBlock(lir->ifTrue()->lir())) { |
12584 | jumpToBlock(lir->ifFalse()); |
12585 | } |
12586 | } |
12587 | |
12588 | void CodeGenerator::visitCompareBigIntDouble(LCompareBigIntDouble* lir) { |
12589 | JSOp op = lir->mir()->jsop(); |
12590 | Register left = ToRegister(lir->left()); |
12591 | FloatRegister right = ToFloatRegister(lir->right()); |
12592 | Register output = ToRegister(lir->output()); |
12593 | |
12594 | masm.setupAlignedABICall(); |
12595 | |
12596 | // Push the operands in reverse order for JSOp::Le and JSOp::Gt: |
12597 | // - |left <= right| is implemented as |right >= left|. |
12598 | // - |left > right| is implemented as |right < left|. |
12599 | if (op == JSOp::Le || op == JSOp::Gt) { |
12600 | masm.passABIArg(right, ABIType::Float64); |
12601 | masm.passABIArg(left); |
12602 | } else { |
12603 | masm.passABIArg(left); |
12604 | masm.passABIArg(right, ABIType::Float64); |
12605 | } |
12606 | |
12607 | using FnBigIntNumber = bool (*)(BigInt*, double); |
12608 | using FnNumberBigInt = bool (*)(double, BigInt*); |
12609 | switch (op) { |
12610 | case JSOp::Eq: { |
12611 | masm.callWithABI<FnBigIntNumber, |
12612 | jit::BigIntNumberEqual<EqualityKind::Equal>>(); |
12613 | break; |
12614 | } |
12615 | case JSOp::Ne: { |
12616 | masm.callWithABI<FnBigIntNumber, |
12617 | jit::BigIntNumberEqual<EqualityKind::NotEqual>>(); |
12618 | break; |
12619 | } |
12620 | case JSOp::Lt: { |
12621 | masm.callWithABI<FnBigIntNumber, |
12622 | jit::BigIntNumberCompare<ComparisonKind::LessThan>>(); |
12623 | break; |
12624 | } |
12625 | case JSOp::Gt: { |
12626 | masm.callWithABI<FnNumberBigInt, |
12627 | jit::NumberBigIntCompare<ComparisonKind::LessThan>>(); |
12628 | break; |
12629 | } |
12630 | case JSOp::Le: { |
12631 | masm.callWithABI< |
12632 | FnNumberBigInt, |
12633 | jit::NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>>(); |
12634 | break; |
12635 | } |
12636 | case JSOp::Ge: { |
12637 | masm.callWithABI< |
12638 | FnBigIntNumber, |
12639 | jit::BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>>(); |
12640 | break; |
12641 | } |
12642 | default: |
12643 | MOZ_CRASH("unhandled op")do { do { } while (false); MOZ_ReportCrash("" "unhandled op", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12643); AnnotateMozCrashReason("MOZ_CRASH(" "unhandled op" ")" ); do { *((volatile int*)__null) = 12643; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
12644 | } |
12645 | |
12646 | masm.storeCallBoolResult(output); |
12647 | } |
12648 | |
12649 | void CodeGenerator::visitCompareBigIntString(LCompareBigIntString* lir) { |
12650 | JSOp op = lir->mir()->jsop(); |
12651 | Register left = ToRegister(lir->left()); |
12652 | Register right = ToRegister(lir->right()); |
12653 | |
12654 | // Push the operands in reverse order for JSOp::Le and JSOp::Gt: |
12655 | // - |left <= right| is implemented as |right >= left|. |
12656 | // - |left > right| is implemented as |right < left|. |
12657 | if (op == JSOp::Le || op == JSOp::Gt) { |
12658 | pushArg(left); |
12659 | pushArg(right); |
12660 | } else { |
12661 | pushArg(right); |
12662 | pushArg(left); |
12663 | } |
12664 | |
12665 | using FnBigIntString = |
12666 | bool (*)(JSContext*, HandleBigInt, HandleString, bool*); |
12667 | using FnStringBigInt = |
12668 | bool (*)(JSContext*, HandleString, HandleBigInt, bool*); |
12669 | |
12670 | switch (op) { |
12671 | case JSOp::Eq: { |
12672 | constexpr auto Equal = EqualityKind::Equal; |
12673 | callVM<FnBigIntString, BigIntStringEqual<Equal>>(lir); |
12674 | break; |
12675 | } |
12676 | case JSOp::Ne: { |
12677 | constexpr auto NotEqual = EqualityKind::NotEqual; |
12678 | callVM<FnBigIntString, BigIntStringEqual<NotEqual>>(lir); |
12679 | break; |
12680 | } |
12681 | case JSOp::Lt: { |
12682 | constexpr auto LessThan = ComparisonKind::LessThan; |
12683 | callVM<FnBigIntString, BigIntStringCompare<LessThan>>(lir); |
12684 | break; |
12685 | } |
12686 | case JSOp::Gt: { |
12687 | constexpr auto LessThan = ComparisonKind::LessThan; |
12688 | callVM<FnStringBigInt, StringBigIntCompare<LessThan>>(lir); |
12689 | break; |
12690 | } |
12691 | case JSOp::Le: { |
12692 | constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual; |
12693 | callVM<FnStringBigInt, StringBigIntCompare<GreaterThanOrEqual>>(lir); |
12694 | break; |
12695 | } |
12696 | case JSOp::Ge: { |
12697 | constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual; |
12698 | callVM<FnBigIntString, BigIntStringCompare<GreaterThanOrEqual>>(lir); |
12699 | break; |
12700 | } |
12701 | default: |
12702 | MOZ_CRASH("Unexpected compare op")do { do { } while (false); MOZ_ReportCrash("" "Unexpected compare op" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12702); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected compare op" ")"); do { *((volatile int*)__null) = 12702; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
12703 | } |
12704 | } |
12705 | |
12706 | void CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir) { |
12707 | MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12708); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12708; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
12708 | lir->mir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12708); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12708; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12709 | |
12710 | JSOp op = lir->mir()->jsop(); |
12711 | MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12711; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12712 | |
12713 | const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedV::ValueIndex); |
12714 | Register output = ToRegister(lir->output()); |
12715 | |
12716 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
12717 | if (!intact) { |
12718 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
12719 | addOutOfLineCode(ool, lir->mir()); |
12720 | |
12721 | Label* nullOrLikeUndefined = ool->label1(); |
12722 | Label* notNullOrLikeUndefined = ool->label2(); |
12723 | |
12724 | { |
12725 | ScratchTagScope tag(masm, value); |
12726 | masm.splitTagForTest(value, tag); |
12727 | |
12728 | masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined); |
12729 | masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined); |
12730 | |
12731 | // Check whether it's a truthy object or a falsy object that emulates |
12732 | // undefined. |
12733 | masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined); |
12734 | } |
12735 | |
12736 | Register objreg = |
12737 | masm.extractObject(value, ToTempUnboxRegister(lir->temp0())); |
12738 | branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, |
12739 | notNullOrLikeUndefined, output, ool); |
12740 | // fall through |
12741 | |
12742 | Label done; |
12743 | |
12744 | // It's not null or undefined, and if it's an object it doesn't |
12745 | // emulate undefined, so it's not like undefined. |
12746 | masm.move32(Imm32(op == JSOp::Ne), output); |
12747 | masm.jump(&done); |
12748 | |
12749 | masm.bind(nullOrLikeUndefined); |
12750 | masm.move32(Imm32(op == JSOp::Eq), output); |
12751 | |
12752 | // Both branches meet here. |
12753 | masm.bind(&done); |
12754 | } else { |
12755 | Label nullOrUndefined, notNullOrLikeUndefined; |
12756 | #if defined(DEBUG1) || defined(FUZZING) |
12757 | Register objreg = Register::Invalid(); |
12758 | #endif |
12759 | { |
12760 | ScratchTagScope tag(masm, value); |
12761 | masm.splitTagForTest(value, tag); |
12762 | |
12763 | masm.branchTestNull(Assembler::Equal, tag, &nullOrUndefined); |
12764 | masm.branchTestUndefined(Assembler::Equal, tag, &nullOrUndefined); |
12765 | |
12766 | #if defined(DEBUG1) || defined(FUZZING) |
12767 | // Check whether it's a truthy object or a falsy object that emulates |
12768 | // undefined. |
12769 | masm.branchTestObject(Assembler::NotEqual, tag, ¬NullOrLikeUndefined); |
12770 | objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp0())); |
12771 | #endif |
12772 | } |
12773 | |
12774 | #if defined(DEBUG1) || defined(FUZZING) |
12775 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
12776 | masm.bind(¬NullOrLikeUndefined); |
12777 | #endif |
12778 | |
12779 | Label done; |
12780 | |
12781 | // It's not null or undefined, and if it's an object it doesn't |
12782 | // emulate undefined. |
12783 | masm.move32(Imm32(op == JSOp::Ne), output); |
12784 | masm.jump(&done); |
12785 | |
12786 | masm.bind(&nullOrUndefined); |
12787 | masm.move32(Imm32(op == JSOp::Eq), output); |
12788 | |
12789 | // Both branches meet here. |
12790 | masm.bind(&done); |
12791 | } |
12792 | } |
12793 | |
12794 | void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV( |
12795 | LIsNullOrLikeUndefinedAndBranchV* lir) { |
12796 | MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12797; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
12797 | lir->cmpMir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12797; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12798 | |
12799 | JSOp op = lir->cmpMir()->jsop(); |
12800 | MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12800; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12801 | |
12802 | const ValueOperand value = |
12803 | ToValue(lir, LIsNullOrLikeUndefinedAndBranchV::Value); |
12804 | |
12805 | MBasicBlock* ifTrue = lir->ifTrue(); |
12806 | MBasicBlock* ifFalse = lir->ifFalse(); |
12807 | |
12808 | if (op == JSOp::Ne) { |
12809 | // Swap branches. |
12810 | std::swap(ifTrue, ifFalse); |
12811 | } |
12812 | |
12813 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
12814 | |
12815 | Label* ifTrueLabel = getJumpLabelForBranch(ifTrue); |
12816 | Label* ifFalseLabel = getJumpLabelForBranch(ifFalse); |
12817 | |
12818 | { |
12819 | ScratchTagScope tag(masm, value); |
12820 | masm.splitTagForTest(value, tag); |
12821 | |
12822 | masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel); |
12823 | masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel); |
12824 | |
12825 | masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel); |
12826 | } |
12827 | |
12828 | bool extractObject = !intact; |
Value stored to 'extractObject' during its initialization is never read | |
12829 | #if defined(DEBUG1) || defined(FUZZING) |
12830 | // always extract objreg if we're in debug and |
12831 | // assertObjectDoesNotEmulateUndefined; |
12832 | extractObject = true; |
12833 | #endif |
12834 | |
12835 | Register objreg = Register::Invalid(); |
12836 | Register scratch = ToRegister(lir->temp()); |
12837 | if (extractObject) { |
12838 | objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox())); |
12839 | } |
12840 | if (!intact) { |
12841 | // Objects that emulate undefined are loosely equal to null/undefined. |
12842 | OutOfLineTestObject* ool = new (alloc()) OutOfLineTestObject(); |
12843 | addOutOfLineCode(ool, lir->cmpMir()); |
12844 | testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch, |
12845 | ool); |
12846 | } else { |
12847 | assertObjectDoesNotEmulateUndefined(objreg, scratch, lir->cmpMir()); |
12848 | // Bug 1874905. This would be nice to optimize out at the MIR level. |
12849 | masm.jump(ifFalseLabel); |
12850 | } |
12851 | } |
12852 | |
12853 | void CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir) { |
12854 | MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12855; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
12855 | lir->mir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12855; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12856 | MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->lhs()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->lhs()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->lhs()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12856); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->lhs()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 12856; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12857 | |
12858 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
12859 | JSOp op = lir->mir()->jsop(); |
12860 | Register output = ToRegister(lir->output()); |
12861 | Register objreg = ToRegister(lir->input()); |
12862 | if (!intact) { |
12863 | MOZ_ASSERT(IsLooseEqualityOp(op),do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12864; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
12864 | "Strict equality should have been folded")do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12864; __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
12865 | |
12866 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
12867 | addOutOfLineCode(ool, lir->mir()); |
12868 | |
12869 | Label* emulatesUndefined = ool->label1(); |
12870 | Label* doesntEmulateUndefined = ool->label2(); |
12871 | |
12872 | branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, |
12873 | doesntEmulateUndefined, output, ool); |
12874 | |
12875 | Label done; |
12876 | |
12877 | masm.move32(Imm32(op == JSOp::Ne), output); |
12878 | masm.jump(&done); |
12879 | |
12880 | masm.bind(emulatesUndefined); |
12881 | masm.move32(Imm32(op == JSOp::Eq), output); |
12882 | masm.bind(&done); |
12883 | } else { |
12884 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
12885 | masm.move32(Imm32(op == JSOp::Ne), output); |
12886 | } |
12887 | } |
12888 | |
12889 | void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT( |
12890 | LIsNullOrLikeUndefinedAndBranchT* lir) { |
12891 | MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12892; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
12892 | lir->cmpMir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12892; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12893 | MOZ_ASSERT(lir->cmpMir()->lhs()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->lhs()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->lhs()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->lhs()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12893); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->lhs()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 12893; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12894 | |
12895 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
12896 | |
12897 | JSOp op = lir->cmpMir()->jsop(); |
12898 | MOZ_ASSERT(IsLooseEqualityOp(op), "Strict equality should have been folded")do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12898); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12898; __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
12899 | |
12900 | MBasicBlock* ifTrue = lir->ifTrue(); |
12901 | MBasicBlock* ifFalse = lir->ifFalse(); |
12902 | |
12903 | if (op == JSOp::Ne) { |
12904 | // Swap branches. |
12905 | std::swap(ifTrue, ifFalse); |
12906 | } |
12907 | |
12908 | Register input = ToRegister(lir->getOperand(0)); |
12909 | Register scratch = ToRegister(lir->temp()); |
12910 | Label* ifTrueLabel = getJumpLabelForBranch(ifTrue); |
12911 | Label* ifFalseLabel = getJumpLabelForBranch(ifFalse); |
12912 | |
12913 | if (intact) { |
12914 | // Bug 1874905. Ideally branches like this would be optimized out. |
12915 | assertObjectDoesNotEmulateUndefined(input, scratch, lir->mir()); |
12916 | masm.jump(ifFalseLabel); |
12917 | } else { |
12918 | auto* ool = new (alloc()) OutOfLineTestObject(); |
12919 | addOutOfLineCode(ool, lir->cmpMir()); |
12920 | |
12921 | // Objects that emulate undefined are loosely equal to null/undefined. |
12922 | testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool); |
12923 | } |
12924 | } |
12925 | |
12926 | void CodeGenerator::visitIsNull(LIsNull* lir) { |
12927 | MCompare::CompareType compareType = lir->mir()->compareType(); |
12928 | MOZ_ASSERT(compareType == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Null)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(compareType == MCompare::Compare_Null))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12928); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12928; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12929 | |
12930 | JSOp op = lir->mir()->jsop(); |
12931 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12931); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12931; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12932 | |
12933 | const ValueOperand value = ToValue(lir, LIsNull::ValueIndex); |
12934 | Register output = ToRegister(lir->output()); |
12935 | |
12936 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
12937 | masm.testNullSet(cond, value, output); |
12938 | } |
12939 | |
12940 | void CodeGenerator::visitIsUndefined(LIsUndefined* lir) { |
12941 | MCompare::CompareType compareType = lir->mir()->compareType(); |
12942 | MOZ_ASSERT(compareType == MCompare::Compare_Undefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Undefined)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(compareType == MCompare::Compare_Undefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Undefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12942); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined" ")"); do { *((volatile int*)__null) = 12942; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12943 | |
12944 | JSOp op = lir->mir()->jsop(); |
12945 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12945); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12945; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12946 | |
12947 | const ValueOperand value = ToValue(lir, LIsUndefined::ValueIndex); |
12948 | Register output = ToRegister(lir->output()); |
12949 | |
12950 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
12951 | masm.testUndefinedSet(cond, value, output); |
12952 | } |
12953 | |
12954 | void CodeGenerator::visitIsNullAndBranch(LIsNullAndBranch* lir) { |
12955 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
12956 | MOZ_ASSERT(compareType == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Null)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(compareType == MCompare::Compare_Null))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12956); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12956; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12957 | |
12958 | JSOp op = lir->cmpMir()->jsop(); |
12959 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12959); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12959; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12960 | |
12961 | const ValueOperand value = ToValue(lir, LIsNullAndBranch::Value); |
12962 | |
12963 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
12964 | |
12965 | MBasicBlock* ifTrue = lir->ifTrue(); |
12966 | MBasicBlock* ifFalse = lir->ifFalse(); |
12967 | |
12968 | if (isNextBlock(ifFalse->lir())) { |
12969 | masm.branchTestNull(cond, value, getJumpLabelForBranch(ifTrue)); |
12970 | } else { |
12971 | masm.branchTestNull(Assembler::InvertCondition(cond), value, |
12972 | getJumpLabelForBranch(ifFalse)); |
12973 | jumpToBlock(ifTrue); |
12974 | } |
12975 | } |
12976 | |
12977 | void CodeGenerator::visitIsUndefinedAndBranch(LIsUndefinedAndBranch* lir) { |
12978 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
12979 | MOZ_ASSERT(compareType == MCompare::Compare_Undefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Undefined)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(compareType == MCompare::Compare_Undefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Undefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12979); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined" ")"); do { *((volatile int*)__null) = 12979; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12980 | |
12981 | JSOp op = lir->cmpMir()->jsop(); |
12982 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12982); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12982; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12983 | |
12984 | const ValueOperand value = ToValue(lir, LIsUndefinedAndBranch::Value); |
12985 | |
12986 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
12987 | |
12988 | MBasicBlock* ifTrue = lir->ifTrue(); |
12989 | MBasicBlock* ifFalse = lir->ifFalse(); |
12990 | |
12991 | if (isNextBlock(ifFalse->lir())) { |
12992 | masm.branchTestUndefined(cond, value, getJumpLabelForBranch(ifTrue)); |
12993 | } else { |
12994 | masm.branchTestUndefined(Assembler::InvertCondition(cond), value, |
12995 | getJumpLabelForBranch(ifFalse)); |
12996 | jumpToBlock(ifTrue); |
12997 | } |
12998 | } |
12999 | |
13000 | void CodeGenerator::visitSameValueDouble(LSameValueDouble* lir) { |
13001 | FloatRegister left = ToFloatRegister(lir->left()); |
13002 | FloatRegister right = ToFloatRegister(lir->right()); |
13003 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
13004 | Register output = ToRegister(lir->output()); |
13005 | |
13006 | masm.sameValueDouble(left, right, temp, output); |
13007 | } |
13008 | |
13009 | void CodeGenerator::visitSameValue(LSameValue* lir) { |
13010 | ValueOperand lhs = ToValue(lir, LSameValue::LhsIndex); |
13011 | ValueOperand rhs = ToValue(lir, LSameValue::RhsIndex); |
13012 | Register output = ToRegister(lir->output()); |
13013 | |
13014 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*); |
13015 | OutOfLineCode* ool = |
13016 | oolCallVM<Fn, SameValue>(lir, ArgList(lhs, rhs), StoreRegisterTo(output)); |
13017 | |
13018 | // First check to see if the values have identical bits. |
13019 | // This is correct for SameValue because SameValue(NaN,NaN) is true, |
13020 | // and SameValue(0,-0) is false. |
13021 | masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(), |
13022 | ool->entry()); |
13023 | masm.move32(Imm32(1), output); |
13024 | |
13025 | // If this fails, call SameValue. |
13026 | masm.bind(ool->rejoin()); |
13027 | } |
13028 | |
13029 | void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs, |
13030 | Register output) { |
13031 | using Fn = |
13032 | JSString* (*)(JSContext*, HandleString, HandleString, js::gc::Heap); |
13033 | OutOfLineCode* ool = oolCallVM<Fn, ConcatStrings<CanGC>>( |
13034 | lir, ArgList(lhs, rhs, static_cast<Imm32>(int32_t(gc::Heap::Default))), |
13035 | StoreRegisterTo(output)); |
13036 | |
13037 | JitCode* stringConcatStub = |
13038 | snapshot_->getZoneStub(JitZone::StubKind::StringConcat); |
13039 | masm.call(stringConcatStub); |
13040 | masm.branchTestPtr(Assembler::Zero, output, output, ool->entry()); |
13041 | |
13042 | masm.bind(ool->rejoin()); |
13043 | } |
13044 | |
13045 | void CodeGenerator::visitConcat(LConcat* lir) { |
13046 | Register lhs = ToRegister(lir->lhs()); |
13047 | Register rhs = ToRegister(lir->rhs()); |
13048 | |
13049 | Register output = ToRegister(lir->output()); |
13050 | |
13051 | MOZ_ASSERT(lhs == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lhs == CallTempReg0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lhs == CallTempReg0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lhs == CallTempReg0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13051); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs == CallTempReg0" ")"); do { *((volatile int*)__null) = 13051; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13052 | MOZ_ASSERT(rhs == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(rhs == CallTempReg1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(rhs == CallTempReg1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("rhs == CallTempReg1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13052); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs == CallTempReg1" ")"); do { *((volatile int*)__null) = 13052; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13053 | MOZ_ASSERT(ToRegister(lir->temp0()) == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp0()) == CallTempReg0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp0()) == CallTempReg0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp0()) == CallTempReg0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp0()) == CallTempReg0" ")"); do { *((volatile int*)__null) = 13053; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13054 | MOZ_ASSERT(ToRegister(lir->temp1()) == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp1()) == CallTempReg1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp1()) == CallTempReg1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp1()) == CallTempReg1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13054); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp1()) == CallTempReg1" ")"); do { *((volatile int*)__null) = 13054; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13055 | MOZ_ASSERT(ToRegister(lir->temp2()) == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp2()) == CallTempReg2)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp2()) == CallTempReg2))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp2()) == CallTempReg2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp2()) == CallTempReg2" ")"); do { *((volatile int*)__null) = 13055; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13056 | MOZ_ASSERT(ToRegister(lir->temp3()) == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp3()) == CallTempReg3)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp3()) == CallTempReg3))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp3()) == CallTempReg3" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp3()) == CallTempReg3" ")"); do { *((volatile int*)__null) = 13056; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13057 | MOZ_ASSERT(ToRegister(lir->temp4()) == CallTempReg4)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp4()) == CallTempReg4)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp4()) == CallTempReg4))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp4()) == CallTempReg4" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13057); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp4()) == CallTempReg4" ")"); do { *((volatile int*)__null) = 13057; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13058 | MOZ_ASSERT(output == CallTempReg5)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output == CallTempReg5)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output == CallTempReg5))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("output == CallTempReg5" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13058); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == CallTempReg5" ")"); do { *((volatile int*)__null) = 13058; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13059 | |
13060 | emitConcat(lir, lhs, rhs, output); |
13061 | } |
13062 | |
13063 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
13064 | Register len, Register byteOpScratch, |
13065 | CharEncoding fromEncoding, CharEncoding toEncoding, |
13066 | size_t maximumLength = SIZE_MAX(18446744073709551615UL)) { |
13067 | // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0 |
13068 | // (checked below in debug builds), and when done |to| must point to the |
13069 | // next available char. |
13070 | |
13071 | #ifdef DEBUG1 |
13072 | Label ok; |
13073 | masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); |
13074 | masm.assumeUnreachable("Length should be greater than 0."); |
13075 | masm.bind(&ok); |
13076 | |
13077 | if (maximumLength != SIZE_MAX(18446744073709551615UL)) { |
13078 | MOZ_ASSERT(maximumLength <= INT32_MAX, "maximum length fits into int32")do { static_assert( mozilla::detail::AssertionConditionType< decltype(maximumLength <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(maximumLength <= (2147483647 )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("maximumLength <= (2147483647)" " (" "maximum length fits into int32" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maximumLength <= (2147483647)" ") (" "maximum length fits into int32" ")"); do { *((volatile int*)__null) = 13078; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
13079 | |
13080 | Label ok; |
13081 | masm.branchPtr(Assembler::BelowOrEqual, len, Imm32(maximumLength), &ok); |
13082 | masm.assumeUnreachable("Length should not exceed maximum length."); |
13083 | masm.bind(&ok); |
13084 | } |
13085 | #endif |
13086 | |
13087 | MOZ_ASSERT_IF(toEncoding == CharEncoding::Latin1,do { if (toEncoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(fromEncoding == CharEncoding::Latin1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fromEncoding == CharEncoding ::Latin1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("fromEncoding == CharEncoding::Latin1", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13088); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1" ")"); do { *((volatile int*)__null) = 13088; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
13088 | fromEncoding == CharEncoding::Latin1)do { if (toEncoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(fromEncoding == CharEncoding::Latin1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fromEncoding == CharEncoding ::Latin1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("fromEncoding == CharEncoding::Latin1", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13088); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1" ")"); do { *((volatile int*)__null) = 13088; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
13089 | |
13090 | size_t fromWidth = |
13091 | fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); |
13092 | size_t toWidth = |
13093 | toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); |
13094 | |
13095 | // Try to copy multiple characters at once when both encoding are equal. |
13096 | if (fromEncoding == toEncoding) { |
13097 | constexpr size_t ptrWidth = sizeof(uintptr_t); |
13098 | |
13099 | // Copy |width| bytes and then adjust |from| and |to|. |
13100 | auto copyCharacters = [&](size_t width) { |
13101 | static_assert(ptrWidth <= 8, "switch handles only up to eight bytes"); |
13102 | |
13103 | switch (width) { |
13104 | case 1: |
13105 | masm.load8ZeroExtend(Address(from, 0), byteOpScratch); |
13106 | masm.store8(byteOpScratch, Address(to, 0)); |
13107 | break; |
13108 | case 2: |
13109 | masm.load16ZeroExtend(Address(from, 0), byteOpScratch); |
13110 | masm.store16(byteOpScratch, Address(to, 0)); |
13111 | break; |
13112 | case 4: |
13113 | masm.load32(Address(from, 0), byteOpScratch); |
13114 | masm.store32(byteOpScratch, Address(to, 0)); |
13115 | break; |
13116 | case 8: |
13117 | MOZ_ASSERT(width == ptrWidth)do { static_assert( mozilla::detail::AssertionConditionType< decltype(width == ptrWidth)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(width == ptrWidth))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("width == ptrWidth" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13117); AnnotateMozCrashReason("MOZ_ASSERT" "(" "width == ptrWidth" ")"); do { *((volatile int*)__null) = 13117; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13118 | masm.loadPtr(Address(from, 0), byteOpScratch); |
13119 | masm.storePtr(byteOpScratch, Address(to, 0)); |
13120 | break; |
13121 | } |
13122 | |
13123 | masm.addPtr(Imm32(width), from); |
13124 | masm.addPtr(Imm32(width), to); |
13125 | }; |
13126 | |
13127 | // First align |len| to pointer width. |
13128 | Label done; |
13129 | for (size_t width = fromWidth; width < ptrWidth; width *= 2) { |
13130 | // Number of characters which fit into |width| bytes. |
13131 | size_t charsPerWidth = width / fromWidth; |
13132 | |
13133 | if (charsPerWidth < maximumLength) { |
13134 | Label next; |
13135 | masm.branchTest32(Assembler::Zero, len, Imm32(charsPerWidth), &next); |
13136 | |
13137 | copyCharacters(width); |
13138 | |
13139 | masm.branchSub32(Assembler::Zero, Imm32(charsPerWidth), len, &done); |
13140 | masm.bind(&next); |
13141 | } else if (charsPerWidth == maximumLength) { |
13142 | copyCharacters(width); |
13143 | masm.sub32(Imm32(charsPerWidth), len); |
13144 | } |
13145 | } |
13146 | |
13147 | size_t maxInlineLength; |
13148 | if (fromEncoding == CharEncoding::Latin1) { |
13149 | maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; |
13150 | } else { |
13151 | maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
13152 | } |
13153 | |
13154 | // Number of characters which fit into a single register. |
13155 | size_t charsPerPtr = ptrWidth / fromWidth; |
13156 | |
13157 | // Unroll small loops. |
13158 | constexpr size_t unrollLoopLimit = 3; |
13159 | size_t loopCount = std::min(maxInlineLength, maximumLength) / charsPerPtr; |
13160 | |
13161 | #ifdef JS_64BIT1 |
13162 | static constexpr size_t latin1MaxInlineByteLength = |
13163 | JSFatInlineString::MAX_LENGTH_LATIN1 * sizeof(char); |
13164 | static constexpr size_t twoByteMaxInlineByteLength = |
13165 | JSFatInlineString::MAX_LENGTH_TWO_BYTE * sizeof(char16_t); |
13166 | |
13167 | // |unrollLoopLimit| should be large enough to allow loop unrolling on |
13168 | // 64-bit targets. |
13169 | static_assert(latin1MaxInlineByteLength / ptrWidth == unrollLoopLimit, |
13170 | "Latin-1 loops are unrolled on 64-bit"); |
13171 | static_assert(twoByteMaxInlineByteLength / ptrWidth == unrollLoopLimit, |
13172 | "Two-byte loops are unrolled on 64-bit"); |
13173 | #endif |
13174 | |
13175 | if (loopCount <= unrollLoopLimit) { |
13176 | Label labels[unrollLoopLimit]; |
13177 | |
13178 | // Check up front how many characters can be copied. |
13179 | for (size_t i = 1; i < loopCount; i++) { |
13180 | masm.branch32(Assembler::Below, len, Imm32((i + 1) * charsPerPtr), |
13181 | &labels[i]); |
13182 | } |
13183 | |
13184 | // Generate the unrolled loop body. |
13185 | for (size_t i = loopCount; i > 0; i--) { |
13186 | copyCharacters(ptrWidth); |
13187 | masm.sub32(Imm32(charsPerPtr), len); |
13188 | |
13189 | // Jump target for the previous length check. |
13190 | if (i != 1) { |
13191 | masm.bind(&labels[i - 1]); |
13192 | } |
13193 | } |
13194 | } else { |
13195 | Label start; |
13196 | masm.bind(&start); |
13197 | copyCharacters(ptrWidth); |
13198 | masm.branchSub32(Assembler::NonZero, Imm32(charsPerPtr), len, &start); |
13199 | } |
13200 | |
13201 | masm.bind(&done); |
13202 | } else { |
13203 | Label start; |
13204 | masm.bind(&start); |
13205 | masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding); |
13206 | masm.storeChar(byteOpScratch, Address(to, 0), toEncoding); |
13207 | masm.addPtr(Imm32(fromWidth), from); |
13208 | masm.addPtr(Imm32(toWidth), to); |
13209 | masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start); |
13210 | } |
13211 | } |
13212 | |
13213 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
13214 | Register len, Register byteOpScratch, |
13215 | CharEncoding encoding, size_t maximumLength) { |
13216 | CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding, |
13217 | maximumLength); |
13218 | } |
13219 | |
13220 | static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, |
13221 | Register destChars, Register temp1, |
13222 | Register temp2) { |
13223 | // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may |
13224 | // have to inflate. |
13225 | |
13226 | Label isLatin1, done; |
13227 | masm.loadStringLength(input, temp1); |
13228 | masm.branchLatin1String(input, &isLatin1); |
13229 | { |
13230 | masm.loadStringChars(input, temp2, CharEncoding::TwoByte); |
13231 | masm.movePtr(temp2, input); |
13232 | CopyStringChars(masm, destChars, input, temp1, temp2, |
13233 | CharEncoding::TwoByte); |
13234 | masm.jump(&done); |
13235 | } |
13236 | masm.bind(&isLatin1); |
13237 | { |
13238 | masm.loadStringChars(input, temp2, CharEncoding::Latin1); |
13239 | masm.movePtr(temp2, input); |
13240 | CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1, |
13241 | CharEncoding::TwoByte); |
13242 | } |
13243 | masm.bind(&done); |
13244 | } |
13245 | |
13246 | static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output, |
13247 | Register length, Register temp, |
13248 | gc::Heap initialStringHeap, |
13249 | Label* failure, |
13250 | CharEncoding encoding) { |
13251 | #ifdef DEBUG1 |
13252 | size_t maxInlineLength; |
13253 | if (encoding == CharEncoding::Latin1) { |
13254 | maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; |
13255 | } else { |
13256 | maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
13257 | } |
13258 | |
13259 | Label ok; |
13260 | masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok); |
13261 | masm.assumeUnreachable("string length too large to be allocated as inline"); |
13262 | masm.bind(&ok); |
13263 | #endif |
13264 | |
13265 | size_t maxThinInlineLength; |
13266 | if (encoding == CharEncoding::Latin1) { |
13267 | maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1; |
13268 | } else { |
13269 | maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
13270 | } |
13271 | |
13272 | Label isFat, allocDone; |
13273 | masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat); |
13274 | { |
13275 | uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS; |
13276 | if (encoding == CharEncoding::Latin1) { |
13277 | flags |= JSString::LATIN1_CHARS_BIT; |
13278 | } |
13279 | masm.newGCString(output, temp, initialStringHeap, failure); |
13280 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
13281 | masm.jump(&allocDone); |
13282 | } |
13283 | masm.bind(&isFat); |
13284 | { |
13285 | uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS; |
13286 | if (encoding == CharEncoding::Latin1) { |
13287 | flags |= JSString::LATIN1_CHARS_BIT; |
13288 | } |
13289 | masm.newGCFatInlineString(output, temp, initialStringHeap, failure); |
13290 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
13291 | } |
13292 | masm.bind(&allocDone); |
13293 | |
13294 | // Store length. |
13295 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
13296 | } |
13297 | |
13298 | static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, |
13299 | Register output, Register temp1, Register temp2, |
13300 | Register temp3, gc::Heap initialStringHeap, |
13301 | Label* failure, CharEncoding encoding) { |
13302 | JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)", |
13303 | (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
13304 | |
13305 | // State: result length in temp2. |
13306 | |
13307 | // Ensure both strings are linear. |
13308 | masm.branchIfRope(lhs, failure); |
13309 | masm.branchIfRope(rhs, failure); |
13310 | |
13311 | // Allocate a JSThinInlineString or JSFatInlineString. |
13312 | AllocateThinOrFatInlineString(masm, output, temp2, temp1, initialStringHeap, |
13313 | failure, encoding); |
13314 | |
13315 | // Load chars pointer in temp2. |
13316 | masm.loadInlineStringCharsForStore(output, temp2); |
13317 | |
13318 | auto copyChars = [&](Register src) { |
13319 | if (encoding == CharEncoding::TwoByte) { |
13320 | CopyStringCharsMaybeInflate(masm, src, temp2, temp1, temp3); |
13321 | } else { |
13322 | masm.loadStringLength(src, temp3); |
13323 | masm.loadStringChars(src, temp1, CharEncoding::Latin1); |
13324 | masm.movePtr(temp1, src); |
13325 | CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1); |
13326 | } |
13327 | }; |
13328 | |
13329 | // Copy lhs chars. Note that this advances temp2 to point to the next |
13330 | // char. This also clobbers the lhs register. |
13331 | copyChars(lhs); |
13332 | |
13333 | // Copy rhs chars. Clobbers the rhs register. |
13334 | copyChars(rhs); |
13335 | } |
13336 | |
13337 | void CodeGenerator::visitSubstr(LSubstr* lir) { |
13338 | Register string = ToRegister(lir->string()); |
13339 | Register begin = ToRegister(lir->begin()); |
13340 | Register length = ToRegister(lir->length()); |
13341 | Register output = ToRegister(lir->output()); |
13342 | Register temp0 = ToRegister(lir->temp0()); |
13343 | Register temp2 = ToRegister(lir->temp2()); |
13344 | |
13345 | // On x86 there are not enough registers. In that case reuse the string |
13346 | // register as temporary. |
13347 | Register temp1 = |
13348 | lir->temp1()->isBogusTemp() ? string : ToRegister(lir->temp1()); |
13349 | |
13350 | size_t maximumLength = SIZE_MAX(18446744073709551615UL); |
13351 | |
13352 | Range* range = lir->mir()->length()->range(); |
13353 | if (range && range->hasInt32UpperBound()) { |
13354 | MOZ_ASSERT(range->upper() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(range->upper() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(range->upper() >= 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("range->upper() >= 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "range->upper() >= 0" ")"); do { *((volatile int*)__null) = 13354; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13355 | maximumLength = size_t(range->upper()); |
13356 | } |
13357 | |
13358 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <= |
13359 | JSThinInlineString::MAX_LENGTH_LATIN1); |
13360 | |
13361 | static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE <= |
13362 | JSFatInlineString::MAX_LENGTH_LATIN1); |
13363 | |
13364 | bool tryFatInlineOrDependent = |
13365 | maximumLength > JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
13366 | bool tryDependent = maximumLength > JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
13367 | |
13368 | #ifdef DEBUG1 |
13369 | if (maximumLength != SIZE_MAX(18446744073709551615UL)) { |
13370 | Label ok; |
13371 | masm.branch32(Assembler::BelowOrEqual, length, Imm32(maximumLength), &ok); |
13372 | masm.assumeUnreachable("length should not exceed maximum length"); |
13373 | masm.bind(&ok); |
13374 | } |
13375 | #endif |
13376 | |
13377 | Label nonZero, nonInput; |
13378 | |
13379 | // For every edge case use the C++ variant. |
13380 | // Note: we also use this upon allocation failure in newGCString and |
13381 | // newGCFatInlineString. To squeeze out even more performance those failures |
13382 | // can be handled by allocate in ool code and returning to jit code to fill |
13383 | // in all data. |
13384 | using Fn = JSString* (*)(JSContext* cx, HandleString str, int32_t begin, |
13385 | int32_t len); |
13386 | OutOfLineCode* ool = oolCallVM<Fn, SubstringKernel>( |
13387 | lir, ArgList(string, begin, length), StoreRegisterTo(output)); |
13388 | Label* slowPath = ool->entry(); |
13389 | Label* done = ool->rejoin(); |
13390 | |
13391 | // Zero length, return emptystring. |
13392 | masm.branchTest32(Assembler::NonZero, length, length, &nonZero); |
13393 | const JSAtomState& names = gen->runtime->names(); |
13394 | masm.movePtr(ImmGCPtr(names.empty_), output); |
13395 | masm.jump(done); |
13396 | |
13397 | // Substring from 0..|str.length|, return str. |
13398 | masm.bind(&nonZero); |
13399 | masm.branch32(Assembler::NotEqual, |
13400 | Address(string, JSString::offsetOfLength()), length, &nonInput); |
13401 | #ifdef DEBUG1 |
13402 | { |
13403 | Label ok; |
13404 | masm.branchTest32(Assembler::Zero, begin, begin, &ok); |
13405 | masm.assumeUnreachable("length == str.length implies begin == 0"); |
13406 | masm.bind(&ok); |
13407 | } |
13408 | #endif |
13409 | masm.movePtr(string, output); |
13410 | masm.jump(done); |
13411 | |
13412 | // Use slow path for ropes. |
13413 | masm.bind(&nonInput); |
13414 | masm.branchIfRope(string, slowPath); |
13415 | |
13416 | // Optimize one and two character strings. |
13417 | Label nonStatic; |
13418 | masm.branch32(Assembler::Above, length, Imm32(2), &nonStatic); |
13419 | { |
13420 | Label loadLengthOne, loadLengthTwo; |
13421 | |
13422 | auto loadChars = [&](CharEncoding encoding, bool fallthru) { |
13423 | size_t size = encoding == CharEncoding::Latin1 ? sizeof(JS::Latin1Char) |
13424 | : sizeof(char16_t); |
13425 | |
13426 | masm.loadStringChars(string, temp0, encoding); |
13427 | masm.loadChar(temp0, begin, temp2, encoding); |
13428 | masm.branch32(Assembler::Equal, length, Imm32(1), &loadLengthOne); |
13429 | masm.loadChar(temp0, begin, temp0, encoding, int32_t(size)); |
13430 | if (!fallthru) { |
13431 | masm.jump(&loadLengthTwo); |
13432 | } |
13433 | }; |
13434 | |
13435 | Label isLatin1; |
13436 | masm.branchLatin1String(string, &isLatin1); |
13437 | loadChars(CharEncoding::TwoByte, /* fallthru = */ false); |
13438 | |
13439 | masm.bind(&isLatin1); |
13440 | loadChars(CharEncoding::Latin1, /* fallthru = */ true); |
13441 | |
13442 | // Try to load a length-two static string. |
13443 | masm.bind(&loadLengthTwo); |
13444 | masm.lookupStaticString(temp2, temp0, output, gen->runtime->staticStrings(), |
13445 | &nonStatic); |
13446 | masm.jump(done); |
13447 | |
13448 | // Try to load a length-one static string. |
13449 | masm.bind(&loadLengthOne); |
13450 | masm.lookupStaticString(temp2, output, gen->runtime->staticStrings(), |
13451 | &nonStatic); |
13452 | masm.jump(done); |
13453 | } |
13454 | masm.bind(&nonStatic); |
13455 | |
13456 | // Allocate either a JSThinInlineString or JSFatInlineString, or jump to |
13457 | // notInline if we need a dependent string. |
13458 | Label notInline; |
13459 | { |
13460 | static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 < |
13461 | JSFatInlineString::MAX_LENGTH_LATIN1); |
13462 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE < |
13463 | JSFatInlineString::MAX_LENGTH_TWO_BYTE); |
13464 | |
13465 | // Use temp2 to store the JS(Thin|Fat)InlineString flags. This avoids having |
13466 | // duplicate newGCString/newGCFatInlineString codegen for Latin1 vs TwoByte |
13467 | // strings. |
13468 | |
13469 | Label allocFat, allocDone; |
13470 | if (tryFatInlineOrDependent) { |
13471 | Label isLatin1, allocThin; |
13472 | masm.branchLatin1String(string, &isLatin1); |
13473 | { |
13474 | if (tryDependent) { |
13475 | masm.branch32(Assembler::Above, length, |
13476 | Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), |
13477 | ¬Inline); |
13478 | } |
13479 | masm.move32(Imm32(0), temp2); |
13480 | masm.branch32(Assembler::Above, length, |
13481 | Imm32(JSThinInlineString::MAX_LENGTH_TWO_BYTE), |
13482 | &allocFat); |
13483 | masm.jump(&allocThin); |
13484 | } |
13485 | |
13486 | masm.bind(&isLatin1); |
13487 | { |
13488 | if (tryDependent) { |
13489 | masm.branch32(Assembler::Above, length, |
13490 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), |
13491 | ¬Inline); |
13492 | } |
13493 | masm.move32(Imm32(JSString::LATIN1_CHARS_BIT), temp2); |
13494 | masm.branch32(Assembler::Above, length, |
13495 | Imm32(JSThinInlineString::MAX_LENGTH_LATIN1), &allocFat); |
13496 | } |
13497 | |
13498 | masm.bind(&allocThin); |
13499 | } else { |
13500 | masm.load32(Address(string, JSString::offsetOfFlags()), temp2); |
13501 | masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp2); |
13502 | } |
13503 | |
13504 | { |
13505 | masm.newGCString(output, temp0, initialStringHeap(), slowPath); |
13506 | masm.or32(Imm32(JSString::INIT_THIN_INLINE_FLAGS), temp2); |
13507 | } |
13508 | |
13509 | if (tryFatInlineOrDependent) { |
13510 | masm.jump(&allocDone); |
13511 | |
13512 | masm.bind(&allocFat); |
13513 | { |
13514 | masm.newGCFatInlineString(output, temp0, initialStringHeap(), slowPath); |
13515 | masm.or32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), temp2); |
13516 | } |
13517 | |
13518 | masm.bind(&allocDone); |
13519 | } |
13520 | |
13521 | masm.store32(temp2, Address(output, JSString::offsetOfFlags())); |
13522 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
13523 | |
13524 | auto initializeInlineString = [&](CharEncoding encoding) { |
13525 | masm.loadStringChars(string, temp0, encoding); |
13526 | masm.addToCharPtr(temp0, begin, encoding); |
13527 | if (temp1 == string) { |
13528 | masm.push(string); |
13529 | } |
13530 | masm.loadInlineStringCharsForStore(output, temp1); |
13531 | CopyStringChars(masm, temp1, temp0, length, temp2, encoding, |
13532 | maximumLength); |
13533 | masm.loadStringLength(output, length); |
13534 | if (temp1 == string) { |
13535 | masm.pop(string); |
13536 | } |
13537 | }; |
13538 | |
13539 | Label isInlineLatin1; |
13540 | masm.branchTest32(Assembler::NonZero, temp2, |
13541 | Imm32(JSString::LATIN1_CHARS_BIT), &isInlineLatin1); |
13542 | initializeInlineString(CharEncoding::TwoByte); |
13543 | masm.jump(done); |
13544 | |
13545 | masm.bind(&isInlineLatin1); |
13546 | initializeInlineString(CharEncoding::Latin1); |
13547 | } |
13548 | |
13549 | // Handle other cases with a DependentString. |
13550 | if (tryDependent) { |
13551 | masm.jump(done); |
13552 | |
13553 | masm.bind(¬Inline); |
13554 | masm.newGCString(output, temp0, gen->initialStringHeap(), slowPath); |
13555 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
13556 | |
13557 | // Note: no post barrier is needed because the dependent string is either |
13558 | // allocated in the nursery or both strings are tenured (if nursery strings |
13559 | // are disabled for this zone). |
13560 | EmitInitDependentStringBase(masm, output, string, temp0, temp2, |
13561 | /* needsPostBarrier = */ false); |
13562 | |
13563 | auto initializeDependentString = [&](CharEncoding encoding) { |
13564 | uint32_t flags = JSString::INIT_DEPENDENT_FLAGS; |
13565 | if (encoding == CharEncoding::Latin1) { |
13566 | flags |= JSString::LATIN1_CHARS_BIT; |
13567 | } |
13568 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
13569 | masm.loadNonInlineStringChars(string, temp0, encoding); |
13570 | masm.addToCharPtr(temp0, begin, encoding); |
13571 | masm.storeNonInlineStringChars(temp0, output); |
13572 | }; |
13573 | |
13574 | Label isLatin1; |
13575 | masm.branchLatin1String(string, &isLatin1); |
13576 | initializeDependentString(CharEncoding::TwoByte); |
13577 | masm.jump(done); |
13578 | |
13579 | masm.bind(&isLatin1); |
13580 | initializeDependentString(CharEncoding::Latin1); |
13581 | } |
13582 | |
13583 | masm.bind(done); |
13584 | } |
13585 | |
13586 | JitCode* JitZone::generateStringConcatStub(JSContext* cx) { |
13587 | JitSpew(JitSpew_Codegen, "# Emitting StringConcat stub"); |
13588 | |
13589 | TempAllocator temp(&cx->tempLifoAlloc()); |
13590 | JitContext jcx(cx); |
13591 | StackMacroAssembler masm(cx, temp); |
13592 | AutoCreatedBy acb(masm, "JitZone::generateStringConcatStub"); |
13593 | |
13594 | Register lhs = CallTempReg0; |
13595 | Register rhs = CallTempReg1; |
13596 | Register temp1 = CallTempReg2; |
13597 | Register temp2 = CallTempReg3; |
13598 | Register temp3 = CallTempReg4; |
13599 | Register output = CallTempReg5; |
13600 | |
13601 | Label failure; |
13602 | #ifdef JS_USE_LINK_REGISTER |
13603 | masm.pushReturnAddress(); |
13604 | #endif |
13605 | masm.Push(FramePointer); |
13606 | masm.moveStackPtrTo(FramePointer); |
13607 | |
13608 | // If lhs is empty, return rhs. |
13609 | Label leftEmpty; |
13610 | masm.loadStringLength(lhs, temp1); |
13611 | masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty); |
13612 | |
13613 | // If rhs is empty, return lhs. |
13614 | Label rightEmpty; |
13615 | masm.loadStringLength(rhs, temp2); |
13616 | masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty); |
13617 | |
13618 | masm.add32(temp1, temp2); |
13619 | |
13620 | // Check if we can use a JSInlineString. The result is a Latin1 string if |
13621 | // lhs and rhs are both Latin1, so we AND the flags. |
13622 | Label isInlineTwoByte, isInlineLatin1; |
13623 | masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1); |
13624 | masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1); |
13625 | |
13626 | Label isLatin1, notInline; |
13627 | masm.branchTest32(Assembler::NonZero, temp1, |
13628 | Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1); |
13629 | { |
13630 | masm.branch32(Assembler::BelowOrEqual, temp2, |
13631 | Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), |
13632 | &isInlineTwoByte); |
13633 | masm.jump(¬Inline); |
13634 | } |
13635 | masm.bind(&isLatin1); |
13636 | { |
13637 | masm.branch32(Assembler::BelowOrEqual, temp2, |
13638 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1); |
13639 | } |
13640 | masm.bind(¬Inline); |
13641 | |
13642 | // Keep AND'ed flags in temp1. |
13643 | |
13644 | // Ensure result length <= JSString::MAX_LENGTH. |
13645 | masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure); |
13646 | |
13647 | // Allocate a new rope, guaranteed to be in the nursery if initialStringHeap |
13648 | // == gc::Heap::Default. (As a result, no post barriers are needed below.) |
13649 | masm.newGCString(output, temp3, initialStringHeap, &failure); |
13650 | |
13651 | // Store rope length and flags. temp1 still holds the result of AND'ing the |
13652 | // lhs and rhs flags, so we just have to clear the other flags to get our rope |
13653 | // flags (Latin1 if both lhs and rhs are Latin1). |
13654 | static_assert(JSString::INIT_ROPE_FLAGS == 0, |
13655 | "Rope type flags must have no bits set"); |
13656 | masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1); |
13657 | masm.store32(temp1, Address(output, JSString::offsetOfFlags())); |
13658 | masm.store32(temp2, Address(output, JSString::offsetOfLength())); |
13659 | |
13660 | // Store left and right nodes. |
13661 | masm.storeRopeChildren(lhs, rhs, output); |
13662 | masm.pop(FramePointer); |
13663 | masm.ret(); |
13664 | |
13665 | masm.bind(&leftEmpty); |
13666 | masm.mov(rhs, output); |
13667 | masm.pop(FramePointer); |
13668 | masm.ret(); |
13669 | |
13670 | masm.bind(&rightEmpty); |
13671 | masm.mov(lhs, output); |
13672 | masm.pop(FramePointer); |
13673 | masm.ret(); |
13674 | |
13675 | masm.bind(&isInlineTwoByte); |
13676 | ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, |
13677 | initialStringHeap, &failure, CharEncoding::TwoByte); |
13678 | masm.pop(FramePointer); |
13679 | masm.ret(); |
13680 | |
13681 | masm.bind(&isInlineLatin1); |
13682 | ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, |
13683 | initialStringHeap, &failure, CharEncoding::Latin1); |
13684 | masm.pop(FramePointer); |
13685 | masm.ret(); |
13686 | |
13687 | masm.bind(&failure); |
13688 | masm.movePtr(ImmPtr(nullptr), output); |
13689 | masm.pop(FramePointer); |
13690 | masm.ret(); |
13691 | |
13692 | Linker linker(masm); |
13693 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
13694 | |
13695 | CollectPerfSpewerJitCodeProfile(code, "StringConcatStub"); |
13696 | #ifdef MOZ_VTUNE1 |
13697 | vtune::MarkStub(code, "StringConcatStub"); |
13698 | #endif |
13699 | |
13700 | return code; |
13701 | } |
13702 | |
13703 | void JitRuntime::generateLazyLinkStub(MacroAssembler& masm) { |
13704 | AutoCreatedBy acb(masm, "JitRuntime::generateLazyLinkStub"); |
13705 | |
13706 | lazyLinkStubOffset_ = startTrampolineCode(masm); |
13707 | |
13708 | #ifdef JS_USE_LINK_REGISTER |
13709 | masm.pushReturnAddress(); |
13710 | #endif |
13711 | masm.Push(FramePointer); |
13712 | masm.moveStackPtrTo(FramePointer); |
13713 | |
13714 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
13715 | Register temp0 = regs.takeAny(); |
13716 | Register temp1 = regs.takeAny(); |
13717 | Register temp2 = regs.takeAny(); |
13718 | |
13719 | masm.loadJSContext(temp0); |
13720 | masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink); |
13721 | masm.moveStackPtrTo(temp1); |
13722 | |
13723 | using Fn = uint8_t* (*)(JSContext* cx, LazyLinkExitFrameLayout* frame); |
13724 | masm.setupUnalignedABICall(temp2); |
13725 | masm.passABIArg(temp0); |
13726 | masm.passABIArg(temp1); |
13727 | masm.callWithABI<Fn, LazyLinkTopActivation>( |
13728 | ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
13729 | |
13730 | // Discard exit frame and restore frame pointer. |
13731 | masm.leaveExitFrame(0); |
13732 | masm.pop(FramePointer); |
13733 | |
13734 | #ifdef JS_USE_LINK_REGISTER |
13735 | // Restore the return address such that the emitPrologue function of the |
13736 | // CodeGenerator can push it back on the stack with pushReturnAddress. |
13737 | masm.popReturnAddress(); |
13738 | #endif |
13739 | masm.jump(ReturnReg); |
13740 | } |
13741 | |
13742 | void JitRuntime::generateInterpreterStub(MacroAssembler& masm) { |
13743 | AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterStub"); |
13744 | |
13745 | interpreterStubOffset_ = startTrampolineCode(masm); |
13746 | |
13747 | #ifdef JS_USE_LINK_REGISTER |
13748 | masm.pushReturnAddress(); |
13749 | #endif |
13750 | masm.Push(FramePointer); |
13751 | masm.moveStackPtrTo(FramePointer); |
13752 | |
13753 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
13754 | Register temp0 = regs.takeAny(); |
13755 | Register temp1 = regs.takeAny(); |
13756 | Register temp2 = regs.takeAny(); |
13757 | |
13758 | masm.loadJSContext(temp0); |
13759 | masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub); |
13760 | masm.moveStackPtrTo(temp1); |
13761 | |
13762 | using Fn = bool (*)(JSContext* cx, InterpreterStubExitFrameLayout* frame); |
13763 | masm.setupUnalignedABICall(temp2); |
13764 | masm.passABIArg(temp0); |
13765 | masm.passABIArg(temp1); |
13766 | masm.callWithABI<Fn, InvokeFromInterpreterStub>( |
13767 | ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
13768 | |
13769 | masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); |
13770 | |
13771 | // Discard exit frame and restore frame pointer. |
13772 | masm.leaveExitFrame(0); |
13773 | masm.pop(FramePointer); |
13774 | |
13775 | // InvokeFromInterpreterStub stores the return value in argv[0], where the |
13776 | // caller stored |this|. Subtract |sizeof(void*)| for the frame pointer we |
13777 | // just popped. |
13778 | masm.loadValue(Address(masm.getStackPointer(), |
13779 | JitFrameLayout::offsetOfThis() - sizeof(void*)), |
13780 | JSReturnOperand); |
13781 | masm.ret(); |
13782 | } |
13783 | |
13784 | void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) { |
13785 | AutoCreatedBy acb(masm, "JitRuntime::generateDoubleToInt32ValueStub"); |
13786 | doubleToInt32ValueStubOffset_ = startTrampolineCode(masm); |
13787 | |
13788 | Label done; |
13789 | masm.branchTestDouble(Assembler::NotEqual, R0, &done); |
13790 | |
13791 | masm.unboxDouble(R0, FloatReg0); |
13792 | masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done, |
13793 | /* negativeZeroCheck = */ false); |
13794 | masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0); |
13795 | |
13796 | masm.bind(&done); |
13797 | masm.abiret(); |
13798 | } |
13799 | |
13800 | void CodeGenerator::visitLinearizeString(LLinearizeString* lir) { |
13801 | Register str = ToRegister(lir->str()); |
13802 | Register output = ToRegister(lir->output()); |
13803 | |
13804 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
13805 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
13806 | lir, ArgList(str), StoreRegisterTo(output)); |
13807 | |
13808 | masm.branchIfRope(str, ool->entry()); |
13809 | |
13810 | masm.movePtr(str, output); |
13811 | masm.bind(ool->rejoin()); |
13812 | } |
13813 | |
13814 | void CodeGenerator::visitLinearizeForCharAccess(LLinearizeForCharAccess* lir) { |
13815 | Register str = ToRegister(lir->str()); |
13816 | Register index = ToRegister(lir->index()); |
13817 | Register output = ToRegister(lir->output()); |
13818 | |
13819 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
13820 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
13821 | lir, ArgList(str), StoreRegisterTo(output)); |
13822 | |
13823 | masm.branchIfNotCanLoadStringChar(str, index, output, ool->entry()); |
13824 | |
13825 | masm.movePtr(str, output); |
13826 | masm.bind(ool->rejoin()); |
13827 | } |
13828 | |
13829 | void CodeGenerator::visitLinearizeForCodePointAccess( |
13830 | LLinearizeForCodePointAccess* lir) { |
13831 | Register str = ToRegister(lir->str()); |
13832 | Register index = ToRegister(lir->index()); |
13833 | Register output = ToRegister(lir->output()); |
13834 | Register temp = ToRegister(lir->temp0()); |
13835 | |
13836 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
13837 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
13838 | lir, ArgList(str), StoreRegisterTo(output)); |
13839 | |
13840 | masm.branchIfNotCanLoadStringCodePoint(str, index, output, temp, |
13841 | ool->entry()); |
13842 | |
13843 | masm.movePtr(str, output); |
13844 | masm.bind(ool->rejoin()); |
13845 | } |
13846 | |
13847 | void CodeGenerator::visitToRelativeStringIndex(LToRelativeStringIndex* lir) { |
13848 | Register index = ToRegister(lir->index()); |
13849 | Register length = ToRegister(lir->length()); |
13850 | Register output = ToRegister(lir->output()); |
13851 | |
13852 | masm.move32(Imm32(0), output); |
13853 | masm.cmp32Move32(Assembler::LessThan, index, Imm32(0), length, output); |
13854 | masm.add32(index, output); |
13855 | } |
13856 | |
13857 | void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) { |
13858 | Register str = ToRegister(lir->str()); |
13859 | Register output = ToRegister(lir->output()); |
13860 | Register temp0 = ToRegister(lir->temp0()); |
13861 | Register temp1 = ToRegister(lir->temp1()); |
13862 | |
13863 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
13864 | |
13865 | if (lir->index()->isBogus()) { |
13866 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)), |
13867 | StoreRegisterTo(output)); |
13868 | masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry()); |
13869 | masm.bind(ool->rejoin()); |
13870 | } else { |
13871 | Register index = ToRegister(lir->index()); |
13872 | |
13873 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index), |
13874 | StoreRegisterTo(output)); |
13875 | masm.loadStringChar(str, index, output, temp0, temp1, ool->entry()); |
13876 | masm.bind(ool->rejoin()); |
13877 | } |
13878 | } |
13879 | |
13880 | void CodeGenerator::visitCharCodeAtOrNegative(LCharCodeAtOrNegative* lir) { |
13881 | Register str = ToRegister(lir->str()); |
13882 | Register output = ToRegister(lir->output()); |
13883 | Register temp0 = ToRegister(lir->temp0()); |
13884 | Register temp1 = ToRegister(lir->temp1()); |
13885 | |
13886 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
13887 | |
13888 | // Return -1 for out-of-bounds access. |
13889 | masm.move32(Imm32(-1), output); |
13890 | |
13891 | if (lir->index()->isBogus()) { |
13892 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)), |
13893 | StoreRegisterTo(output)); |
13894 | |
13895 | masm.branch32(Assembler::Equal, Address(str, JSString::offsetOfLength()), |
13896 | Imm32(0), ool->rejoin()); |
13897 | masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry()); |
13898 | masm.bind(ool->rejoin()); |
13899 | } else { |
13900 | Register index = ToRegister(lir->index()); |
13901 | |
13902 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index), |
13903 | StoreRegisterTo(output)); |
13904 | |
13905 | masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), |
13906 | temp0, ool->rejoin()); |
13907 | masm.loadStringChar(str, index, output, temp0, temp1, ool->entry()); |
13908 | masm.bind(ool->rejoin()); |
13909 | } |
13910 | } |
13911 | |
13912 | void CodeGenerator::visitCodePointAt(LCodePointAt* lir) { |
13913 | Register str = ToRegister(lir->str()); |
13914 | Register index = ToRegister(lir->index()); |
13915 | Register output = ToRegister(lir->output()); |
13916 | Register temp0 = ToRegister(lir->temp0()); |
13917 | Register temp1 = ToRegister(lir->temp1()); |
13918 | |
13919 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
13920 | auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index), |
13921 | StoreRegisterTo(output)); |
13922 | |
13923 | masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry()); |
13924 | masm.bind(ool->rejoin()); |
13925 | } |
13926 | |
13927 | void CodeGenerator::visitCodePointAtOrNegative(LCodePointAtOrNegative* lir) { |
13928 | Register str = ToRegister(lir->str()); |
13929 | Register index = ToRegister(lir->index()); |
13930 | Register output = ToRegister(lir->output()); |
13931 | Register temp0 = ToRegister(lir->temp0()); |
13932 | Register temp1 = ToRegister(lir->temp1()); |
13933 | |
13934 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
13935 | auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index), |
13936 | StoreRegisterTo(output)); |
13937 | |
13938 | // Return -1 for out-of-bounds access. |
13939 | masm.move32(Imm32(-1), output); |
13940 | |
13941 | masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), |
13942 | temp0, ool->rejoin()); |
13943 | masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry()); |
13944 | masm.bind(ool->rejoin()); |
13945 | } |
13946 | |
13947 | void CodeGenerator::visitNegativeToNaN(LNegativeToNaN* lir) { |
13948 | Register input = ToRegister(lir->input()); |
13949 | ValueOperand output = ToOutValue(lir); |
13950 | |
13951 | masm.tagValue(JSVAL_TYPE_INT32, input, output); |
13952 | |
13953 | Label done; |
13954 | masm.branchTest32(Assembler::NotSigned, input, input, &done); |
13955 | masm.moveValue(JS::NaNValue(), output); |
13956 | masm.bind(&done); |
13957 | } |
13958 | |
13959 | void CodeGenerator::visitNegativeToUndefined(LNegativeToUndefined* lir) { |
13960 | Register input = ToRegister(lir->input()); |
13961 | ValueOperand output = ToOutValue(lir); |
13962 | |
13963 | masm.tagValue(JSVAL_TYPE_INT32, input, output); |
13964 | |
13965 | Label done; |
13966 | masm.branchTest32(Assembler::NotSigned, input, input, &done); |
13967 | masm.moveValue(JS::UndefinedValue(), output); |
13968 | masm.bind(&done); |
13969 | } |
13970 | |
13971 | void CodeGenerator::visitFromCharCode(LFromCharCode* lir) { |
13972 | Register code = ToRegister(lir->code()); |
13973 | Register output = ToRegister(lir->output()); |
13974 | |
13975 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
13976 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
13977 | StoreRegisterTo(output)); |
13978 | |
13979 | // OOL path if code >= UNIT_STATIC_LIMIT. |
13980 | masm.lookupStaticString(code, output, gen->runtime->staticStrings(), |
13981 | ool->entry()); |
13982 | |
13983 | masm.bind(ool->rejoin()); |
13984 | } |
13985 | |
13986 | void CodeGenerator::visitFromCharCodeEmptyIfNegative( |
13987 | LFromCharCodeEmptyIfNegative* lir) { |
13988 | Register code = ToRegister(lir->code()); |
13989 | Register output = ToRegister(lir->output()); |
13990 | |
13991 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
13992 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
13993 | StoreRegisterTo(output)); |
13994 | |
13995 | // Return the empty string for negative inputs. |
13996 | const JSAtomState& names = gen->runtime->names(); |
13997 | masm.movePtr(ImmGCPtr(names.empty_), output); |
13998 | masm.branchTest32(Assembler::Signed, code, code, ool->rejoin()); |
13999 | |
14000 | // OOL path if code >= UNIT_STATIC_LIMIT. |
14001 | masm.lookupStaticString(code, output, gen->runtime->staticStrings(), |
14002 | ool->entry()); |
14003 | |
14004 | masm.bind(ool->rejoin()); |
14005 | } |
14006 | |
14007 | void CodeGenerator::visitFromCharCodeUndefinedIfNegative( |
14008 | LFromCharCodeUndefinedIfNegative* lir) { |
14009 | Register code = ToRegister(lir->code()); |
14010 | ValueOperand output = ToOutValue(lir); |
14011 | Register temp = output.scratchReg(); |
14012 | |
14013 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
14014 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
14015 | StoreRegisterTo(temp)); |
14016 | |
14017 | // Return |undefined| for negative inputs. |
14018 | Label done; |
14019 | masm.moveValue(UndefinedValue(), output); |
14020 | masm.branchTest32(Assembler::Signed, code, code, &done); |
14021 | |
14022 | // OOL path if code >= UNIT_STATIC_LIMIT. |
14023 | masm.lookupStaticString(code, temp, gen->runtime->staticStrings(), |
14024 | ool->entry()); |
14025 | |
14026 | masm.bind(ool->rejoin()); |
14027 | masm.tagValue(JSVAL_TYPE_STRING, temp, output); |
14028 | |
14029 | masm.bind(&done); |
14030 | } |
14031 | |
14032 | void CodeGenerator::visitFromCodePoint(LFromCodePoint* lir) { |
14033 | Register codePoint = ToRegister(lir->codePoint()); |
14034 | Register output = ToRegister(lir->output()); |
14035 | Register temp0 = ToRegister(lir->temp0()); |
14036 | Register temp1 = ToRegister(lir->temp1()); |
14037 | LSnapshot* snapshot = lir->snapshot(); |
14038 | |
14039 | // The OOL path is only taken when we can't allocate the inline string. |
14040 | using Fn = JSLinearString* (*)(JSContext*, char32_t); |
14041 | auto* ool = oolCallVM<Fn, js::StringFromCodePoint>(lir, ArgList(codePoint), |
14042 | StoreRegisterTo(output)); |
14043 | |
14044 | Label isTwoByte; |
14045 | Label* done = ool->rejoin(); |
14046 | |
14047 | static_assert( |
14048 | StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR, |
14049 | "Latin-1 strings can be loaded from static strings"); |
14050 | |
14051 | { |
14052 | masm.lookupStaticString(codePoint, output, gen->runtime->staticStrings(), |
14053 | &isTwoByte); |
14054 | masm.jump(done); |
14055 | } |
14056 | masm.bind(&isTwoByte); |
14057 | { |
14058 | // Use a bailout if the input is not a valid code point, because |
14059 | // MFromCodePoint is movable and it'd be observable when a moved |
14060 | // fromCodePoint throws an exception before its actual call site. |
14061 | bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax), |
14062 | snapshot); |
14063 | |
14064 | // Allocate a JSThinInlineString. |
14065 | { |
14066 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE >= 2, |
14067 | "JSThinInlineString can hold a supplementary code point"); |
14068 | |
14069 | uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS; |
14070 | masm.newGCString(output, temp0, gen->initialStringHeap(), ool->entry()); |
14071 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
14072 | } |
14073 | |
14074 | Label isSupplementary; |
14075 | masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(unicode::NonBMPMin), |
14076 | &isSupplementary); |
14077 | { |
14078 | // Store length. |
14079 | masm.store32(Imm32(1), Address(output, JSString::offsetOfLength())); |
14080 | |
14081 | // Load chars pointer in temp0. |
14082 | masm.loadInlineStringCharsForStore(output, temp0); |
14083 | |
14084 | masm.store16(codePoint, Address(temp0, 0)); |
14085 | |
14086 | masm.jump(done); |
14087 | } |
14088 | masm.bind(&isSupplementary); |
14089 | { |
14090 | // Store length. |
14091 | masm.store32(Imm32(2), Address(output, JSString::offsetOfLength())); |
14092 | |
14093 | // Load chars pointer in temp0. |
14094 | masm.loadInlineStringCharsForStore(output, temp0); |
14095 | |
14096 | // Inlined unicode::LeadSurrogate(uint32_t). |
14097 | masm.move32(codePoint, temp1); |
14098 | masm.rshift32(Imm32(10), temp1); |
14099 | masm.add32(Imm32(unicode::LeadSurrogateMin - (unicode::NonBMPMin >> 10)), |
14100 | temp1); |
14101 | |
14102 | masm.store16(temp1, Address(temp0, 0)); |
14103 | |
14104 | // Inlined unicode::TrailSurrogate(uint32_t). |
14105 | masm.move32(codePoint, temp1); |
14106 | masm.and32(Imm32(0x3FF), temp1); |
14107 | masm.or32(Imm32(unicode::TrailSurrogateMin), temp1); |
14108 | |
14109 | masm.store16(temp1, Address(temp0, sizeof(char16_t))); |
14110 | } |
14111 | } |
14112 | |
14113 | masm.bind(done); |
14114 | } |
14115 | |
14116 | void CodeGenerator::visitStringIncludes(LStringIncludes* lir) { |
14117 | pushArg(ToRegister(lir->searchString())); |
14118 | pushArg(ToRegister(lir->string())); |
14119 | |
14120 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14121 | callVM<Fn, js::StringIncludes>(lir); |
14122 | } |
14123 | |
14124 | template <typename LIns> |
14125 | static void CallStringMatch(MacroAssembler& masm, LIns* lir, OutOfLineCode* ool, |
14126 | LiveRegisterSet volatileRegs) { |
14127 | Register string = ToRegister(lir->string()); |
14128 | Register output = ToRegister(lir->output()); |
14129 | Register tempLength = ToRegister(lir->temp0()); |
14130 | Register tempChars = ToRegister(lir->temp1()); |
14131 | Register maybeTempPat = ToTempRegisterOrInvalid(lir->temp2()); |
14132 | |
14133 | const JSLinearString* searchString = lir->searchString(); |
14134 | size_t length = searchString->length(); |
14135 | MOZ_ASSERT(length == 1 || length == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length == 1 || length == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length == 1 || length == 2)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length == 1 || length == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14135); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length == 1 || length == 2" ")"); do { *((volatile int*)__null) = 14135; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14136 | |
14137 | // The additional temp register is only needed when searching for two |
14138 | // pattern characters. |
14139 | MOZ_ASSERT_IF(length == 2, maybeTempPat != InvalidReg)do { if (length == 2) { do { static_assert( mozilla::detail:: AssertionConditionType<decltype(maybeTempPat != InvalidReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(maybeTempPat != InvalidReg))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("maybeTempPat != InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14139); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeTempPat != InvalidReg" ")"); do { *((volatile int*)__null) = 14139; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
14140 | |
14141 | if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) { |
14142 | masm.move32(Imm32(0), output); |
14143 | } else { |
14144 | masm.move32(Imm32(-1), output); |
14145 | } |
14146 | |
14147 | masm.loadStringLength(string, tempLength); |
14148 | |
14149 | // Can't be a substring when the string is smaller than the search string. |
14150 | Label done; |
14151 | masm.branch32(Assembler::Below, tempLength, Imm32(length), ool->rejoin()); |
14152 | |
14153 | bool searchStringIsPureTwoByte = false; |
14154 | if (searchString->hasTwoByteChars()) { |
14155 | JS::AutoCheckCannotGC nogc; |
14156 | searchStringIsPureTwoByte = |
14157 | !mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc)); |
14158 | } |
14159 | |
14160 | // Pure two-byte strings can't occur in a Latin-1 string. |
14161 | if (searchStringIsPureTwoByte) { |
14162 | masm.branchLatin1String(string, ool->rejoin()); |
14163 | } |
14164 | |
14165 | // Slow path when we need to linearize the string. |
14166 | masm.branchIfRope(string, ool->entry()); |
14167 | |
14168 | Label restoreVolatile; |
14169 | |
14170 | auto callMatcher = [&](CharEncoding encoding) { |
14171 | masm.loadStringChars(string, tempChars, encoding); |
14172 | |
14173 | LiveGeneralRegisterSet liveRegs; |
14174 | if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) { |
14175 | // Save |tempChars| to compute the result index. |
14176 | liveRegs.add(tempChars); |
14177 | |
14178 | #ifdef DEBUG1 |
14179 | // Save |tempLength| in debug-mode for assertions. |
14180 | liveRegs.add(tempLength); |
14181 | #endif |
14182 | |
14183 | // Exclude non-volatile registers. |
14184 | liveRegs.set() = GeneralRegisterSet::Intersect( |
14185 | liveRegs.set(), GeneralRegisterSet::Volatile()); |
14186 | |
14187 | masm.PushRegsInMask(liveRegs); |
14188 | } |
14189 | |
14190 | if (length == 1) { |
14191 | char16_t pat = searchString->latin1OrTwoByteChar(0); |
14192 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14193; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
14193 | pat <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14193; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
14194 | |
14195 | masm.move32(Imm32(pat), output); |
14196 | |
14197 | masm.setupAlignedABICall(); |
14198 | masm.passABIArg(tempChars); |
14199 | masm.passABIArg(output); |
14200 | masm.passABIArg(tempLength); |
14201 | if (encoding == CharEncoding::Latin1) { |
14202 | using Fn = const char* (*)(const char*, char, size_t); |
14203 | masm.callWithABI<Fn, mozilla::SIMD::memchr8>( |
14204 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
14205 | } else { |
14206 | using Fn = const char16_t* (*)(const char16_t*, char16_t, size_t); |
14207 | masm.callWithABI<Fn, mozilla::SIMD::memchr16>( |
14208 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
14209 | } |
14210 | } else { |
14211 | char16_t pat0 = searchString->latin1OrTwoByteChar(0); |
14212 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat0 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat0 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat0 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14213); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14213; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
14213 | pat0 <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat0 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat0 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat0 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14213); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14213; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
14214 | |
14215 | char16_t pat1 = searchString->latin1OrTwoByteChar(1); |
14216 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat1 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat1 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat1 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14217); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14217; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
14217 | pat1 <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat1 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat1 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat1 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14217); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14217; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
14218 | |
14219 | masm.move32(Imm32(pat0), output); |
14220 | masm.move32(Imm32(pat1), maybeTempPat); |
14221 | |
14222 | masm.setupAlignedABICall(); |
14223 | masm.passABIArg(tempChars); |
14224 | masm.passABIArg(output); |
14225 | masm.passABIArg(maybeTempPat); |
14226 | masm.passABIArg(tempLength); |
14227 | if (encoding == CharEncoding::Latin1) { |
14228 | using Fn = const char* (*)(const char*, char, char, size_t); |
14229 | masm.callWithABI<Fn, mozilla::SIMD::memchr2x8>( |
14230 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
14231 | } else { |
14232 | using Fn = |
14233 | const char16_t* (*)(const char16_t*, char16_t, char16_t, size_t); |
14234 | masm.callWithABI<Fn, mozilla::SIMD::memchr2x16>( |
14235 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
14236 | } |
14237 | } |
14238 | |
14239 | masm.storeCallPointerResult(output); |
14240 | |
14241 | // Convert to string index for `indexOf`. |
14242 | if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) { |
14243 | // Restore |tempChars|. (And in debug mode |tempLength|.) |
14244 | masm.PopRegsInMask(liveRegs); |
14245 | |
14246 | Label found; |
14247 | masm.branchPtr(Assembler::NotEqual, output, ImmPtr(nullptr), &found); |
14248 | { |
14249 | masm.move32(Imm32(-1), output); |
14250 | masm.jump(&restoreVolatile); |
14251 | } |
14252 | masm.bind(&found); |
14253 | |
14254 | #ifdef DEBUG1 |
14255 | // Check lower bound. |
14256 | Label lower; |
14257 | masm.branchPtr(Assembler::AboveOrEqual, output, tempChars, &lower); |
14258 | masm.assumeUnreachable("result pointer below string chars"); |
14259 | masm.bind(&lower); |
14260 | |
14261 | // Compute the end position of the characters. |
14262 | auto scale = encoding == CharEncoding::Latin1 ? TimesOne : TimesTwo; |
14263 | masm.computeEffectiveAddress(BaseIndex(tempChars, tempLength, scale), |
14264 | tempLength); |
14265 | |
14266 | // Check upper bound. |
14267 | Label upper; |
14268 | masm.branchPtr(Assembler::Below, output, tempLength, &upper); |
14269 | masm.assumeUnreachable("result pointer above string chars"); |
14270 | masm.bind(&upper); |
14271 | #endif |
14272 | |
14273 | masm.subPtr(tempChars, output); |
14274 | |
14275 | if (encoding == CharEncoding::TwoByte) { |
14276 | masm.rshiftPtr(Imm32(1), output); |
14277 | } |
14278 | } |
14279 | }; |
14280 | |
14281 | volatileRegs.takeUnchecked(output); |
14282 | volatileRegs.takeUnchecked(tempLength); |
14283 | volatileRegs.takeUnchecked(tempChars); |
14284 | if (maybeTempPat != InvalidReg) { |
14285 | volatileRegs.takeUnchecked(maybeTempPat); |
14286 | } |
14287 | masm.PushRegsInMask(volatileRegs); |
14288 | |
14289 | // Handle the case when the input is a Latin-1 string. |
14290 | if (!searchStringIsPureTwoByte) { |
14291 | Label twoByte; |
14292 | masm.branchTwoByteString(string, &twoByte); |
14293 | { |
14294 | callMatcher(CharEncoding::Latin1); |
14295 | masm.jump(&restoreVolatile); |
14296 | } |
14297 | masm.bind(&twoByte); |
14298 | } |
14299 | |
14300 | // Handle the case when the input is a two-byte string. |
14301 | callMatcher(CharEncoding::TwoByte); |
14302 | |
14303 | masm.bind(&restoreVolatile); |
14304 | masm.PopRegsInMask(volatileRegs); |
14305 | |
14306 | // Convert to bool for `includes`. |
14307 | if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) { |
14308 | masm.cmpPtrSet(Assembler::NotEqual, output, ImmPtr(nullptr), output); |
14309 | } |
14310 | |
14311 | masm.bind(ool->rejoin()); |
14312 | } |
14313 | |
14314 | void CodeGenerator::visitStringIncludesSIMD(LStringIncludesSIMD* lir) { |
14315 | Register string = ToRegister(lir->string()); |
14316 | Register output = ToRegister(lir->output()); |
14317 | const JSLinearString* searchString = lir->searchString(); |
14318 | |
14319 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14320 | auto* ool = oolCallVM<Fn, js::StringIncludes>( |
14321 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
14322 | |
14323 | CallStringMatch(masm, lir, ool, liveVolatileRegs(lir)); |
14324 | } |
14325 | |
14326 | void CodeGenerator::visitStringIndexOf(LStringIndexOf* lir) { |
14327 | pushArg(ToRegister(lir->searchString())); |
14328 | pushArg(ToRegister(lir->string())); |
14329 | |
14330 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
14331 | callVM<Fn, js::StringIndexOf>(lir); |
14332 | } |
14333 | |
14334 | void CodeGenerator::visitStringIndexOfSIMD(LStringIndexOfSIMD* lir) { |
14335 | Register string = ToRegister(lir->string()); |
14336 | Register output = ToRegister(lir->output()); |
14337 | const JSLinearString* searchString = lir->searchString(); |
14338 | |
14339 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
14340 | auto* ool = oolCallVM<Fn, js::StringIndexOf>( |
14341 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
14342 | |
14343 | CallStringMatch(masm, lir, ool, liveVolatileRegs(lir)); |
14344 | } |
14345 | |
14346 | void CodeGenerator::visitStringLastIndexOf(LStringLastIndexOf* lir) { |
14347 | pushArg(ToRegister(lir->searchString())); |
14348 | pushArg(ToRegister(lir->string())); |
14349 | |
14350 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
14351 | callVM<Fn, js::StringLastIndexOf>(lir); |
14352 | } |
14353 | |
14354 | void CodeGenerator::visitStringStartsWith(LStringStartsWith* lir) { |
14355 | pushArg(ToRegister(lir->searchString())); |
14356 | pushArg(ToRegister(lir->string())); |
14357 | |
14358 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14359 | callVM<Fn, js::StringStartsWith>(lir); |
14360 | } |
14361 | |
14362 | void CodeGenerator::visitStringStartsWithInline(LStringStartsWithInline* lir) { |
14363 | Register string = ToRegister(lir->string()); |
14364 | Register output = ToRegister(lir->output()); |
14365 | Register temp = ToRegister(lir->temp0()); |
14366 | |
14367 | const JSLinearString* searchString = lir->searchString(); |
14368 | |
14369 | size_t length = searchString->length(); |
14370 | MOZ_ASSERT(length > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14370); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0" ")"); do { *((volatile int*)__null) = 14370; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14371 | |
14372 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14373 | auto* ool = oolCallVM<Fn, js::StringStartsWith>( |
14374 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
14375 | |
14376 | masm.move32(Imm32(0), output); |
14377 | |
14378 | // Can't be a prefix when the string is smaller than the search string. |
14379 | masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()), |
14380 | Imm32(length), ool->rejoin()); |
14381 | |
14382 | // Unwind ropes at the start if possible. |
14383 | Label compare; |
14384 | masm.movePtr(string, temp); |
14385 | masm.branchIfNotRope(temp, &compare); |
14386 | |
14387 | Label unwindRope; |
14388 | masm.bind(&unwindRope); |
14389 | masm.loadRopeLeftChild(temp, output); |
14390 | masm.movePtr(output, temp); |
14391 | |
14392 | // If the left child is smaller than the search string, jump into the VM to |
14393 | // linearize the string. |
14394 | masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()), |
14395 | Imm32(length), ool->entry()); |
14396 | |
14397 | // Otherwise keep unwinding ropes. |
14398 | masm.branchIfRope(temp, &unwindRope); |
14399 | |
14400 | masm.bind(&compare); |
14401 | |
14402 | // If operands point to the same instance, it's trivially a prefix. |
14403 | Label notPointerEqual; |
14404 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString), |
14405 | ¬PointerEqual); |
14406 | masm.move32(Imm32(1), output); |
14407 | masm.jump(ool->rejoin()); |
14408 | masm.bind(¬PointerEqual); |
14409 | |
14410 | if (searchString->hasTwoByteChars()) { |
14411 | // Pure two-byte strings can't be a prefix of Latin-1 strings. |
14412 | JS::AutoCheckCannotGC nogc; |
14413 | if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) { |
14414 | Label compareChars; |
14415 | masm.branchTwoByteString(temp, &compareChars); |
14416 | masm.move32(Imm32(0), output); |
14417 | masm.jump(ool->rejoin()); |
14418 | masm.bind(&compareChars); |
14419 | } |
14420 | } |
14421 | |
14422 | // Load the input string's characters. |
14423 | Register stringChars = output; |
14424 | masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry()); |
14425 | |
14426 | // Start comparing character by character. |
14427 | masm.compareStringChars(JSOp::Eq, stringChars, searchString, output); |
14428 | |
14429 | masm.bind(ool->rejoin()); |
14430 | } |
14431 | |
14432 | void CodeGenerator::visitStringEndsWith(LStringEndsWith* lir) { |
14433 | pushArg(ToRegister(lir->searchString())); |
14434 | pushArg(ToRegister(lir->string())); |
14435 | |
14436 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14437 | callVM<Fn, js::StringEndsWith>(lir); |
14438 | } |
14439 | |
14440 | void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) { |
14441 | Register string = ToRegister(lir->string()); |
14442 | Register output = ToRegister(lir->output()); |
14443 | Register temp = ToRegister(lir->temp0()); |
14444 | |
14445 | const JSLinearString* searchString = lir->searchString(); |
14446 | |
14447 | size_t length = searchString->length(); |
14448 | MOZ_ASSERT(length > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0" ")"); do { *((volatile int*)__null) = 14448; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14449 | |
14450 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14451 | auto* ool = oolCallVM<Fn, js::StringEndsWith>( |
14452 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
14453 | |
14454 | masm.move32(Imm32(0), output); |
14455 | |
14456 | // Can't be a suffix when the string is smaller than the search string. |
14457 | masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()), |
14458 | Imm32(length), ool->rejoin()); |
14459 | |
14460 | // Unwind ropes at the end if possible. |
14461 | Label compare; |
14462 | masm.movePtr(string, temp); |
14463 | masm.branchIfNotRope(temp, &compare); |
14464 | |
14465 | Label unwindRope; |
14466 | masm.bind(&unwindRope); |
14467 | masm.loadRopeRightChild(temp, output); |
14468 | masm.movePtr(output, temp); |
14469 | |
14470 | // If the right child is smaller than the search string, jump into the VM to |
14471 | // linearize the string. |
14472 | masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()), |
14473 | Imm32(length), ool->entry()); |
14474 | |
14475 | // Otherwise keep unwinding ropes. |
14476 | masm.branchIfRope(temp, &unwindRope); |
14477 | |
14478 | masm.bind(&compare); |
14479 | |
14480 | // If operands point to the same instance, it's trivially a suffix. |
14481 | Label notPointerEqual; |
14482 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString), |
14483 | ¬PointerEqual); |
14484 | masm.move32(Imm32(1), output); |
14485 | masm.jump(ool->rejoin()); |
14486 | masm.bind(¬PointerEqual); |
14487 | |
14488 | CharEncoding encoding = searchString->hasLatin1Chars() |
14489 | ? CharEncoding::Latin1 |
14490 | : CharEncoding::TwoByte; |
14491 | if (encoding == CharEncoding::TwoByte) { |
14492 | // Pure two-byte strings can't be a suffix of Latin-1 strings. |
14493 | JS::AutoCheckCannotGC nogc; |
14494 | if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) { |
14495 | Label compareChars; |
14496 | masm.branchTwoByteString(temp, &compareChars); |
14497 | masm.move32(Imm32(0), output); |
14498 | masm.jump(ool->rejoin()); |
14499 | masm.bind(&compareChars); |
14500 | } |
14501 | } |
14502 | |
14503 | // Load the input string's characters. |
14504 | Register stringChars = output; |
14505 | masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry()); |
14506 | |
14507 | // Move string-char pointer to the suffix string. |
14508 | masm.loadStringLength(temp, temp); |
14509 | masm.sub32(Imm32(length), temp); |
14510 | masm.addToCharPtr(stringChars, temp, encoding); |
14511 | |
14512 | // Start comparing character by character. |
14513 | masm.compareStringChars(JSOp::Eq, stringChars, searchString, output); |
14514 | |
14515 | masm.bind(ool->rejoin()); |
14516 | } |
14517 | |
14518 | void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) { |
14519 | Register string = ToRegister(lir->string()); |
14520 | Register output = ToRegister(lir->output()); |
14521 | Register temp0 = ToRegister(lir->temp0()); |
14522 | Register temp1 = ToRegister(lir->temp1()); |
14523 | Register temp2 = ToRegister(lir->temp2()); |
14524 | |
14525 | // On x86 there are not enough registers. In that case reuse the string |
14526 | // register as a temporary. |
14527 | Register temp3 = |
14528 | lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3()); |
14529 | Register temp4 = ToRegister(lir->temp4()); |
14530 | |
14531 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
14532 | OutOfLineCode* ool = oolCallVM<Fn, js::StringToLowerCase>( |
14533 | lir, ArgList(string), StoreRegisterTo(output)); |
14534 | |
14535 | // Take the slow path if the string isn't a linear Latin-1 string. |
14536 | Imm32 linearLatin1Bits(JSString::LINEAR_BIT | JSString::LATIN1_CHARS_BIT); |
14537 | Register flags = temp0; |
14538 | masm.load32(Address(string, JSString::offsetOfFlags()), flags); |
14539 | masm.and32(linearLatin1Bits, flags); |
14540 | masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry()); |
14541 | |
14542 | Register length = temp0; |
14543 | masm.loadStringLength(string, length); |
14544 | |
14545 | // Return the input if it's the empty string. |
14546 | Label notEmptyString; |
14547 | masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬EmptyString); |
14548 | { |
14549 | masm.movePtr(string, output); |
14550 | masm.jump(ool->rejoin()); |
14551 | } |
14552 | masm.bind(¬EmptyString); |
14553 | |
14554 | Register inputChars = temp1; |
14555 | masm.loadStringChars(string, inputChars, CharEncoding::Latin1); |
14556 | |
14557 | Register toLowerCaseTable = temp2; |
14558 | masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable); |
14559 | |
14560 | // Single element strings can be directly retrieved from static strings cache. |
14561 | Label notSingleElementString; |
14562 | masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleElementString); |
14563 | { |
14564 | Register current = temp4; |
14565 | |
14566 | masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); |
14567 | masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), |
14568 | current); |
14569 | masm.lookupStaticString(current, output, gen->runtime->staticStrings()); |
14570 | |
14571 | masm.jump(ool->rejoin()); |
14572 | } |
14573 | masm.bind(¬SingleElementString); |
14574 | |
14575 | // Use the OOL-path when the string is too long. This prevents scanning long |
14576 | // strings which have upper case characters only near the end a second time in |
14577 | // the VM. |
14578 | constexpr int32_t MaxInlineLength = 64; |
14579 | masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry()); |
14580 | |
14581 | { |
14582 | // Check if there are any characters which need to be converted. |
14583 | // |
14584 | // This extra loop gives a small performance improvement for strings which |
14585 | // are already lower cased and lets us avoid calling into the runtime for |
14586 | // non-inline, all lower case strings. But more importantly it avoids |
14587 | // repeated inline allocation failures: |
14588 | // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the |
14589 | // |js::StringToLowerCase| runtime function when the result string can't be |
14590 | // allocated inline. And |js::StringToLowerCase| directly returns the input |
14591 | // string when no characters need to be converted. That means it won't |
14592 | // trigger GC to clear up the free nursery space, so the next toLowerCase() |
14593 | // call will again fail to inline allocate the result string. |
14594 | Label hasUpper; |
14595 | { |
14596 | Register checkInputChars = output; |
14597 | masm.movePtr(inputChars, checkInputChars); |
14598 | |
14599 | Register current = temp4; |
14600 | |
14601 | Label start; |
14602 | masm.bind(&start); |
14603 | masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1); |
14604 | masm.branch8(Assembler::NotEqual, |
14605 | BaseIndex(toLowerCaseTable, current, TimesOne), current, |
14606 | &hasUpper); |
14607 | masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars); |
14608 | masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); |
14609 | |
14610 | // Input is already in lower case. |
14611 | masm.movePtr(string, output); |
14612 | masm.jump(ool->rejoin()); |
14613 | } |
14614 | masm.bind(&hasUpper); |
14615 | |
14616 | // |length| was clobbered above, reload. |
14617 | masm.loadStringLength(string, length); |
14618 | |
14619 | // Call into the runtime when we can't create an inline string. |
14620 | masm.branch32(Assembler::Above, length, |
14621 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry()); |
14622 | |
14623 | AllocateThinOrFatInlineString(masm, output, length, temp4, |
14624 | initialStringHeap(), ool->entry(), |
14625 | CharEncoding::Latin1); |
14626 | |
14627 | if (temp3 == string) { |
14628 | masm.push(string); |
14629 | } |
14630 | |
14631 | Register outputChars = temp3; |
14632 | masm.loadInlineStringCharsForStore(output, outputChars); |
14633 | |
14634 | { |
14635 | Register current = temp4; |
14636 | |
14637 | Label start; |
14638 | masm.bind(&start); |
14639 | masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); |
14640 | masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), |
14641 | current); |
14642 | masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1); |
14643 | masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars); |
14644 | masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars); |
14645 | masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); |
14646 | } |
14647 | |
14648 | if (temp3 == string) { |
14649 | masm.pop(string); |
14650 | } |
14651 | } |
14652 | |
14653 | masm.bind(ool->rejoin()); |
14654 | } |
14655 | |
14656 | void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) { |
14657 | pushArg(ToRegister(lir->string())); |
14658 | |
14659 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
14660 | callVM<Fn, js::StringToUpperCase>(lir); |
14661 | } |
14662 | |
14663 | void CodeGenerator::visitCharCodeToLowerCase(LCharCodeToLowerCase* lir) { |
14664 | Register code = ToRegister(lir->code()); |
14665 | Register output = ToRegister(lir->output()); |
14666 | Register temp = ToRegister(lir->temp0()); |
14667 | |
14668 | using Fn = JSString* (*)(JSContext*, int32_t); |
14669 | auto* ool = oolCallVM<Fn, jit::CharCodeToLowerCase>(lir, ArgList(code), |
14670 | StoreRegisterTo(output)); |
14671 | |
14672 | constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1; |
14673 | |
14674 | // OOL path if code >= NonLatin1Min. |
14675 | masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry()); |
14676 | |
14677 | // Convert to lower case. |
14678 | masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), temp); |
14679 | masm.load8ZeroExtend(BaseIndex(temp, code, TimesOne), temp); |
14680 | |
14681 | // Load static string for lower case character. |
14682 | masm.lookupStaticString(temp, output, gen->runtime->staticStrings()); |
14683 | |
14684 | masm.bind(ool->rejoin()); |
14685 | } |
14686 | |
14687 | void CodeGenerator::visitCharCodeToUpperCase(LCharCodeToUpperCase* lir) { |
14688 | Register code = ToRegister(lir->code()); |
14689 | Register output = ToRegister(lir->output()); |
14690 | Register temp = ToRegister(lir->temp0()); |
14691 | |
14692 | using Fn = JSString* (*)(JSContext*, int32_t); |
14693 | auto* ool = oolCallVM<Fn, jit::CharCodeToUpperCase>(lir, ArgList(code), |
14694 | StoreRegisterTo(output)); |
14695 | |
14696 | constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1; |
14697 | |
14698 | // OOL path if code >= NonLatin1Min. |
14699 | masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry()); |
14700 | |
14701 | // Most one element Latin-1 strings can be directly retrieved from the |
14702 | // static strings cache, except the following three characters: |
14703 | // |
14704 | // 1. ToUpper(U+00B5) = 0+039C |
14705 | // 2. ToUpper(U+00FF) = 0+0178 |
14706 | // 3. ToUpper(U+00DF) = 0+0053 0+0053 |
14707 | masm.branch32(Assembler::Equal, code, Imm32(unicode::MICRO_SIGN), |
14708 | ool->entry()); |
14709 | masm.branch32(Assembler::Equal, code, |
14710 | Imm32(unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS), |
14711 | ool->entry()); |
14712 | masm.branch32(Assembler::Equal, code, |
14713 | Imm32(unicode::LATIN_SMALL_LETTER_SHARP_S), ool->entry()); |
14714 | |
14715 | // Inline unicode::ToUpperCase (without the special case for ASCII characters) |
14716 | |
14717 | constexpr size_t shift = unicode::CharInfoShift; |
14718 | |
14719 | // code >> shift |
14720 | masm.move32(code, temp); |
14721 | masm.rshift32(Imm32(shift), temp); |
14722 | |
14723 | // index = index1[code >> shift]; |
14724 | masm.movePtr(ImmPtr(unicode::index1), output); |
14725 | masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp); |
14726 | |
14727 | // (code & ((1 << shift) - 1) |
14728 | masm.move32(code, output); |
14729 | masm.and32(Imm32((1 << shift) - 1), output); |
14730 | |
14731 | // (index << shift) + (code & ((1 << shift) - 1)) |
14732 | masm.lshift32(Imm32(shift), temp); |
14733 | masm.add32(output, temp); |
14734 | |
14735 | // index = index2[(index << shift) + (code & ((1 << shift) - 1))] |
14736 | masm.movePtr(ImmPtr(unicode::index2), output); |
14737 | masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp); |
14738 | |
14739 | // Compute |index * 6| through |(index * 3) * TimesTwo|. |
14740 | static_assert(sizeof(unicode::CharacterInfo) == 6); |
14741 | masm.mulBy3(temp, temp); |
14742 | |
14743 | // upperCase = js_charinfo[index].upperCase |
14744 | masm.movePtr(ImmPtr(unicode::js_charinfo), output); |
14745 | masm.load16ZeroExtend(BaseIndex(output, temp, TimesTwo, |
14746 | offsetof(unicode::CharacterInfo, upperCase)__builtin_offsetof(unicode::CharacterInfo, upperCase)), |
14747 | temp); |
14748 | |
14749 | // uint16_t(ch) + upperCase |
14750 | masm.add32(code, temp); |
14751 | |
14752 | // Clear any high bits added when performing the unsigned 16-bit addition |
14753 | // through a signed 32-bit addition. |
14754 | masm.move8ZeroExtend(temp, temp); |
14755 | |
14756 | // Load static string for upper case character. |
14757 | masm.lookupStaticString(temp, output, gen->runtime->staticStrings()); |
14758 | |
14759 | masm.bind(ool->rejoin()); |
14760 | } |
14761 | |
14762 | void CodeGenerator::visitStringTrimStartIndex(LStringTrimStartIndex* lir) { |
14763 | Register string = ToRegister(lir->string()); |
14764 | Register output = ToRegister(lir->output()); |
14765 | |
14766 | auto volatileRegs = liveVolatileRegs(lir); |
14767 | volatileRegs.takeUnchecked(output); |
14768 | |
14769 | masm.PushRegsInMask(volatileRegs); |
14770 | |
14771 | using Fn = int32_t (*)(const JSString*); |
14772 | masm.setupAlignedABICall(); |
14773 | masm.passABIArg(string); |
14774 | masm.callWithABI<Fn, jit::StringTrimStartIndex>(); |
14775 | masm.storeCallInt32Result(output); |
14776 | |
14777 | masm.PopRegsInMask(volatileRegs); |
14778 | } |
14779 | |
14780 | void CodeGenerator::visitStringTrimEndIndex(LStringTrimEndIndex* lir) { |
14781 | Register string = ToRegister(lir->string()); |
14782 | Register start = ToRegister(lir->start()); |
14783 | Register output = ToRegister(lir->output()); |
14784 | |
14785 | auto volatileRegs = liveVolatileRegs(lir); |
14786 | volatileRegs.takeUnchecked(output); |
14787 | |
14788 | masm.PushRegsInMask(volatileRegs); |
14789 | |
14790 | using Fn = int32_t (*)(const JSString*, int32_t); |
14791 | masm.setupAlignedABICall(); |
14792 | masm.passABIArg(string); |
14793 | masm.passABIArg(start); |
14794 | masm.callWithABI<Fn, jit::StringTrimEndIndex>(); |
14795 | masm.storeCallInt32Result(output); |
14796 | |
14797 | masm.PopRegsInMask(volatileRegs); |
14798 | } |
14799 | |
14800 | void CodeGenerator::visitStringSplit(LStringSplit* lir) { |
14801 | pushArg(Imm32(INT32_MAX(2147483647))); |
14802 | pushArg(ToRegister(lir->separator())); |
14803 | pushArg(ToRegister(lir->string())); |
14804 | |
14805 | using Fn = ArrayObject* (*)(JSContext*, HandleString, HandleString, uint32_t); |
14806 | callVM<Fn, js::StringSplitString>(lir); |
14807 | } |
14808 | |
14809 | void CodeGenerator::visitInitializedLength(LInitializedLength* lir) { |
14810 | Address initLength(ToRegister(lir->elements()), |
14811 | ObjectElements::offsetOfInitializedLength()); |
14812 | masm.load32(initLength, ToRegister(lir->output())); |
14813 | } |
14814 | |
14815 | void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) { |
14816 | Address initLength(ToRegister(lir->elements()), |
14817 | ObjectElements::offsetOfInitializedLength()); |
14818 | SetLengthFromIndex(masm, lir->index(), initLength); |
14819 | } |
14820 | |
14821 | void CodeGenerator::visitNotI(LNotI* lir) { |
14822 | Register input = ToRegister(lir->input()); |
14823 | Register output = ToRegister(lir->output()); |
14824 | |
14825 | masm.cmp32Set(Assembler::Equal, input, Imm32(0), output); |
14826 | } |
14827 | |
14828 | void CodeGenerator::visitNotIPtr(LNotIPtr* lir) { |
14829 | Register input = ToRegister(lir->input()); |
14830 | Register output = ToRegister(lir->output()); |
14831 | |
14832 | masm.cmpPtrSet(Assembler::Equal, input, ImmWord(0), output); |
14833 | } |
14834 | |
14835 | void CodeGenerator::visitNotI64(LNotI64* lir) { |
14836 | Register64 input = ToRegister64(lir->inputI64()); |
14837 | Register output = ToRegister(lir->output()); |
14838 | |
14839 | masm.cmp64Set(Assembler::Equal, input, Imm64(0), output); |
14840 | } |
14841 | |
14842 | void CodeGenerator::visitNotBI(LNotBI* lir) { |
14843 | Register input = ToRegister(lir->input()); |
14844 | Register output = ToRegister(lir->output()); |
14845 | |
14846 | masm.cmp32Set(Assembler::Equal, Address(input, BigInt::offsetOfLength()), |
14847 | Imm32(0), output); |
14848 | } |
14849 | |
14850 | void CodeGenerator::visitNotO(LNotO* lir) { |
14851 | Register objreg = ToRegister(lir->input()); |
14852 | Register output = ToRegister(lir->output()); |
14853 | |
14854 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
14855 | if (intact) { |
14856 | // Bug 1874905: It would be fantastic if this could be optimized out. |
14857 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
14858 | masm.move32(Imm32(0), output); |
14859 | } else { |
14860 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
14861 | addOutOfLineCode(ool, lir->mir()); |
14862 | |
14863 | Label* ifEmulatesUndefined = ool->label1(); |
14864 | Label* ifDoesntEmulateUndefined = ool->label2(); |
14865 | |
14866 | branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined, |
14867 | ifDoesntEmulateUndefined, output, ool); |
14868 | // fall through |
14869 | |
14870 | Label join; |
14871 | |
14872 | masm.move32(Imm32(0), output); |
14873 | masm.jump(&join); |
14874 | |
14875 | masm.bind(ifEmulatesUndefined); |
14876 | masm.move32(Imm32(1), output); |
14877 | |
14878 | masm.bind(&join); |
14879 | } |
14880 | } |
14881 | |
14882 | void CodeGenerator::visitNotV(LNotV* lir) { |
14883 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
14884 | addOutOfLineCode(ool, lir->mir()); |
14885 | |
14886 | Label* ifTruthy = ool->label1(); |
14887 | Label* ifFalsy = ool->label2(); |
14888 | |
14889 | ValueOperand input = ToValue(lir, LNotV::InputIndex); |
14890 | Register tempToUnbox = ToTempUnboxRegister(lir->temp1()); |
14891 | FloatRegister floatTemp = ToFloatRegister(lir->temp0()); |
14892 | Register output = ToRegister(lir->output()); |
14893 | const TypeDataList& observedTypes = lir->mir()->observedTypes(); |
14894 | |
14895 | testValueTruthy(input, tempToUnbox, output, floatTemp, observedTypes, |
14896 | ifTruthy, ifFalsy, ool); |
14897 | |
14898 | Label join; |
14899 | |
14900 | // Note that the testValueTruthy call above may choose to fall through |
14901 | // to ifTruthy instead of branching there. |
14902 | masm.bind(ifTruthy); |
14903 | masm.move32(Imm32(0), output); |
14904 | masm.jump(&join); |
14905 | |
14906 | masm.bind(ifFalsy); |
14907 | masm.move32(Imm32(1), output); |
14908 | |
14909 | // both branches meet here. |
14910 | masm.bind(&join); |
14911 | } |
14912 | |
14913 | void CodeGenerator::visitBoundsCheck(LBoundsCheck* lir) { |
14914 | const LAllocation* index = lir->index(); |
14915 | const LAllocation* length = lir->length(); |
14916 | LSnapshot* snapshot = lir->snapshot(); |
14917 | |
14918 | MIRType type = lir->mir()->type(); |
14919 | |
14920 | auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) { |
14921 | if (type == MIRType::Int32) { |
14922 | bailoutCmp32(cond, lhs, rhs, snapshot); |
14923 | } else { |
14924 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14924); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14924; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14925 | bailoutCmpPtr(cond, lhs, rhs, snapshot); |
14926 | } |
14927 | }; |
14928 | |
14929 | auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs, |
14930 | int32_t rhs) { |
14931 | if (type == MIRType::Int32) { |
14932 | bailoutCmp32(cond, lhs, Imm32(rhs), snapshot); |
14933 | } else { |
14934 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14934; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14935 | bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot); |
14936 | } |
14937 | }; |
14938 | |
14939 | if (index->isConstant()) { |
14940 | // Use uint32 so that the comparison is unsigned. |
14941 | uint32_t idx = ToInt32(index); |
14942 | if (length->isConstant()) { |
14943 | uint32_t len = ToInt32(lir->length()); |
14944 | if (idx < len) { |
14945 | return; |
14946 | } |
14947 | bailout(snapshot); |
14948 | return; |
14949 | } |
14950 | |
14951 | if (length->isRegister()) { |
14952 | bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), idx); |
14953 | } else { |
14954 | bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), idx); |
14955 | } |
14956 | return; |
14957 | } |
14958 | |
14959 | Register indexReg = ToRegister(index); |
14960 | if (length->isConstant()) { |
14961 | bailoutCmpConstant(Assembler::AboveOrEqual, indexReg, ToInt32(length)); |
14962 | } else if (length->isRegister()) { |
14963 | bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), indexReg); |
14964 | } else { |
14965 | bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), indexReg); |
14966 | } |
14967 | } |
14968 | |
14969 | void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) { |
14970 | int32_t min = lir->mir()->minimum(); |
14971 | int32_t max = lir->mir()->maximum(); |
14972 | MOZ_ASSERT(max >= min)do { static_assert( mozilla::detail::AssertionConditionType< decltype(max >= min)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(max >= min))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("max >= min", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "max >= min" ")"); do { *((volatile int*)__null) = 14972; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14973 | |
14974 | LSnapshot* snapshot = lir->snapshot(); |
14975 | MIRType type = lir->mir()->type(); |
14976 | |
14977 | const LAllocation* length = lir->length(); |
14978 | Register temp = ToRegister(lir->getTemp(0)); |
14979 | |
14980 | auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) { |
14981 | if (type == MIRType::Int32) { |
14982 | bailoutCmp32(cond, lhs, rhs, snapshot); |
14983 | } else { |
14984 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14984; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14985 | bailoutCmpPtr(cond, lhs, rhs, snapshot); |
14986 | } |
14987 | }; |
14988 | |
14989 | auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs, |
14990 | int32_t rhs) { |
14991 | if (type == MIRType::Int32) { |
14992 | bailoutCmp32(cond, lhs, Imm32(rhs), snapshot); |
14993 | } else { |
14994 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14994; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14995 | bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot); |
14996 | } |
14997 | }; |
14998 | |
14999 | if (lir->index()->isConstant()) { |
15000 | int32_t nmin, nmax; |
15001 | int32_t index = ToInt32(lir->index()); |
15002 | if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) { |
15003 | if (length->isRegister()) { |
15004 | bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax); |
15005 | } else { |
15006 | bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), nmax); |
15007 | } |
15008 | return; |
15009 | } |
15010 | masm.mov(ImmWord(index), temp); |
15011 | } else { |
15012 | masm.mov(ToRegister(lir->index()), temp); |
15013 | } |
15014 | |
15015 | // If the minimum and maximum differ then do an underflow check first. |
15016 | // If the two are the same then doing an unsigned comparison on the |
15017 | // length will also catch a negative index. |
15018 | if (min != max) { |
15019 | if (min != 0) { |
15020 | Label bail; |
15021 | if (type == MIRType::Int32) { |
15022 | masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail); |
15023 | } else { |
15024 | masm.branchAddPtr(Assembler::Overflow, Imm32(min), temp, &bail); |
15025 | } |
15026 | bailoutFrom(&bail, snapshot); |
15027 | } |
15028 | |
15029 | bailoutCmpConstant(Assembler::LessThan, temp, 0); |
15030 | |
15031 | if (min != 0) { |
15032 | int32_t diff; |
15033 | if (SafeSub(max, min, &diff)) { |
15034 | max = diff; |
15035 | } else { |
15036 | if (type == MIRType::Int32) { |
15037 | masm.sub32(Imm32(min), temp); |
15038 | } else { |
15039 | masm.subPtr(Imm32(min), temp); |
15040 | } |
15041 | } |
15042 | } |
15043 | } |
15044 | |
15045 | // Compute the maximum possible index. No overflow check is needed when |
15046 | // max > 0. We can only wraparound to a negative number, which will test as |
15047 | // larger than all nonnegative numbers in the unsigned comparison, and the |
15048 | // length is required to be nonnegative (else testing a negative length |
15049 | // would succeed on any nonnegative index). |
15050 | if (max != 0) { |
15051 | if (max < 0) { |
15052 | Label bail; |
15053 | if (type == MIRType::Int32) { |
15054 | masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail); |
15055 | } else { |
15056 | masm.branchAddPtr(Assembler::Overflow, Imm32(max), temp, &bail); |
15057 | } |
15058 | bailoutFrom(&bail, snapshot); |
15059 | } else { |
15060 | if (type == MIRType::Int32) { |
15061 | masm.add32(Imm32(max), temp); |
15062 | } else { |
15063 | masm.addPtr(Imm32(max), temp); |
15064 | } |
15065 | } |
15066 | } |
15067 | |
15068 | if (length->isRegister()) { |
15069 | bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), temp); |
15070 | } else { |
15071 | bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), temp); |
15072 | } |
15073 | } |
15074 | |
15075 | void CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir) { |
15076 | int32_t min = lir->mir()->minimum(); |
15077 | bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min), |
15078 | lir->snapshot()); |
15079 | } |
15080 | |
15081 | void CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir) { |
15082 | MOZ_ASSERT(JitOptions.spectreIndexMasking)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitOptions.spectreIndexMasking)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitOptions.spectreIndexMasking ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "JitOptions.spectreIndexMasking", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15082); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitOptions.spectreIndexMasking" ")"); do { *((volatile int*)__null) = 15082; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15083 | |
15084 | const LAllocation* length = lir->length(); |
15085 | Register index = ToRegister(lir->index()); |
15086 | Register output = ToRegister(lir->output()); |
15087 | |
15088 | if (lir->mir()->type() == MIRType::Int32) { |
15089 | if (length->isRegister()) { |
15090 | masm.spectreMaskIndex32(index, ToRegister(length), output); |
15091 | } else { |
15092 | masm.spectreMaskIndex32(index, ToAddress(length), output); |
15093 | } |
15094 | } else { |
15095 | MOZ_ASSERT(lir->mir()->type() == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::IntPtr)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::IntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15095); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 15095; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15096 | if (length->isRegister()) { |
15097 | masm.spectreMaskIndexPtr(index, ToRegister(length), output); |
15098 | } else { |
15099 | masm.spectreMaskIndexPtr(index, ToAddress(length), output); |
15100 | } |
15101 | } |
15102 | } |
15103 | |
15104 | class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator> { |
15105 | LInstruction* ins_; |
15106 | |
15107 | public: |
15108 | explicit OutOfLineStoreElementHole(LInstruction* ins) : ins_(ins) { |
15109 | MOZ_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->isStoreElementHoleV() || ins->isStoreElementHoleT ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ins->isStoreElementHoleV() || ins->isStoreElementHoleT ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ins->isStoreElementHoleV() || ins->isStoreElementHoleT()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->isStoreElementHoleV() || ins->isStoreElementHoleT()" ")"); do { *((volatile int*)__null) = 15109; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15110 | } |
15111 | |
15112 | void accept(CodeGenerator* codegen) override { |
15113 | codegen->visitOutOfLineStoreElementHole(this); |
15114 | } |
15115 | |
15116 | MStoreElementHole* mir() const { |
15117 | return ins_->isStoreElementHoleV() ? ins_->toStoreElementHoleV()->mir() |
15118 | : ins_->toStoreElementHoleT()->mir(); |
15119 | } |
15120 | LInstruction* ins() const { return ins_; } |
15121 | }; |
15122 | |
15123 | void CodeGenerator::emitStoreHoleCheck(Register elements, |
15124 | const LAllocation* index, |
15125 | LSnapshot* snapshot) { |
15126 | Label bail; |
15127 | if (index->isConstant()) { |
15128 | Address dest(elements, ToInt32(index) * sizeof(js::Value)); |
15129 | masm.branchTestMagic(Assembler::Equal, dest, &bail); |
15130 | } else { |
15131 | BaseObjectElementIndex dest(elements, ToRegister(index)); |
15132 | masm.branchTestMagic(Assembler::Equal, dest, &bail); |
15133 | } |
15134 | bailoutFrom(&bail, snapshot); |
15135 | } |
15136 | |
15137 | void CodeGenerator::emitStoreElementTyped(const LAllocation* value, |
15138 | MIRType valueType, Register elements, |
15139 | const LAllocation* index) { |
15140 | MOZ_ASSERT(valueType != MIRType::MagicHole)do { static_assert( mozilla::detail::AssertionConditionType< decltype(valueType != MIRType::MagicHole)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(valueType != MIRType::MagicHole ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "valueType != MIRType::MagicHole", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15140); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole" ")"); do { *((volatile int*)__null) = 15140; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15141 | ConstantOrRegister v = ToConstantOrRegister(value, valueType); |
15142 | if (index->isConstant()) { |
15143 | Address dest(elements, ToInt32(index) * sizeof(js::Value)); |
15144 | masm.storeUnboxedValue(v, valueType, dest); |
15145 | } else { |
15146 | BaseObjectElementIndex dest(elements, ToRegister(index)); |
15147 | masm.storeUnboxedValue(v, valueType, dest); |
15148 | } |
15149 | } |
15150 | |
15151 | void CodeGenerator::visitStoreElementT(LStoreElementT* store) { |
15152 | Register elements = ToRegister(store->elements()); |
15153 | const LAllocation* index = store->index(); |
15154 | |
15155 | if (store->mir()->needsBarrier()) { |
15156 | emitPreBarrier(elements, index); |
15157 | } |
15158 | |
15159 | if (store->mir()->needsHoleCheck()) { |
15160 | emitStoreHoleCheck(elements, index, store->snapshot()); |
15161 | } |
15162 | |
15163 | emitStoreElementTyped(store->value(), store->mir()->value()->type(), elements, |
15164 | index); |
15165 | } |
15166 | |
15167 | void CodeGenerator::visitStoreElementV(LStoreElementV* lir) { |
15168 | const ValueOperand value = ToValue(lir, LStoreElementV::Value); |
15169 | Register elements = ToRegister(lir->elements()); |
15170 | const LAllocation* index = lir->index(); |
15171 | |
15172 | if (lir->mir()->needsBarrier()) { |
15173 | emitPreBarrier(elements, index); |
15174 | } |
15175 | |
15176 | if (lir->mir()->needsHoleCheck()) { |
15177 | emitStoreHoleCheck(elements, index, lir->snapshot()); |
15178 | } |
15179 | |
15180 | if (lir->index()->isConstant()) { |
15181 | Address dest(elements, ToInt32(lir->index()) * sizeof(js::Value)); |
15182 | masm.storeValue(value, dest); |
15183 | } else { |
15184 | BaseObjectElementIndex dest(elements, ToRegister(lir->index())); |
15185 | masm.storeValue(value, dest); |
15186 | } |
15187 | } |
15188 | |
15189 | void CodeGenerator::visitStoreHoleValueElement(LStoreHoleValueElement* lir) { |
15190 | Register elements = ToRegister(lir->elements()); |
15191 | Register index = ToRegister(lir->index()); |
15192 | |
15193 | Address elementsFlags(elements, ObjectElements::offsetOfFlags()); |
15194 | masm.or32(Imm32(ObjectElements::NON_PACKED), elementsFlags); |
15195 | |
15196 | BaseObjectElementIndex element(elements, index); |
15197 | masm.storeValue(MagicValue(JS_ELEMENTS_HOLE), element); |
15198 | } |
15199 | |
15200 | void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) { |
15201 | auto* ool = new (alloc()) OutOfLineStoreElementHole(lir); |
15202 | addOutOfLineCode(ool, lir->mir()); |
15203 | |
15204 | Register obj = ToRegister(lir->object()); |
15205 | Register elements = ToRegister(lir->elements()); |
15206 | Register index = ToRegister(lir->index()); |
15207 | Register temp = ToRegister(lir->temp0()); |
15208 | |
15209 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
15210 | masm.spectreBoundsCheck32(index, initLength, temp, ool->entry()); |
15211 | |
15212 | emitPreBarrier(elements, lir->index()); |
15213 | |
15214 | masm.bind(ool->rejoin()); |
15215 | emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), elements, |
15216 | lir->index()); |
15217 | |
15218 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
15219 | LiveRegisterSet regs = liveVolatileRegs(lir); |
15220 | ConstantOrRegister val = |
15221 | ToConstantOrRegister(lir->value(), lir->mir()->value()->type()); |
15222 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp, val); |
15223 | } |
15224 | } |
15225 | |
15226 | void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) { |
15227 | auto* ool = new (alloc()) OutOfLineStoreElementHole(lir); |
15228 | addOutOfLineCode(ool, lir->mir()); |
15229 | |
15230 | Register obj = ToRegister(lir->object()); |
15231 | Register elements = ToRegister(lir->elements()); |
15232 | Register index = ToRegister(lir->index()); |
15233 | const ValueOperand value = ToValue(lir, LStoreElementHoleV::ValueIndex); |
15234 | Register temp = ToRegister(lir->temp0()); |
15235 | |
15236 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
15237 | masm.spectreBoundsCheck32(index, initLength, temp, ool->entry()); |
15238 | |
15239 | emitPreBarrier(elements, lir->index()); |
15240 | |
15241 | masm.bind(ool->rejoin()); |
15242 | masm.storeValue(value, BaseObjectElementIndex(elements, index)); |
15243 | |
15244 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
15245 | LiveRegisterSet regs = liveVolatileRegs(lir); |
15246 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp, |
15247 | ConstantOrRegister(value)); |
15248 | } |
15249 | } |
15250 | |
15251 | void CodeGenerator::visitOutOfLineStoreElementHole( |
15252 | OutOfLineStoreElementHole* ool) { |
15253 | Register object, elements, index; |
15254 | LInstruction* ins = ool->ins(); |
15255 | mozilla::Maybe<ConstantOrRegister> value; |
15256 | Register temp; |
15257 | |
15258 | if (ins->isStoreElementHoleV()) { |
15259 | LStoreElementHoleV* store = ins->toStoreElementHoleV(); |
15260 | object = ToRegister(store->object()); |
15261 | elements = ToRegister(store->elements()); |
15262 | index = ToRegister(store->index()); |
15263 | value.emplace( |
15264 | TypedOrValueRegister(ToValue(store, LStoreElementHoleV::ValueIndex))); |
15265 | temp = ToRegister(store->temp0()); |
15266 | } else { |
15267 | LStoreElementHoleT* store = ins->toStoreElementHoleT(); |
15268 | object = ToRegister(store->object()); |
15269 | elements = ToRegister(store->elements()); |
15270 | index = ToRegister(store->index()); |
15271 | if (store->value()->isConstant()) { |
15272 | value.emplace( |
15273 | ConstantOrRegister(store->value()->toConstant()->toJSValue())); |
15274 | } else { |
15275 | MIRType valueType = store->mir()->value()->type(); |
15276 | value.emplace( |
15277 | TypedOrValueRegister(valueType, ToAnyRegister(store->value()))); |
15278 | } |
15279 | temp = ToRegister(store->temp0()); |
15280 | } |
15281 | |
15282 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
15283 | |
15284 | // We're out-of-bounds. We only handle the index == initlength case. |
15285 | // If index > initializedLength, bail out. Note that this relies on the |
15286 | // condition flags sticking from the incoming branch. |
15287 | // Also note: this branch does not need Spectre mitigations, doing that for |
15288 | // the capacity check below is sufficient. |
15289 | Label allocElement, addNewElement; |
15290 | #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ |
15291 | defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) |
15292 | // Had to reimplement for MIPS because there are no flags. |
15293 | bailoutCmp32(Assembler::NotEqual, initLength, index, ins->snapshot()); |
15294 | #else |
15295 | bailoutIf(Assembler::NotEqual, ins->snapshot()); |
15296 | #endif |
15297 | |
15298 | // If index < capacity, we can add a dense element inline. If not, we need |
15299 | // to allocate more elements first. |
15300 | masm.spectreBoundsCheck32( |
15301 | index, Address(elements, ObjectElements::offsetOfCapacity()), temp, |
15302 | &allocElement); |
15303 | masm.jump(&addNewElement); |
15304 | |
15305 | masm.bind(&allocElement); |
15306 | |
15307 | // Save all live volatile registers, except |temp|. |
15308 | LiveRegisterSet liveRegs = liveVolatileRegs(ins); |
15309 | liveRegs.takeUnchecked(temp); |
15310 | masm.PushRegsInMask(liveRegs); |
15311 | |
15312 | masm.setupAlignedABICall(); |
15313 | masm.loadJSContext(temp); |
15314 | masm.passABIArg(temp); |
15315 | masm.passABIArg(object); |
15316 | |
15317 | using Fn = bool (*)(JSContext*, NativeObject*); |
15318 | masm.callWithABI<Fn, NativeObject::addDenseElementPure>(); |
15319 | masm.storeCallPointerResult(temp); |
15320 | |
15321 | masm.PopRegsInMask(liveRegs); |
15322 | bailoutIfFalseBool(temp, ins->snapshot()); |
15323 | |
15324 | // Load the reallocated elements pointer. |
15325 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elements); |
15326 | |
15327 | masm.bind(&addNewElement); |
15328 | |
15329 | // Increment initLength |
15330 | masm.add32(Imm32(1), initLength); |
15331 | |
15332 | // If length is now <= index, increment length too. |
15333 | Label skipIncrementLength; |
15334 | Address length(elements, ObjectElements::offsetOfLength()); |
15335 | masm.branch32(Assembler::Above, length, index, &skipIncrementLength); |
15336 | masm.add32(Imm32(1), length); |
15337 | masm.bind(&skipIncrementLength); |
15338 | |
15339 | // Jump to the inline path where we will store the value. |
15340 | // We rejoin after the prebarrier, because the memory is uninitialized. |
15341 | masm.jump(ool->rejoin()); |
15342 | } |
15343 | |
15344 | void CodeGenerator::visitArrayPopShift(LArrayPopShift* lir) { |
15345 | Register obj = ToRegister(lir->object()); |
15346 | Register temp1 = ToRegister(lir->temp0()); |
15347 | Register temp2 = ToRegister(lir->temp1()); |
15348 | ValueOperand out = ToOutValue(lir); |
15349 | |
15350 | Label bail; |
15351 | if (lir->mir()->mode() == MArrayPopShift::Pop) { |
15352 | masm.packedArrayPop(obj, out, temp1, temp2, &bail); |
15353 | } else { |
15354 | MOZ_ASSERT(lir->mir()->mode() == MArrayPopShift::Shift)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->mode() == MArrayPopShift::Shift)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->mode() == MArrayPopShift::Shift))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->mode() == MArrayPopShift::Shift" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MArrayPopShift::Shift" ")"); do { *((volatile int*)__null) = 15354; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15355 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
15356 | masm.packedArrayShift(obj, out, temp1, temp2, volatileRegs, &bail); |
15357 | } |
15358 | bailoutFrom(&bail, lir->snapshot()); |
15359 | } |
15360 | |
15361 | class OutOfLineArrayPush : public OutOfLineCodeBase<CodeGenerator> { |
15362 | LArrayPush* ins_; |
15363 | |
15364 | public: |
15365 | explicit OutOfLineArrayPush(LArrayPush* ins) : ins_(ins) {} |
15366 | |
15367 | void accept(CodeGenerator* codegen) override { |
15368 | codegen->visitOutOfLineArrayPush(this); |
15369 | } |
15370 | |
15371 | LArrayPush* ins() const { return ins_; } |
15372 | }; |
15373 | |
15374 | void CodeGenerator::visitArrayPush(LArrayPush* lir) { |
15375 | Register obj = ToRegister(lir->object()); |
15376 | Register elementsTemp = ToRegister(lir->temp0()); |
15377 | Register length = ToRegister(lir->output()); |
15378 | ValueOperand value = ToValue(lir, LArrayPush::ValueIndex); |
15379 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
15380 | |
15381 | auto* ool = new (alloc()) OutOfLineArrayPush(lir); |
15382 | addOutOfLineCode(ool, lir->mir()); |
15383 | |
15384 | // Load obj->elements in elementsTemp. |
15385 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); |
15386 | |
15387 | Address initLengthAddr(elementsTemp, |
15388 | ObjectElements::offsetOfInitializedLength()); |
15389 | Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength()); |
15390 | Address capacityAddr(elementsTemp, ObjectElements::offsetOfCapacity()); |
15391 | |
15392 | // Bail out if length != initLength. |
15393 | masm.load32(lengthAddr, length); |
15394 | bailoutCmp32(Assembler::NotEqual, initLengthAddr, length, lir->snapshot()); |
15395 | |
15396 | // If length < capacity, we can add a dense element inline. If not, we |
15397 | // need to allocate more elements. |
15398 | masm.spectreBoundsCheck32(length, capacityAddr, spectreTemp, ool->entry()); |
15399 | masm.bind(ool->rejoin()); |
15400 | |
15401 | // Store the value. |
15402 | masm.storeValue(value, BaseObjectElementIndex(elementsTemp, length)); |
15403 | |
15404 | // Update length and initialized length. |
15405 | masm.add32(Imm32(1), length); |
15406 | masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); |
15407 | masm.store32(length, Address(elementsTemp, |
15408 | ObjectElements::offsetOfInitializedLength())); |
15409 | |
15410 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
15411 | LiveRegisterSet regs = liveVolatileRegs(lir); |
15412 | regs.addUnchecked(length); |
15413 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->output()->output(), |
15414 | elementsTemp, ConstantOrRegister(value), |
15415 | /* indexDiff = */ -1); |
15416 | } |
15417 | } |
15418 | |
15419 | void CodeGenerator::visitOutOfLineArrayPush(OutOfLineArrayPush* ool) { |
15420 | LArrayPush* ins = ool->ins(); |
15421 | |
15422 | Register object = ToRegister(ins->object()); |
15423 | Register temp = ToRegister(ins->temp0()); |
15424 | |
15425 | LiveRegisterSet liveRegs = liveVolatileRegs(ins); |
15426 | liveRegs.takeUnchecked(temp); |
15427 | liveRegs.addUnchecked(ToRegister(ins->output())); |
15428 | liveRegs.addUnchecked(ToValue(ins, LArrayPush::ValueIndex)); |
15429 | |
15430 | masm.PushRegsInMask(liveRegs); |
15431 | |
15432 | masm.setupAlignedABICall(); |
15433 | masm.loadJSContext(temp); |
15434 | masm.passABIArg(temp); |
15435 | masm.passABIArg(object); |
15436 | |
15437 | using Fn = bool (*)(JSContext*, NativeObject* obj); |
15438 | masm.callWithABI<Fn, NativeObject::addDenseElementPure>(); |
15439 | masm.storeCallPointerResult(temp); |
15440 | |
15441 | masm.PopRegsInMask(liveRegs); |
15442 | bailoutIfFalseBool(temp, ins->snapshot()); |
15443 | |
15444 | // Load the reallocated elements pointer. |
15445 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
15446 | |
15447 | masm.jump(ool->rejoin()); |
15448 | } |
15449 | |
15450 | void CodeGenerator::visitArraySlice(LArraySlice* lir) { |
15451 | Register object = ToRegister(lir->object()); |
15452 | Register begin = ToRegister(lir->begin()); |
15453 | Register end = ToRegister(lir->end()); |
15454 | Register temp0 = ToRegister(lir->temp0()); |
15455 | Register temp1 = ToRegister(lir->temp1()); |
15456 | |
15457 | Label call, fail; |
15458 | |
15459 | Label bail; |
15460 | masm.branchArrayIsNotPacked(object, temp0, temp1, &bail); |
15461 | bailoutFrom(&bail, lir->snapshot()); |
15462 | |
15463 | // Try to allocate an object. |
15464 | TemplateObject templateObject(lir->mir()->templateObj()); |
15465 | masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(), |
15466 | &fail); |
15467 | |
15468 | masm.jump(&call); |
15469 | { |
15470 | masm.bind(&fail); |
15471 | masm.movePtr(ImmPtr(nullptr), temp0); |
15472 | } |
15473 | masm.bind(&call); |
15474 | |
15475 | pushArg(temp0); |
15476 | pushArg(end); |
15477 | pushArg(begin); |
15478 | pushArg(object); |
15479 | |
15480 | using Fn = |
15481 | JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); |
15482 | callVM<Fn, ArraySliceDense>(lir); |
15483 | } |
15484 | |
15485 | void CodeGenerator::visitArgumentsSlice(LArgumentsSlice* lir) { |
15486 | Register object = ToRegister(lir->object()); |
15487 | Register begin = ToRegister(lir->begin()); |
15488 | Register end = ToRegister(lir->end()); |
15489 | Register temp0 = ToRegister(lir->temp0()); |
15490 | Register temp1 = ToRegister(lir->temp1()); |
15491 | |
15492 | Label call, fail; |
15493 | |
15494 | // Try to allocate an object. |
15495 | TemplateObject templateObject(lir->mir()->templateObj()); |
15496 | masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(), |
15497 | &fail); |
15498 | |
15499 | masm.jump(&call); |
15500 | { |
15501 | masm.bind(&fail); |
15502 | masm.movePtr(ImmPtr(nullptr), temp0); |
15503 | } |
15504 | masm.bind(&call); |
15505 | |
15506 | pushArg(temp0); |
15507 | pushArg(end); |
15508 | pushArg(begin); |
15509 | pushArg(object); |
15510 | |
15511 | using Fn = |
15512 | JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); |
15513 | callVM<Fn, ArgumentsSliceDense>(lir); |
15514 | } |
15515 | |
15516 | #ifdef DEBUG1 |
15517 | void CodeGenerator::emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin, |
15518 | const RegisterOrInt32& count, |
15519 | Register numActualArgs) { |
15520 | // |begin| must be positive or zero. |
15521 | if (begin.is<Register>()) { |
15522 | Label beginOk; |
15523 | masm.branch32(Assembler::GreaterThanOrEqual, begin.as<Register>(), Imm32(0), |
15524 | &beginOk); |
15525 | masm.assumeUnreachable("begin < 0"); |
15526 | masm.bind(&beginOk); |
15527 | } else { |
15528 | MOZ_ASSERT(begin.as<int32_t>() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(begin.as<int32_t>() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(begin.as<int32_t>() >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("begin.as<int32_t>() >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15528); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() >= 0" ")"); do { *((volatile int*)__null) = 15528; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15529 | } |
15530 | |
15531 | // |count| must be positive or zero. |
15532 | if (count.is<Register>()) { |
15533 | Label countOk; |
15534 | masm.branch32(Assembler::GreaterThanOrEqual, count.as<Register>(), Imm32(0), |
15535 | &countOk); |
15536 | masm.assumeUnreachable("count < 0"); |
15537 | masm.bind(&countOk); |
15538 | } else { |
15539 | MOZ_ASSERT(count.as<int32_t>() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(count.as<int32_t>() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(count.as<int32_t>() >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("count.as<int32_t>() >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15539); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count.as<int32_t>() >= 0" ")"); do { *((volatile int*)__null) = 15539; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15540 | } |
15541 | |
15542 | // |begin| must be less-or-equal to |numActualArgs|. |
15543 | Label argsBeginOk; |
15544 | if (begin.is<Register>()) { |
15545 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(), |
15546 | &argsBeginOk); |
15547 | } else { |
15548 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
15549 | Imm32(begin.as<int32_t>()), &argsBeginOk); |
15550 | } |
15551 | masm.assumeUnreachable("begin <= numActualArgs"); |
15552 | masm.bind(&argsBeginOk); |
15553 | |
15554 | // |count| must be less-or-equal to |numActualArgs|. |
15555 | Label argsCountOk; |
15556 | if (count.is<Register>()) { |
15557 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, count.as<Register>(), |
15558 | &argsCountOk); |
15559 | } else { |
15560 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
15561 | Imm32(count.as<int32_t>()), &argsCountOk); |
15562 | } |
15563 | masm.assumeUnreachable("count <= numActualArgs"); |
15564 | masm.bind(&argsCountOk); |
15565 | |
15566 | // |begin| and |count| must be preserved, but |numActualArgs| can be changed. |
15567 | // |
15568 | // Pre-condition: |count| <= |numActualArgs| |
15569 | // Condition to test: |begin + count| <= |numActualArgs| |
15570 | // Transform to: |begin| <= |numActualArgs - count| |
15571 | if (count.is<Register>()) { |
15572 | masm.subPtr(count.as<Register>(), numActualArgs); |
15573 | } else { |
15574 | masm.subPtr(Imm32(count.as<int32_t>()), numActualArgs); |
15575 | } |
15576 | |
15577 | // |begin + count| must be less-or-equal to |numActualArgs|. |
15578 | Label argsBeginCountOk; |
15579 | if (begin.is<Register>()) { |
15580 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(), |
15581 | &argsBeginCountOk); |
15582 | } else { |
15583 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
15584 | Imm32(begin.as<int32_t>()), &argsBeginCountOk); |
15585 | } |
15586 | masm.assumeUnreachable("begin + count <= numActualArgs"); |
15587 | masm.bind(&argsBeginCountOk); |
15588 | } |
15589 | #endif |
15590 | |
15591 | template <class ArgumentsSlice> |
15592 | void CodeGenerator::emitNewArray(ArgumentsSlice* lir, |
15593 | const RegisterOrInt32& count, Register output, |
15594 | Register temp) { |
15595 | using Fn = ArrayObject* (*)(JSContext*, int32_t); |
15596 | auto* ool = count.match( |
15597 | [&](Register count) { |
15598 | return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>( |
15599 | lir, ArgList(count), StoreRegisterTo(output)); |
15600 | }, |
15601 | [&](int32_t count) { |
15602 | return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>( |
15603 | lir, ArgList(Imm32(count)), StoreRegisterTo(output)); |
15604 | }); |
15605 | |
15606 | TemplateObject templateObject(lir->mir()->templateObj()); |
15607 | MOZ_ASSERT(templateObject.isArrayObject())do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateObject.isArrayObject())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(templateObject.isArrayObject ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("templateObject.isArrayObject()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15607); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject.isArrayObject()" ")"); do { *((volatile int*)__null) = 15607; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15608 | |
15609 | auto templateNativeObj = templateObject.asTemplateNativeObject(); |
15610 | MOZ_ASSERT(templateNativeObj.getArrayLength() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateNativeObj.getArrayLength() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(templateNativeObj.getArrayLength() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("templateNativeObj.getArrayLength() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15610); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getArrayLength() == 0" ")"); do { *((volatile int*)__null) = 15610; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15611 | MOZ_ASSERT(templateNativeObj.getDenseInitializedLength() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateNativeObj.getDenseInitializedLength() == 0)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(templateNativeObj.getDenseInitializedLength() == 0)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("templateNativeObj.getDenseInitializedLength() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15611); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getDenseInitializedLength() == 0" ")"); do { *((volatile int*)__null) = 15611; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15612 | MOZ_ASSERT(!templateNativeObj.hasDynamicElements())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!templateNativeObj.hasDynamicElements())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(!templateNativeObj.hasDynamicElements()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!templateNativeObj.hasDynamicElements()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15612); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateNativeObj.hasDynamicElements()" ")"); do { *((volatile int*)__null) = 15612; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15613 | |
15614 | // Check array capacity. Call into the VM if the template object's capacity |
15615 | // is too small. |
15616 | bool tryAllocate = count.match( |
15617 | [&](Register count) { |
15618 | masm.branch32(Assembler::Above, count, |
15619 | Imm32(templateNativeObj.getDenseCapacity()), |
15620 | ool->entry()); |
15621 | return true; |
15622 | }, |
15623 | [&](int32_t count) { |
15624 | MOZ_ASSERT(count >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(count >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(count >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("count >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count >= 0" ")"); do { *((volatile int*)__null) = 15624; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15625 | if (uint32_t(count) > templateNativeObj.getDenseCapacity()) { |
15626 | masm.jump(ool->entry()); |
15627 | return false; |
15628 | } |
15629 | return true; |
15630 | }); |
15631 | |
15632 | if (tryAllocate) { |
15633 | // Try to allocate an object. |
15634 | masm.createGCObject(output, temp, templateObject, lir->mir()->initialHeap(), |
15635 | ool->entry()); |
15636 | |
15637 | auto setInitializedLengthAndLength = [&](auto count) { |
15638 | const int elementsOffset = NativeObject::offsetOfFixedElements(); |
15639 | |
15640 | // Update initialized length. |
15641 | Address initLength( |
15642 | output, elementsOffset + ObjectElements::offsetOfInitializedLength()); |
15643 | masm.store32(count, initLength); |
15644 | |
15645 | // Update length. |
15646 | Address length(output, elementsOffset + ObjectElements::offsetOfLength()); |
15647 | masm.store32(count, length); |
15648 | }; |
15649 | |
15650 | // The array object was successfully created. Set the length and initialized |
15651 | // length and then proceed to fill the elements. |
15652 | count.match([&](Register count) { setInitializedLengthAndLength(count); }, |
15653 | [&](int32_t count) { |
15654 | if (count > 0) { |
15655 | setInitializedLengthAndLength(Imm32(count)); |
15656 | } |
15657 | }); |
15658 | } |
15659 | |
15660 | masm.bind(ool->rejoin()); |
15661 | } |
15662 | |
15663 | void CodeGenerator::visitFrameArgumentsSlice(LFrameArgumentsSlice* lir) { |
15664 | Register begin = ToRegister(lir->begin()); |
15665 | Register count = ToRegister(lir->count()); |
15666 | Register temp = ToRegister(lir->temp0()); |
15667 | Register output = ToRegister(lir->output()); |
15668 | |
15669 | #ifdef DEBUG1 |
15670 | masm.loadNumActualArgs(FramePointer, temp); |
15671 | emitAssertArgumentsSliceBounds(RegisterOrInt32(begin), RegisterOrInt32(count), |
15672 | temp); |
15673 | #endif |
15674 | |
15675 | emitNewArray(lir, RegisterOrInt32(count), output, temp); |
15676 | |
15677 | Label done; |
15678 | masm.branch32(Assembler::Equal, count, Imm32(0), &done); |
15679 | { |
15680 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
15681 | allRegs.take(begin); |
15682 | allRegs.take(count); |
15683 | allRegs.take(temp); |
15684 | allRegs.take(output); |
15685 | |
15686 | ValueOperand value = allRegs.takeAnyValue(); |
15687 | |
15688 | LiveRegisterSet liveRegs; |
15689 | liveRegs.add(output); |
15690 | liveRegs.add(begin); |
15691 | liveRegs.add(value); |
15692 | |
15693 | masm.PushRegsInMask(liveRegs); |
15694 | |
15695 | // Initialize all elements. |
15696 | |
15697 | Register elements = output; |
15698 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
15699 | |
15700 | Register argIndex = begin; |
15701 | |
15702 | Register index = temp; |
15703 | masm.move32(Imm32(0), index); |
15704 | |
15705 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
15706 | BaseValueIndex argPtr(FramePointer, argIndex, argvOffset); |
15707 | |
15708 | Label loop; |
15709 | masm.bind(&loop); |
15710 | |
15711 | masm.loadValue(argPtr, value); |
15712 | |
15713 | // We don't need a pre-barrier, because the element at |index| is guaranteed |
15714 | // to be a non-GC thing (either uninitialized memory or the magic hole |
15715 | // value). |
15716 | masm.storeValue(value, BaseObjectElementIndex(elements, index)); |
15717 | |
15718 | masm.add32(Imm32(1), index); |
15719 | masm.add32(Imm32(1), argIndex); |
15720 | |
15721 | masm.branch32(Assembler::LessThan, index, count, &loop); |
15722 | |
15723 | masm.PopRegsInMask(liveRegs); |
15724 | |
15725 | // Emit a post-write barrier if |output| is tenured. |
15726 | // |
15727 | // We expect that |output| is nursery allocated, so it isn't worth the |
15728 | // trouble to check if no frame argument is a nursery thing, which would |
15729 | // allow to omit the post-write barrier. |
15730 | masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done); |
15731 | |
15732 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
15733 | volatileRegs.takeUnchecked(temp); |
15734 | if (output.volatile_()) { |
15735 | volatileRegs.addUnchecked(output); |
15736 | } |
15737 | |
15738 | masm.PushRegsInMask(volatileRegs); |
15739 | emitPostWriteBarrier(output); |
15740 | masm.PopRegsInMask(volatileRegs); |
15741 | } |
15742 | masm.bind(&done); |
15743 | } |
15744 | |
15745 | CodeGenerator::RegisterOrInt32 CodeGenerator::ToRegisterOrInt32( |
15746 | const LAllocation* allocation) { |
15747 | if (allocation->isConstant()) { |
15748 | return RegisterOrInt32(allocation->toConstant()->toInt32()); |
15749 | } |
15750 | return RegisterOrInt32(ToRegister(allocation)); |
15751 | } |
15752 | |
15753 | void CodeGenerator::visitInlineArgumentsSlice(LInlineArgumentsSlice* lir) { |
15754 | RegisterOrInt32 begin = ToRegisterOrInt32(lir->begin()); |
15755 | RegisterOrInt32 count = ToRegisterOrInt32(lir->count()); |
15756 | Register temp = ToRegister(lir->temp()); |
15757 | Register output = ToRegister(lir->output()); |
15758 | |
15759 | uint32_t numActuals = lir->mir()->numActuals(); |
15760 | |
15761 | #ifdef DEBUG1 |
15762 | masm.move32(Imm32(numActuals), temp); |
15763 | |
15764 | emitAssertArgumentsSliceBounds(begin, count, temp); |
15765 | #endif |
15766 | |
15767 | emitNewArray(lir, count, output, temp); |
15768 | |
15769 | // We're done if there are no actual arguments. |
15770 | if (numActuals == 0) { |
15771 | return; |
15772 | } |
15773 | |
15774 | // Check if any arguments have to be copied. |
15775 | Label done; |
15776 | if (count.is<Register>()) { |
15777 | masm.branch32(Assembler::Equal, count.as<Register>(), Imm32(0), &done); |
15778 | } else if (count.as<int32_t>() == 0) { |
15779 | return; |
15780 | } |
15781 | |
15782 | auto getArg = [&](uint32_t i) { |
15783 | return toConstantOrRegister(lir, LInlineArgumentsSlice::ArgIndex(i), |
15784 | lir->mir()->getArg(i)->type()); |
15785 | }; |
15786 | |
15787 | auto storeArg = [&](uint32_t i, auto dest) { |
15788 | // We don't need a pre-barrier because the element at |index| is guaranteed |
15789 | // to be a non-GC thing (either uninitialized memory or the magic hole |
15790 | // value). |
15791 | masm.storeConstantOrRegister(getArg(i), dest); |
15792 | }; |
15793 | |
15794 | // Initialize all elements. |
15795 | if (numActuals == 1) { |
15796 | // There's exactly one argument. We've checked that |count| is non-zero, |
15797 | // which implies that |begin| must be zero. |
15798 | MOZ_ASSERT_IF(begin.is<int32_t>(), begin.as<int32_t>() == 0)do { if (begin.is<int32_t>()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(begin.as<int32_t >() == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(begin.as<int32_t>() == 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("begin.as<int32_t>() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15798); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() == 0" ")"); do { *((volatile int*)__null) = 15798; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
15799 | |
15800 | Register elements = temp; |
15801 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
15802 | |
15803 | storeArg(0, Address(elements, 0)); |
15804 | } else if (begin.is<Register>()) { |
15805 | // There is more than one argument and |begin| isn't a compile-time |
15806 | // constant. Iterate through 0..numActuals to search for |begin| and then |
15807 | // start copying |count| arguments from that index. |
15808 | |
15809 | LiveGeneralRegisterSet liveRegs; |
15810 | liveRegs.add(output); |
15811 | liveRegs.add(begin.as<Register>()); |
15812 | |
15813 | masm.PushRegsInMask(liveRegs); |
15814 | |
15815 | Register elements = output; |
15816 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
15817 | |
15818 | Register argIndex = begin.as<Register>(); |
15819 | |
15820 | Register index = temp; |
15821 | masm.move32(Imm32(0), index); |
15822 | |
15823 | Label doneLoop; |
15824 | for (uint32_t i = 0; i < numActuals; ++i) { |
15825 | Label next; |
15826 | masm.branch32(Assembler::NotEqual, argIndex, Imm32(i), &next); |
15827 | |
15828 | storeArg(i, BaseObjectElementIndex(elements, index)); |
15829 | |
15830 | masm.add32(Imm32(1), index); |
15831 | masm.add32(Imm32(1), argIndex); |
15832 | |
15833 | if (count.is<Register>()) { |
15834 | masm.branch32(Assembler::GreaterThanOrEqual, index, |
15835 | count.as<Register>(), &doneLoop); |
15836 | } else { |
15837 | masm.branch32(Assembler::GreaterThanOrEqual, index, |
15838 | Imm32(count.as<int32_t>()), &doneLoop); |
15839 | } |
15840 | |
15841 | masm.bind(&next); |
15842 | } |
15843 | masm.bind(&doneLoop); |
15844 | |
15845 | masm.PopRegsInMask(liveRegs); |
15846 | } else { |
15847 | // There is more than one argument and |begin| is a compile-time constant. |
15848 | |
15849 | Register elements = temp; |
15850 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
15851 | |
15852 | int32_t argIndex = begin.as<int32_t>(); |
15853 | |
15854 | int32_t index = 0; |
15855 | |
15856 | Label doneLoop; |
15857 | for (uint32_t i = argIndex; i < numActuals; ++i) { |
15858 | storeArg(i, Address(elements, index * sizeof(Value))); |
15859 | |
15860 | index += 1; |
15861 | |
15862 | if (count.is<Register>()) { |
15863 | masm.branch32(Assembler::LessThanOrEqual, count.as<Register>(), |
15864 | Imm32(index), &doneLoop); |
15865 | } else { |
15866 | if (index >= count.as<int32_t>()) { |
15867 | break; |
15868 | } |
15869 | } |
15870 | } |
15871 | masm.bind(&doneLoop); |
15872 | } |
15873 | |
15874 | // Determine if we have to emit post-write barrier. |
15875 | // |
15876 | // If either |begin| or |count| is a constant, use their value directly. |
15877 | // Otherwise assume we copy all inline arguments from 0..numActuals. |
15878 | bool postWriteBarrier = false; |
15879 | uint32_t actualBegin = begin.match([](Register) { return 0; }, |
15880 | [](int32_t value) { return value; }); |
15881 | uint32_t actualCount = |
15882 | count.match([=](Register) { return numActuals; }, |
15883 | [](int32_t value) -> uint32_t { return value; }); |
15884 | for (uint32_t i = 0; i < actualCount; ++i) { |
15885 | ConstantOrRegister arg = getArg(actualBegin + i); |
15886 | if (arg.constant()) { |
15887 | Value v = arg.value(); |
15888 | if (v.isGCThing() && IsInsideNursery(v.toGCThing())) { |
15889 | postWriteBarrier = true; |
15890 | } |
15891 | } else { |
15892 | MIRType type = arg.reg().type(); |
15893 | if (type == MIRType::Value || NeedsPostBarrier(type)) { |
15894 | postWriteBarrier = true; |
15895 | } |
15896 | } |
15897 | } |
15898 | |
15899 | // Emit a post-write barrier if |output| is tenured and we couldn't |
15900 | // determine at compile-time that no barrier is needed. |
15901 | if (postWriteBarrier) { |
15902 | masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done); |
15903 | |
15904 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
15905 | volatileRegs.takeUnchecked(temp); |
15906 | if (output.volatile_()) { |
15907 | volatileRegs.addUnchecked(output); |
15908 | } |
15909 | |
15910 | masm.PushRegsInMask(volatileRegs); |
15911 | emitPostWriteBarrier(output); |
15912 | masm.PopRegsInMask(volatileRegs); |
15913 | } |
15914 | |
15915 | masm.bind(&done); |
15916 | } |
15917 | |
15918 | void CodeGenerator::visitNormalizeSliceTerm(LNormalizeSliceTerm* lir) { |
15919 | Register value = ToRegister(lir->value()); |
15920 | Register length = ToRegister(lir->length()); |
15921 | Register output = ToRegister(lir->output()); |
15922 | |
15923 | masm.move32(value, output); |
15924 | |
15925 | Label positive; |
15926 | masm.branch32(Assembler::GreaterThanOrEqual, value, Imm32(0), &positive); |
15927 | |
15928 | Label done; |
15929 | masm.add32(length, output); |
15930 | masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(0), &done); |
15931 | masm.move32(Imm32(0), output); |
15932 | masm.jump(&done); |
15933 | |
15934 | masm.bind(&positive); |
15935 | masm.cmp32Move32(Assembler::LessThan, length, value, length, output); |
15936 | |
15937 | masm.bind(&done); |
15938 | } |
15939 | |
15940 | void CodeGenerator::visitArrayJoin(LArrayJoin* lir) { |
15941 | Label skipCall; |
15942 | |
15943 | Register output = ToRegister(lir->output()); |
15944 | Register sep = ToRegister(lir->separator()); |
15945 | Register array = ToRegister(lir->array()); |
15946 | Register temp = ToRegister(lir->temp0()); |
15947 | |
15948 | // Fast path for simple length <= 1 cases. |
15949 | { |
15950 | masm.loadPtr(Address(array, NativeObject::offsetOfElements()), temp); |
15951 | Address length(temp, ObjectElements::offsetOfLength()); |
15952 | Address initLength(temp, ObjectElements::offsetOfInitializedLength()); |
15953 | |
15954 | // Check for length == 0 |
15955 | Label notEmpty; |
15956 | masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬Empty); |
15957 | const JSAtomState& names = gen->runtime->names(); |
15958 | masm.movePtr(ImmGCPtr(names.empty_), output); |
15959 | masm.jump(&skipCall); |
15960 | |
15961 | masm.bind(¬Empty); |
15962 | Label notSingleString; |
15963 | // Check for length == 1, initializedLength >= 1, arr[0].isString() |
15964 | masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleString); |
15965 | masm.branch32(Assembler::LessThan, initLength, Imm32(1), ¬SingleString); |
15966 | |
15967 | Address elem0(temp, 0); |
15968 | masm.branchTestString(Assembler::NotEqual, elem0, ¬SingleString); |
15969 | |
15970 | // At this point, 'output' can be used as a scratch register, since we're |
15971 | // guaranteed to succeed. |
15972 | masm.unboxString(elem0, output); |
15973 | masm.jump(&skipCall); |
15974 | masm.bind(¬SingleString); |
15975 | } |
15976 | |
15977 | pushArg(sep); |
15978 | pushArg(array); |
15979 | |
15980 | using Fn = JSString* (*)(JSContext*, HandleObject, HandleString); |
15981 | callVM<Fn, jit::ArrayJoin>(lir); |
15982 | masm.bind(&skipCall); |
15983 | } |
15984 | |
15985 | void CodeGenerator::visitObjectKeys(LObjectKeys* lir) { |
15986 | Register object = ToRegister(lir->object()); |
15987 | |
15988 | pushArg(object); |
15989 | |
15990 | using Fn = JSObject* (*)(JSContext*, HandleObject); |
15991 | callVM<Fn, jit::ObjectKeys>(lir); |
15992 | } |
15993 | |
15994 | void CodeGenerator::visitObjectKeysLength(LObjectKeysLength* lir) { |
15995 | Register object = ToRegister(lir->object()); |
15996 | |
15997 | pushArg(object); |
15998 | |
15999 | using Fn = bool (*)(JSContext*, HandleObject, int32_t*); |
16000 | callVM<Fn, jit::ObjectKeysLength>(lir); |
16001 | } |
16002 | |
16003 | void CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir) { |
16004 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
16005 | TypedOrValueRegister val = |
16006 | toConstantOrRegister(lir, LGetIteratorCache::ValueIndex, |
16007 | lir->mir()->value()->type()) |
16008 | .reg(); |
16009 | Register output = ToRegister(lir->output()); |
16010 | Register temp0 = ToRegister(lir->temp0()); |
16011 | Register temp1 = ToRegister(lir->temp1()); |
16012 | |
16013 | IonGetIteratorIC ic(liveRegs, val, output, temp0, temp1); |
16014 | addIC(lir, allocateIC(ic)); |
16015 | } |
16016 | |
16017 | void CodeGenerator::visitOptimizeSpreadCallCache( |
16018 | LOptimizeSpreadCallCache* lir) { |
16019 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
16020 | ValueOperand val = ToValue(lir, LOptimizeSpreadCallCache::ValueIndex); |
16021 | ValueOperand output = ToOutValue(lir); |
16022 | Register temp = ToRegister(lir->temp0()); |
16023 | |
16024 | IonOptimizeSpreadCallIC ic(liveRegs, val, output, temp); |
16025 | addIC(lir, allocateIC(ic)); |
16026 | } |
16027 | |
16028 | void CodeGenerator::visitCloseIterCache(LCloseIterCache* lir) { |
16029 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
16030 | Register iter = ToRegister(lir->iter()); |
16031 | Register temp = ToRegister(lir->temp0()); |
16032 | CompletionKind kind = CompletionKind(lir->mir()->completionKind()); |
16033 | |
16034 | IonCloseIterIC ic(liveRegs, iter, temp, kind); |
16035 | addIC(lir, allocateIC(ic)); |
16036 | } |
16037 | |
16038 | void CodeGenerator::visitOptimizeGetIteratorCache( |
16039 | LOptimizeGetIteratorCache* lir) { |
16040 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
16041 | ValueOperand val = ToValue(lir, LOptimizeGetIteratorCache::ValueIndex); |
16042 | Register output = ToRegister(lir->output()); |
16043 | Register temp = ToRegister(lir->temp0()); |
16044 | |
16045 | IonOptimizeGetIteratorIC ic(liveRegs, val, output, temp); |
16046 | addIC(lir, allocateIC(ic)); |
16047 | } |
16048 | |
16049 | void CodeGenerator::visitIteratorMore(LIteratorMore* lir) { |
16050 | const Register obj = ToRegister(lir->iterator()); |
16051 | const ValueOperand output = ToOutValue(lir); |
16052 | const Register temp = ToRegister(lir->temp0()); |
16053 | |
16054 | masm.iteratorMore(obj, output, temp); |
16055 | } |
16056 | |
16057 | void CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch* lir) { |
16058 | ValueOperand input = ToValue(lir, LIsNoIterAndBranch::Input); |
16059 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
16060 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
16061 | |
16062 | masm.branchTestMagic(Assembler::Equal, input, ifTrue); |
16063 | |
16064 | if (!isNextBlock(lir->ifFalse()->lir())) { |
16065 | masm.jump(ifFalse); |
16066 | } |
16067 | } |
16068 | |
16069 | void CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) { |
16070 | const Register obj = ToRegister(lir->object()); |
16071 | const Register temp0 = ToRegister(lir->temp0()); |
16072 | const Register temp1 = ToRegister(lir->temp1()); |
16073 | const Register temp2 = ToRegister(lir->temp2()); |
16074 | |
16075 | masm.iteratorClose(obj, temp0, temp1, temp2); |
16076 | } |
16077 | |
16078 | void CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) { |
16079 | // read number of actual arguments from the JS frame. |
16080 | Register argc = ToRegister(lir->output()); |
16081 | masm.loadNumActualArgs(FramePointer, argc); |
16082 | } |
16083 | |
16084 | void CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) { |
16085 | ValueOperand result = ToOutValue(lir); |
16086 | const LAllocation* index = lir->index(); |
16087 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
16088 | |
16089 | // This instruction is used to access actual arguments and formal arguments. |
16090 | // The number of Values on the stack is |max(numFormals, numActuals)|, so we |
16091 | // assert |index < numFormals || index < numActuals| in debug builds. |
16092 | DebugOnly<size_t> numFormals = gen->outerInfo().script()->function()->nargs(); |
16093 | |
16094 | if (index->isConstant()) { |
16095 | int32_t i = index->toConstant()->toInt32(); |
16096 | #ifdef DEBUG1 |
16097 | if (uint32_t(i) >= numFormals) { |
16098 | Label ok; |
16099 | Register argc = result.scratchReg(); |
16100 | masm.loadNumActualArgs(FramePointer, argc); |
16101 | masm.branch32(Assembler::Above, argc, Imm32(i), &ok); |
16102 | masm.assumeUnreachable("Invalid argument index"); |
16103 | masm.bind(&ok); |
16104 | } |
16105 | #endif |
16106 | Address argPtr(FramePointer, sizeof(Value) * i + argvOffset); |
16107 | masm.loadValue(argPtr, result); |
16108 | } else { |
16109 | Register i = ToRegister(index); |
16110 | #ifdef DEBUG1 |
16111 | Label ok; |
16112 | Register argc = result.scratchReg(); |
16113 | masm.branch32(Assembler::Below, i, Imm32(numFormals), &ok); |
16114 | masm.loadNumActualArgs(FramePointer, argc); |
16115 | masm.branch32(Assembler::Above, argc, i, &ok); |
16116 | masm.assumeUnreachable("Invalid argument index"); |
16117 | masm.bind(&ok); |
16118 | #endif |
16119 | BaseValueIndex argPtr(FramePointer, i, argvOffset); |
16120 | masm.loadValue(argPtr, result); |
16121 | } |
16122 | } |
16123 | |
16124 | void CodeGenerator::visitGetFrameArgumentHole(LGetFrameArgumentHole* lir) { |
16125 | ValueOperand result = ToOutValue(lir); |
16126 | Register index = ToRegister(lir->index()); |
16127 | Register length = ToRegister(lir->length()); |
16128 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0()); |
16129 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
16130 | |
16131 | Label outOfBounds, done; |
16132 | masm.spectreBoundsCheck32(index, length, spectreTemp, &outOfBounds); |
16133 | |
16134 | BaseValueIndex argPtr(FramePointer, index, argvOffset); |
16135 | masm.loadValue(argPtr, result); |
16136 | masm.jump(&done); |
16137 | |
16138 | masm.bind(&outOfBounds); |
16139 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
16140 | masm.moveValue(UndefinedValue(), result); |
16141 | |
16142 | masm.bind(&done); |
16143 | } |
16144 | |
16145 | void CodeGenerator::visitRest(LRest* lir) { |
16146 | Register numActuals = ToRegister(lir->numActuals()); |
16147 | Register temp0 = ToRegister(lir->temp0()); |
16148 | Register temp1 = ToRegister(lir->temp1()); |
16149 | Register temp2 = ToRegister(lir->temp2()); |
16150 | Register temp3 = ToRegister(lir->temp3()); |
16151 | unsigned numFormals = lir->mir()->numFormals(); |
16152 | |
16153 | constexpr uint32_t arrayCapacity = 2; |
16154 | |
16155 | if (Shape* shape = lir->mir()->shape()) { |
16156 | uint32_t arrayLength = 0; |
16157 | gc::AllocKind allocKind = GuessArrayGCKind(arrayCapacity); |
16158 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(allocKind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16158); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 16158; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16159 | allocKind = ForegroundToBackgroundAllocKind(allocKind); |
16160 | MOZ_ASSERT(GetGCKindSlots(allocKind) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements ::VALUES_PER_HEADER)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 16161; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
16161 | arrayCapacity + ObjectElements::VALUES_PER_HEADER)do { static_assert( mozilla::detail::AssertionConditionType< decltype(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements ::VALUES_PER_HEADER)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 16161; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16162 | |
16163 | Label joinAlloc, failAlloc; |
16164 | masm.movePtr(ImmGCPtr(shape), temp0); |
16165 | masm.createArrayWithFixedElements(temp2, temp0, temp1, InvalidReg, |
16166 | arrayLength, arrayCapacity, 0, 0, |
16167 | allocKind, gc::Heap::Default, &failAlloc); |
16168 | masm.jump(&joinAlloc); |
16169 | { |
16170 | masm.bind(&failAlloc); |
16171 | masm.movePtr(ImmPtr(nullptr), temp2); |
16172 | } |
16173 | masm.bind(&joinAlloc); |
16174 | } else { |
16175 | masm.movePtr(ImmPtr(nullptr), temp2); |
16176 | } |
16177 | |
16178 | // Set temp1 to the address of the first actual argument. |
16179 | size_t actualsOffset = JitFrameLayout::offsetOfActualArgs(); |
16180 | masm.computeEffectiveAddress(Address(FramePointer, actualsOffset), temp1); |
16181 | |
16182 | // Compute array length: max(numActuals - numFormals, 0). |
16183 | Register lengthReg; |
16184 | if (numFormals) { |
16185 | lengthReg = temp0; |
16186 | Label emptyLength, joinLength; |
16187 | masm.branch32(Assembler::LessThanOrEqual, numActuals, Imm32(numFormals), |
16188 | &emptyLength); |
16189 | { |
16190 | masm.move32(numActuals, lengthReg); |
16191 | masm.sub32(Imm32(numFormals), lengthReg); |
16192 | |
16193 | // Skip formal arguments. |
16194 | masm.addPtr(Imm32(sizeof(Value) * numFormals), temp1); |
16195 | |
16196 | masm.jump(&joinLength); |
16197 | } |
16198 | masm.bind(&emptyLength); |
16199 | { |
16200 | masm.move32(Imm32(0), lengthReg); |
16201 | |
16202 | // Leave temp1 pointed to the start of actuals() when the rest-array |
16203 | // length is zero. We don't use |actuals() + numFormals| because |
16204 | // |numFormals| can be any non-negative int32 value when this MRest was |
16205 | // created from scalar replacement optimizations. And it seems |
16206 | // questionable to compute a Value* pointer which points to who knows |
16207 | // where. |
16208 | } |
16209 | masm.bind(&joinLength); |
16210 | } else { |
16211 | // Use numActuals directly when there are no formals. |
16212 | lengthReg = numActuals; |
16213 | } |
16214 | |
16215 | // Try to initialize the array elements. |
16216 | Label vmCall, done; |
16217 | if (lir->mir()->shape()) { |
16218 | // Call into C++ if we failed to allocate an array or there are more than |
16219 | // |arrayCapacity| elements. |
16220 | masm.branchTestPtr(Assembler::Zero, temp2, temp2, &vmCall); |
16221 | masm.branch32(Assembler::Above, lengthReg, Imm32(arrayCapacity), &vmCall); |
16222 | |
16223 | // The array must be nursery allocated so no post barrier is needed. |
16224 | #ifdef DEBUG1 |
16225 | Label ok; |
16226 | masm.branchPtrInNurseryChunk(Assembler::Equal, temp2, temp3, &ok); |
16227 | masm.assumeUnreachable("Unexpected tenured object for LRest"); |
16228 | masm.bind(&ok); |
16229 | #endif |
16230 | |
16231 | Label initialized; |
16232 | masm.branch32(Assembler::Equal, lengthReg, Imm32(0), &initialized); |
16233 | |
16234 | // Store length and initializedLength. |
16235 | Register elements = temp3; |
16236 | masm.loadPtr(Address(temp2, NativeObject::offsetOfElements()), elements); |
16237 | Address lengthAddr(elements, ObjectElements::offsetOfLength()); |
16238 | Address initLengthAddr(elements, |
16239 | ObjectElements::offsetOfInitializedLength()); |
16240 | masm.store32(lengthReg, lengthAddr); |
16241 | masm.store32(lengthReg, initLengthAddr); |
16242 | |
16243 | // Store either one or two elements. This may clobber lengthReg (temp0). |
16244 | static_assert(arrayCapacity == 2, "code handles 1 or 2 elements"); |
16245 | Label storeFirst; |
16246 | masm.branch32(Assembler::Equal, lengthReg, Imm32(1), &storeFirst); |
16247 | masm.storeValue(Address(temp1, sizeof(Value)), |
16248 | Address(elements, sizeof(Value)), temp0); |
16249 | masm.bind(&storeFirst); |
16250 | masm.storeValue(Address(temp1, 0), Address(elements, 0), temp0); |
16251 | |
16252 | // Done. |
16253 | masm.bind(&initialized); |
16254 | masm.movePtr(temp2, ReturnReg); |
16255 | masm.jump(&done); |
16256 | } |
16257 | |
16258 | masm.bind(&vmCall); |
16259 | |
16260 | pushArg(temp2); |
16261 | pushArg(temp1); |
16262 | pushArg(lengthReg); |
16263 | |
16264 | using Fn = |
16265 | ArrayObject* (*)(JSContext*, uint32_t, Value*, Handle<ArrayObject*>); |
16266 | callVM<Fn, InitRestParameter>(lir); |
16267 | |
16268 | masm.bind(&done); |
16269 | } |
16270 | |
16271 | // Create a stackmap from the given safepoint, with the structure: |
16272 | // |
16273 | // <reg dump, if any> |
16274 | // | ++ <body (general spill)> |
16275 | // | | ++ <space for Frame> |
16276 | // | | ++ <inbound args> |
16277 | // | | | |
16278 | // Lowest Addr Highest Addr |
16279 | // | |
16280 | // framePushedAtStackMapBase |
16281 | // |
16282 | // The caller owns the resulting stackmap. This assumes a grow-down stack. |
16283 | // |
16284 | // For non-debug builds, if the stackmap would contain no pointers, no |
16285 | // stackmap is created, and nullptr is returned. For a debug build, a |
16286 | // stackmap is always created and returned. |
16287 | // |
16288 | // Depending on the type of safepoint, the stackmap may need to account for |
16289 | // spilled registers. WasmSafepointKind::LirCall corresponds to LIR nodes where |
16290 | // isCall() == true, for which the register allocator will spill/restore all |
16291 | // live registers at the LIR level - in this case, the LSafepoint sees only live |
16292 | // values on the stack, never in registers. WasmSafepointKind::CodegenCall, on |
16293 | // the other hand, is for LIR nodes which may manually spill/restore live |
16294 | // registers in codegen, in which case the stackmap must account for this. Traps |
16295 | // also require tracking of live registers, but spilling is handled by the trap |
16296 | // mechanism. |
16297 | static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint, |
16298 | const RegisterOffsets& trapExitLayout, |
16299 | size_t trapExitLayoutNumWords, |
16300 | size_t nInboundStackArgBytes, |
16301 | wasm::StackMap** result) { |
16302 | // Ensure this is defined on all return paths. |
16303 | *result = nullptr; |
16304 | |
16305 | // The size of the wasm::Frame itself. |
16306 | const size_t nFrameBytes = sizeof(wasm::Frame); |
16307 | |
16308 | // This is the number of bytes spilled for live registers, outside of a trap. |
16309 | // For traps, trapExitLayout and trapExitLayoutNumWords will be used. |
16310 | const size_t nRegisterDumpBytes = |
16311 | MacroAssembler::PushRegsInMaskSizeInBytes(safepoint.liveRegs()); |
16312 | |
16313 | // As mentioned above, for WasmSafepointKind::LirCall, register spills and |
16314 | // restores are handled at the LIR level and there should therefore be no live |
16315 | // registers to handle here. |
16316 | MOZ_ASSERT_IF(safepoint.wasmSafepointKind() == WasmSafepointKind::LirCall,do { if (safepoint.wasmSafepointKind() == WasmSafepointKind:: LirCall) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(nRegisterDumpBytes == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(nRegisterDumpBytes == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { *((volatile int*)__null) = 16317; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
16317 | nRegisterDumpBytes == 0)do { if (safepoint.wasmSafepointKind() == WasmSafepointKind:: LirCall) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(nRegisterDumpBytes == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(nRegisterDumpBytes == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { *((volatile int*)__null) = 16317; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
16318 | MOZ_ASSERT(nRegisterDumpBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nRegisterDumpBytes % sizeof(void*) == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nRegisterDumpBytes % sizeof(void*) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes % sizeof(void*) == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16318); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16318; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16319 | |
16320 | // This is the number of bytes in the general spill area, below the Frame. |
16321 | const size_t nBodyBytes = safepoint.framePushedAtStackMapBase(); |
16322 | |
16323 | // The stack map owns any alignment padding around inbound stack args. |
16324 | const size_t nInboundStackArgBytesAligned = |
16325 | wasm::AlignStackArgAreaSize(nInboundStackArgBytes); |
16326 | |
16327 | // This is the number of bytes in the general spill area, the Frame, and the |
16328 | // incoming args, but not including any register dump area. |
16329 | const size_t nNonRegisterBytes = |
16330 | nBodyBytes + nFrameBytes + nInboundStackArgBytesAligned; |
16331 | MOZ_ASSERT(nNonRegisterBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nNonRegisterBytes % sizeof(void*) == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nNonRegisterBytes % sizeof(void*) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nNonRegisterBytes % sizeof(void*) == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16331); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nNonRegisterBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16331; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16332 | |
16333 | // This is the number of bytes in the register dump area, if any, below the |
16334 | // general spill area. |
16335 | const size_t nRegisterBytes = |
16336 | (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) |
16337 | ? (trapExitLayoutNumWords * sizeof(void*)) |
16338 | : nRegisterDumpBytes; |
16339 | |
16340 | // This is the total number of bytes covered by the map. |
16341 | const size_t nTotalBytes = nNonRegisterBytes + nRegisterBytes; |
16342 | |
16343 | #ifndef DEBUG1 |
16344 | bool needStackMap = !(safepoint.wasmAnyRefRegs().empty() && |
16345 | safepoint.wasmAnyRefSlots().empty() && |
16346 | safepoint.slotsOrElementsSlots().empty()); |
16347 | |
16348 | // There are no references, and this is a non-debug build, so don't bother |
16349 | // building the stackmap. |
16350 | if (!needStackMap) { |
16351 | return true; |
16352 | } |
16353 | #endif |
16354 | |
16355 | wasm::StackMap* stackMap = |
16356 | wasm::StackMap::create(nTotalBytes / sizeof(void*)); |
16357 | if (!stackMap) { |
16358 | return false; |
16359 | } |
16360 | if (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) { |
16361 | stackMap->setExitStubWords(trapExitLayoutNumWords); |
16362 | } |
16363 | |
16364 | // REG DUMP AREA, if any. |
16365 | size_t regDumpWords = 0; |
16366 | const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs(); |
16367 | const LiveGeneralRegisterSet slotsOrElementsRegs = |
16368 | safepoint.slotsOrElementsRegs(); |
16369 | const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union( |
16370 | wasmAnyRefRegs.set(), slotsOrElementsRegs.set())); |
16371 | GeneralRegisterForwardIterator refRegsIter(refRegs); |
16372 | switch (safepoint.wasmSafepointKind()) { |
16373 | case WasmSafepointKind::LirCall: |
16374 | case WasmSafepointKind::StackSwitch: |
16375 | case WasmSafepointKind::CodegenCall: { |
16376 | size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*); |
16377 | regDumpWords += spilledNumWords; |
16378 | |
16379 | for (; refRegsIter.more(); ++refRegsIter) { |
16380 | Register reg = *refRegsIter; |
16381 | size_t offsetFromSpillBase = |
16382 | safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) / |
16383 | sizeof(void*); |
16384 | MOZ_ASSERT(0 < offsetFromSpillBase &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16385); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" ")"); do { *((volatile int*)__null) = 16385; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
16385 | offsetFromSpillBase <= spilledNumWords)do { static_assert( mozilla::detail::AssertionConditionType< decltype(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16385); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" ")"); do { *((volatile int*)__null) = 16385; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16386 | size_t index = spilledNumWords - offsetFromSpillBase; |
16387 | |
16388 | if (wasmAnyRefRegs.has(reg)) { |
16389 | stackMap->set(index, wasm::StackMap::AnyRef); |
16390 | } else { |
16391 | MOZ_ASSERT(slotsOrElementsRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotsOrElementsRegs.has(reg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slotsOrElementsRegs.has(reg) ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "slotsOrElementsRegs.has(reg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16391); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)" ")"); do { *((volatile int*)__null) = 16391; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16392 | stackMap->set(index, wasm::StackMap::ArrayDataPointer); |
16393 | } |
16394 | } |
16395 | // Float and vector registers do not have to be handled; they cannot |
16396 | // contain wasm anyrefs, and they are spilled after general-purpose |
16397 | // registers. Gprs are therefore closest to the spill base and thus their |
16398 | // offset calculation does not need to account for other spills. |
16399 | } break; |
16400 | case WasmSafepointKind::Trap: { |
16401 | regDumpWords += trapExitLayoutNumWords; |
16402 | |
16403 | for (; refRegsIter.more(); ++refRegsIter) { |
16404 | Register reg = *refRegsIter; |
16405 | size_t offsetFromTop = trapExitLayout.getOffset(reg); |
16406 | |
16407 | // If this doesn't hold, the associated register wasn't saved by |
16408 | // the trap exit stub. Better to crash now than much later, in |
16409 | // some obscure place, and possibly with security consequences. |
16410 | MOZ_RELEASE_ASSERT(offsetFromTop < trapExitLayoutNumWords)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetFromTop < trapExitLayoutNumWords)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(offsetFromTop < trapExitLayoutNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("offsetFromTop < trapExitLayoutNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16410); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "offsetFromTop < trapExitLayoutNumWords" ")"); do { *((volatile int*)__null) = 16410; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16411 | |
16412 | // offsetFromTop is an offset in words down from the highest |
16413 | // address in the exit stub save area. Switch it around to be an |
16414 | // offset up from the bottom of the (integer register) save area. |
16415 | size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop; |
16416 | |
16417 | if (wasmAnyRefRegs.has(reg)) { |
16418 | stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef); |
16419 | } else { |
16420 | MOZ_ASSERT(slotsOrElementsRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotsOrElementsRegs.has(reg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slotsOrElementsRegs.has(reg) ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "slotsOrElementsRegs.has(reg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16420); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)" ")"); do { *((volatile int*)__null) = 16420; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16421 | stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer); |
16422 | } |
16423 | } |
16424 | } break; |
16425 | default: |
16426 | MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16426); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")" ); do { *((volatile int*)__null) = 16426; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
16427 | } |
16428 | |
16429 | // Ensure other reg/slot collections on LSafepoint are empty. |
16430 | MOZ_ASSERT(safepoint.gcRegs().empty() && safepoint.gcSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.gcRegs().empty() && safepoint.gcSlots ().empty())>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(safepoint.gcRegs().empty() && safepoint.gcSlots().empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("safepoint.gcRegs().empty() && safepoint.gcSlots().empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16430); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.gcRegs().empty() && safepoint.gcSlots().empty()" ")"); do { *((volatile int*)__null) = 16430; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16431 | #ifdef JS_NUNBOX32 |
16432 | MOZ_ASSERT(safepoint.nunboxParts().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.nunboxParts().empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(safepoint.nunboxParts().empty ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("safepoint.nunboxParts().empty()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16432); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.nunboxParts().empty()" ")"); do { *((volatile int*)__null) = 16432; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16433 | #elif JS_PUNBOX641 |
16434 | MOZ_ASSERT(safepoint.valueRegs().empty() && safepoint.valueSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.valueRegs().empty() && safepoint.valueSlots ().empty())>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(safepoint.valueRegs().empty() && safepoint.valueSlots().empty()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("safepoint.valueRegs().empty() && safepoint.valueSlots().empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16434); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.valueRegs().empty() && safepoint.valueSlots().empty()" ")"); do { *((volatile int*)__null) = 16434; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16435 | #endif |
16436 | |
16437 | // BODY (GENERAL SPILL) AREA and FRAME and INCOMING ARGS |
16438 | // Deal with roots on the stack. |
16439 | const LSafepoint::SlotList& wasmAnyRefSlots = safepoint.wasmAnyRefSlots(); |
16440 | for (SafepointSlotEntry wasmAnyRefSlot : wasmAnyRefSlots) { |
16441 | // The following needs to correspond with JitFrameLayout::slotRef |
16442 | // wasmAnyRefSlot.stack == 0 means the slot is in the args area |
16443 | if (wasmAnyRefSlot.stack) { |
16444 | // It's a slot in the body allocation, so .slot is interpreted |
16445 | // as an index downwards from the Frame* |
16446 | MOZ_ASSERT(wasmAnyRefSlot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasmAnyRefSlot.slot <= nBodyBytes)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wasmAnyRefSlot.slot <= nBodyBytes ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wasmAnyRefSlot.slot <= nBodyBytes", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16446); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot <= nBodyBytes" ")"); do { *((volatile int*)__null) = 16446; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16447 | uint32_t offsetInBytes = nBodyBytes - wasmAnyRefSlot.slot; |
16448 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16448; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16449 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
16450 | wasm::StackMap::AnyRef); |
16451 | } else { |
16452 | // It's an argument slot |
16453 | MOZ_ASSERT(wasmAnyRefSlot.slot < nInboundStackArgBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasmAnyRefSlot.slot < nInboundStackArgBytes)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(wasmAnyRefSlot.slot < nInboundStackArgBytes))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("wasmAnyRefSlot.slot < nInboundStackArgBytes" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16453); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot < nInboundStackArgBytes" ")"); do { *((volatile int*)__null) = 16453; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16454 | uint32_t offsetInBytes = nBodyBytes + nFrameBytes + wasmAnyRefSlot.slot; |
16455 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16456 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
16457 | wasm::StackMap::AnyRef); |
16458 | } |
16459 | } |
16460 | |
16461 | // Track array data pointers on the stack |
16462 | const LSafepoint::SlotList& slots = safepoint.slotsOrElementsSlots(); |
16463 | for (SafepointSlotEntry slot : slots) { |
16464 | MOZ_ASSERT(slot.stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot.stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot.stack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot.stack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16464); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack" ")"); do { *((volatile int*)__null) = 16464; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16465 | |
16466 | // It's a slot in the body allocation, so .slot is interpreted |
16467 | // as an index downwards from the Frame* |
16468 | MOZ_ASSERT(slot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot.slot <= nBodyBytes)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot.slot <= nBodyBytes)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot.slot <= nBodyBytes" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16468); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { *((volatile int*)__null) = 16468; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16469 | uint32_t offsetInBytes = nBodyBytes - slot.slot; |
16470 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16470; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16471 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
16472 | wasm::StackMap::Kind::ArrayDataPointer); |
16473 | } |
16474 | |
16475 | // Record in the map, how far down from the highest address the Frame* is. |
16476 | // Take the opportunity to check that we haven't marked any part of the |
16477 | // Frame itself as a pointer. |
16478 | stackMap->setFrameOffsetFromTop((nInboundStackArgBytesAligned + nFrameBytes) / |
16479 | sizeof(void*)); |
16480 | #ifdef DEBUG1 |
16481 | for (uint32_t i = 0; i < nFrameBytes / sizeof(void*); i++) { |
16482 | MOZ_ASSERT(stackMap->get(stackMap->header.numMappedWords -do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16484; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
16483 | stackMap->header.frameOffsetFromTop + i) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16484; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
16484 | wasm::StackMap::Kind::POD)do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16484; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16485 | } |
16486 | #endif |
16487 | |
16488 | *result = stackMap; |
16489 | return true; |
16490 | } |
16491 | |
16492 | bool CodeGenerator::generateWasm(wasm::CallIndirectId callIndirectId, |
16493 | const wasm::TrapSiteDesc& entryTrapSiteDesc, |
16494 | const wasm::ArgTypeVector& argTypes, |
16495 | const RegisterOffsets& trapExitLayout, |
16496 | size_t trapExitLayoutNumWords, |
16497 | wasm::FuncOffsets* offsets, |
16498 | wasm::StackMaps* stackMaps, |
16499 | wasm::Decoder* decoder) { |
16500 | AutoCreatedBy acb(masm, "CodeGenerator::generateWasm"); |
16501 | |
16502 | JitSpew(JitSpew_Codegen, "# Emitting wasm code"); |
16503 | |
16504 | size_t nInboundStackArgBytes = StackArgAreaSizeUnaligned(argTypes); |
16505 | inboundStackArgBytes_ = nInboundStackArgBytes; |
16506 | |
16507 | wasm::GenerateFunctionPrologue(masm, callIndirectId, mozilla::Nothing(), |
16508 | offsets); |
16509 | |
16510 | MOZ_ASSERT(masm.framePushed() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("masm.framePushed() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16510); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { *((volatile int*)__null) = 16510; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16511 | |
16512 | // Very large frames are implausible, probably an attack. |
16513 | if (frameSize() > wasm::MaxFrameSize) { |
16514 | return decoder->fail(decoder->beginOffset(), "stack frame is too large"); |
16515 | } |
16516 | |
16517 | if (omitOverRecursedCheck()) { |
16518 | masm.reserveStack(frameSize()); |
16519 | } else { |
16520 | std::pair<CodeOffset, uint32_t> pair = |
16521 | masm.wasmReserveStackChecked(frameSize(), entryTrapSiteDesc); |
16522 | CodeOffset trapInsnOffset = pair.first; |
16523 | size_t nBytesReservedBeforeTrap = pair.second; |
16524 | |
16525 | wasm::StackMap* functionEntryStackMap = nullptr; |
16526 | if (!CreateStackMapForFunctionEntryTrap( |
16527 | argTypes, trapExitLayout, trapExitLayoutNumWords, |
16528 | nBytesReservedBeforeTrap, nInboundStackArgBytes, |
16529 | &functionEntryStackMap)) { |
16530 | return false; |
16531 | } |
16532 | |
16533 | // In debug builds, we'll always have a stack map, even if there are no |
16534 | // refs to track. |
16535 | MOZ_ASSERT(functionEntryStackMap)do { static_assert( mozilla::detail::AssertionConditionType< decltype(functionEntryStackMap)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(functionEntryStackMap))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("functionEntryStackMap" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "functionEntryStackMap" ")"); do { *((volatile int*)__null) = 16535; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16536 | |
16537 | if (functionEntryStackMap && |
16538 | !stackMaps->add((uint8_t*)(uintptr_t)trapInsnOffset.offset(), |
16539 | functionEntryStackMap)) { |
16540 | functionEntryStackMap->destroy(); |
16541 | return false; |
16542 | } |
16543 | } |
16544 | |
16545 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16545); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 16545; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16546 | |
16547 | if (!generateBody()) { |
16548 | return false; |
16549 | } |
16550 | |
16551 | masm.bind(&returnLabel_); |
16552 | wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets); |
16553 | |
16554 | if (!generateOutOfLineCode()) { |
16555 | return false; |
16556 | } |
16557 | |
16558 | masm.flush(); |
16559 | if (masm.oom()) { |
16560 | return false; |
16561 | } |
16562 | |
16563 | offsets->end = masm.currentOffset(); |
16564 | |
16565 | MOZ_ASSERT(!masm.failureLabel()->used())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!masm.failureLabel()->used())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!masm.failureLabel()->used ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!masm.failureLabel()->used()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16565); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!masm.failureLabel()->used()" ")"); do { *((volatile int*)__null) = 16565; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16566 | MOZ_ASSERT(snapshots_.listSize() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(snapshots_.listSize() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(snapshots_.listSize() == 0)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("snapshots_.listSize() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.listSize() == 0" ")"); do { *((volatile int*)__null) = 16566; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16567 | MOZ_ASSERT(snapshots_.RVATableSize() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(snapshots_.RVATableSize() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(snapshots_.RVATableSize() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("snapshots_.RVATableSize() == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.RVATableSize() == 0" ")"); do { *((volatile int*)__null) = 16567; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16568 | MOZ_ASSERT(recovers_.size() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(recovers_.size() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(recovers_.size() == 0))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("recovers_.size() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size() == 0" ")"); do { *((volatile int*)__null) = 16568; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16569 | MOZ_ASSERT(graph.numConstants() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(graph.numConstants() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(graph.numConstants() == 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("graph.numConstants() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16569); AnnotateMozCrashReason("MOZ_ASSERT" "(" "graph.numConstants() == 0" ")"); do { *((volatile int*)__null) = 16569; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16570 | MOZ_ASSERT(osiIndices_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(osiIndices_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(osiIndices_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("osiIndices_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osiIndices_.empty()" ")"); do { *((volatile int*)__null) = 16570; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16571 | MOZ_ASSERT(icList_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(icList_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(icList_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("icList_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "icList_.empty()" ")"); do { *((volatile int*)__null) = 16571; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16572 | MOZ_ASSERT(safepoints_.size() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoints_.size() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(safepoints_.size() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("safepoints_.size() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoints_.size() == 0" ")"); do { *((volatile int*)__null) = 16572; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16573 | MOZ_ASSERT(!scriptCounts_)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!scriptCounts_)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!scriptCounts_))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!scriptCounts_" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16573); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!scriptCounts_" ")"); do { *((volatile int*)__null) = 16573; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16574 | |
16575 | // Convert the safepoints to stackmaps and add them to our running |
16576 | // collection thereof. |
16577 | for (CodegenSafepointIndex& index : safepointIndices_) { |
16578 | wasm::StackMap* stackMap = nullptr; |
16579 | if (!CreateStackMapFromLSafepoint(*index.safepoint(), trapExitLayout, |
16580 | trapExitLayoutNumWords, |
16581 | nInboundStackArgBytes, &stackMap)) { |
16582 | return false; |
16583 | } |
16584 | |
16585 | // In debug builds, we'll always have a stack map. |
16586 | MOZ_ASSERT(stackMap)do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(stackMap))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("stackMap", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap" ")" ); do { *((volatile int*)__null) = 16586; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
16587 | if (!stackMap) { |
16588 | continue; |
16589 | } |
16590 | |
16591 | if (!stackMaps->add((uint8_t*)(uintptr_t)index.displacement(), stackMap)) { |
16592 | stackMap->destroy(); |
16593 | return false; |
16594 | } |
16595 | } |
16596 | |
16597 | return true; |
16598 | } |
16599 | |
16600 | bool CodeGenerator::generate(const WarpSnapshot* snapshot) { |
16601 | AutoCreatedBy acb(masm, "CodeGenerator::generate"); |
16602 | |
16603 | MOZ_ASSERT(snapshot)do { static_assert( mozilla::detail::AssertionConditionType< decltype(snapshot)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(snapshot))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("snapshot", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshot" ")" ); do { *((volatile int*)__null) = 16603; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
16604 | snapshot_ = snapshot; |
16605 | |
16606 | JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%u:%u", |
16607 | gen->outerInfo().script()->filename(), |
16608 | gen->outerInfo().script()->lineno(), |
16609 | gen->outerInfo().script()->column().oneOriginValue()); |
16610 | |
16611 | // Initialize native code table with an entry to the start of |
16612 | // top-level script. |
16613 | InlineScriptTree* tree = gen->outerInfo().inlineScriptTree(); |
16614 | jsbytecode* startPC = tree->script()->code(); |
16615 | BytecodeSite* startSite = new (gen->alloc()) BytecodeSite(tree, startPC); |
16616 | if (!addNativeToBytecodeEntry(startSite)) { |
16617 | return false; |
16618 | } |
16619 | |
16620 | if (!safepoints_.init(gen->alloc())) { |
16621 | return false; |
16622 | } |
16623 | |
16624 | size_t maxSafepointIndices = |
16625 | graph.numSafepoints() + graph.extraSafepointUses(); |
16626 | if (!safepointIndices_.reserve(maxSafepointIndices)) { |
16627 | return false; |
16628 | } |
16629 | if (!osiIndices_.reserve(graph.numSafepoints())) { |
16630 | return false; |
16631 | } |
16632 | |
16633 | perfSpewer_.recordOffset(masm, "Prologue"); |
16634 | if (!generatePrologue()) { |
16635 | return false; |
16636 | } |
16637 | |
16638 | // Reset native => bytecode map table with top-level script and startPc. |
16639 | if (!addNativeToBytecodeEntry(startSite)) { |
16640 | return false; |
16641 | } |
16642 | |
16643 | if (!generateBody()) { |
16644 | return false; |
16645 | } |
16646 | |
16647 | // Reset native => bytecode map table with top-level script and startPc. |
16648 | if (!addNativeToBytecodeEntry(startSite)) { |
16649 | return false; |
16650 | } |
16651 | |
16652 | perfSpewer_.recordOffset(masm, "Epilogue"); |
16653 | if (!generateEpilogue()) { |
16654 | return false; |
16655 | } |
16656 | |
16657 | // Reset native => bytecode map table with top-level script and startPc. |
16658 | if (!addNativeToBytecodeEntry(startSite)) { |
16659 | return false; |
16660 | } |
16661 | |
16662 | perfSpewer_.recordOffset(masm, "InvalidateEpilogue"); |
16663 | generateInvalidateEpilogue(); |
16664 | |
16665 | // native => bytecode entries for OOL code will be added |
16666 | // by CodeGeneratorShared::generateOutOfLineCode |
16667 | perfSpewer_.recordOffset(masm, "OOLCode"); |
16668 | if (!generateOutOfLineCode()) { |
16669 | return false; |
16670 | } |
16671 | |
16672 | // Add terminal entry. |
16673 | if (!addNativeToBytecodeEntry(startSite)) { |
16674 | return false; |
16675 | } |
16676 | |
16677 | // Dump Native to bytecode entries to spew. |
16678 | dumpNativeToBytecodeEntries(); |
16679 | |
16680 | // We encode safepoints after the OSI-point offsets have been determined. |
16681 | if (!encodeSafepoints()) { |
16682 | return false; |
16683 | } |
16684 | |
16685 | // If this assertion trips, then you have multiple things to do: |
16686 | // |
16687 | // This assertion will report if a safepoint is used multiple times for the |
16688 | // same instruction. To fix this assertion make sure to call |
16689 | // `lirGraph_.addExtraSafepointUses(..);` in the Lowering phase. |
16690 | // |
16691 | // However, this non-worrying issue might hide a more dramatic security issue, |
16692 | // which is that having multiple encoding of a safepoint in a single LIR |
16693 | // instruction is not safe, unless: |
16694 | // |
16695 | // - The multiple uses of the safepoints are in different code path. i-e |
16696 | // there should be not single execution trace making use of multiple |
16697 | // calls within a single instruction. |
16698 | // |
16699 | // - There is enough space to encode data in-place of the call instruction. |
16700 | // Such that a patched-call site does not corrupt the code path on another |
16701 | // execution trace. |
16702 | // |
16703 | // This issue is caused by the way invalidation works, to keep the code alive |
16704 | // when invalidated code is only referenced by the stack. This works by |
16705 | // storing data in-place of the calling code, which thus becomes unsafe to |
16706 | // execute. |
16707 | MOZ_ASSERT(safepointIndices_.length() <= maxSafepointIndices)do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepointIndices_.length() <= maxSafepointIndices )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(safepointIndices_.length() <= maxSafepointIndices ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "safepointIndices_.length() <= maxSafepointIndices", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16707); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepointIndices_.length() <= maxSafepointIndices" ")"); do { *((volatile int*)__null) = 16707; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16708 | |
16709 | // For each instruction with a safepoint, we have an OSI point inserted after |
16710 | // which handles bailouts in case of invalidation of the code. |
16711 | MOZ_ASSERT(osiIndices_.length() == graph.numSafepoints())do { static_assert( mozilla::detail::AssertionConditionType< decltype(osiIndices_.length() == graph.numSafepoints())>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(osiIndices_.length() == graph.numSafepoints()))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("osiIndices_.length() == graph.numSafepoints()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osiIndices_.length() == graph.numSafepoints()" ")"); do { *((volatile int*)__null) = 16711; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16712 | |
16713 | return !masm.oom(); |
16714 | } |
16715 | |
16716 | static bool AddInlinedCompilations(JSContext* cx, HandleScript script, |
16717 | IonCompilationId compilationId, |
16718 | const WarpSnapshot* snapshot, |
16719 | bool* isValid) { |
16720 | MOZ_ASSERT(!*isValid)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!*isValid)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!*isValid))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!*isValid", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16720); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*isValid" ")"); do { *((volatile int*)__null) = 16720; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16721 | RecompileInfo recompileInfo(script, compilationId); |
16722 | |
16723 | JitZone* jitZone = cx->zone()->jitZone(); |
16724 | |
16725 | for (const auto* scriptSnapshot : snapshot->scripts()) { |
16726 | JSScript* inlinedScript = scriptSnapshot->script(); |
16727 | if (inlinedScript == script) { |
16728 | continue; |
16729 | } |
16730 | |
16731 | // TODO(post-Warp): This matches FinishCompilation and is necessary to |
16732 | // ensure in-progress compilations are canceled when an inlined functon |
16733 | // becomes a debuggee. See the breakpoint-14.js jit-test. |
16734 | // When TI is gone, try to clean this up by moving AddInlinedCompilations to |
16735 | // WarpOracle so that we can handle this as part of addPendingRecompile |
16736 | // instead of requiring this separate check. |
16737 | if (inlinedScript->isDebuggee()) { |
16738 | *isValid = false; |
16739 | return true; |
16740 | } |
16741 | |
16742 | if (!jitZone->addInlinedCompilation(recompileInfo, inlinedScript)) { |
16743 | return false; |
16744 | } |
16745 | } |
16746 | |
16747 | *isValid = true; |
16748 | return true; |
16749 | } |
16750 | |
16751 | struct EmulatesUndefinedDependency final : public CompilationDependency { |
16752 | explicit EmulatesUndefinedDependency() |
16753 | : CompilationDependency(CompilationDependency::Type::EmulatesUndefined) { |
16754 | }; |
16755 | |
16756 | virtual bool operator==(CompilationDependency& dep) { |
16757 | // Since the emulates undefined fuse is runtime wide, they are all equal |
16758 | return dep.type == type; |
16759 | } |
16760 | |
16761 | virtual bool checkDependency(JSContext* cx) { |
16762 | return cx->runtime()->hasSeenObjectEmulateUndefinedFuse.ref().intact(); |
16763 | } |
16764 | |
16765 | virtual bool registerDependency(JSContext* cx, HandleScript script) { |
16766 | MOZ_ASSERT(checkDependency(cx))do { static_assert( mozilla::detail::AssertionConditionType< decltype(checkDependency(cx))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(checkDependency(cx)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("checkDependency(cx)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16766); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checkDependency(cx)" ")"); do { *((volatile int*)__null) = 16766; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16767 | return cx->runtime() |
16768 | ->hasSeenObjectEmulateUndefinedFuse.ref() |
16769 | .addFuseDependency(cx, script); |
16770 | } |
16771 | |
16772 | virtual UniquePtr<CompilationDependency> clone() { |
16773 | return MakeUnique<EmulatesUndefinedDependency>(); |
16774 | } |
16775 | }; |
16776 | |
16777 | bool CodeGenerator::addHasSeenObjectEmulateUndefinedFuseDependency() { |
16778 | EmulatesUndefinedDependency dep; |
16779 | return mirGen().tracker.addDependency(dep); |
16780 | } |
16781 | |
16782 | bool CodeGenerator::link(JSContext* cx) { |
16783 | AutoCreatedBy acb(masm, "CodeGenerator::link"); |
16784 | |
16785 | // We cancel off-thread Ion compilations in a few places during GC, but if |
16786 | // this compilation was performed off-thread it will already have been |
16787 | // removed from the relevant lists by this point. Don't allow GC here. |
16788 | JS::AutoAssertNoGC nogc(cx); |
16789 | |
16790 | RootedScript script(cx, gen->outerInfo().script()); |
16791 | MOZ_ASSERT(!script->hasIonScript())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!script->hasIonScript())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!script->hasIonScript())) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!script->hasIonScript()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!script->hasIonScript()" ")"); do { *((volatile int*)__null) = 16791; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16792 | |
16793 | if (scriptCounts_ && !script->hasScriptCounts() && |
16794 | !script->initScriptCounts(cx)) { |
16795 | return false; |
16796 | } |
16797 | |
16798 | JitZone* jitZone = cx->zone()->jitZone(); |
16799 | |
16800 | IonCompilationId compilationId = |
16801 | cx->runtime()->jitRuntime()->nextCompilationId(); |
16802 | jitZone->currentCompilationIdRef().emplace(compilationId); |
16803 | auto resetCurrentId = mozilla::MakeScopeExit( |
16804 | [jitZone] { jitZone->currentCompilationIdRef().reset(); }); |
16805 | |
16806 | // Record constraints. If an error occured, returns false and potentially |
16807 | // prevent future compilations. Otherwise, if an invalidation occured, then |
16808 | // skip the current compilation. |
16809 | bool isValid = false; |
16810 | |
16811 | // If an inlined script is invalidated (for example, by attaching |
16812 | // a debugger), we must also invalidate the parent IonScript. |
16813 | if (!AddInlinedCompilations(cx, script, compilationId, snapshot_, &isValid)) { |
16814 | return false; |
16815 | } |
16816 | |
16817 | // This compilation is no longer valid; don't proceed, but return true as this |
16818 | // isn't an error case either. |
16819 | if (!isValid) { |
16820 | return true; |
16821 | } |
16822 | |
16823 | CompilationDependencyTracker& tracker = mirGen().tracker; |
16824 | // Make sure we're using the same realm as this context. |
16825 | MOZ_ASSERT(mirGen().realm->realmPtr() == cx->realm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mirGen().realm->realmPtr() == cx->realm())> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mirGen().realm->realmPtr() == cx->realm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("mirGen().realm->realmPtr() == cx->realm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16825); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mirGen().realm->realmPtr() == cx->realm()" ")"); do { *((volatile int*)__null) = 16825; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16826 | if (!tracker.checkDependencies(cx)) { |
16827 | return true; |
16828 | } |
16829 | |
16830 | for (auto& dep : tracker.dependencies) { |
16831 | if (!dep->registerDependency(cx, script)) { |
16832 | return false; // Should we make sure we only return false on OOM and then |
16833 | // eat the OOM here? |
16834 | } |
16835 | } |
16836 | |
16837 | uint32_t argumentSlots = (gen->outerInfo().nargs() + 1) * sizeof(Value); |
16838 | |
16839 | size_t numNurseryObjects = snapshot_->nurseryObjects().length(); |
16840 | |
16841 | IonScript* ionScript = IonScript::New( |
16842 | cx, compilationId, graph.localSlotsSize(), argumentSlots, frameDepth_, |
16843 | snapshots_.listSize(), snapshots_.RVATableSize(), recovers_.size(), |
16844 | graph.numConstants(), numNurseryObjects, safepointIndices_.length(), |
16845 | osiIndices_.length(), icList_.length(), runtimeData_.length(), |
16846 | safepoints_.size()); |
16847 | if (!ionScript) { |
16848 | return false; |
16849 | } |
16850 | #ifdef DEBUG1 |
16851 | ionScript->setICHash(snapshot_->icHash()); |
16852 | #endif |
16853 | |
16854 | auto freeIonScript = mozilla::MakeScopeExit([&ionScript] { |
16855 | // Use js_free instead of IonScript::Destroy: the cache list is still |
16856 | // uninitialized. |
16857 | js_free(ionScript); |
16858 | }); |
16859 | |
16860 | Linker linker(masm); |
16861 | JitCode* code = linker.newCode(cx, CodeKind::Ion); |
16862 | if (!code) { |
16863 | return false; |
16864 | } |
16865 | |
16866 | // Encode native to bytecode map if profiling is enabled. |
16867 | if (isProfilerInstrumentationEnabled()) { |
16868 | // Generate native-to-bytecode main table. |
16869 | IonEntry::ScriptList scriptList; |
16870 | if (!generateCompactNativeToBytecodeMap(cx, code, scriptList)) { |
16871 | return false; |
16872 | } |
16873 | |
16874 | uint8_t* ionTableAddr = |
16875 | ((uint8_t*)nativeToBytecodeMap_.get()) + nativeToBytecodeTableOffset_; |
16876 | JitcodeIonTable* ionTable = (JitcodeIonTable*)ionTableAddr; |
16877 | |
16878 | // Construct the IonEntry that will go into the global table. |
16879 | auto entry = MakeJitcodeGlobalEntry<IonEntry>( |
16880 | cx, code, code->raw(), code->rawEnd(), std::move(scriptList), ionTable); |
16881 | if (!entry) { |
16882 | return false; |
16883 | } |
16884 | (void)nativeToBytecodeMap_.release(); // Table is now owned by |entry|. |
16885 | |
16886 | // Add entry to the global table. |
16887 | JitcodeGlobalTable* globalTable = |
16888 | cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); |
16889 | if (!globalTable->addEntry(std::move(entry))) { |
16890 | return false; |
16891 | } |
16892 | |
16893 | // Mark the jitcode as having a bytecode map. |
16894 | code->setHasBytecodeMap(); |
16895 | } else { |
16896 | // Add a dumy jitcodeGlobalTable entry. |
16897 | auto entry = MakeJitcodeGlobalEntry<DummyEntry>(cx, code, code->raw(), |
16898 | code->rawEnd()); |
16899 | if (!entry) { |
16900 | return false; |
16901 | } |
16902 | |
16903 | // Add entry to the global table. |
16904 | JitcodeGlobalTable* globalTable = |
16905 | cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); |
16906 | if (!globalTable->addEntry(std::move(entry))) { |
16907 | return false; |
16908 | } |
16909 | |
16910 | // Mark the jitcode as having a bytecode map. |
16911 | code->setHasBytecodeMap(); |
16912 | } |
16913 | |
16914 | ionScript->setMethod(code); |
16915 | |
16916 | // If the Gecko Profiler is enabled, mark IonScript as having been |
16917 | // instrumented accordingly. |
16918 | if (isProfilerInstrumentationEnabled()) { |
16919 | ionScript->setHasProfilingInstrumentation(); |
16920 | } |
16921 | |
16922 | Assembler::PatchDataWithValueCheck( |
16923 | CodeLocationLabel(code, invalidateEpilogueData_), ImmPtr(ionScript), |
16924 | ImmPtr((void*)-1)); |
16925 | |
16926 | for (CodeOffset offset : ionScriptLabels_) { |
16927 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, offset), |
16928 | ImmPtr(ionScript), ImmPtr((void*)-1)); |
16929 | } |
16930 | |
16931 | for (NurseryObjectLabel label : ionNurseryObjectLabels_) { |
16932 | void* entry = ionScript->addressOfNurseryObject(label.nurseryIndex); |
16933 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset), |
16934 | ImmPtr(entry), ImmPtr((void*)-1)); |
16935 | } |
16936 | |
16937 | // for generating inline caches during the execution. |
16938 | if (runtimeData_.length()) { |
16939 | ionScript->copyRuntimeData(&runtimeData_[0]); |
16940 | } |
16941 | if (icList_.length()) { |
16942 | ionScript->copyICEntries(&icList_[0]); |
16943 | } |
16944 | |
16945 | for (size_t i = 0; i < icInfo_.length(); i++) { |
16946 | IonIC& ic = ionScript->getICFromIndex(i); |
16947 | Assembler::PatchDataWithValueCheck( |
16948 | CodeLocationLabel(code, icInfo_[i].icOffsetForJump), |
16949 | ImmPtr(ic.codeRawPtr()), ImmPtr((void*)-1)); |
16950 | Assembler::PatchDataWithValueCheck( |
16951 | CodeLocationLabel(code, icInfo_[i].icOffsetForPush), ImmPtr(&ic), |
16952 | ImmPtr((void*)-1)); |
16953 | } |
16954 | |
16955 | JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)", (void*)ionScript, |
16956 | (void*)code->raw()); |
16957 | |
16958 | ionScript->setInvalidationEpilogueDataOffset( |
16959 | invalidateEpilogueData_.offset()); |
16960 | if (jsbytecode* osrPc = gen->outerInfo().osrPc()) { |
16961 | ionScript->setOsrPc(osrPc); |
16962 | ionScript->setOsrEntryOffset(getOsrEntryOffset()); |
16963 | } |
16964 | ionScript->setInvalidationEpilogueOffset(invalidate_.offset()); |
16965 | |
16966 | perfSpewer_.saveProfile(cx, script, code); |
16967 | |
16968 | #ifdef MOZ_VTUNE1 |
16969 | vtune::MarkScript(code, script, "ion"); |
16970 | #endif |
16971 | |
16972 | // Set a Ion counter hint for this script. |
16973 | if (cx->runtime()->jitRuntime()->hasJitHintsMap()) { |
16974 | JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap(); |
16975 | jitHints->recordIonCompilation(script); |
16976 | } |
16977 | |
16978 | // for marking during GC. |
16979 | if (safepointIndices_.length()) { |
16980 | ionScript->copySafepointIndices(&safepointIndices_[0]); |
16981 | } |
16982 | if (safepoints_.size()) { |
16983 | ionScript->copySafepoints(&safepoints_); |
16984 | } |
16985 | |
16986 | // for recovering from an Ion Frame. |
16987 | if (osiIndices_.length()) { |
16988 | ionScript->copyOsiIndices(&osiIndices_[0]); |
16989 | } |
16990 | if (snapshots_.listSize()) { |
16991 | ionScript->copySnapshots(&snapshots_); |
16992 | } |
16993 | MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size())do { if (snapshots_.listSize()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(recovers_.size() )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(recovers_.size()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("recovers_.size()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size()" ")"); do { *((volatile int*)__null) = 16993; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
16994 | if (recovers_.size()) { |
16995 | ionScript->copyRecovers(&recovers_); |
16996 | } |
16997 | if (graph.numConstants()) { |
16998 | const Value* vp = graph.constantPool(); |
16999 | ionScript->copyConstants(vp); |
17000 | for (size_t i = 0; i < graph.numConstants(); i++) { |
17001 | const Value& v = vp[i]; |
17002 | if (v.isGCThing()) { |
17003 | if (gc::StoreBuffer* sb = v.toGCThing()->storeBuffer()) { |
17004 | sb->putWholeCell(script); |
17005 | break; |
17006 | } |
17007 | } |
17008 | } |
17009 | } |
17010 | |
17011 | // Attach any generated script counts to the script. |
17012 | if (IonScriptCounts* counts = extractScriptCounts()) { |
17013 | script->addIonCounts(counts); |
17014 | } |
17015 | // WARNING: Code after this point must be infallible! |
17016 | |
17017 | // Copy the list of nursery objects. Note that the store buffer can add |
17018 | // HeapPtr edges that must be cleared in IonScript::Destroy. See the |
17019 | // infallibility warning above. |
17020 | const auto& nurseryObjects = snapshot_->nurseryObjects(); |
17021 | for (size_t i = 0; i < nurseryObjects.length(); i++) { |
17022 | ionScript->nurseryObjects()[i].init(nurseryObjects[i]); |
17023 | } |
17024 | |
17025 | // Transfer ownership of the IonScript to the JitScript. At this point enough |
17026 | // of the IonScript must be initialized for IonScript::Destroy to work. |
17027 | freeIonScript.release(); |
17028 | script->jitScript()->setIonScript(script, ionScript); |
17029 | |
17030 | return true; |
17031 | } |
17032 | |
17033 | // An out-of-line path to convert a boxed int32 to either a float or double. |
17034 | class OutOfLineUnboxFloatingPoint : public OutOfLineCodeBase<CodeGenerator> { |
17035 | LUnboxFloatingPoint* unboxFloatingPoint_; |
17036 | |
17037 | public: |
17038 | explicit OutOfLineUnboxFloatingPoint(LUnboxFloatingPoint* unboxFloatingPoint) |
17039 | : unboxFloatingPoint_(unboxFloatingPoint) {} |
17040 | |
17041 | void accept(CodeGenerator* codegen) override { |
17042 | codegen->visitOutOfLineUnboxFloatingPoint(this); |
17043 | } |
17044 | |
17045 | LUnboxFloatingPoint* unboxFloatingPoint() const { |
17046 | return unboxFloatingPoint_; |
17047 | } |
17048 | }; |
17049 | |
17050 | void CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint* lir) { |
17051 | const ValueOperand box = ToValue(lir, LUnboxFloatingPoint::Input); |
17052 | const LDefinition* result = lir->output(); |
17053 | |
17054 | // Out-of-line path to convert int32 to double or bailout |
17055 | // if this instruction is fallible. |
17056 | OutOfLineUnboxFloatingPoint* ool = |
17057 | new (alloc()) OutOfLineUnboxFloatingPoint(lir); |
17058 | addOutOfLineCode(ool, lir->mir()); |
17059 | |
17060 | FloatRegister resultReg = ToFloatRegister(result); |
17061 | masm.branchTestDouble(Assembler::NotEqual, box, ool->entry()); |
17062 | masm.unboxDouble(box, resultReg); |
17063 | if (lir->type() == MIRType::Float32) { |
17064 | masm.convertDoubleToFloat32(resultReg, resultReg); |
17065 | } |
17066 | masm.bind(ool->rejoin()); |
17067 | } |
17068 | |
17069 | void CodeGenerator::visitOutOfLineUnboxFloatingPoint( |
17070 | OutOfLineUnboxFloatingPoint* ool) { |
17071 | LUnboxFloatingPoint* ins = ool->unboxFloatingPoint(); |
17072 | const ValueOperand value = ToValue(ins, LUnboxFloatingPoint::Input); |
17073 | |
17074 | if (ins->mir()->fallible()) { |
17075 | Label bail; |
17076 | masm.branchTestInt32(Assembler::NotEqual, value, &bail); |
17077 | bailoutFrom(&bail, ins->snapshot()); |
17078 | } |
17079 | if (ins->type() == MIRType::Float32) { |
17080 | masm.convertInt32ToFloat32(value.payloadOrValueReg(), |
17081 | ToFloatRegister(ins->output())); |
17082 | } else { |
17083 | masm.convertInt32ToDouble(value.payloadOrValueReg(), |
17084 | ToFloatRegister(ins->output())); |
17085 | } |
17086 | masm.jump(ool->rejoin()); |
17087 | } |
17088 | |
17089 | void CodeGenerator::visitCallBindVar(LCallBindVar* lir) { |
17090 | pushArg(ToRegister(lir->environmentChain())); |
17091 | |
17092 | using Fn = JSObject* (*)(JSContext*, JSObject*); |
17093 | callVM<Fn, BindVarOperation>(lir); |
17094 | } |
17095 | |
17096 | void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) { |
17097 | Register obj = ToRegister(lir->getOperand(0)); |
17098 | ValueOperand idVal = ToValue(lir, LMegamorphicSetElement::IndexIndex); |
17099 | ValueOperand value = ToValue(lir, LMegamorphicSetElement::ValueIndex); |
17100 | |
17101 | Register temp0 = ToRegister(lir->temp0()); |
17102 | // See comment in LIROps.yaml (x86 is short on registers) |
17103 | #ifndef JS_CODEGEN_X86 |
17104 | Register temp1 = ToRegister(lir->temp1()); |
17105 | Register temp2 = ToRegister(lir->temp2()); |
17106 | #endif |
17107 | |
17108 | // The instruction is marked as call-instruction so only these registers are |
17109 | // live. |
17110 | LiveRegisterSet liveRegs; |
17111 | liveRegs.addUnchecked(obj); |
17112 | liveRegs.addUnchecked(idVal); |
17113 | liveRegs.addUnchecked(value); |
17114 | liveRegs.addUnchecked(temp0); |
17115 | #ifndef JS_CODEGEN_X86 |
17116 | liveRegs.addUnchecked(temp1); |
17117 | liveRegs.addUnchecked(temp2); |
17118 | #endif |
17119 | |
17120 | Label cacheHit, done; |
17121 | #ifdef JS_CODEGEN_X86 |
17122 | masm.emitMegamorphicCachedSetSlot( |
17123 | idVal, obj, temp0, value, liveRegs, &cacheHit, |
17124 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
17125 | EmitPreBarrier(masm, addr, mirType); |
17126 | }); |
17127 | #else |
17128 | masm.emitMegamorphicCachedSetSlot( |
17129 | idVal, obj, temp0, temp1, temp2, value, liveRegs, &cacheHit, |
17130 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
17131 | EmitPreBarrier(masm, addr, mirType); |
17132 | }); |
17133 | #endif |
17134 | |
17135 | pushArg(Imm32(lir->mir()->strict())); |
17136 | pushArg(ToValue(lir, LMegamorphicSetElement::ValueIndex)); |
17137 | pushArg(ToValue(lir, LMegamorphicSetElement::IndexIndex)); |
17138 | pushArg(obj); |
17139 | |
17140 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool); |
17141 | callVM<Fn, js::jit::SetElementMegamorphic<true>>(lir); |
17142 | |
17143 | masm.jump(&done); |
17144 | masm.bind(&cacheHit); |
17145 | |
17146 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done); |
17147 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done); |
17148 | |
17149 | // Note: because this is a call-instruction, no registers need to be saved. |
17150 | MOZ_ASSERT(lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17150); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { *((volatile int*)__null) = 17150; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17151 | emitPostWriteBarrier(obj); |
17152 | |
17153 | masm.bind(&done); |
17154 | } |
17155 | |
17156 | void CodeGenerator::visitLoadScriptedProxyHandler( |
17157 | LLoadScriptedProxyHandler* ins) { |
17158 | Register obj = ToRegister(ins->getOperand(0)); |
17159 | Register output = ToRegister(ins->output()); |
17160 | |
17161 | masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), output); |
17162 | |
17163 | Label bail; |
17164 | Address handlerAddr(output, js::detail::ProxyReservedSlots::offsetOfSlot( |
17165 | ScriptedProxyHandler::HANDLER_EXTRA)); |
17166 | masm.fallibleUnboxObject(handlerAddr, output, &bail); |
17167 | bailoutFrom(&bail, ins->snapshot()); |
17168 | } |
17169 | |
17170 | #ifdef JS_PUNBOX641 |
17171 | void CodeGenerator::visitCheckScriptedProxyGetResult( |
17172 | LCheckScriptedProxyGetResult* ins) { |
17173 | ValueOperand target = ToValue(ins, LCheckScriptedProxyGetResult::TargetIndex); |
17174 | ValueOperand value = ToValue(ins, LCheckScriptedProxyGetResult::ValueIndex); |
17175 | ValueOperand id = ToValue(ins, LCheckScriptedProxyGetResult::IdIndex); |
17176 | Register scratch = ToRegister(ins->temp0()); |
17177 | Register scratch2 = ToRegister(ins->temp1()); |
17178 | |
17179 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, |
17180 | MutableHandleValue); |
17181 | OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>( |
17182 | ins, ArgList(scratch, id, value), StoreValueTo(value)); |
17183 | |
17184 | masm.unboxObject(target, scratch); |
17185 | masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch, |
17186 | scratch2, ool->entry()); |
17187 | masm.bind(ool->rejoin()); |
17188 | } |
17189 | #endif |
17190 | |
17191 | void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) { |
17192 | ValueOperand id = ToValue(ins, LIdToStringOrSymbol::IdIndex); |
17193 | ValueOperand output = ToOutValue(ins); |
17194 | Register scratch = ToRegister(ins->temp0()); |
17195 | |
17196 | masm.moveValue(id, output); |
17197 | |
17198 | Label done, callVM; |
17199 | Label bail; |
17200 | { |
17201 | ScratchTagScope tag(masm, output); |
17202 | masm.splitTagForTest(output, tag); |
17203 | masm.branchTestString(Assembler::Equal, tag, &done); |
17204 | masm.branchTestSymbol(Assembler::Equal, tag, &done); |
17205 | masm.branchTestInt32(Assembler::NotEqual, tag, &bail); |
17206 | } |
17207 | |
17208 | masm.unboxInt32(output, scratch); |
17209 | |
17210 | using Fn = JSLinearString* (*)(JSContext*, int); |
17211 | OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>( |
17212 | ins, ArgList(scratch), StoreRegisterTo(output.scratchReg())); |
17213 | |
17214 | masm.lookupStaticIntString(scratch, output.scratchReg(), |
17215 | gen->runtime->staticStrings(), ool->entry()); |
17216 | |
17217 | masm.bind(ool->rejoin()); |
17218 | masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output); |
17219 | masm.bind(&done); |
17220 | |
17221 | bailoutFrom(&bail, ins->snapshot()); |
17222 | } |
17223 | |
17224 | void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) { |
17225 | const Register obj = ToRegister(ins->getOperand(0)); |
17226 | size_t slot = ins->mir()->slot(); |
17227 | ValueOperand result = ToOutValue(ins); |
17228 | |
17229 | masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result); |
17230 | } |
17231 | |
17232 | void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) { |
17233 | const Register obj = ToRegister(ins->getOperand(0)); |
17234 | size_t slot = ins->mir()->slot(); |
17235 | AnyRegister result = ToAnyRegister(ins->getDef(0)); |
17236 | MIRType type = ins->mir()->type(); |
17237 | |
17238 | masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), |
17239 | type, result); |
17240 | } |
17241 | |
17242 | template <typename T> |
17243 | static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type, |
17244 | bool fallible, AnyRegister dest, Label* fail) { |
17245 | if (type == MIRType::Double) { |
17246 | MOZ_ASSERT(dest.isFloat())do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.isFloat())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.isFloat()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("dest.isFloat()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17246); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.isFloat()" ")"); do { *((volatile int*)__null) = 17246; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17247 | masm.ensureDouble(src, dest.fpu(), fail); |
17248 | return; |
17249 | } |
17250 | if (fallible) { |
17251 | switch (type) { |
17252 | case MIRType::Int32: |
17253 | masm.fallibleUnboxInt32(src, dest.gpr(), fail); |
17254 | break; |
17255 | case MIRType::Boolean: |
17256 | masm.fallibleUnboxBoolean(src, dest.gpr(), fail); |
17257 | break; |
17258 | case MIRType::Object: |
17259 | masm.fallibleUnboxObject(src, dest.gpr(), fail); |
17260 | break; |
17261 | case MIRType::String: |
17262 | masm.fallibleUnboxString(src, dest.gpr(), fail); |
17263 | break; |
17264 | case MIRType::Symbol: |
17265 | masm.fallibleUnboxSymbol(src, dest.gpr(), fail); |
17266 | break; |
17267 | case MIRType::BigInt: |
17268 | masm.fallibleUnboxBigInt(src, dest.gpr(), fail); |
17269 | break; |
17270 | default: |
17271 | MOZ_CRASH("Unexpected MIRType")do { do { } while (false); MOZ_ReportCrash("" "Unexpected MIRType" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17271); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected MIRType" ")"); do { *((volatile int*)__null) = 17271; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
17272 | } |
17273 | return; |
17274 | } |
17275 | masm.loadUnboxedValue(src, type, dest); |
17276 | } |
17277 | |
17278 | void CodeGenerator::visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* ins) { |
17279 | const MLoadFixedSlotAndUnbox* mir = ins->mir(); |
17280 | MIRType type = mir->type(); |
17281 | Register input = ToRegister(ins->object()); |
17282 | AnyRegister result = ToAnyRegister(ins->output()); |
17283 | size_t slot = mir->slot(); |
17284 | |
17285 | Address address(input, NativeObject::getFixedSlotOffset(slot)); |
17286 | |
17287 | Label bail; |
17288 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
17289 | if (mir->fallible()) { |
17290 | bailoutFrom(&bail, ins->snapshot()); |
17291 | } |
17292 | } |
17293 | |
17294 | void CodeGenerator::visitLoadDynamicSlotAndUnbox( |
17295 | LLoadDynamicSlotAndUnbox* ins) { |
17296 | const MLoadDynamicSlotAndUnbox* mir = ins->mir(); |
17297 | MIRType type = mir->type(); |
17298 | Register input = ToRegister(ins->slots()); |
17299 | AnyRegister result = ToAnyRegister(ins->output()); |
17300 | size_t slot = mir->slot(); |
17301 | |
17302 | Address address(input, slot * sizeof(JS::Value)); |
17303 | |
17304 | Label bail; |
17305 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
17306 | if (mir->fallible()) { |
17307 | bailoutFrom(&bail, ins->snapshot()); |
17308 | } |
17309 | } |
17310 | |
17311 | void CodeGenerator::visitLoadElementAndUnbox(LLoadElementAndUnbox* ins) { |
17312 | const MLoadElementAndUnbox* mir = ins->mir(); |
17313 | MIRType type = mir->type(); |
17314 | Register elements = ToRegister(ins->elements()); |
17315 | AnyRegister result = ToAnyRegister(ins->output()); |
17316 | |
17317 | Label bail; |
17318 | if (ins->index()->isConstant()) { |
17319 | NativeObject::elementsSizeMustNotOverflow(); |
17320 | int32_t offset = ToInt32(ins->index()) * sizeof(Value); |
17321 | Address address(elements, offset); |
17322 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
17323 | } else { |
17324 | BaseObjectElementIndex address(elements, ToRegister(ins->index())); |
17325 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
17326 | } |
17327 | |
17328 | if (mir->fallible()) { |
17329 | bailoutFrom(&bail, ins->snapshot()); |
17330 | } |
17331 | } |
17332 | |
17333 | class OutOfLineAtomizeSlot : public OutOfLineCodeBase<CodeGenerator> { |
17334 | LInstruction* lir_; |
17335 | Register stringReg_; |
17336 | Address slotAddr_; |
17337 | TypedOrValueRegister dest_; |
17338 | |
17339 | public: |
17340 | OutOfLineAtomizeSlot(LInstruction* lir, Register stringReg, Address slotAddr, |
17341 | TypedOrValueRegister dest) |
17342 | : lir_(lir), stringReg_(stringReg), slotAddr_(slotAddr), dest_(dest) {} |
17343 | |
17344 | void accept(CodeGenerator* codegen) final { |
17345 | codegen->visitOutOfLineAtomizeSlot(this); |
17346 | } |
17347 | LInstruction* lir() const { return lir_; } |
17348 | Register stringReg() const { return stringReg_; } |
17349 | Address slotAddr() const { return slotAddr_; } |
17350 | TypedOrValueRegister dest() const { return dest_; } |
17351 | }; |
17352 | |
17353 | void CodeGenerator::visitOutOfLineAtomizeSlot(OutOfLineAtomizeSlot* ool) { |
17354 | LInstruction* lir = ool->lir(); |
17355 | Register stringReg = ool->stringReg(); |
17356 | Address slotAddr = ool->slotAddr(); |
17357 | TypedOrValueRegister dest = ool->dest(); |
17358 | |
17359 | // This code is called with a non-atomic string in |stringReg|. |
17360 | // When it returns, |stringReg| contains an unboxed pointer to an |
17361 | // atomized version of that string, and |slotAddr| contains a |
17362 | // StringValue pointing to that atom. If |dest| is a ValueOperand, |
17363 | // it contains the same StringValue; otherwise we assert that |dest| |
17364 | // is |stringReg|. |
17365 | |
17366 | saveLive(lir); |
17367 | pushArg(stringReg); |
17368 | |
17369 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
17370 | callVM<Fn, js::AtomizeString>(lir); |
17371 | StoreRegisterTo(stringReg).generate(this); |
17372 | restoreLiveIgnore(lir, StoreRegisterTo(stringReg).clobbered()); |
17373 | |
17374 | if (dest.hasValue()) { |
17375 | masm.moveValue( |
17376 | TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)), |
17377 | dest.valueReg()); |
17378 | } else { |
17379 | MOZ_ASSERT(dest.typedReg().gpr() == stringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.typedReg().gpr() == stringReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.typedReg().gpr() == stringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "dest.typedReg().gpr() == stringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17379); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg" ")"); do { *((volatile int*)__null) = 17379; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17380 | } |
17381 | |
17382 | emitPreBarrier(slotAddr); |
17383 | masm.storeTypedOrValue(dest, slotAddr); |
17384 | |
17385 | // We don't need a post-barrier because atoms aren't nursery-allocated. |
17386 | #ifdef DEBUG1 |
17387 | // We need a temp register for the nursery check. Spill something. |
17388 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
17389 | allRegs.take(stringReg); |
17390 | Register temp = allRegs.takeAny(); |
17391 | masm.push(temp); |
17392 | |
17393 | Label tenured; |
17394 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, stringReg, temp, &tenured); |
17395 | masm.assumeUnreachable("AtomizeString returned a nursery pointer"); |
17396 | masm.bind(&tenured); |
17397 | |
17398 | masm.pop(temp); |
17399 | #endif |
17400 | |
17401 | masm.jump(ool->rejoin()); |
17402 | } |
17403 | |
17404 | void CodeGenerator::emitMaybeAtomizeSlot(LInstruction* ins, Register stringReg, |
17405 | Address slotAddr, |
17406 | TypedOrValueRegister dest) { |
17407 | OutOfLineAtomizeSlot* ool = |
17408 | new (alloc()) OutOfLineAtomizeSlot(ins, stringReg, slotAddr, dest); |
17409 | addOutOfLineCode(ool, ins->mirRaw()->toInstruction()); |
17410 | masm.branchTest32(Assembler::NonZero, |
17411 | Address(stringReg, JSString::offsetOfFlags()), |
17412 | Imm32(JSString::ATOM_BIT), ool->rejoin()); |
17413 | |
17414 | masm.branchTest32(Assembler::Zero, |
17415 | Address(stringReg, JSString::offsetOfFlags()), |
17416 | Imm32(JSString::ATOM_REF_BIT), ool->entry()); |
17417 | masm.loadPtr(Address(stringReg, JSAtomRefString::offsetOfAtom()), stringReg); |
17418 | |
17419 | if (dest.hasValue()) { |
17420 | masm.moveValue( |
17421 | TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)), |
17422 | dest.valueReg()); |
17423 | } else { |
17424 | MOZ_ASSERT(dest.typedReg().gpr() == stringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.typedReg().gpr() == stringReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.typedReg().gpr() == stringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "dest.typedReg().gpr() == stringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg" ")"); do { *((volatile int*)__null) = 17424; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17425 | } |
17426 | |
17427 | emitPreBarrier(slotAddr); |
17428 | masm.storeTypedOrValue(dest, slotAddr); |
17429 | |
17430 | masm.bind(ool->rejoin()); |
17431 | } |
17432 | |
17433 | void CodeGenerator::visitLoadFixedSlotAndAtomize( |
17434 | LLoadFixedSlotAndAtomize* ins) { |
17435 | Register obj = ToRegister(ins->getOperand(0)); |
17436 | Register temp = ToRegister(ins->temp0()); |
17437 | size_t slot = ins->mir()->slot(); |
17438 | ValueOperand result = ToOutValue(ins); |
17439 | |
17440 | Address slotAddr(obj, NativeObject::getFixedSlotOffset(slot)); |
17441 | masm.loadValue(slotAddr, result); |
17442 | |
17443 | Label notString; |
17444 | masm.branchTestString(Assembler::NotEqual, result, ¬String); |
17445 | masm.unboxString(result, temp); |
17446 | emitMaybeAtomizeSlot(ins, temp, slotAddr, result); |
17447 | masm.bind(¬String); |
17448 | } |
17449 | |
17450 | void CodeGenerator::visitLoadDynamicSlotAndAtomize( |
17451 | LLoadDynamicSlotAndAtomize* ins) { |
17452 | ValueOperand result = ToOutValue(ins); |
17453 | Register temp = ToRegister(ins->temp0()); |
17454 | Register base = ToRegister(ins->input()); |
17455 | int32_t offset = ins->mir()->slot() * sizeof(js::Value); |
17456 | |
17457 | Address slotAddr(base, offset); |
17458 | masm.loadValue(slotAddr, result); |
17459 | |
17460 | Label notString; |
17461 | masm.branchTestString(Assembler::NotEqual, result, ¬String); |
17462 | masm.unboxString(result, temp); |
17463 | emitMaybeAtomizeSlot(ins, temp, slotAddr, result); |
17464 | masm.bind(¬String); |
17465 | } |
17466 | |
17467 | void CodeGenerator::visitLoadFixedSlotUnboxAndAtomize( |
17468 | LLoadFixedSlotUnboxAndAtomize* ins) { |
17469 | const MLoadFixedSlotAndUnbox* mir = ins->mir(); |
17470 | MOZ_ASSERT(mir->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->type() == MIRType::String)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mir->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 17470; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17471 | Register input = ToRegister(ins->object()); |
17472 | AnyRegister result = ToAnyRegister(ins->output()); |
17473 | size_t slot = mir->slot(); |
17474 | |
17475 | Address slotAddr(input, NativeObject::getFixedSlotOffset(slot)); |
17476 | |
17477 | Label bail; |
17478 | EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result, |
17479 | &bail); |
17480 | emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr, |
17481 | TypedOrValueRegister(MIRType::String, result)); |
17482 | |
17483 | if (mir->fallible()) { |
17484 | bailoutFrom(&bail, ins->snapshot()); |
17485 | } |
17486 | } |
17487 | |
17488 | void CodeGenerator::visitLoadDynamicSlotUnboxAndAtomize( |
17489 | LLoadDynamicSlotUnboxAndAtomize* ins) { |
17490 | const MLoadDynamicSlotAndUnbox* mir = ins->mir(); |
17491 | MOZ_ASSERT(mir->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->type() == MIRType::String)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mir->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 17491; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17492 | Register input = ToRegister(ins->slots()); |
17493 | AnyRegister result = ToAnyRegister(ins->output()); |
17494 | size_t slot = mir->slot(); |
17495 | |
17496 | Address slotAddr(input, slot * sizeof(JS::Value)); |
17497 | |
17498 | Label bail; |
17499 | EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result, |
17500 | &bail); |
17501 | emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr, |
17502 | TypedOrValueRegister(MIRType::String, result)); |
17503 | |
17504 | if (mir->fallible()) { |
17505 | bailoutFrom(&bail, ins->snapshot()); |
17506 | } |
17507 | } |
17508 | |
17509 | void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) { |
17510 | const Register obj = ToRegister(ins->getOperand(0)); |
17511 | const ValueOperand value = ToValue(ins, LAddAndStoreSlot::ValueIndex); |
17512 | const Register maybeTemp = ToTempRegisterOrInvalid(ins->temp0()); |
17513 | |
17514 | Shape* shape = ins->mir()->shape(); |
17515 | masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) { |
17516 | EmitPreBarrier(masm, addr, MIRType::Shape); |
17517 | }); |
17518 | |
17519 | // Perform the store. No pre-barrier required since this is a new |
17520 | // initialization. |
17521 | |
17522 | uint32_t offset = ins->mir()->slotOffset(); |
17523 | if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) { |
17524 | Address slot(obj, offset); |
17525 | masm.storeValue(value, slot); |
17526 | } else { |
17527 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), maybeTemp); |
17528 | Address slot(maybeTemp, offset); |
17529 | masm.storeValue(value, slot); |
17530 | } |
17531 | } |
17532 | |
17533 | void CodeGenerator::visitAllocateAndStoreSlot(LAllocateAndStoreSlot* ins) { |
17534 | const Register obj = ToRegister(ins->getOperand(0)); |
17535 | const ValueOperand value = ToValue(ins, LAllocateAndStoreSlot::ValueIndex); |
17536 | const Register temp0 = ToRegister(ins->temp0()); |
17537 | const Register temp1 = ToRegister(ins->temp1()); |
17538 | |
17539 | masm.Push(obj); |
17540 | masm.Push(value); |
17541 | |
17542 | using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount); |
17543 | masm.setupAlignedABICall(); |
17544 | masm.loadJSContext(temp0); |
17545 | masm.passABIArg(temp0); |
17546 | masm.passABIArg(obj); |
17547 | masm.move32(Imm32(ins->mir()->numNewSlots()), temp1); |
17548 | masm.passABIArg(temp1); |
17549 | masm.callWithABI<Fn, NativeObject::growSlotsPure>(); |
17550 | masm.storeCallPointerResult(temp0); |
17551 | |
17552 | masm.Pop(value); |
17553 | masm.Pop(obj); |
17554 | |
17555 | bailoutIfFalseBool(temp0, ins->snapshot()); |
17556 | |
17557 | masm.storeObjShape(ins->mir()->shape(), obj, |
17558 | [](MacroAssembler& masm, const Address& addr) { |
17559 | EmitPreBarrier(masm, addr, MIRType::Shape); |
17560 | }); |
17561 | |
17562 | // Perform the store. No pre-barrier required since this is a new |
17563 | // initialization. |
17564 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0); |
17565 | Address slot(temp0, ins->mir()->slotOffset()); |
17566 | masm.storeValue(value, slot); |
17567 | } |
17568 | |
17569 | void CodeGenerator::visitAddSlotAndCallAddPropHook( |
17570 | LAddSlotAndCallAddPropHook* ins) { |
17571 | const Register obj = ToRegister(ins->object()); |
17572 | const ValueOperand value = |
17573 | ToValue(ins, LAddSlotAndCallAddPropHook::ValueIndex); |
17574 | |
17575 | pushArg(ImmGCPtr(ins->mir()->shape())); |
17576 | pushArg(value); |
17577 | pushArg(obj); |
17578 | |
17579 | using Fn = |
17580 | bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, Handle<Shape*>); |
17581 | callVM<Fn, AddSlotAndCallAddPropHook>(ins); |
17582 | } |
17583 | |
17584 | void CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV* ins) { |
17585 | const Register obj = ToRegister(ins->getOperand(0)); |
17586 | size_t slot = ins->mir()->slot(); |
17587 | |
17588 | const ValueOperand value = ToValue(ins, LStoreFixedSlotV::ValueIndex); |
17589 | |
17590 | Address address(obj, NativeObject::getFixedSlotOffset(slot)); |
17591 | if (ins->mir()->needsBarrier()) { |
17592 | emitPreBarrier(address); |
17593 | } |
17594 | |
17595 | masm.storeValue(value, address); |
17596 | } |
17597 | |
17598 | void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) { |
17599 | const Register obj = ToRegister(ins->getOperand(0)); |
17600 | size_t slot = ins->mir()->slot(); |
17601 | |
17602 | const LAllocation* value = ins->value(); |
17603 | MIRType valueType = ins->mir()->value()->type(); |
17604 | |
17605 | Address address(obj, NativeObject::getFixedSlotOffset(slot)); |
17606 | if (ins->mir()->needsBarrier()) { |
17607 | emitPreBarrier(address); |
17608 | } |
17609 | |
17610 | ConstantOrRegister nvalue = |
17611 | value->isConstant() |
17612 | ? ConstantOrRegister(value->toConstant()->toJSValue()) |
17613 | : TypedOrValueRegister(valueType, ToAnyRegister(value)); |
17614 | masm.storeConstantOrRegister(nvalue, address); |
17615 | } |
17616 | |
17617 | void CodeGenerator::visitGetNameCache(LGetNameCache* ins) { |
17618 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17619 | Register envChain = ToRegister(ins->envObj()); |
17620 | ValueOperand output = ToOutValue(ins); |
17621 | Register temp = ToRegister(ins->temp0()); |
17622 | |
17623 | IonGetNameIC ic(liveRegs, envChain, output, temp); |
17624 | addIC(ins, allocateIC(ic)); |
17625 | } |
17626 | |
17627 | void CodeGenerator::addGetPropertyCache(LInstruction* ins, |
17628 | LiveRegisterSet liveRegs, |
17629 | TypedOrValueRegister value, |
17630 | const ConstantOrRegister& id, |
17631 | ValueOperand output) { |
17632 | CacheKind kind = CacheKind::GetElem; |
17633 | if (id.constant() && id.value().isString()) { |
17634 | JSString* idString = id.value().toString(); |
17635 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
17636 | kind = CacheKind::GetProp; |
17637 | } |
17638 | } |
17639 | IonGetPropertyIC cache(kind, liveRegs, value, id, output); |
17640 | addIC(ins, allocateIC(cache)); |
17641 | } |
17642 | |
17643 | void CodeGenerator::addSetPropertyCache(LInstruction* ins, |
17644 | LiveRegisterSet liveRegs, |
17645 | Register objReg, Register temp, |
17646 | const ConstantOrRegister& id, |
17647 | const ConstantOrRegister& value, |
17648 | bool strict) { |
17649 | CacheKind kind = CacheKind::SetElem; |
17650 | if (id.constant() && id.value().isString()) { |
17651 | JSString* idString = id.value().toString(); |
17652 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
17653 | kind = CacheKind::SetProp; |
17654 | } |
17655 | } |
17656 | IonSetPropertyIC cache(kind, liveRegs, objReg, temp, id, value, strict); |
17657 | addIC(ins, allocateIC(cache)); |
17658 | } |
17659 | |
17660 | ConstantOrRegister CodeGenerator::toConstantOrRegister(LInstruction* lir, |
17661 | size_t n, MIRType type) { |
17662 | if (type == MIRType::Value) { |
17663 | return TypedOrValueRegister(ToValue(lir, n)); |
17664 | } |
17665 | |
17666 | const LAllocation* value = lir->getOperand(n); |
17667 | if (value->isConstant()) { |
17668 | return ConstantOrRegister(value->toConstant()->toJSValue()); |
17669 | } |
17670 | |
17671 | return TypedOrValueRegister(type, ToAnyRegister(value)); |
17672 | } |
17673 | |
17674 | void CodeGenerator::visitGetPropertyCache(LGetPropertyCache* ins) { |
17675 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17676 | TypedOrValueRegister value = |
17677 | toConstantOrRegister(ins, LGetPropertyCache::ValueIndex, |
17678 | ins->mir()->value()->type()) |
17679 | .reg(); |
17680 | ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCache::IdIndex, |
17681 | ins->mir()->idval()->type()); |
17682 | ValueOperand output = ToOutValue(ins); |
17683 | addGetPropertyCache(ins, liveRegs, value, id, output); |
17684 | } |
17685 | |
17686 | void CodeGenerator::visitGetPropSuperCache(LGetPropSuperCache* ins) { |
17687 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17688 | Register obj = ToRegister(ins->obj()); |
17689 | TypedOrValueRegister receiver = |
17690 | toConstantOrRegister(ins, LGetPropSuperCache::ReceiverIndex, |
17691 | ins->mir()->receiver()->type()) |
17692 | .reg(); |
17693 | ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCache::IdIndex, |
17694 | ins->mir()->idval()->type()); |
17695 | ValueOperand output = ToOutValue(ins); |
17696 | |
17697 | CacheKind kind = CacheKind::GetElemSuper; |
17698 | if (id.constant() && id.value().isString()) { |
17699 | JSString* idString = id.value().toString(); |
17700 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
17701 | kind = CacheKind::GetPropSuper; |
17702 | } |
17703 | } |
17704 | |
17705 | IonGetPropSuperIC cache(kind, liveRegs, obj, receiver, id, output); |
17706 | addIC(ins, allocateIC(cache)); |
17707 | } |
17708 | |
17709 | void CodeGenerator::visitBindNameCache(LBindNameCache* ins) { |
17710 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17711 | Register envChain = ToRegister(ins->environmentChain()); |
17712 | Register output = ToRegister(ins->output()); |
17713 | Register temp = ToRegister(ins->temp0()); |
17714 | |
17715 | IonBindNameIC ic(liveRegs, envChain, output, temp); |
17716 | addIC(ins, allocateIC(ic)); |
17717 | } |
17718 | |
17719 | void CodeGenerator::visitHasOwnCache(LHasOwnCache* ins) { |
17720 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17721 | TypedOrValueRegister value = |
17722 | toConstantOrRegister(ins, LHasOwnCache::ValueIndex, |
17723 | ins->mir()->value()->type()) |
17724 | .reg(); |
17725 | TypedOrValueRegister id = toConstantOrRegister(ins, LHasOwnCache::IdIndex, |
17726 | ins->mir()->idval()->type()) |
17727 | .reg(); |
17728 | Register output = ToRegister(ins->output()); |
17729 | |
17730 | IonHasOwnIC cache(liveRegs, value, id, output); |
17731 | addIC(ins, allocateIC(cache)); |
17732 | } |
17733 | |
17734 | void CodeGenerator::visitCheckPrivateFieldCache(LCheckPrivateFieldCache* ins) { |
17735 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17736 | TypedOrValueRegister value = |
17737 | toConstantOrRegister(ins, LCheckPrivateFieldCache::ValueIndex, |
17738 | ins->mir()->value()->type()) |
17739 | .reg(); |
17740 | TypedOrValueRegister id = |
17741 | toConstantOrRegister(ins, LCheckPrivateFieldCache::IdIndex, |
17742 | ins->mir()->idval()->type()) |
17743 | .reg(); |
17744 | Register output = ToRegister(ins->output()); |
17745 | |
17746 | IonCheckPrivateFieldIC cache(liveRegs, value, id, output); |
17747 | addIC(ins, allocateIC(cache)); |
17748 | } |
17749 | |
17750 | void CodeGenerator::visitNewPrivateName(LNewPrivateName* ins) { |
17751 | pushArg(ImmGCPtr(ins->mir()->name())); |
17752 | |
17753 | using Fn = JS::Symbol* (*)(JSContext*, Handle<JSAtom*>); |
17754 | callVM<Fn, NewPrivateName>(ins); |
17755 | } |
17756 | |
17757 | void CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty* lir) { |
17758 | pushArg(ImmGCPtr(lir->mir()->name())); |
17759 | pushArg(ToValue(lir, LCallDeleteProperty::ValueIndex)); |
17760 | |
17761 | using Fn = bool (*)(JSContext*, HandleValue, Handle<PropertyName*>, bool*); |
17762 | if (lir->mir()->strict()) { |
17763 | callVM<Fn, DelPropOperation<true>>(lir); |
17764 | } else { |
17765 | callVM<Fn, DelPropOperation<false>>(lir); |
17766 | } |
17767 | } |
17768 | |
17769 | void CodeGenerator::visitCallDeleteElement(LCallDeleteElement* lir) { |
17770 | pushArg(ToValue(lir, LCallDeleteElement::IndexIndex)); |
17771 | pushArg(ToValue(lir, LCallDeleteElement::ValueIndex)); |
17772 | |
17773 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*); |
17774 | if (lir->mir()->strict()) { |
17775 | callVM<Fn, DelElemOperation<true>>(lir); |
17776 | } else { |
17777 | callVM<Fn, DelElemOperation<false>>(lir); |
17778 | } |
17779 | } |
17780 | |
17781 | void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) { |
17782 | Register obj = ToRegister(lir->object()); |
17783 | Register iterObj = ToRegister(lir->output()); |
17784 | Register temp = ToRegister(lir->temp0()); |
17785 | Register temp2 = ToRegister(lir->temp1()); |
17786 | Register temp3 = ToRegister(lir->temp2()); |
17787 | |
17788 | using Fn = PropertyIteratorObject* (*)(JSContext*, HandleObject); |
17789 | OutOfLineCode* ool = (lir->mir()->wantsIndices()) |
17790 | ? oolCallVM<Fn, GetIteratorWithIndices>( |
17791 | lir, ArgList(obj), StoreRegisterTo(iterObj)) |
17792 | : oolCallVM<Fn, GetIterator>( |
17793 | lir, ArgList(obj), StoreRegisterTo(iterObj)); |
17794 | |
17795 | masm.maybeLoadIteratorFromShape(obj, iterObj, temp, temp2, temp3, |
17796 | ool->entry()); |
17797 | |
17798 | Register nativeIter = temp; |
17799 | masm.loadPrivate( |
17800 | Address(iterObj, PropertyIteratorObject::offsetOfIteratorSlot()), |
17801 | nativeIter); |
17802 | |
17803 | if (lir->mir()->wantsIndices()) { |
17804 | // At least one consumer of the output of this iterator has been optimized |
17805 | // to use iterator indices. If the cached iterator doesn't include indices, |
17806 | // but it was marked to indicate that we can create them if needed, then we |
17807 | // do a VM call to replace the cached iterator with a fresh iterator |
17808 | // including indices. |
17809 | masm.branchNativeIteratorIndices(Assembler::Equal, nativeIter, temp2, |
17810 | NativeIteratorIndices::AvailableOnRequest, |
17811 | ool->entry()); |
17812 | } |
17813 | |
17814 | Address iterFlagsAddr(nativeIter, NativeIterator::offsetOfFlagsAndCount()); |
17815 | masm.storePtr( |
17816 | obj, Address(nativeIter, NativeIterator::offsetOfObjectBeingIterated())); |
17817 | masm.or32(Imm32(NativeIterator::Flags::Active), iterFlagsAddr); |
17818 | |
17819 | Register enumeratorsAddr = temp2; |
17820 | masm.movePtr(ImmPtr(lir->mir()->enumeratorsAddr()), enumeratorsAddr); |
17821 | masm.registerIterator(enumeratorsAddr, nativeIter, temp3); |
17822 | |
17823 | // Generate post-write barrier for storing to |iterObj->objectBeingIterated_|. |
17824 | // We already know that |iterObj| is tenured, so we only have to check |obj|. |
17825 | Label skipBarrier; |
17826 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp2, &skipBarrier); |
17827 | { |
17828 | LiveRegisterSet save = liveVolatileRegs(lir); |
17829 | save.takeUnchecked(temp); |
17830 | save.takeUnchecked(temp2); |
17831 | save.takeUnchecked(temp3); |
17832 | if (iterObj.volatile_()) { |
17833 | save.addUnchecked(iterObj); |
17834 | } |
17835 | |
17836 | masm.PushRegsInMask(save); |
17837 | emitPostWriteBarrier(iterObj); |
17838 | masm.PopRegsInMask(save); |
17839 | } |
17840 | masm.bind(&skipBarrier); |
17841 | |
17842 | masm.bind(ool->rejoin()); |
17843 | } |
17844 | |
17845 | void CodeGenerator::visitValueToIterator(LValueToIterator* lir) { |
17846 | pushArg(ToValue(lir, LValueToIterator::ValueIndex)); |
17847 | |
17848 | using Fn = PropertyIteratorObject* (*)(JSContext*, HandleValue); |
17849 | callVM<Fn, ValueToIterator>(lir); |
17850 | } |
17851 | |
17852 | void CodeGenerator::visitIteratorHasIndicesAndBranch( |
17853 | LIteratorHasIndicesAndBranch* lir) { |
17854 | Register iterator = ToRegister(lir->iterator()); |
17855 | Register object = ToRegister(lir->object()); |
17856 | Register temp = ToRegister(lir->temp()); |
17857 | Register temp2 = ToRegister(lir->temp2()); |
17858 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
17859 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
17860 | |
17861 | // Check that the iterator has indices available. |
17862 | Address nativeIterAddr(iterator, |
17863 | PropertyIteratorObject::offsetOfIteratorSlot()); |
17864 | masm.loadPrivate(nativeIterAddr, temp); |
17865 | masm.branchNativeIteratorIndices(Assembler::NotEqual, temp, temp2, |
17866 | NativeIteratorIndices::Valid, ifFalse); |
17867 | |
17868 | // Guard that the first shape stored in the iterator matches the current |
17869 | // shape of the iterated object. |
17870 | Address firstShapeAddr(temp, NativeIterator::offsetOfFirstShape()); |
17871 | masm.loadPtr(firstShapeAddr, temp); |
17872 | masm.branchTestObjShape(Assembler::NotEqual, object, temp, temp2, object, |
17873 | ifFalse); |
17874 | |
17875 | if (!isNextBlock(lir->ifTrue()->lir())) { |
17876 | masm.jump(ifTrue); |
17877 | } |
17878 | } |
17879 | |
17880 | void CodeGenerator::visitLoadSlotByIteratorIndex( |
17881 | LLoadSlotByIteratorIndex* lir) { |
17882 | Register object = ToRegister(lir->object()); |
17883 | Register iterator = ToRegister(lir->iterator()); |
17884 | Register temp = ToRegister(lir->temp0()); |
17885 | Register temp2 = ToRegister(lir->temp1()); |
17886 | ValueOperand result = ToOutValue(lir); |
17887 | |
17888 | masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2); |
17889 | |
17890 | Label notDynamicSlot, notFixedSlot, done; |
17891 | masm.branch32(Assembler::NotEqual, temp2, |
17892 | Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), |
17893 | ¬DynamicSlot); |
17894 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
17895 | masm.loadValue(BaseValueIndex(temp2, temp), result); |
17896 | masm.jump(&done); |
17897 | |
17898 | masm.bind(¬DynamicSlot); |
17899 | masm.branch32(Assembler::NotEqual, temp2, |
17900 | Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), ¬FixedSlot); |
17901 | // Fixed slot |
17902 | masm.loadValue(BaseValueIndex(object, temp, sizeof(NativeObject)), result); |
17903 | masm.jump(&done); |
17904 | masm.bind(¬FixedSlot); |
17905 | |
17906 | #ifdef DEBUG1 |
17907 | Label kindOkay; |
17908 | masm.branch32(Assembler::Equal, temp2, |
17909 | Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); |
17910 | masm.assumeUnreachable("Invalid PropertyIndex::Kind"); |
17911 | masm.bind(&kindOkay); |
17912 | #endif |
17913 | |
17914 | // Dense element |
17915 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2); |
17916 | Label indexOkay; |
17917 | Address initLength(temp2, ObjectElements::offsetOfInitializedLength()); |
17918 | masm.branch32(Assembler::Above, initLength, temp, &indexOkay); |
17919 | masm.assumeUnreachable("Dense element out of bounds"); |
17920 | masm.bind(&indexOkay); |
17921 | |
17922 | masm.loadValue(BaseObjectElementIndex(temp2, temp), result); |
17923 | masm.bind(&done); |
17924 | } |
17925 | |
17926 | void CodeGenerator::visitStoreSlotByIteratorIndex( |
17927 | LStoreSlotByIteratorIndex* lir) { |
17928 | Register object = ToRegister(lir->object()); |
17929 | Register iterator = ToRegister(lir->iterator()); |
17930 | ValueOperand value = ToValue(lir, LStoreSlotByIteratorIndex::ValueIndex); |
17931 | Register temp = ToRegister(lir->temp0()); |
17932 | Register temp2 = ToRegister(lir->temp1()); |
17933 | |
17934 | masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2); |
17935 | |
17936 | Label notDynamicSlot, notFixedSlot, done, doStore; |
17937 | masm.branch32(Assembler::NotEqual, temp2, |
17938 | Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), |
17939 | ¬DynamicSlot); |
17940 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
17941 | masm.computeEffectiveAddress(BaseValueIndex(temp2, temp), temp); |
17942 | masm.jump(&doStore); |
17943 | |
17944 | masm.bind(¬DynamicSlot); |
17945 | masm.branch32(Assembler::NotEqual, temp2, |
17946 | Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), ¬FixedSlot); |
17947 | // Fixed slot |
17948 | masm.computeEffectiveAddress( |
17949 | BaseValueIndex(object, temp, sizeof(NativeObject)), temp); |
17950 | masm.jump(&doStore); |
17951 | masm.bind(¬FixedSlot); |
17952 | |
17953 | #ifdef DEBUG1 |
17954 | Label kindOkay; |
17955 | masm.branch32(Assembler::Equal, temp2, |
17956 | Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); |
17957 | masm.assumeUnreachable("Invalid PropertyIndex::Kind"); |
17958 | masm.bind(&kindOkay); |
17959 | #endif |
17960 | |
17961 | // Dense element |
17962 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2); |
17963 | Label indexOkay; |
17964 | Address initLength(temp2, ObjectElements::offsetOfInitializedLength()); |
17965 | masm.branch32(Assembler::Above, initLength, temp, &indexOkay); |
17966 | masm.assumeUnreachable("Dense element out of bounds"); |
17967 | masm.bind(&indexOkay); |
17968 | |
17969 | BaseObjectElementIndex elementAddress(temp2, temp); |
17970 | masm.computeEffectiveAddress(elementAddress, temp); |
17971 | |
17972 | masm.bind(&doStore); |
17973 | Address storeAddress(temp, 0); |
17974 | emitPreBarrier(storeAddress); |
17975 | masm.storeValue(value, storeAddress); |
17976 | |
17977 | masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp2, &done); |
17978 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp2, &done); |
17979 | |
17980 | saveVolatile(temp2); |
17981 | emitPostWriteBarrier(object); |
17982 | restoreVolatile(temp2); |
17983 | |
17984 | masm.bind(&done); |
17985 | } |
17986 | |
17987 | void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) { |
17988 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17989 | Register objReg = ToRegister(ins->object()); |
17990 | Register temp = ToRegister(ins->temp0()); |
17991 | |
17992 | ConstantOrRegister id = toConstantOrRegister(ins, LSetPropertyCache::IdIndex, |
17993 | ins->mir()->idval()->type()); |
17994 | ConstantOrRegister value = toConstantOrRegister( |
17995 | ins, LSetPropertyCache::ValueIndex, ins->mir()->value()->type()); |
17996 | |
17997 | addSetPropertyCache(ins, liveRegs, objReg, temp, id, value, |
17998 | ins->mir()->strict()); |
17999 | } |
18000 | |
18001 | void CodeGenerator::visitThrow(LThrow* lir) { |
18002 | pushArg(ToValue(lir, LThrow::ValueIndex)); |
18003 | |
18004 | using Fn = bool (*)(JSContext*, HandleValue); |
18005 | callVM<Fn, js::ThrowOperation>(lir); |
18006 | } |
18007 | |
18008 | void CodeGenerator::visitThrowWithStack(LThrowWithStack* lir) { |
18009 | pushArg(ToValue(lir, LThrowWithStack::StackIndex)); |
18010 | pushArg(ToValue(lir, LThrowWithStack::ValueIndex)); |
18011 | |
18012 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue); |
18013 | callVM<Fn, js::ThrowWithStackOperation>(lir); |
18014 | } |
18015 | |
18016 | class OutOfLineTypeOfV : public OutOfLineCodeBase<CodeGenerator> { |
18017 | LTypeOfV* ins_; |
18018 | |
18019 | public: |
18020 | explicit OutOfLineTypeOfV(LTypeOfV* ins) : ins_(ins) {} |
18021 | |
18022 | void accept(CodeGenerator* codegen) override { |
18023 | codegen->visitOutOfLineTypeOfV(this); |
18024 | } |
18025 | LTypeOfV* ins() const { return ins_; } |
18026 | }; |
18027 | |
18028 | void CodeGenerator::emitTypeOfJSType(JSValueType type, Register output) { |
18029 | switch (type) { |
18030 | case JSVAL_TYPE_OBJECT: |
18031 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
18032 | break; |
18033 | case JSVAL_TYPE_DOUBLE: |
18034 | case JSVAL_TYPE_INT32: |
18035 | masm.move32(Imm32(JSTYPE_NUMBER), output); |
18036 | break; |
18037 | case JSVAL_TYPE_BOOLEAN: |
18038 | masm.move32(Imm32(JSTYPE_BOOLEAN), output); |
18039 | break; |
18040 | case JSVAL_TYPE_UNDEFINED: |
18041 | masm.move32(Imm32(JSTYPE_UNDEFINED), output); |
18042 | break; |
18043 | case JSVAL_TYPE_NULL: |
18044 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
18045 | break; |
18046 | case JSVAL_TYPE_STRING: |
18047 | masm.move32(Imm32(JSTYPE_STRING), output); |
18048 | break; |
18049 | case JSVAL_TYPE_SYMBOL: |
18050 | masm.move32(Imm32(JSTYPE_SYMBOL), output); |
18051 | break; |
18052 | case JSVAL_TYPE_BIGINT: |
18053 | masm.move32(Imm32(JSTYPE_BIGINT), output); |
18054 | break; |
18055 | default: |
18056 | MOZ_CRASH("Unsupported JSValueType")do { do { } while (false); MOZ_ReportCrash("" "Unsupported JSValueType" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18056); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported JSValueType" ")"); do { *((volatile int*)__null) = 18056; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18057 | } |
18058 | } |
18059 | |
18060 | void CodeGenerator::emitTypeOfCheck(JSValueType type, Register tag, |
18061 | Register output, Label* done, |
18062 | Label* oolObject) { |
18063 | Label notMatch; |
18064 | switch (type) { |
18065 | case JSVAL_TYPE_OBJECT: |
18066 | // The input may be a callable object (result is "function") or |
18067 | // may emulate undefined (result is "undefined"). Use an OOL path. |
18068 | masm.branchTestObject(Assembler::Equal, tag, oolObject); |
18069 | return; |
18070 | case JSVAL_TYPE_DOUBLE: |
18071 | case JSVAL_TYPE_INT32: |
18072 | masm.branchTestNumber(Assembler::NotEqual, tag, ¬Match); |
18073 | break; |
18074 | default: |
18075 | masm.branchTestType(Assembler::NotEqual, tag, type, ¬Match); |
18076 | break; |
18077 | } |
18078 | |
18079 | emitTypeOfJSType(type, output); |
18080 | masm.jump(done); |
18081 | masm.bind(¬Match); |
18082 | } |
18083 | |
18084 | void CodeGenerator::visitTypeOfV(LTypeOfV* lir) { |
18085 | const ValueOperand value = ToValue(lir, LTypeOfV::InputIndex); |
18086 | Register output = ToRegister(lir->output()); |
18087 | Register tag = masm.extractTag(value, output); |
18088 | |
18089 | Label done; |
18090 | |
18091 | auto* ool = new (alloc()) OutOfLineTypeOfV(lir); |
18092 | addOutOfLineCode(ool, lir->mir()); |
18093 | |
18094 | const std::initializer_list<JSValueType> defaultOrder = { |
18095 | JSVAL_TYPE_OBJECT, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_UNDEFINED, |
18096 | JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_STRING, |
18097 | JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT}; |
18098 | |
18099 | mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder); |
18100 | |
18101 | // Generate checks for previously observed types first. |
18102 | // The TypeDataList is sorted by descending frequency. |
18103 | for (auto& observed : lir->mir()->observedTypes()) { |
18104 | JSValueType type = observed.type(); |
18105 | |
18106 | // Unify number types. |
18107 | if (type == JSVAL_TYPE_INT32) { |
18108 | type = JSVAL_TYPE_DOUBLE; |
18109 | } |
18110 | |
18111 | remaining -= type; |
18112 | |
18113 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
18114 | } |
18115 | |
18116 | // Generate checks for remaining types. |
18117 | for (auto type : defaultOrder) { |
18118 | if (!remaining.contains(type)) { |
18119 | continue; |
18120 | } |
18121 | remaining -= type; |
18122 | |
18123 | if (remaining.isEmpty() && type != JSVAL_TYPE_OBJECT) { |
18124 | // We can skip the check for the last remaining type, unless the type is |
18125 | // JSVAL_TYPE_OBJECT, which may have to go through the OOL path. |
18126 | #ifdef DEBUG1 |
18127 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
18128 | masm.assumeUnreachable("Unexpected Value type in visitTypeOfV"); |
18129 | #else |
18130 | emitTypeOfJSType(type, output); |
18131 | #endif |
18132 | } else { |
18133 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
18134 | } |
18135 | } |
18136 | MOZ_ASSERT(remaining.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(remaining.isEmpty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(remaining.isEmpty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("remaining.isEmpty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18136); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { *((volatile int*)__null) = 18136; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
18137 | |
18138 | masm.bind(&done); |
18139 | masm.bind(ool->rejoin()); |
18140 | } |
18141 | |
18142 | void CodeGenerator::emitTypeOfObject(Register obj, Register output, |
18143 | Label* done) { |
18144 | Label slowCheck, isObject, isCallable, isUndefined; |
18145 | masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable, |
18146 | &isUndefined); |
18147 | |
18148 | masm.bind(&isCallable); |
18149 | masm.move32(Imm32(JSTYPE_FUNCTION), output); |
18150 | masm.jump(done); |
18151 | |
18152 | masm.bind(&isUndefined); |
18153 | masm.move32(Imm32(JSTYPE_UNDEFINED), output); |
18154 | masm.jump(done); |
18155 | |
18156 | masm.bind(&isObject); |
18157 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
18158 | masm.jump(done); |
18159 | |
18160 | masm.bind(&slowCheck); |
18161 | |
18162 | saveVolatile(output); |
18163 | using Fn = JSType (*)(JSObject*); |
18164 | masm.setupAlignedABICall(); |
18165 | masm.passABIArg(obj); |
18166 | masm.callWithABI<Fn, js::TypeOfObject>(); |
18167 | masm.storeCallInt32Result(output); |
18168 | restoreVolatile(output); |
18169 | } |
18170 | |
18171 | void CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) { |
18172 | LTypeOfV* ins = ool->ins(); |
18173 | |
18174 | ValueOperand input = ToValue(ins, LTypeOfV::InputIndex); |
18175 | Register temp = ToTempUnboxRegister(ins->temp0()); |
18176 | Register output = ToRegister(ins->output()); |
18177 | |
18178 | Register obj = masm.extractObject(input, temp); |
18179 | emitTypeOfObject(obj, output, ool->rejoin()); |
18180 | masm.jump(ool->rejoin()); |
18181 | } |
18182 | |
18183 | void CodeGenerator::visitTypeOfO(LTypeOfO* lir) { |
18184 | Register obj = ToRegister(lir->object()); |
18185 | Register output = ToRegister(lir->output()); |
18186 | |
18187 | Label done; |
18188 | emitTypeOfObject(obj, output, &done); |
18189 | masm.bind(&done); |
18190 | } |
18191 | |
18192 | void CodeGenerator::visitTypeOfName(LTypeOfName* lir) { |
18193 | Register input = ToRegister(lir->input()); |
18194 | Register output = ToRegister(lir->output()); |
18195 | |
18196 | #ifdef DEBUG1 |
18197 | Label ok; |
18198 | masm.branch32(Assembler::Below, input, Imm32(JSTYPE_LIMIT), &ok); |
18199 | masm.assumeUnreachable("bad JSType"); |
18200 | masm.bind(&ok); |
18201 | #endif |
18202 | |
18203 | static_assert(JSTYPE_UNDEFINED == 0); |
18204 | |
18205 | masm.movePtr(ImmPtr(&gen->runtime->names().undefined), output); |
18206 | masm.loadPtr(BaseIndex(output, input, ScalePointer), output); |
18207 | } |
18208 | |
18209 | class OutOfLineTypeOfIsNonPrimitiveV : public OutOfLineCodeBase<CodeGenerator> { |
18210 | LTypeOfIsNonPrimitiveV* ins_; |
18211 | |
18212 | public: |
18213 | explicit OutOfLineTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* ins) |
18214 | : ins_(ins) {} |
18215 | |
18216 | void accept(CodeGenerator* codegen) override { |
18217 | codegen->visitOutOfLineTypeOfIsNonPrimitiveV(this); |
18218 | } |
18219 | auto* ins() const { return ins_; } |
18220 | }; |
18221 | |
18222 | class OutOfLineTypeOfIsNonPrimitiveO : public OutOfLineCodeBase<CodeGenerator> { |
18223 | LTypeOfIsNonPrimitiveO* ins_; |
18224 | |
18225 | public: |
18226 | explicit OutOfLineTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* ins) |
18227 | : ins_(ins) {} |
18228 | |
18229 | void accept(CodeGenerator* codegen) override { |
18230 | codegen->visitOutOfLineTypeOfIsNonPrimitiveO(this); |
18231 | } |
18232 | auto* ins() const { return ins_; } |
18233 | }; |
18234 | |
18235 | void CodeGenerator::emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj, |
18236 | Register output) { |
18237 | saveVolatile(output); |
18238 | using Fn = JSType (*)(JSObject*); |
18239 | masm.setupAlignedABICall(); |
18240 | masm.passABIArg(obj); |
18241 | masm.callWithABI<Fn, js::TypeOfObject>(); |
18242 | masm.storeCallInt32Result(output); |
18243 | restoreVolatile(output); |
18244 | |
18245 | auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false); |
18246 | masm.cmp32Set(cond, output, Imm32(mir->jstype()), output); |
18247 | } |
18248 | |
18249 | void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveV( |
18250 | OutOfLineTypeOfIsNonPrimitiveV* ool) { |
18251 | auto* ins = ool->ins(); |
18252 | ValueOperand input = ToValue(ins, LTypeOfIsNonPrimitiveV::InputIndex); |
18253 | Register output = ToRegister(ins->output()); |
18254 | Register temp = ToTempUnboxRegister(ins->temp0()); |
18255 | |
18256 | Register obj = masm.extractObject(input, temp); |
18257 | |
18258 | emitTypeOfIsObjectOOL(ins->mir(), obj, output); |
18259 | |
18260 | masm.jump(ool->rejoin()); |
18261 | } |
18262 | |
18263 | void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveO( |
18264 | OutOfLineTypeOfIsNonPrimitiveO* ool) { |
18265 | auto* ins = ool->ins(); |
18266 | Register input = ToRegister(ins->input()); |
18267 | Register output = ToRegister(ins->output()); |
18268 | |
18269 | emitTypeOfIsObjectOOL(ins->mir(), input, output); |
18270 | |
18271 | masm.jump(ool->rejoin()); |
18272 | } |
18273 | |
18274 | void CodeGenerator::emitTypeOfIsObject(MTypeOfIs* mir, Register obj, |
18275 | Register output, Label* success, |
18276 | Label* fail, Label* slowCheck) { |
18277 | Label* isObject = fail; |
18278 | Label* isFunction = fail; |
18279 | Label* isUndefined = fail; |
18280 | |
18281 | switch (mir->jstype()) { |
18282 | case JSTYPE_UNDEFINED: |
18283 | isUndefined = success; |
18284 | break; |
18285 | |
18286 | case JSTYPE_OBJECT: |
18287 | isObject = success; |
18288 | break; |
18289 | |
18290 | case JSTYPE_FUNCTION: |
18291 | isFunction = success; |
18292 | break; |
18293 | |
18294 | case JSTYPE_STRING: |
18295 | case JSTYPE_NUMBER: |
18296 | case JSTYPE_BOOLEAN: |
18297 | case JSTYPE_SYMBOL: |
18298 | case JSTYPE_BIGINT: |
18299 | #ifdef ENABLE_RECORD_TUPLE |
18300 | case JSTYPE_RECORD: |
18301 | case JSTYPE_TUPLE: |
18302 | #endif |
18303 | case JSTYPE_LIMIT: |
18304 | MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18304); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type" ")"); do { *((volatile int*)__null) = 18304; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18305 | } |
18306 | |
18307 | masm.typeOfObject(obj, output, slowCheck, isObject, isFunction, isUndefined); |
18308 | |
18309 | auto op = mir->jsop(); |
18310 | |
18311 | Label done; |
18312 | masm.bind(fail); |
18313 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
18314 | masm.jump(&done); |
18315 | masm.bind(success); |
18316 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
18317 | masm.bind(&done); |
18318 | } |
18319 | |
18320 | void CodeGenerator::visitTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* lir) { |
18321 | ValueOperand input = ToValue(lir, LTypeOfIsNonPrimitiveV::InputIndex); |
18322 | Register output = ToRegister(lir->output()); |
18323 | Register temp = ToTempUnboxRegister(lir->temp0()); |
18324 | |
18325 | auto* mir = lir->mir(); |
18326 | |
18327 | auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveV(lir); |
18328 | addOutOfLineCode(ool, mir); |
18329 | |
18330 | Label success, fail; |
18331 | |
18332 | switch (mir->jstype()) { |
18333 | case JSTYPE_UNDEFINED: { |
18334 | ScratchTagScope tag(masm, input); |
18335 | masm.splitTagForTest(input, tag); |
18336 | |
18337 | masm.branchTestUndefined(Assembler::Equal, tag, &success); |
18338 | masm.branchTestObject(Assembler::NotEqual, tag, &fail); |
18339 | break; |
18340 | } |
18341 | |
18342 | case JSTYPE_OBJECT: { |
18343 | ScratchTagScope tag(masm, input); |
18344 | masm.splitTagForTest(input, tag); |
18345 | |
18346 | masm.branchTestNull(Assembler::Equal, tag, &success); |
18347 | masm.branchTestObject(Assembler::NotEqual, tag, &fail); |
18348 | break; |
18349 | } |
18350 | |
18351 | case JSTYPE_FUNCTION: { |
18352 | masm.branchTestObject(Assembler::NotEqual, input, &fail); |
18353 | break; |
18354 | } |
18355 | |
18356 | case JSTYPE_STRING: |
18357 | case JSTYPE_NUMBER: |
18358 | case JSTYPE_BOOLEAN: |
18359 | case JSTYPE_SYMBOL: |
18360 | case JSTYPE_BIGINT: |
18361 | #ifdef ENABLE_RECORD_TUPLE |
18362 | case JSTYPE_RECORD: |
18363 | case JSTYPE_TUPLE: |
18364 | #endif |
18365 | case JSTYPE_LIMIT: |
18366 | MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18366); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type" ")"); do { *((volatile int*)__null) = 18366; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18367 | } |
18368 | |
18369 | Register obj = masm.extractObject(input, temp); |
18370 | |
18371 | emitTypeOfIsObject(mir, obj, output, &success, &fail, ool->entry()); |
18372 | |
18373 | masm.bind(ool->rejoin()); |
18374 | } |
18375 | |
18376 | void CodeGenerator::visitTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* lir) { |
18377 | Register input = ToRegister(lir->input()); |
18378 | Register output = ToRegister(lir->output()); |
18379 | |
18380 | auto* mir = lir->mir(); |
18381 | |
18382 | auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveO(lir); |
18383 | addOutOfLineCode(ool, mir); |
18384 | |
18385 | Label success, fail; |
18386 | emitTypeOfIsObject(mir, input, output, &success, &fail, ool->entry()); |
18387 | |
18388 | masm.bind(ool->rejoin()); |
18389 | } |
18390 | |
18391 | void CodeGenerator::visitTypeOfIsPrimitive(LTypeOfIsPrimitive* lir) { |
18392 | ValueOperand input = ToValue(lir, LTypeOfIsPrimitive::InputIndex); |
18393 | Register output = ToRegister(lir->output()); |
18394 | |
18395 | auto* mir = lir->mir(); |
18396 | auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false); |
18397 | |
18398 | switch (mir->jstype()) { |
18399 | case JSTYPE_STRING: |
18400 | masm.testStringSet(cond, input, output); |
18401 | break; |
18402 | case JSTYPE_NUMBER: |
18403 | masm.testNumberSet(cond, input, output); |
18404 | break; |
18405 | case JSTYPE_BOOLEAN: |
18406 | masm.testBooleanSet(cond, input, output); |
18407 | break; |
18408 | case JSTYPE_SYMBOL: |
18409 | masm.testSymbolSet(cond, input, output); |
18410 | break; |
18411 | case JSTYPE_BIGINT: |
18412 | masm.testBigIntSet(cond, input, output); |
18413 | break; |
18414 | |
18415 | case JSTYPE_UNDEFINED: |
18416 | case JSTYPE_OBJECT: |
18417 | case JSTYPE_FUNCTION: |
18418 | #ifdef ENABLE_RECORD_TUPLE |
18419 | case JSTYPE_RECORD: |
18420 | case JSTYPE_TUPLE: |
18421 | #endif |
18422 | case JSTYPE_LIMIT: |
18423 | MOZ_CRASH("Non-primitive type")do { do { } while (false); MOZ_ReportCrash("" "Non-primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18423); AnnotateMozCrashReason("MOZ_CRASH(" "Non-primitive type" ")"); do { *((volatile int*)__null) = 18423; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18424 | } |
18425 | } |
18426 | |
18427 | void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) { |
18428 | pushArg(ToValue(lir, LToAsyncIter::NextMethodIndex)); |
18429 | pushArg(ToRegister(lir->iterator())); |
18430 | |
18431 | using Fn = JSObject* (*)(JSContext*, HandleObject, HandleValue); |
18432 | callVM<Fn, js::CreateAsyncFromSyncIterator>(lir); |
18433 | } |
18434 | |
18435 | void CodeGenerator::visitToPropertyKeyCache(LToPropertyKeyCache* lir) { |
18436 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
18437 | ValueOperand input = ToValue(lir, LToPropertyKeyCache::InputIndex); |
18438 | ValueOperand output = ToOutValue(lir); |
18439 | |
18440 | IonToPropertyKeyIC ic(liveRegs, input, output); |
18441 | addIC(lir, allocateIC(ic)); |
18442 | } |
18443 | |
18444 | void CodeGenerator::visitLoadElementV(LLoadElementV* load) { |
18445 | Register elements = ToRegister(load->elements()); |
18446 | const ValueOperand out = ToOutValue(load); |
18447 | |
18448 | if (load->index()->isConstant()) { |
18449 | NativeObject::elementsSizeMustNotOverflow(); |
18450 | int32_t offset = ToInt32(load->index()) * sizeof(Value); |
18451 | masm.loadValue(Address(elements, offset), out); |
18452 | } else { |
18453 | masm.loadValue(BaseObjectElementIndex(elements, ToRegister(load->index())), |
18454 | out); |
18455 | } |
18456 | |
18457 | Label testMagic; |
18458 | masm.branchTestMagic(Assembler::Equal, out, &testMagic); |
18459 | bailoutFrom(&testMagic, load->snapshot()); |
18460 | } |
18461 | |
18462 | void CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) { |
18463 | Register elements = ToRegister(lir->elements()); |
18464 | Register index = ToRegister(lir->index()); |
18465 | Register initLength = ToRegister(lir->initLength()); |
18466 | const ValueOperand out = ToOutValue(lir); |
18467 | |
18468 | const MLoadElementHole* mir = lir->mir(); |
18469 | |
18470 | // If the index is out of bounds, load |undefined|. Otherwise, load the |
18471 | // value. |
18472 | Label outOfBounds, done; |
18473 | masm.spectreBoundsCheck32(index, initLength, out.scratchReg(), &outOfBounds); |
18474 | |
18475 | masm.loadValue(BaseObjectElementIndex(elements, index), out); |
18476 | |
18477 | // If the value wasn't a hole, we're done. Otherwise, we'll load undefined. |
18478 | masm.branchTestMagic(Assembler::NotEqual, out, &done); |
18479 | |
18480 | if (mir->needsNegativeIntCheck()) { |
18481 | Label loadUndefined; |
18482 | masm.jump(&loadUndefined); |
18483 | |
18484 | masm.bind(&outOfBounds); |
18485 | |
18486 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
18487 | |
18488 | masm.bind(&loadUndefined); |
18489 | } else { |
18490 | masm.bind(&outOfBounds); |
18491 | } |
18492 | masm.moveValue(UndefinedValue(), out); |
18493 | |
18494 | masm.bind(&done); |
18495 | } |
18496 | |
18497 | void CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) { |
18498 | Register elements = ToRegister(lir->elements()); |
18499 | Register temp0 = ToTempRegisterOrInvalid(lir->temp0()); |
18500 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
18501 | AnyRegister out = ToAnyRegister(lir->output()); |
18502 | |
18503 | const MLoadUnboxedScalar* mir = lir->mir(); |
18504 | |
18505 | Scalar::Type storageType = mir->storageType(); |
18506 | |
18507 | LiveRegisterSet volatileRegs; |
18508 | if (MacroAssembler::LoadRequiresCall(storageType)) { |
18509 | volatileRegs = liveVolatileRegs(lir); |
18510 | } |
18511 | |
18512 | Label fail; |
18513 | if (lir->index()->isConstant()) { |
18514 | Address source = |
18515 | ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment()); |
18516 | masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail, |
18517 | volatileRegs); |
18518 | } else { |
18519 | BaseIndex source(elements, ToRegister(lir->index()), |
18520 | ScaleFromScalarType(storageType), mir->offsetAdjustment()); |
18521 | masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail, |
18522 | volatileRegs); |
18523 | } |
18524 | |
18525 | if (fail.used()) { |
18526 | bailoutFrom(&fail, lir->snapshot()); |
18527 | } |
18528 | } |
18529 | |
18530 | void CodeGenerator::visitLoadUnboxedInt64(LLoadUnboxedInt64* lir) { |
18531 | Register elements = ToRegister(lir->elements()); |
18532 | Register64 out = ToOutRegister64(lir); |
18533 | |
18534 | const MLoadUnboxedScalar* mir = lir->mir(); |
18535 | |
18536 | Scalar::Type storageType = mir->storageType(); |
18537 | |
18538 | if (lir->index()->isConstant()) { |
18539 | Address source = |
18540 | ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment()); |
18541 | masm.load64(source, out); |
18542 | } else { |
18543 | BaseIndex source(elements, ToRegister(lir->index()), |
18544 | ScaleFromScalarType(storageType), mir->offsetAdjustment()); |
18545 | masm.load64(source, out); |
18546 | } |
18547 | } |
18548 | |
18549 | void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) { |
18550 | Register elements = ToRegister(lir->elements()); |
18551 | const LAllocation* littleEndian = lir->littleEndian(); |
18552 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
18553 | Register temp2 = ToTempRegisterOrInvalid(lir->temp2()); |
18554 | Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64()); |
18555 | AnyRegister out = ToAnyRegister(lir->output()); |
18556 | |
18557 | const MLoadDataViewElement* mir = lir->mir(); |
18558 | Scalar::Type storageType = mir->storageType(); |
18559 | |
18560 | LiveRegisterSet volatileRegs; |
18561 | if (MacroAssembler::LoadRequiresCall(storageType)) { |
18562 | volatileRegs = liveVolatileRegs(lir); |
18563 | } |
18564 | |
18565 | BaseIndex source(elements, ToRegister(lir->index()), TimesOne); |
18566 | |
18567 | bool noSwap = littleEndian->isConstant() && |
18568 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
18569 | |
18570 | // Directly load if no byte swap is needed and the platform supports unaligned |
18571 | // accesses for the access. (Such support is assumed for integer types.) |
18572 | if (noSwap && (!Scalar::isFloatingType(storageType) || |
18573 | MacroAssembler::SupportsFastUnalignedFPAccesses())) { |
18574 | Label fail; |
18575 | masm.loadFromTypedArray(storageType, source, out, temp1, temp2, &fail, |
18576 | volatileRegs); |
18577 | |
18578 | if (fail.used()) { |
18579 | bailoutFrom(&fail, lir->snapshot()); |
18580 | } |
18581 | return; |
18582 | } |
18583 | |
18584 | // Load the value into a gpr register. |
18585 | switch (storageType) { |
18586 | case Scalar::Int16: |
18587 | masm.load16UnalignedSignExtend(source, out.gpr()); |
18588 | break; |
18589 | case Scalar::Uint16: |
18590 | masm.load16UnalignedZeroExtend(source, out.gpr()); |
18591 | break; |
18592 | case Scalar::Int32: |
18593 | masm.load32Unaligned(source, out.gpr()); |
18594 | break; |
18595 | case Scalar::Uint32: |
18596 | masm.load32Unaligned(source, out.isFloat() ? temp1 : out.gpr()); |
18597 | break; |
18598 | case Scalar::Float16: |
18599 | masm.load16UnalignedZeroExtend(source, temp1); |
18600 | break; |
18601 | case Scalar::Float32: |
18602 | masm.load32Unaligned(source, temp1); |
18603 | break; |
18604 | case Scalar::Float64: |
18605 | masm.load64Unaligned(source, temp64); |
18606 | break; |
18607 | case Scalar::Int8: |
18608 | case Scalar::Uint8: |
18609 | case Scalar::Uint8Clamped: |
18610 | case Scalar::BigInt64: |
18611 | case Scalar::BigUint64: |
18612 | default: |
18613 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18613); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18613; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18614 | } |
18615 | |
18616 | if (!noSwap) { |
18617 | // Swap the bytes in the loaded value. |
18618 | Label skip; |
18619 | if (!littleEndian->isConstant()) { |
18620 | masm.branch32( |
18621 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
18622 | ToRegister(littleEndian), Imm32(0), &skip); |
18623 | } |
18624 | |
18625 | switch (storageType) { |
18626 | case Scalar::Int16: |
18627 | masm.byteSwap16SignExtend(out.gpr()); |
18628 | break; |
18629 | case Scalar::Uint16: |
18630 | masm.byteSwap16ZeroExtend(out.gpr()); |
18631 | break; |
18632 | case Scalar::Int32: |
18633 | masm.byteSwap32(out.gpr()); |
18634 | break; |
18635 | case Scalar::Uint32: |
18636 | masm.byteSwap32(out.isFloat() ? temp1 : out.gpr()); |
18637 | break; |
18638 | case Scalar::Float16: |
18639 | masm.byteSwap16ZeroExtend(temp1); |
18640 | break; |
18641 | case Scalar::Float32: |
18642 | masm.byteSwap32(temp1); |
18643 | break; |
18644 | case Scalar::Float64: |
18645 | masm.byteSwap64(temp64); |
18646 | break; |
18647 | case Scalar::Int8: |
18648 | case Scalar::Uint8: |
18649 | case Scalar::Uint8Clamped: |
18650 | case Scalar::BigInt64: |
18651 | case Scalar::BigUint64: |
18652 | default: |
18653 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18653); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18653; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18654 | } |
18655 | |
18656 | if (skip.used()) { |
18657 | masm.bind(&skip); |
18658 | } |
18659 | } |
18660 | |
18661 | // Move the value into the output register. |
18662 | switch (storageType) { |
18663 | case Scalar::Int16: |
18664 | case Scalar::Uint16: |
18665 | case Scalar::Int32: |
18666 | break; |
18667 | case Scalar::Uint32: |
18668 | if (out.isFloat()) { |
18669 | masm.convertUInt32ToDouble(temp1, out.fpu()); |
18670 | } else { |
18671 | // Bail out if the value doesn't fit into a signed int32 value. This |
18672 | // is what allows MLoadDataViewElement to have a type() of |
18673 | // MIRType::Int32 for UInt32 array loads. |
18674 | bailoutTest32(Assembler::Signed, out.gpr(), out.gpr(), lir->snapshot()); |
18675 | } |
18676 | break; |
18677 | case Scalar::Float16: |
18678 | masm.moveGPRToFloat16(temp1, out.fpu(), temp2, volatileRegs); |
18679 | masm.canonicalizeFloat(out.fpu()); |
18680 | break; |
18681 | case Scalar::Float32: |
18682 | masm.moveGPRToFloat32(temp1, out.fpu()); |
18683 | masm.canonicalizeFloat(out.fpu()); |
18684 | break; |
18685 | case Scalar::Float64: |
18686 | masm.moveGPR64ToDouble(temp64, out.fpu()); |
18687 | masm.canonicalizeDouble(out.fpu()); |
18688 | break; |
18689 | case Scalar::Int8: |
18690 | case Scalar::Uint8: |
18691 | case Scalar::Uint8Clamped: |
18692 | case Scalar::BigInt64: |
18693 | case Scalar::BigUint64: |
18694 | default: |
18695 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18695); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18695; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18696 | } |
18697 | } |
18698 | |
18699 | void CodeGenerator::visitLoadDataViewElement64(LLoadDataViewElement64* lir) { |
18700 | Register elements = ToRegister(lir->elements()); |
18701 | const LAllocation* littleEndian = lir->littleEndian(); |
18702 | Register64 out = ToOutRegister64(lir); |
18703 | |
18704 | MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->storageType()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Scalar::isBigIntType(lir->mir()->storageType() ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(Scalar::isBigIntType(lir->mir()->storageType() )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("Scalar::isBigIntType(lir->mir()->storageType())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18704); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->storageType())" ")"); do { *((volatile int*)__null) = 18704; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
18705 | |
18706 | BaseIndex source(elements, ToRegister(lir->index()), TimesOne); |
18707 | |
18708 | bool noSwap = littleEndian->isConstant() && |
18709 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
18710 | |
18711 | // Load the value into a register. |
18712 | masm.load64Unaligned(source, out); |
18713 | |
18714 | if (!noSwap) { |
18715 | // Swap the bytes in the loaded value. |
18716 | Label skip; |
18717 | if (!littleEndian->isConstant()) { |
18718 | masm.branch32( |
18719 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
18720 | ToRegister(littleEndian), Imm32(0), &skip); |
18721 | } |
18722 | |
18723 | masm.byteSwap64(out); |
18724 | |
18725 | if (skip.used()) { |
18726 | masm.bind(&skip); |
18727 | } |
18728 | } |
18729 | } |
18730 | |
18731 | void CodeGenerator::visitLoadTypedArrayElementHole( |
18732 | LLoadTypedArrayElementHole* lir) { |
18733 | Register elements = ToRegister(lir->elements()); |
18734 | Register index = ToRegister(lir->index()); |
18735 | Register length = ToRegister(lir->length()); |
18736 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
18737 | const ValueOperand out = ToOutValue(lir); |
18738 | |
18739 | Register scratch = out.scratchReg(); |
18740 | |
18741 | // Load undefined if index >= length. |
18742 | Label outOfBounds, done; |
18743 | masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds); |
18744 | |
18745 | Scalar::Type arrayType = lir->mir()->arrayType(); |
18746 | |
18747 | LiveRegisterSet volatileRegs; |
18748 | if (MacroAssembler::LoadRequiresCall(arrayType)) { |
18749 | volatileRegs = liveVolatileRegs(lir); |
18750 | } |
18751 | |
18752 | Label fail; |
18753 | BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); |
18754 | MacroAssembler::Uint32Mode uint32Mode = |
18755 | lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble |
18756 | : MacroAssembler::Uint32Mode::FailOnDouble; |
18757 | masm.loadFromTypedArray(arrayType, source, out, uint32Mode, temp, &fail, |
18758 | volatileRegs); |
18759 | masm.jump(&done); |
18760 | |
18761 | masm.bind(&outOfBounds); |
18762 | masm.moveValue(UndefinedValue(), out); |
18763 | |
18764 | if (fail.used()) { |
18765 | bailoutFrom(&fail, lir->snapshot()); |
18766 | } |
18767 | |
18768 | masm.bind(&done); |
18769 | } |
18770 | |
18771 | void CodeGenerator::visitLoadTypedArrayElementHoleBigInt( |
18772 | LLoadTypedArrayElementHoleBigInt* lir) { |
18773 | Register elements = ToRegister(lir->elements()); |
18774 | Register index = ToRegister(lir->index()); |
18775 | Register length = ToRegister(lir->length()); |
18776 | const ValueOperand out = ToOutValue(lir); |
18777 | |
18778 | Register temp = ToRegister(lir->temp()); |
18779 | |
18780 | // On x86 there are not enough registers. In that case reuse the output |
18781 | // registers as temporaries. |
18782 | #ifdef JS_CODEGEN_X86 |
18783 | MOZ_ASSERT(lir->temp64().isBogusTemp())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->temp64().isBogusTemp())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->temp64().isBogusTemp ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->temp64().isBogusTemp()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18783); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->temp64().isBogusTemp()" ")"); do { *((volatile int*)__null) = 18783; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
18784 | Register64 temp64 = out.toRegister64(); |
18785 | #else |
18786 | Register64 temp64 = ToRegister64(lir->temp64()); |
18787 | #endif |
18788 | |
18789 | // Load undefined if index >= length. |
18790 | Label outOfBounds, done; |
18791 | masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds); |
18792 | |
18793 | Scalar::Type arrayType = lir->mir()->arrayType(); |
18794 | BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); |
18795 | masm.load64(source, temp64); |
18796 | |
18797 | #ifdef JS_CODEGEN_X86 |
18798 | Register bigInt = temp; |
18799 | Register maybeTemp = InvalidReg; |
18800 | #else |
18801 | Register bigInt = out.scratchReg(); |
18802 | Register maybeTemp = temp; |
18803 | #endif |
18804 | emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp); |
18805 | |
18806 | masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out); |
18807 | masm.jump(&done); |
18808 | |
18809 | masm.bind(&outOfBounds); |
18810 | masm.moveValue(UndefinedValue(), out); |
18811 | |
18812 | masm.bind(&done); |
18813 | } |
18814 | |
18815 | template <SwitchTableType tableType> |
18816 | class OutOfLineSwitch : public OutOfLineCodeBase<CodeGenerator> { |
18817 | using LabelsVector = Vector<Label, 0, JitAllocPolicy>; |
18818 | using CodeLabelsVector = Vector<CodeLabel, 0, JitAllocPolicy>; |
18819 | LabelsVector labels_; |
18820 | CodeLabelsVector codeLabels_; |
18821 | CodeLabel start_; |
18822 | bool isOutOfLine_; |
18823 | |
18824 | void accept(CodeGenerator* codegen) override { |
18825 | codegen->visitOutOfLineSwitch(this); |
18826 | } |
18827 | |
18828 | public: |
18829 | explicit OutOfLineSwitch(TempAllocator& alloc) |
18830 | : labels_(alloc), codeLabels_(alloc), isOutOfLine_(false) {} |
18831 | |
18832 | CodeLabel* start() { return &start_; } |
18833 | |
18834 | CodeLabelsVector& codeLabels() { return codeLabels_; } |
18835 | LabelsVector& labels() { return labels_; } |
18836 | |
18837 | void jumpToCodeEntries(MacroAssembler& masm, Register index, Register temp) { |
18838 | Register base; |
18839 | if (tableType == SwitchTableType::Inline) { |
18840 | #if defined(JS_CODEGEN_ARM) |
18841 | base = ::js::jit::pc; |
18842 | #else |
18843 | MOZ_CRASH("NYI: SwitchTableType::Inline")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::Inline" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18843); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::Inline" ")"); do { *((volatile int*)__null) = 18843; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18844 | #endif |
18845 | } else { |
18846 | #if defined(JS_CODEGEN_ARM) |
18847 | MOZ_CRASH("NYI: SwitchTableType::OutOfLine")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::OutOfLine" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18847); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine" ")"); do { *((volatile int*)__null) = 18847; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18848 | #else |
18849 | masm.mov(start(), temp); |
18850 | base = temp; |
18851 | #endif |
18852 | } |
18853 | BaseIndex jumpTarget(base, index, ScalePointer); |
18854 | masm.branchToComputedAddress(jumpTarget); |
18855 | } |
18856 | |
18857 | // Register an entry in the switch table. |
18858 | void addTableEntry(MacroAssembler& masm) { |
18859 | if ((!isOutOfLine_ && tableType == SwitchTableType::Inline) || |
18860 | (isOutOfLine_ && tableType == SwitchTableType::OutOfLine)) { |
18861 | CodeLabel cl; |
18862 | masm.writeCodePointer(&cl); |
18863 | masm.propagateOOM(codeLabels_.append(std::move(cl))); |
18864 | } |
18865 | } |
18866 | // Register the code, to which the table will jump to. |
18867 | void addCodeEntry(MacroAssembler& masm) { |
18868 | Label entry; |
18869 | masm.bind(&entry); |
18870 | masm.propagateOOM(labels_.append(std::move(entry))); |
18871 | } |
18872 | |
18873 | void setOutOfLine() { isOutOfLine_ = true; } |
18874 | }; |
18875 | |
18876 | template <SwitchTableType tableType> |
18877 | void CodeGenerator::visitOutOfLineSwitch( |
18878 | OutOfLineSwitch<tableType>* jumpTable) { |
18879 | jumpTable->setOutOfLine(); |
18880 | auto& labels = jumpTable->labels(); |
18881 | |
18882 | if (tableType == SwitchTableType::OutOfLine) { |
18883 | #if defined(JS_CODEGEN_ARM) |
18884 | MOZ_CRASH("NYI: SwitchTableType::OutOfLine")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::OutOfLine" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18884); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine" ")"); do { *((volatile int*)__null) = 18884; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18885 | #elif defined(JS_CODEGEN_NONE) |
18886 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18886); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 18886; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
18887 | #else |
18888 | |
18889 | # if defined(JS_CODEGEN_ARM64) |
18890 | AutoForbidPoolsAndNops afp( |
18891 | &masm, |
18892 | (labels.length() + 1) * (sizeof(void*) / vixl::kInstructionSize)); |
18893 | # endif |
18894 | |
18895 | masm.haltingAlign(sizeof(void*)); |
18896 | |
18897 | // Bind the address of the jump table and reserve the space for code |
18898 | // pointers to jump in the newly generated code. |
18899 | masm.bind(jumpTable->start()); |
18900 | masm.addCodeLabel(*jumpTable->start()); |
18901 | for (size_t i = 0, e = labels.length(); i < e; i++) { |
18902 | jumpTable->addTableEntry(masm); |
18903 | } |
18904 | #endif |
18905 | } |
18906 | |
18907 | // Register all reserved pointers of the jump table to target labels. The |
18908 | // entries of the jump table need to be absolute addresses and thus must be |
18909 | // patched after codegen is finished. |
18910 | auto& codeLabels = jumpTable->codeLabels(); |
18911 | for (size_t i = 0, e = codeLabels.length(); i < e; i++) { |
18912 | auto& cl = codeLabels[i]; |
18913 | cl.target()->bind(labels[i].offset()); |
18914 | masm.addCodeLabel(cl); |
18915 | } |
18916 | } |
18917 | |
18918 | template void CodeGenerator::visitOutOfLineSwitch( |
18919 | OutOfLineSwitch<SwitchTableType::Inline>* jumpTable); |
18920 | template void CodeGenerator::visitOutOfLineSwitch( |
18921 | OutOfLineSwitch<SwitchTableType::OutOfLine>* jumpTable); |
18922 | |
18923 | template <typename T> |
18924 | static inline void StoreToTypedArray(MacroAssembler& masm, |
18925 | Scalar::Type writeType, |
18926 | const LAllocation* value, const T& dest, |
18927 | Register temp, |
18928 | LiveRegisterSet volatileRegs) { |
18929 | if (Scalar::isFloatingType(writeType)) { |
18930 | masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, temp, |
18931 | volatileRegs); |
18932 | } else { |
18933 | if (value->isConstant()) { |
18934 | masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest); |
18935 | } else { |
18936 | masm.storeToTypedIntArray(writeType, ToRegister(value), dest); |
18937 | } |
18938 | } |
18939 | } |
18940 | |
18941 | void CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) { |
18942 | Register elements = ToRegister(lir->elements()); |
18943 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
18944 | const LAllocation* value = lir->value(); |
18945 | |
18946 | const MStoreUnboxedScalar* mir = lir->mir(); |
18947 | |
18948 | Scalar::Type writeType = mir->writeType(); |
18949 | |
18950 | LiveRegisterSet volatileRegs; |
18951 | if (MacroAssembler::StoreRequiresCall(writeType)) { |
18952 | volatileRegs = liveVolatileRegs(lir); |
18953 | } |
18954 | |
18955 | if (lir->index()->isConstant()) { |
18956 | Address dest = ToAddress(elements, lir->index(), writeType); |
18957 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
18958 | } else { |
18959 | BaseIndex dest(elements, ToRegister(lir->index()), |
18960 | ScaleFromScalarType(writeType)); |
18961 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
18962 | } |
18963 | } |
18964 | |
18965 | void CodeGenerator::visitStoreUnboxedInt64(LStoreUnboxedInt64* lir) { |
18966 | Register elements = ToRegister(lir->elements()); |
18967 | Register64 value = ToRegister64(lir->value()); |
18968 | |
18969 | Scalar::Type writeType = lir->mir()->writeType(); |
18970 | |
18971 | if (lir->index()->isConstant()) { |
18972 | Address dest = ToAddress(elements, lir->index(), writeType); |
18973 | masm.storeToTypedBigIntArray(writeType, value, dest); |
18974 | } else { |
18975 | BaseIndex dest(elements, ToRegister(lir->index()), |
18976 | ScaleFromScalarType(writeType)); |
18977 | masm.storeToTypedBigIntArray(writeType, value, dest); |
18978 | } |
18979 | } |
18980 | |
18981 | void CodeGenerator::visitStoreDataViewElement(LStoreDataViewElement* lir) { |
18982 | Register elements = ToRegister(lir->elements()); |
18983 | const LAllocation* value = lir->value(); |
18984 | const LAllocation* littleEndian = lir->littleEndian(); |
18985 | Register temp = ToTempRegisterOrInvalid(lir->temp()); |
18986 | Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64()); |
18987 | |
18988 | const MStoreDataViewElement* mir = lir->mir(); |
18989 | Scalar::Type writeType = mir->writeType(); |
18990 | |
18991 | LiveRegisterSet volatileRegs; |
18992 | if (MacroAssembler::StoreRequiresCall(writeType)) { |
18993 | volatileRegs = liveVolatileRegs(lir); |
18994 | } |
18995 | |
18996 | BaseIndex dest(elements, ToRegister(lir->index()), TimesOne); |
18997 | |
18998 | bool noSwap = littleEndian->isConstant() && |
18999 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
19000 | |
19001 | // Directly store if no byte swap is needed and the platform supports |
19002 | // unaligned accesses for the access. (Such support is assumed for integer |
19003 | // types.) |
19004 | if (noSwap && (!Scalar::isFloatingType(writeType) || |
19005 | MacroAssembler::SupportsFastUnalignedFPAccesses())) { |
19006 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
19007 | return; |
19008 | } |
19009 | |
19010 | // Load the value into a gpr register. |
19011 | switch (writeType) { |
19012 | case Scalar::Int16: |
19013 | case Scalar::Uint16: |
19014 | case Scalar::Int32: |
19015 | case Scalar::Uint32: |
19016 | if (value->isConstant()) { |
19017 | masm.move32(Imm32(ToInt32(value)), temp); |
19018 | } else { |
19019 | masm.move32(ToRegister(value), temp); |
19020 | } |
19021 | break; |
19022 | case Scalar::Float16: { |
19023 | FloatRegister fvalue = ToFloatRegister(value); |
19024 | masm.canonicalizeFloatIfDeterministic(fvalue); |
19025 | masm.moveFloat16ToGPR(fvalue, temp, volatileRegs); |
19026 | break; |
19027 | } |
19028 | case Scalar::Float32: { |
19029 | FloatRegister fvalue = ToFloatRegister(value); |
19030 | masm.canonicalizeFloatIfDeterministic(fvalue); |
19031 | masm.moveFloat32ToGPR(fvalue, temp); |
19032 | break; |
19033 | } |
19034 | case Scalar::Float64: { |
19035 | FloatRegister fvalue = ToFloatRegister(value); |
19036 | masm.canonicalizeDoubleIfDeterministic(fvalue); |
19037 | masm.moveDoubleToGPR64(fvalue, temp64); |
19038 | break; |
19039 | } |
19040 | case Scalar::Int8: |
19041 | case Scalar::Uint8: |
19042 | case Scalar::Uint8Clamped: |
19043 | case Scalar::BigInt64: |
19044 | case Scalar::BigUint64: |
19045 | default: |
19046 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19046); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 19046; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
19047 | } |
19048 | |
19049 | if (!noSwap) { |
19050 | // Swap the bytes in the loaded value. |
19051 | Label skip; |
19052 | if (!littleEndian->isConstant()) { |
19053 | masm.branch32( |
19054 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
19055 | ToRegister(littleEndian), Imm32(0), &skip); |
19056 | } |
19057 | |
19058 | switch (writeType) { |
19059 | case Scalar::Int16: |
19060 | masm.byteSwap16SignExtend(temp); |
19061 | break; |
19062 | case Scalar::Uint16: |
19063 | case Scalar::Float16: |
19064 | masm.byteSwap16ZeroExtend(temp); |
19065 | break; |
19066 | case Scalar::Int32: |
19067 | case Scalar::Uint32: |
19068 | case Scalar::Float32: |
19069 | masm.byteSwap32(temp); |
19070 | break; |
19071 | case Scalar::Float64: |
19072 | masm.byteSwap64(temp64); |
19073 | break; |
19074 | case Scalar::Int8: |
19075 | case Scalar::Uint8: |
19076 | case Scalar::Uint8Clamped: |
19077 | case Scalar::BigInt64: |
19078 | case Scalar::BigUint64: |
19079 | default: |
19080 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19080); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 19080; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
19081 | } |
19082 | |
19083 | if (skip.used()) { |
19084 | masm.bind(&skip); |
19085 | } |
19086 | } |
19087 | |
19088 | // Store the value into the destination. |
19089 | switch (writeType) { |
19090 | case Scalar::Int16: |
19091 | case Scalar::Uint16: |
19092 | case Scalar::Float16: |
19093 | masm.store16Unaligned(temp, dest); |
19094 | break; |
19095 | case Scalar::Int32: |
19096 | case Scalar::Uint32: |
19097 | case Scalar::Float32: |
19098 | masm.store32Unaligned(temp, dest); |
19099 | break; |
19100 | case Scalar::Float64: |
19101 | masm.store64Unaligned(temp64, dest); |
19102 | break; |
19103 | case Scalar::Int8: |
19104 | case Scalar::Uint8: |
19105 | case Scalar::Uint8Clamped: |
19106 | case Scalar::BigInt64: |
19107 | case Scalar::BigUint64: |
19108 | default: |
19109 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19109); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 19109; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
19110 | } |
19111 | } |
19112 | |
19113 | void CodeGenerator::visitStoreDataViewElement64(LStoreDataViewElement64* lir) { |
19114 | Register elements = ToRegister(lir->elements()); |
19115 | Register64 value = ToRegister64(lir->value()); |
19116 | const LAllocation* littleEndian = lir->littleEndian(); |
19117 | Register64 temp = ToTempRegister64OrInvalid(lir->temp()); |
19118 | |
19119 | MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->writeType()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Scalar::isBigIntType(lir->mir()->writeType())) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(Scalar::isBigIntType(lir->mir()->writeType())) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Scalar::isBigIntType(lir->mir()->writeType())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->writeType())" ")"); do { *((volatile int*)__null) = 19119; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19120 | |
19121 | BaseIndex dest(elements, ToRegister(lir->index()), TimesOne); |
19122 | |
19123 | bool noSwap = littleEndian->isConstant() && |
19124 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
19125 | |
19126 | if (!noSwap) { |
19127 | // Preserve the input value. |
19128 | if (temp != Register64::Invalid()) { |
19129 | masm.move64(value, temp); |
19130 | value = temp; |
19131 | } else { |
19132 | masm.Push(value); |
19133 | } |
19134 | |
19135 | // Swap the bytes in the loaded value. |
19136 | Label skip; |
19137 | if (!littleEndian->isConstant()) { |
19138 | masm.branch32( |
19139 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
19140 | ToRegister(littleEndian), Imm32(0), &skip); |
19141 | } |
19142 | |
19143 | masm.byteSwap64(value); |
19144 | |
19145 | if (skip.used()) { |
19146 | masm.bind(&skip); |
19147 | } |
19148 | } |
19149 | |
19150 | // Store the value into the destination. |
19151 | masm.store64Unaligned(value, dest); |
19152 | |
19153 | // Restore |value| if it was modified. |
19154 | if (!noSwap && temp == Register64::Invalid()) { |
19155 | masm.Pop(value); |
19156 | } |
19157 | } |
19158 | |
19159 | void CodeGenerator::visitStoreTypedArrayElementHole( |
19160 | LStoreTypedArrayElementHole* lir) { |
19161 | Register elements = ToRegister(lir->elements()); |
19162 | const LAllocation* value = lir->value(); |
19163 | |
19164 | Scalar::Type arrayType = lir->mir()->arrayType(); |
19165 | |
19166 | Register index = ToRegister(lir->index()); |
19167 | const LAllocation* length = lir->length(); |
19168 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
19169 | |
19170 | LiveRegisterSet volatileRegs; |
19171 | if (MacroAssembler::StoreRequiresCall(arrayType)) { |
19172 | volatileRegs = liveVolatileRegs(lir); |
19173 | } |
19174 | |
19175 | Label skip; |
19176 | if (length->isRegister()) { |
19177 | masm.spectreBoundsCheckPtr(index, ToRegister(length), temp, &skip); |
19178 | } else { |
19179 | masm.spectreBoundsCheckPtr(index, ToAddress(length), temp, &skip); |
19180 | } |
19181 | |
19182 | BaseIndex dest(elements, index, ScaleFromScalarType(arrayType)); |
19183 | StoreToTypedArray(masm, arrayType, value, dest, temp, volatileRegs); |
19184 | |
19185 | masm.bind(&skip); |
19186 | } |
19187 | |
19188 | void CodeGenerator::visitStoreTypedArrayElementHoleInt64( |
19189 | LStoreTypedArrayElementHoleInt64* lir) { |
19190 | Register elements = ToRegister(lir->elements()); |
19191 | Register64 value = ToRegister64(lir->value()); |
19192 | |
19193 | Scalar::Type arrayType = lir->mir()->arrayType(); |
19194 | |
19195 | Register index = ToRegister(lir->index()); |
19196 | const LAllocation* length = lir->length(); |
19197 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp()); |
19198 | |
19199 | Label skip; |
19200 | if (length->isRegister()) { |
19201 | masm.spectreBoundsCheckPtr(index, ToRegister(length), spectreTemp, &skip); |
19202 | } else { |
19203 | masm.spectreBoundsCheckPtr(index, ToAddress(length), spectreTemp, &skip); |
19204 | } |
19205 | |
19206 | BaseIndex dest(elements, index, ScaleFromScalarType(arrayType)); |
19207 | masm.storeToTypedBigIntArray(arrayType, value, dest); |
19208 | |
19209 | masm.bind(&skip); |
19210 | } |
19211 | |
19212 | void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { |
19213 | masm.memoryBarrier(ins->type()); |
19214 | } |
19215 | |
19216 | void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) { |
19217 | Register value = ToRegister(lir->value()); |
19218 | Register output = ToRegister(lir->output()); |
19219 | |
19220 | masm.atomicIsLockFreeJS(value, output); |
19221 | } |
19222 | |
19223 | void CodeGenerator::visitAtomicPause(LAtomicPause* lir) { masm.atomicPause(); } |
19224 | |
19225 | void CodeGenerator::visitClampIToUint8(LClampIToUint8* lir) { |
19226 | Register output = ToRegister(lir->output()); |
19227 | MOZ_ASSERT(output == ToRegister(lir->input()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(output == ToRegister(lir->input()))>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(output == ToRegister(lir->input())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output == ToRegister(lir->input())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19227); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == ToRegister(lir->input())" ")"); do { *((volatile int*)__null) = 19227; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19228 | masm.clampIntToUint8(output); |
19229 | } |
19230 | |
19231 | void CodeGenerator::visitClampDToUint8(LClampDToUint8* lir) { |
19232 | FloatRegister input = ToFloatRegister(lir->input()); |
19233 | Register output = ToRegister(lir->output()); |
19234 | masm.clampDoubleToUint8(input, output); |
19235 | } |
19236 | |
19237 | void CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) { |
19238 | ValueOperand operand = ToValue(lir, LClampVToUint8::InputIndex); |
19239 | FloatRegister tempFloat = ToFloatRegister(lir->temp0()); |
19240 | Register output = ToRegister(lir->output()); |
19241 | |
19242 | using Fn = bool (*)(JSContext*, JSString*, double*); |
19243 | OutOfLineCode* oolString = oolCallVM<Fn, StringToNumber>( |
19244 | lir, ArgList(output), StoreFloatRegisterTo(tempFloat)); |
19245 | Label* stringEntry = oolString->entry(); |
19246 | Label* stringRejoin = oolString->rejoin(); |
19247 | |
19248 | Label fails; |
19249 | masm.clampValueToUint8(operand, stringEntry, stringRejoin, output, tempFloat, |
19250 | output, &fails); |
19251 | |
19252 | bailoutFrom(&fails, lir->snapshot()); |
19253 | } |
19254 | |
19255 | void CodeGenerator::visitInCache(LInCache* ins) { |
19256 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
19257 | |
19258 | ConstantOrRegister key = |
19259 | toConstantOrRegister(ins, LInCache::LhsIndex, ins->mir()->key()->type()); |
19260 | Register object = ToRegister(ins->rhs()); |
19261 | Register output = ToRegister(ins->output()); |
19262 | Register temp = ToRegister(ins->temp0()); |
19263 | |
19264 | IonInIC cache(liveRegs, key, object, output, temp); |
19265 | addIC(ins, allocateIC(cache)); |
19266 | } |
19267 | |
19268 | void CodeGenerator::visitInArray(LInArray* lir) { |
19269 | const MInArray* mir = lir->mir(); |
19270 | Register elements = ToRegister(lir->elements()); |
19271 | Register initLength = ToRegister(lir->initLength()); |
19272 | Register output = ToRegister(lir->output()); |
19273 | |
19274 | Label falseBranch, done, trueBranch; |
19275 | |
19276 | if (lir->index()->isConstant()) { |
19277 | int32_t index = ToInt32(lir->index()); |
19278 | |
19279 | if (index < 0) { |
19280 | MOZ_ASSERT(mir->needsNegativeIntCheck())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->needsNegativeIntCheck())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->needsNegativeIntCheck ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mir->needsNegativeIntCheck()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19280); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->needsNegativeIntCheck()" ")"); do { *((volatile int*)__null) = 19280; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19281 | bailout(lir->snapshot()); |
19282 | return; |
19283 | } |
19284 | |
19285 | masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), |
19286 | &falseBranch); |
19287 | |
19288 | NativeObject::elementsSizeMustNotOverflow(); |
19289 | Address address = Address(elements, index * sizeof(Value)); |
19290 | masm.branchTestMagic(Assembler::Equal, address, &falseBranch); |
19291 | } else { |
19292 | Register index = ToRegister(lir->index()); |
19293 | |
19294 | Label negativeIntCheck; |
19295 | Label* failedInitLength = &falseBranch; |
19296 | if (mir->needsNegativeIntCheck()) { |
19297 | failedInitLength = &negativeIntCheck; |
19298 | } |
19299 | |
19300 | masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength); |
19301 | |
19302 | BaseObjectElementIndex address(elements, index); |
19303 | masm.branchTestMagic(Assembler::Equal, address, &falseBranch); |
19304 | |
19305 | if (mir->needsNegativeIntCheck()) { |
19306 | masm.jump(&trueBranch); |
19307 | masm.bind(&negativeIntCheck); |
19308 | |
19309 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
19310 | |
19311 | masm.jump(&falseBranch); |
19312 | } |
19313 | } |
19314 | |
19315 | masm.bind(&trueBranch); |
19316 | masm.move32(Imm32(1), output); |
19317 | masm.jump(&done); |
19318 | |
19319 | masm.bind(&falseBranch); |
19320 | masm.move32(Imm32(0), output); |
19321 | masm.bind(&done); |
19322 | } |
19323 | |
19324 | void CodeGenerator::visitGuardElementNotHole(LGuardElementNotHole* lir) { |
19325 | Register elements = ToRegister(lir->elements()); |
19326 | const LAllocation* index = lir->index(); |
19327 | |
19328 | Label testMagic; |
19329 | if (index->isConstant()) { |
19330 | Address address(elements, ToInt32(index) * sizeof(js::Value)); |
19331 | masm.branchTestMagic(Assembler::Equal, address, &testMagic); |
19332 | } else { |
19333 | BaseObjectElementIndex address(elements, ToRegister(index)); |
19334 | masm.branchTestMagic(Assembler::Equal, address, &testMagic); |
19335 | } |
19336 | bailoutFrom(&testMagic, lir->snapshot()); |
19337 | } |
19338 | |
19339 | void CodeGenerator::visitInstanceOfO(LInstanceOfO* ins) { |
19340 | Register protoReg = ToRegister(ins->rhs()); |
19341 | emitInstanceOf(ins, protoReg); |
19342 | } |
19343 | |
19344 | void CodeGenerator::visitInstanceOfV(LInstanceOfV* ins) { |
19345 | Register protoReg = ToRegister(ins->rhs()); |
19346 | emitInstanceOf(ins, protoReg); |
19347 | } |
19348 | |
19349 | void CodeGenerator::emitInstanceOf(LInstruction* ins, Register protoReg) { |
19350 | // This path implements fun_hasInstance when the function's prototype is |
19351 | // known to be the object in protoReg |
19352 | |
19353 | Label done; |
19354 | Register output = ToRegister(ins->getDef(0)); |
19355 | |
19356 | // If the lhs is a primitive, the result is false. |
19357 | Register objReg; |
19358 | if (ins->isInstanceOfV()) { |
19359 | Label isObject; |
19360 | ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex); |
19361 | masm.branchTestObject(Assembler::Equal, lhsValue, &isObject); |
19362 | masm.mov(ImmWord(0), output); |
19363 | masm.jump(&done); |
19364 | masm.bind(&isObject); |
19365 | objReg = masm.extractObject(lhsValue, output); |
19366 | } else { |
19367 | objReg = ToRegister(ins->toInstanceOfO()->lhs()); |
19368 | } |
19369 | |
19370 | // Crawl the lhs's prototype chain in a loop to search for prototypeObject. |
19371 | // This follows the main loop of js::IsPrototypeOf, though additionally breaks |
19372 | // out of the loop on Proxy::LazyProto. |
19373 | |
19374 | // Load the lhs's prototype. |
19375 | masm.loadObjProto(objReg, output); |
19376 | |
19377 | Label testLazy; |
19378 | { |
19379 | Label loopPrototypeChain; |
19380 | masm.bind(&loopPrototypeChain); |
19381 | |
19382 | // Test for the target prototype object. |
19383 | Label notPrototypeObject; |
19384 | masm.branchPtr(Assembler::NotEqual, output, protoReg, ¬PrototypeObject); |
19385 | masm.mov(ImmWord(1), output); |
19386 | masm.jump(&done); |
19387 | masm.bind(¬PrototypeObject); |
19388 | |
19389 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19389); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 19389; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19390 | |
19391 | // Test for nullptr or Proxy::LazyProto |
19392 | masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy); |
19393 | |
19394 | // Load the current object's prototype. |
19395 | masm.loadObjProto(output, output); |
19396 | |
19397 | masm.jump(&loopPrototypeChain); |
19398 | } |
19399 | |
19400 | // Make a VM call if an object with a lazy proto was found on the prototype |
19401 | // chain. This currently occurs only for cross compartment wrappers, which |
19402 | // we do not expect to be compared with non-wrapper functions from this |
19403 | // compartment. Otherwise, we stopped on a nullptr prototype and the output |
19404 | // register is already correct. |
19405 | |
19406 | using Fn = bool (*)(JSContext*, HandleObject, JSObject*, bool*); |
19407 | auto* ool = oolCallVM<Fn, IsPrototypeOf>(ins, ArgList(protoReg, objReg), |
19408 | StoreRegisterTo(output)); |
19409 | |
19410 | // Regenerate the original lhs object for the VM call. |
19411 | Label regenerate, *lazyEntry; |
19412 | if (objReg != output) { |
19413 | lazyEntry = ool->entry(); |
19414 | } else { |
19415 | masm.bind(®enerate); |
19416 | lazyEntry = ®enerate; |
19417 | if (ins->isInstanceOfV()) { |
19418 | ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex); |
19419 | objReg = masm.extractObject(lhsValue, output); |
19420 | } else { |
19421 | objReg = ToRegister(ins->toInstanceOfO()->lhs()); |
19422 | } |
19423 | MOZ_ASSERT(objReg == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(objReg == output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(objReg == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("objReg == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "objReg == output" ")"); do { *((volatile int*)__null) = 19423; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19424 | masm.jump(ool->entry()); |
19425 | } |
19426 | |
19427 | masm.bind(&testLazy); |
19428 | masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry); |
19429 | |
19430 | masm.bind(&done); |
19431 | masm.bind(ool->rejoin()); |
19432 | } |
19433 | |
19434 | void CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins) { |
19435 | // The Lowering ensures that RHS is an object, and that LHS is a value. |
19436 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
19437 | TypedOrValueRegister lhs = |
19438 | TypedOrValueRegister(ToValue(ins, LInstanceOfCache::LHS)); |
19439 | Register rhs = ToRegister(ins->rhs()); |
19440 | Register output = ToRegister(ins->output()); |
19441 | |
19442 | IonInstanceOfIC ic(liveRegs, lhs, rhs, output); |
19443 | addIC(ins, allocateIC(ic)); |
19444 | } |
19445 | |
19446 | void CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) { |
19447 | const Register JSContextReg = ToRegister(ins->getJSContextReg()); |
19448 | const Register ObjectReg = ToRegister(ins->getObjectReg()); |
19449 | const Register PrivateReg = ToRegister(ins->getPrivReg()); |
19450 | const Register ValueReg = ToRegister(ins->getValueReg()); |
19451 | |
19452 | Label haveValue; |
19453 | if (ins->mir()->valueMayBeInSlot()) { |
19454 | size_t slot = ins->mir()->domMemberSlotIndex(); |
19455 | // It's a bit annoying to redo these slot calculations, which duplcate |
19456 | // LSlots and a few other things like that, but I'm not sure there's a |
19457 | // way to reuse those here. |
19458 | // |
19459 | // If this ever gets fixed to work with proxies (by not assuming that |
19460 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
19461 | // match fixed slot indices), we can reenable MGetDOMProperty for |
19462 | // proxies in IonBuilder. |
19463 | if (slot < NativeObject::MAX_FIXED_SLOTS) { |
19464 | masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)), |
19465 | JSReturnOperand); |
19466 | } else { |
19467 | // It's a dynamic slot. |
19468 | slot -= NativeObject::MAX_FIXED_SLOTS; |
19469 | // Use PrivateReg as a scratch register for the slots pointer. |
19470 | masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()), |
19471 | PrivateReg); |
19472 | masm.loadValue(Address(PrivateReg, slot * sizeof(js::Value)), |
19473 | JSReturnOperand); |
19474 | } |
19475 | masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue); |
19476 | } |
19477 | |
19478 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
19479 | |
19480 | masm.checkStackAlignment(); |
19481 | |
19482 | // Make space for the outparam. Pre-initialize it to UndefinedValue so we |
19483 | // can trace it at GC time. |
19484 | masm.Push(UndefinedValue()); |
19485 | // We pass the pointer to our out param as an instance of |
19486 | // JSJitGetterCallArgs, since on the binary level it's the same thing. |
19487 | static_assert(sizeof(JSJitGetterCallArgs) == sizeof(Value*)); |
19488 | masm.moveStackPtrTo(ValueReg); |
19489 | |
19490 | masm.Push(ObjectReg); |
19491 | |
19492 | LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind()); |
19493 | |
19494 | // Rooting will happen at GC time. |
19495 | masm.moveStackPtrTo(ObjectReg); |
19496 | |
19497 | Realm* getterRealm = ins->mir()->getterRealm(); |
19498 | if (gen->realm->realmPtr() != getterRealm) { |
19499 | // We use JSContextReg as scratch register here. |
19500 | masm.switchToRealm(getterRealm, JSContextReg); |
19501 | } |
19502 | |
19503 | uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); |
19504 | masm.loadJSContext(JSContextReg); |
19505 | masm.enterFakeExitFrame(JSContextReg, JSContextReg, |
19506 | ExitFrameType::IonDOMGetter); |
19507 | |
19508 | markSafepointAt(safepointOffset, ins); |
19509 | |
19510 | masm.setupAlignedABICall(); |
19511 | masm.loadJSContext(JSContextReg); |
19512 | masm.passABIArg(JSContextReg); |
19513 | masm.passABIArg(ObjectReg); |
19514 | masm.passABIArg(PrivateReg); |
19515 | masm.passABIArg(ValueReg); |
19516 | ensureOsiSpace(); |
19517 | masm.callWithABI(DynamicFunction<JSJitGetterOp>(ins->mir()->fun()), |
19518 | ABIType::General, |
19519 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
19520 | |
19521 | if (ins->mir()->isInfallible()) { |
19522 | masm.loadValue(Address(masm.getStackPointer(), |
19523 | IonDOMExitFrameLayout::offsetOfResult()), |
19524 | JSReturnOperand); |
19525 | } else { |
19526 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
19527 | |
19528 | masm.loadValue(Address(masm.getStackPointer(), |
19529 | IonDOMExitFrameLayout::offsetOfResult()), |
19530 | JSReturnOperand); |
19531 | } |
19532 | |
19533 | // Switch back to the current realm if needed. Note: if the getter threw an |
19534 | // exception, the exception handler will do this. |
19535 | if (gen->realm->realmPtr() != getterRealm) { |
19536 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
19537 | "Clobbering ReturnReg should not affect the return value"); |
19538 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
19539 | } |
19540 | |
19541 | // Until C++ code is instrumented against Spectre, prevent speculative |
19542 | // execution from returning any private data. |
19543 | if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) { |
19544 | masm.speculationBarrier(); |
19545 | } |
19546 | |
19547 | masm.adjustStack(IonDOMExitFrameLayout::Size()); |
19548 | |
19549 | masm.bind(&haveValue); |
19550 | |
19551 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19551); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 19551; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19552 | } |
19553 | |
19554 | void CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins) { |
19555 | // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to |
19556 | // use an LLoadFixedSlotV or some subclass of it for this case: that would |
19557 | // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then |
19558 | // we'd have to duplicate a bunch of stuff we now get for free from |
19559 | // MGetDOMProperty. |
19560 | // |
19561 | // If this ever gets fixed to work with proxies (by not assuming that |
19562 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
19563 | // match fixed slot indices), we can reenable MGetDOMMember for |
19564 | // proxies in IonBuilder. |
19565 | Register object = ToRegister(ins->object()); |
19566 | size_t slot = ins->mir()->domMemberSlotIndex(); |
19567 | ValueOperand result = ToOutValue(ins); |
19568 | |
19569 | masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)), |
19570 | result); |
19571 | } |
19572 | |
19573 | void CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT* ins) { |
19574 | // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to |
19575 | // use an LLoadFixedSlotT or some subclass of it for this case: that would |
19576 | // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then |
19577 | // we'd have to duplicate a bunch of stuff we now get for free from |
19578 | // MGetDOMProperty. |
19579 | // |
19580 | // If this ever gets fixed to work with proxies (by not assuming that |
19581 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
19582 | // match fixed slot indices), we can reenable MGetDOMMember for |
19583 | // proxies in IonBuilder. |
19584 | Register object = ToRegister(ins->object()); |
19585 | size_t slot = ins->mir()->domMemberSlotIndex(); |
19586 | AnyRegister result = ToAnyRegister(ins->getDef(0)); |
19587 | MIRType type = ins->mir()->type(); |
19588 | |
19589 | masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)), |
19590 | type, result); |
19591 | } |
19592 | |
19593 | void CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) { |
19594 | const Register JSContextReg = ToRegister(ins->getJSContextReg()); |
19595 | const Register ObjectReg = ToRegister(ins->getObjectReg()); |
19596 | const Register PrivateReg = ToRegister(ins->getPrivReg()); |
19597 | const Register ValueReg = ToRegister(ins->getValueReg()); |
19598 | |
19599 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
19600 | |
19601 | masm.checkStackAlignment(); |
19602 | |
19603 | // Push the argument. Rooting will happen at GC time. |
19604 | ValueOperand argVal = ToValue(ins, LSetDOMProperty::Value); |
19605 | masm.Push(argVal); |
19606 | // We pass the pointer to our out param as an instance of |
19607 | // JSJitGetterCallArgs, since on the binary level it's the same thing. |
19608 | static_assert(sizeof(JSJitSetterCallArgs) == sizeof(Value*)); |
19609 | masm.moveStackPtrTo(ValueReg); |
19610 | |
19611 | masm.Push(ObjectReg); |
19612 | |
19613 | LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind()); |
19614 | |
19615 | // Rooting will happen at GC time. |
19616 | masm.moveStackPtrTo(ObjectReg); |
19617 | |
19618 | Realm* setterRealm = ins->mir()->setterRealm(); |
19619 | if (gen->realm->realmPtr() != setterRealm) { |
19620 | // We use JSContextReg as scratch register here. |
19621 | masm.switchToRealm(setterRealm, JSContextReg); |
19622 | } |
19623 | |
19624 | uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); |
19625 | masm.loadJSContext(JSContextReg); |
19626 | masm.enterFakeExitFrame(JSContextReg, JSContextReg, |
19627 | ExitFrameType::IonDOMSetter); |
19628 | |
19629 | markSafepointAt(safepointOffset, ins); |
19630 | |
19631 | masm.setupAlignedABICall(); |
19632 | masm.loadJSContext(JSContextReg); |
19633 | masm.passABIArg(JSContextReg); |
19634 | masm.passABIArg(ObjectReg); |
19635 | masm.passABIArg(PrivateReg); |
19636 | masm.passABIArg(ValueReg); |
19637 | ensureOsiSpace(); |
19638 | masm.callWithABI(DynamicFunction<JSJitSetterOp>(ins->mir()->fun()), |
19639 | ABIType::General, |
19640 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
19641 | |
19642 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
19643 | |
19644 | // Switch back to the current realm if needed. Note: if the setter threw an |
19645 | // exception, the exception handler will do this. |
19646 | if (gen->realm->realmPtr() != setterRealm) { |
19647 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
19648 | } |
19649 | |
19650 | masm.adjustStack(IonDOMExitFrameLayout::Size()); |
19651 | |
19652 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19652); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 19652; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19653 | } |
19654 | |
19655 | void CodeGenerator::visitLoadDOMExpandoValue(LLoadDOMExpandoValue* ins) { |
19656 | Register proxy = ToRegister(ins->proxy()); |
19657 | ValueOperand out = ToOutValue(ins); |
19658 | |
19659 | masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()), |
19660 | out.scratchReg()); |
19661 | masm.loadValue(Address(out.scratchReg(), |
19662 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()), |
19663 | out); |
19664 | } |
19665 | |
19666 | void CodeGenerator::visitLoadDOMExpandoValueGuardGeneration( |
19667 | LLoadDOMExpandoValueGuardGeneration* ins) { |
19668 | Register proxy = ToRegister(ins->proxy()); |
19669 | ValueOperand out = ToOutValue(ins); |
19670 | |
19671 | Label bail; |
19672 | masm.loadDOMExpandoValueGuardGeneration(proxy, out, |
19673 | ins->mir()->expandoAndGeneration(), |
19674 | ins->mir()->generation(), &bail); |
19675 | bailoutFrom(&bail, ins->snapshot()); |
19676 | } |
19677 | |
19678 | void CodeGenerator::visitLoadDOMExpandoValueIgnoreGeneration( |
19679 | LLoadDOMExpandoValueIgnoreGeneration* ins) { |
19680 | Register proxy = ToRegister(ins->proxy()); |
19681 | ValueOperand out = ToOutValue(ins); |
19682 | |
19683 | masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()), |
19684 | out.scratchReg()); |
19685 | |
19686 | // Load the ExpandoAndGeneration* from the PrivateValue. |
19687 | masm.loadPrivate( |
19688 | Address(out.scratchReg(), |
19689 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()), |
19690 | out.scratchReg()); |
19691 | |
19692 | // Load expandoAndGeneration->expando into the output Value register. |
19693 | masm.loadValue( |
19694 | Address(out.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), out); |
19695 | } |
19696 | |
19697 | void CodeGenerator::visitGuardDOMExpandoMissingOrGuardShape( |
19698 | LGuardDOMExpandoMissingOrGuardShape* ins) { |
19699 | Register temp = ToRegister(ins->temp0()); |
19700 | ValueOperand input = |
19701 | ToValue(ins, LGuardDOMExpandoMissingOrGuardShape::InputIndex); |
19702 | |
19703 | Label done; |
19704 | masm.branchTestUndefined(Assembler::Equal, input, &done); |
19705 | |
19706 | masm.debugAssertIsObject(input); |
19707 | masm.unboxObject(input, temp); |
19708 | // The expando object is not used in this case, so we don't need Spectre |
19709 | // mitigations. |
19710 | Label bail; |
19711 | masm.branchTestObjShapeNoSpectreMitigations(Assembler::NotEqual, temp, |
19712 | ins->mir()->shape(), &bail); |
19713 | bailoutFrom(&bail, ins->snapshot()); |
19714 | |
19715 | masm.bind(&done); |
19716 | } |
19717 | |
19718 | class OutOfLineIsCallable : public OutOfLineCodeBase<CodeGenerator> { |
19719 | Register object_; |
19720 | Register output_; |
19721 | |
19722 | public: |
19723 | OutOfLineIsCallable(Register object, Register output) |
19724 | : object_(object), output_(output) {} |
19725 | |
19726 | void accept(CodeGenerator* codegen) override { |
19727 | codegen->visitOutOfLineIsCallable(this); |
19728 | } |
19729 | Register object() const { return object_; } |
19730 | Register output() const { return output_; } |
19731 | }; |
19732 | |
19733 | void CodeGenerator::visitIsCallableO(LIsCallableO* ins) { |
19734 | Register object = ToRegister(ins->object()); |
19735 | Register output = ToRegister(ins->output()); |
19736 | |
19737 | OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(object, output); |
19738 | addOutOfLineCode(ool, ins->mir()); |
19739 | |
19740 | masm.isCallable(object, output, ool->entry()); |
19741 | |
19742 | masm.bind(ool->rejoin()); |
19743 | } |
19744 | |
19745 | void CodeGenerator::visitIsCallableV(LIsCallableV* ins) { |
19746 | ValueOperand val = ToValue(ins, LIsCallableV::ObjectIndex); |
19747 | Register output = ToRegister(ins->output()); |
19748 | Register temp = ToRegister(ins->temp0()); |
19749 | |
19750 | Label notObject; |
19751 | masm.fallibleUnboxObject(val, temp, ¬Object); |
19752 | |
19753 | OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(temp, output); |
19754 | addOutOfLineCode(ool, ins->mir()); |
19755 | |
19756 | masm.isCallable(temp, output, ool->entry()); |
19757 | masm.jump(ool->rejoin()); |
19758 | |
19759 | masm.bind(¬Object); |
19760 | masm.move32(Imm32(0), output); |
19761 | |
19762 | masm.bind(ool->rejoin()); |
19763 | } |
19764 | |
19765 | void CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool) { |
19766 | Register object = ool->object(); |
19767 | Register output = ool->output(); |
19768 | |
19769 | saveVolatile(output); |
19770 | using Fn = bool (*)(JSObject* obj); |
19771 | masm.setupAlignedABICall(); |
19772 | masm.passABIArg(object); |
19773 | masm.callWithABI<Fn, ObjectIsCallable>(); |
19774 | masm.storeCallBoolResult(output); |
19775 | restoreVolatile(output); |
19776 | masm.jump(ool->rejoin()); |
19777 | } |
19778 | |
19779 | class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator> { |
19780 | LIsConstructor* ins_; |
19781 | |
19782 | public: |
19783 | explicit OutOfLineIsConstructor(LIsConstructor* ins) : ins_(ins) {} |
19784 | |
19785 | void accept(CodeGenerator* codegen) override { |
19786 | codegen->visitOutOfLineIsConstructor(this); |
19787 | } |
19788 | LIsConstructor* ins() const { return ins_; } |
19789 | }; |
19790 | |
19791 | void CodeGenerator::visitIsConstructor(LIsConstructor* ins) { |
19792 | Register object = ToRegister(ins->object()); |
19793 | Register output = ToRegister(ins->output()); |
19794 | |
19795 | OutOfLineIsConstructor* ool = new (alloc()) OutOfLineIsConstructor(ins); |
19796 | addOutOfLineCode(ool, ins->mir()); |
19797 | |
19798 | masm.isConstructor(object, output, ool->entry()); |
19799 | |
19800 | masm.bind(ool->rejoin()); |
19801 | } |
19802 | |
19803 | void CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool) { |
19804 | LIsConstructor* ins = ool->ins(); |
19805 | Register object = ToRegister(ins->object()); |
19806 | Register output = ToRegister(ins->output()); |
19807 | |
19808 | saveVolatile(output); |
19809 | using Fn = bool (*)(JSObject* obj); |
19810 | masm.setupAlignedABICall(); |
19811 | masm.passABIArg(object); |
19812 | masm.callWithABI<Fn, ObjectIsConstructor>(); |
19813 | masm.storeCallBoolResult(output); |
19814 | restoreVolatile(output); |
19815 | masm.jump(ool->rejoin()); |
19816 | } |
19817 | |
19818 | void CodeGenerator::visitIsCrossRealmArrayConstructor( |
19819 | LIsCrossRealmArrayConstructor* ins) { |
19820 | Register object = ToRegister(ins->object()); |
19821 | Register output = ToRegister(ins->output()); |
19822 | |
19823 | masm.setIsCrossRealmArrayConstructor(object, output); |
19824 | } |
19825 | |
19826 | static void EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool, |
19827 | Register obj, Register output, |
19828 | Label* notArray = nullptr) { |
19829 | masm.loadObjClassUnsafe(obj, output); |
19830 | |
19831 | Label isArray; |
19832 | masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_), |
19833 | &isArray); |
19834 | |
19835 | // Branch to OOL path if it's a proxy. |
19836 | masm.branchTestClassIsProxy(true, output, ool->entry()); |
19837 | |
19838 | if (notArray) { |
19839 | masm.bind(notArray); |
19840 | } |
19841 | masm.move32(Imm32(0), output); |
19842 | masm.jump(ool->rejoin()); |
19843 | |
19844 | masm.bind(&isArray); |
19845 | masm.move32(Imm32(1), output); |
19846 | |
19847 | masm.bind(ool->rejoin()); |
19848 | } |
19849 | |
19850 | void CodeGenerator::visitIsArrayO(LIsArrayO* lir) { |
19851 | Register object = ToRegister(lir->object()); |
19852 | Register output = ToRegister(lir->output()); |
19853 | |
19854 | using Fn = bool (*)(JSContext*, HandleObject, bool*); |
19855 | OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>( |
19856 | lir, ArgList(object), StoreRegisterTo(output)); |
19857 | EmitObjectIsArray(masm, ool, object, output); |
19858 | } |
19859 | |
19860 | void CodeGenerator::visitIsArrayV(LIsArrayV* lir) { |
19861 | ValueOperand val = ToValue(lir, LIsArrayV::ValueIndex); |
19862 | Register output = ToRegister(lir->output()); |
19863 | Register temp = ToRegister(lir->temp0()); |
19864 | |
19865 | Label notArray; |
19866 | masm.fallibleUnboxObject(val, temp, ¬Array); |
19867 | |
19868 | using Fn = bool (*)(JSContext*, HandleObject, bool*); |
19869 | OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>( |
19870 | lir, ArgList(temp), StoreRegisterTo(output)); |
19871 | EmitObjectIsArray(masm, ool, temp, output, ¬Array); |
19872 | } |
19873 | |
19874 | void CodeGenerator::visitIsTypedArray(LIsTypedArray* lir) { |
19875 | Register object = ToRegister(lir->object()); |
19876 | Register output = ToRegister(lir->output()); |
19877 | |
19878 | OutOfLineCode* ool = nullptr; |
19879 | if (lir->mir()->isPossiblyWrapped()) { |
19880 | using Fn = bool (*)(JSContext*, JSObject*, bool*); |
19881 | ool = oolCallVM<Fn, jit::IsPossiblyWrappedTypedArray>( |
19882 | lir, ArgList(object), StoreRegisterTo(output)); |
19883 | } |
19884 | |
19885 | Label notTypedArray; |
19886 | Label done; |
19887 | |
19888 | masm.loadObjClassUnsafe(object, output); |
19889 | masm.branchIfClassIsNotTypedArray(output, ¬TypedArray); |
19890 | |
19891 | masm.move32(Imm32(1), output); |
19892 | masm.jump(&done); |
19893 | masm.bind(¬TypedArray); |
19894 | if (ool) { |
19895 | Label notProxy; |
19896 | masm.branchTestClassIsProxy(false, output, ¬Proxy); |
19897 | masm.branchTestProxyHandlerFamily(Assembler::Equal, object, output, |
19898 | &Wrapper::family, ool->entry()); |
19899 | masm.bind(¬Proxy); |
19900 | } |
19901 | masm.move32(Imm32(0), output); |
19902 | masm.bind(&done); |
19903 | if (ool) { |
19904 | masm.bind(ool->rejoin()); |
19905 | } |
19906 | } |
19907 | |
19908 | void CodeGenerator::visitIsObject(LIsObject* ins) { |
19909 | Register output = ToRegister(ins->output()); |
19910 | ValueOperand value = ToValue(ins, LIsObject::ObjectIndex); |
19911 | masm.testObjectSet(Assembler::Equal, value, output); |
19912 | } |
19913 | |
19914 | void CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins) { |
19915 | ValueOperand value = ToValue(ins, LIsObjectAndBranch::Input); |
19916 | |
19917 | MBasicBlock* ifTrue = ins->ifTrue(); |
19918 | MBasicBlock* ifFalse = ins->ifFalse(); |
19919 | |
19920 | if (isNextBlock(ifFalse->lir())) { |
19921 | masm.branchTestObject(Assembler::Equal, value, |
19922 | getJumpLabelForBranch(ifTrue)); |
19923 | } else { |
19924 | masm.branchTestObject(Assembler::NotEqual, value, |
19925 | getJumpLabelForBranch(ifFalse)); |
19926 | jumpToBlock(ifTrue); |
19927 | } |
19928 | } |
19929 | |
19930 | void CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins) { |
19931 | Register output = ToRegister(ins->output()); |
19932 | ValueOperand value = ToValue(ins, LIsNullOrUndefined::InputIndex); |
19933 | |
19934 | Label isNotNull, done; |
19935 | masm.branchTestNull(Assembler::NotEqual, value, &isNotNull); |
19936 | |
19937 | masm.move32(Imm32(1), output); |
19938 | masm.jump(&done); |
19939 | |
19940 | masm.bind(&isNotNull); |
19941 | masm.testUndefinedSet(Assembler::Equal, value, output); |
19942 | |
19943 | masm.bind(&done); |
19944 | } |
19945 | |
19946 | void CodeGenerator::visitIsNullOrUndefinedAndBranch( |
19947 | LIsNullOrUndefinedAndBranch* ins) { |
19948 | Label* ifTrue = getJumpLabelForBranch(ins->ifTrue()); |
19949 | Label* ifFalse = getJumpLabelForBranch(ins->ifFalse()); |
19950 | ValueOperand value = ToValue(ins, LIsNullOrUndefinedAndBranch::Input); |
19951 | |
19952 | ScratchTagScope tag(masm, value); |
19953 | masm.splitTagForTest(value, tag); |
19954 | |
19955 | masm.branchTestNull(Assembler::Equal, tag, ifTrue); |
19956 | masm.branchTestUndefined(Assembler::Equal, tag, ifTrue); |
19957 | |
19958 | if (!isNextBlock(ins->ifFalse()->lir())) { |
19959 | masm.jump(ifFalse); |
19960 | } |
19961 | } |
19962 | |
19963 | void CodeGenerator::loadOutermostJSScript(Register reg) { |
19964 | // The "outermost" JSScript means the script that we are compiling |
19965 | // basically; this is not always the script associated with the |
19966 | // current basic block, which might be an inlined script. |
19967 | |
19968 | MIRGraph& graph = current->mir()->graph(); |
19969 | MBasicBlock* entryBlock = graph.entryBlock(); |
19970 | masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg); |
19971 | } |
19972 | |
19973 | void CodeGenerator::loadJSScriptForBlock(MBasicBlock* block, Register reg) { |
19974 | // The current JSScript means the script for the current |
19975 | // basic block. This may be an inlined script. |
19976 | |
19977 | JSScript* script = block->info().script(); |
19978 | masm.movePtr(ImmGCPtr(script), reg); |
19979 | } |
19980 | |
19981 | void CodeGenerator::visitHasClass(LHasClass* ins) { |
19982 | Register lhs = ToRegister(ins->lhs()); |
19983 | Register output = ToRegister(ins->output()); |
19984 | |
19985 | masm.loadObjClassUnsafe(lhs, output); |
19986 | masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), |
19987 | output); |
19988 | } |
19989 | |
19990 | void CodeGenerator::visitGuardToClass(LGuardToClass* ins) { |
19991 | Register lhs = ToRegister(ins->lhs()); |
19992 | Register temp = ToRegister(ins->temp0()); |
19993 | |
19994 | // branchTestObjClass may zero the object register on speculative paths |
19995 | // (we should have a defineReuseInput allocation in this case). |
19996 | Register spectreRegToZero = lhs; |
19997 | |
19998 | Label notEqual; |
19999 | |
20000 | masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(), |
20001 | temp, spectreRegToZero, ¬Equal); |
20002 | |
20003 | // Can't return null-return here, so bail. |
20004 | bailoutFrom(¬Equal, ins->snapshot()); |
20005 | } |
20006 | |
20007 | void CodeGenerator::visitGuardToEitherClass(LGuardToEitherClass* ins) { |
20008 | Register lhs = ToRegister(ins->lhs()); |
20009 | Register temp = ToRegister(ins->temp0()); |
20010 | |
20011 | // branchTestObjClass may zero the object register on speculative paths |
20012 | // (we should have a defineReuseInput allocation in this case). |
20013 | Register spectreRegToZero = lhs; |
20014 | |
20015 | Label notEqual; |
20016 | |
20017 | masm.branchTestObjClass(Assembler::NotEqual, lhs, |
20018 | {ins->mir()->getClass1(), ins->mir()->getClass2()}, |
20019 | temp, spectreRegToZero, ¬Equal); |
20020 | |
20021 | // Can't return null-return here, so bail. |
20022 | bailoutFrom(¬Equal, ins->snapshot()); |
20023 | } |
20024 | |
20025 | void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) { |
20026 | Register lhs = ToRegister(ins->lhs()); |
20027 | Register temp = ToRegister(ins->temp0()); |
20028 | |
20029 | // branchTestObjClass may zero the object register on speculative paths |
20030 | // (we should have a defineReuseInput allocation in this case). |
20031 | Register spectreRegToZero = lhs; |
20032 | |
20033 | Label notEqual; |
20034 | |
20035 | masm.branchTestObjIsFunction(Assembler::NotEqual, lhs, temp, spectreRegToZero, |
20036 | ¬Equal); |
20037 | |
20038 | // Can't return null-return here, so bail. |
20039 | bailoutFrom(¬Equal, ins->snapshot()); |
20040 | } |
20041 | |
20042 | void CodeGenerator::visitObjectClassToString(LObjectClassToString* lir) { |
20043 | Register obj = ToRegister(lir->lhs()); |
20044 | Register temp = ToRegister(lir->temp0()); |
20045 | |
20046 | using Fn = JSString* (*)(JSContext*, JSObject*); |
20047 | masm.setupAlignedABICall(); |
20048 | masm.loadJSContext(temp); |
20049 | masm.passABIArg(temp); |
20050 | masm.passABIArg(obj); |
20051 | masm.callWithABI<Fn, js::ObjectClassToString>(); |
20052 | |
20053 | bailoutCmpPtr(Assembler::Equal, ReturnReg, ImmWord(0), lir->snapshot()); |
20054 | } |
20055 | |
20056 | void CodeGenerator::visitWasmParameter(LWasmParameter* lir) {} |
20057 | |
20058 | void CodeGenerator::visitWasmParameterI64(LWasmParameterI64* lir) {} |
20059 | |
20060 | void CodeGenerator::visitWasmReturn(LWasmReturn* lir) { |
20061 | // Don't emit a jump to the return label if this is the last block. |
20062 | if (current->mir() != *gen->graph().poBegin()) { |
20063 | masm.jump(&returnLabel_); |
20064 | } |
20065 | } |
20066 | |
20067 | void CodeGenerator::visitWasmReturnI64(LWasmReturnI64* lir) { |
20068 | // Don't emit a jump to the return label if this is the last block. |
20069 | if (current->mir() != *gen->graph().poBegin()) { |
20070 | masm.jump(&returnLabel_); |
20071 | } |
20072 | } |
20073 | |
20074 | void CodeGenerator::visitWasmReturnVoid(LWasmReturnVoid* lir) { |
20075 | // Don't emit a jump to the return label if this is the last block. |
20076 | if (current->mir() != *gen->graph().poBegin()) { |
20077 | masm.jump(&returnLabel_); |
20078 | } |
20079 | } |
20080 | |
20081 | void CodeGenerator::emitAssertRangeI(MIRType type, const Range* r, |
20082 | Register input) { |
20083 | // Check the lower bound. |
20084 | if (r->hasInt32LowerBound() && r->lower() > INT32_MIN(-2147483647-1)) { |
20085 | Label success; |
20086 | if (type == MIRType::Int32 || type == MIRType::Boolean) { |
20087 | masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), |
20088 | &success); |
20089 | } else { |
20090 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20090); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 20090; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20091 | masm.branchPtr(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), |
20092 | &success); |
20093 | } |
20094 | masm.assumeUnreachable( |
20095 | "Integer input should be equal or higher than Lowerbound."); |
20096 | masm.bind(&success); |
20097 | } |
20098 | |
20099 | // Check the upper bound. |
20100 | if (r->hasInt32UpperBound() && r->upper() < INT32_MAX(2147483647)) { |
20101 | Label success; |
20102 | if (type == MIRType::Int32 || type == MIRType::Boolean) { |
20103 | masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), |
20104 | &success); |
20105 | } else { |
20106 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20106); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 20106; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20107 | masm.branchPtr(Assembler::LessThanOrEqual, input, Imm32(r->upper()), |
20108 | &success); |
20109 | } |
20110 | masm.assumeUnreachable( |
20111 | "Integer input should be lower or equal than Upperbound."); |
20112 | masm.bind(&success); |
20113 | } |
20114 | |
20115 | // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and |
20116 | // r->exponent(), there's nothing to check, because if we ended up in the |
20117 | // integer range checking code, the value is already in an integer register |
20118 | // in the integer range. |
20119 | } |
20120 | |
20121 | void CodeGenerator::emitAssertRangeD(const Range* r, FloatRegister input, |
20122 | FloatRegister temp) { |
20123 | // Check the lower bound. |
20124 | if (r->hasInt32LowerBound()) { |
20125 | Label success; |
20126 | masm.loadConstantDouble(r->lower(), temp); |
20127 | if (r->canBeNaN()) { |
20128 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &success); |
20129 | } |
20130 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, |
20131 | &success); |
20132 | masm.assumeUnreachable( |
20133 | "Double input should be equal or higher than Lowerbound."); |
20134 | masm.bind(&success); |
20135 | } |
20136 | // Check the upper bound. |
20137 | if (r->hasInt32UpperBound()) { |
20138 | Label success; |
20139 | masm.loadConstantDouble(r->upper(), temp); |
20140 | if (r->canBeNaN()) { |
20141 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &success); |
20142 | } |
20143 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success); |
20144 | masm.assumeUnreachable( |
20145 | "Double input should be lower or equal than Upperbound."); |
20146 | masm.bind(&success); |
20147 | } |
20148 | |
20149 | // This code does not yet check r->canHaveFractionalPart(). This would require |
20150 | // new assembler interfaces to make rounding instructions available. |
20151 | |
20152 | if (!r->canBeNegativeZero()) { |
20153 | Label success; |
20154 | |
20155 | // First, test for being equal to 0.0, which also includes -0.0. |
20156 | masm.loadConstantDouble(0.0, temp); |
20157 | masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp, |
20158 | &success); |
20159 | |
20160 | // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is |
20161 | // -Infinity instead of Infinity. |
20162 | masm.loadConstantDouble(1.0, temp); |
20163 | masm.divDouble(input, temp); |
20164 | masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success); |
20165 | |
20166 | masm.assumeUnreachable("Input shouldn't be negative zero."); |
20167 | |
20168 | masm.bind(&success); |
20169 | } |
20170 | |
20171 | if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() && |
20172 | r->exponent() < FloatingPoint<double>::kExponentBias) { |
20173 | // Check the bounds implied by the maximum exponent. |
20174 | Label exponentLoOk; |
20175 | masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp); |
20176 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk); |
20177 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, |
20178 | &exponentLoOk); |
20179 | masm.assumeUnreachable("Check for exponent failed."); |
20180 | masm.bind(&exponentLoOk); |
20181 | |
20182 | Label exponentHiOk; |
20183 | masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp); |
20184 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk); |
20185 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, |
20186 | &exponentHiOk); |
20187 | masm.assumeUnreachable("Check for exponent failed."); |
20188 | masm.bind(&exponentHiOk); |
20189 | } else if (!r->hasInt32Bounds() && !r->canBeNaN()) { |
20190 | // If we think the value can't be NaN, check that it isn't. |
20191 | Label notnan; |
20192 | masm.branchDouble(Assembler::DoubleOrdered, input, input, ¬nan); |
20193 | masm.assumeUnreachable("Input shouldn't be NaN."); |
20194 | masm.bind(¬nan); |
20195 | |
20196 | // If we think the value also can't be an infinity, check that it isn't. |
20197 | if (!r->canBeInfiniteOrNaN()) { |
20198 | Label notposinf; |
20199 | masm.loadConstantDouble(PositiveInfinity<double>(), temp); |
20200 | masm.branchDouble(Assembler::DoubleLessThan, input, temp, ¬posinf); |
20201 | masm.assumeUnreachable("Input shouldn't be +Inf."); |
20202 | masm.bind(¬posinf); |
20203 | |
20204 | Label notneginf; |
20205 | masm.loadConstantDouble(NegativeInfinity<double>(), temp); |
20206 | masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, ¬neginf); |
20207 | masm.assumeUnreachable("Input shouldn't be -Inf."); |
20208 | masm.bind(¬neginf); |
20209 | } |
20210 | } |
20211 | } |
20212 | |
20213 | void CodeGenerator::visitAssertClass(LAssertClass* ins) { |
20214 | Register obj = ToRegister(ins->input()); |
20215 | Register temp = ToRegister(ins->getTemp(0)); |
20216 | |
20217 | Label success; |
20218 | if (ins->mir()->getClass() == &FunctionClass) { |
20219 | // Allow both possible function classes here. |
20220 | masm.branchTestObjIsFunctionNoSpectreMitigations(Assembler::Equal, obj, |
20221 | temp, &success); |
20222 | } else { |
20223 | masm.branchTestObjClassNoSpectreMitigations( |
20224 | Assembler::Equal, obj, ins->mir()->getClass(), temp, &success); |
20225 | } |
20226 | masm.assumeUnreachable("Wrong KnownClass during run-time"); |
20227 | masm.bind(&success); |
20228 | } |
20229 | |
20230 | void CodeGenerator::visitAssertShape(LAssertShape* ins) { |
20231 | Register obj = ToRegister(ins->input()); |
20232 | |
20233 | Label success; |
20234 | masm.branchTestObjShapeNoSpectreMitigations(Assembler::Equal, obj, |
20235 | ins->mir()->shape(), &success); |
20236 | masm.assumeUnreachable("Wrong Shape during run-time"); |
20237 | masm.bind(&success); |
20238 | } |
20239 | |
20240 | void CodeGenerator::visitAssertRangeI(LAssertRangeI* ins) { |
20241 | Register input = ToRegister(ins->input()); |
20242 | const Range* r = ins->range(); |
20243 | |
20244 | emitAssertRangeI(ins->mir()->input()->type(), r, input); |
20245 | } |
20246 | |
20247 | void CodeGenerator::visitAssertRangeD(LAssertRangeD* ins) { |
20248 | FloatRegister input = ToFloatRegister(ins->input()); |
20249 | FloatRegister temp = ToFloatRegister(ins->temp()); |
20250 | const Range* r = ins->range(); |
20251 | |
20252 | emitAssertRangeD(r, input, temp); |
20253 | } |
20254 | |
20255 | void CodeGenerator::visitAssertRangeF(LAssertRangeF* ins) { |
20256 | FloatRegister input = ToFloatRegister(ins->input()); |
20257 | FloatRegister temp = ToFloatRegister(ins->temp()); |
20258 | FloatRegister temp2 = ToFloatRegister(ins->temp2()); |
20259 | |
20260 | const Range* r = ins->range(); |
20261 | |
20262 | masm.convertFloat32ToDouble(input, temp); |
20263 | emitAssertRangeD(r, temp, temp2); |
20264 | } |
20265 | |
20266 | void CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) { |
20267 | const Range* r = ins->range(); |
20268 | const ValueOperand value = ToValue(ins, LAssertRangeV::Input); |
20269 | Label done; |
20270 | |
20271 | { |
20272 | ScratchTagScope tag(masm, value); |
20273 | masm.splitTagForTest(value, tag); |
20274 | |
20275 | { |
20276 | Label isNotInt32; |
20277 | masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32); |
20278 | { |
20279 | ScratchTagScopeRelease _(&tag); |
20280 | Register unboxInt32 = ToTempUnboxRegister(ins->temp()); |
20281 | Register input = masm.extractInt32(value, unboxInt32); |
20282 | emitAssertRangeI(MIRType::Int32, r, input); |
20283 | masm.jump(&done); |
20284 | } |
20285 | masm.bind(&isNotInt32); |
20286 | } |
20287 | |
20288 | { |
20289 | Label isNotDouble; |
20290 | masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble); |
20291 | { |
20292 | ScratchTagScopeRelease _(&tag); |
20293 | FloatRegister input = ToFloatRegister(ins->floatTemp1()); |
20294 | FloatRegister temp = ToFloatRegister(ins->floatTemp2()); |
20295 | masm.unboxDouble(value, input); |
20296 | emitAssertRangeD(r, input, temp); |
20297 | masm.jump(&done); |
20298 | } |
20299 | masm.bind(&isNotDouble); |
20300 | } |
20301 | } |
20302 | |
20303 | masm.assumeUnreachable("Incorrect range for Value."); |
20304 | masm.bind(&done); |
20305 | } |
20306 | |
20307 | void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) { |
20308 | using Fn = bool (*)(JSContext*); |
20309 | OutOfLineCode* ool = |
20310 | oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing()); |
20311 | |
20312 | const void* interruptAddr = gen->runtime->addressOfInterruptBits(); |
20313 | masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0), |
20314 | ool->entry()); |
20315 | masm.bind(ool->rejoin()); |
20316 | } |
20317 | |
20318 | void CodeGenerator::visitOutOfLineResumableWasmTrap( |
20319 | OutOfLineResumableWasmTrap* ool) { |
20320 | LInstruction* lir = ool->lir(); |
20321 | masm.wasmTrap(ool->trap(), ool->trapSiteDesc()); |
20322 | |
20323 | markSafepointAt(masm.currentOffset(), lir); |
20324 | |
20325 | // Note that masm.framePushed() doesn't include the register dump area. |
20326 | // That will be taken into account when the StackMap is created from the |
20327 | // LSafepoint. |
20328 | lir->safepoint()->setFramePushedAtStackMapBase(ool->framePushed()); |
20329 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::Trap); |
20330 | |
20331 | masm.jump(ool->rejoin()); |
20332 | } |
20333 | |
20334 | void CodeGenerator::visitOutOfLineAbortingWasmTrap( |
20335 | OutOfLineAbortingWasmTrap* ool) { |
20336 | masm.wasmTrap(ool->trap(), ool->trapSiteDesc()); |
20337 | } |
20338 | |
20339 | void CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir) { |
20340 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20340; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20341 | |
20342 | OutOfLineResumableWasmTrap* ool = new (alloc()) OutOfLineResumableWasmTrap( |
20343 | lir, masm.framePushed(), lir->mir()->trapSiteDesc(), |
20344 | wasm::Trap::CheckInterrupt); |
20345 | addOutOfLineCode(ool, lir->mir()); |
20346 | masm.branch32( |
20347 | Assembler::NotEqual, |
20348 | Address(ToRegister(lir->instance()), wasm::Instance::offsetOfInterrupt()), |
20349 | Imm32(0), ool->entry()); |
20350 | masm.bind(ool->rejoin()); |
20351 | } |
20352 | |
20353 | void CodeGenerator::visitWasmTrap(LWasmTrap* lir) { |
20354 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20354; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20355 | const MWasmTrap* mir = lir->mir(); |
20356 | |
20357 | masm.wasmTrap(mir->trap(), mir->trapSiteDesc()); |
20358 | } |
20359 | |
20360 | void CodeGenerator::visitWasmTrapIfNull(LWasmTrapIfNull* lir) { |
20361 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20361; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20362 | const MWasmTrapIfNull* mir = lir->mir(); |
20363 | Label nonNull; |
20364 | Register ref = ToRegister(lir->ref()); |
20365 | |
20366 | masm.branchWasmAnyRefIsNull(false, ref, &nonNull); |
20367 | masm.wasmTrap(mir->trap(), mir->trapSiteDesc()); |
20368 | masm.bind(&nonNull); |
20369 | } |
20370 | |
20371 | void CodeGenerator::visitWasmRefIsSubtypeOfAbstract( |
20372 | LWasmRefIsSubtypeOfAbstract* ins) { |
20373 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20373); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20373; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20374 | |
20375 | const MWasmRefIsSubtypeOfAbstract* mir = ins->mir(); |
20376 | MOZ_ASSERT(!mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!mir->destType().isTypeRef())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!mir->destType().isTypeRef ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!mir->destType().isTypeRef()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()" ")"); do { *((volatile int*)__null) = 20376; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20377 | |
20378 | Register ref = ToRegister(ins->ref()); |
20379 | Register superSTV = Register::Invalid(); |
20380 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
20381 | Register scratch2 = Register::Invalid(); |
20382 | Register result = ToRegister(ins->output()); |
20383 | Label onSuccess; |
20384 | Label onFail; |
20385 | Label join; |
20386 | masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(), |
20387 | &onSuccess, /*onSuccess=*/true, superSTV, |
20388 | scratch1, scratch2); |
20389 | masm.bind(&onFail); |
20390 | masm.xor32(result, result); |
20391 | masm.jump(&join); |
20392 | masm.bind(&onSuccess); |
20393 | masm.move32(Imm32(1), result); |
20394 | masm.bind(&join); |
20395 | } |
20396 | |
20397 | void CodeGenerator::visitWasmRefIsSubtypeOfConcrete( |
20398 | LWasmRefIsSubtypeOfConcrete* ins) { |
20399 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20399); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20399; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20400 | |
20401 | const MWasmRefIsSubtypeOfConcrete* mir = ins->mir(); |
20402 | MOZ_ASSERT(mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->destType().isTypeRef())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->destType().isTypeRef ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mir->destType().isTypeRef()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20402); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()" ")"); do { *((volatile int*)__null) = 20402; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20403 | |
20404 | Register ref = ToRegister(ins->ref()); |
20405 | Register superSTV = ToRegister(ins->superSTV()); |
20406 | Register scratch1 = ToRegister(ins->temp0()); |
20407 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
20408 | Register result = ToRegister(ins->output()); |
20409 | Label onSuccess; |
20410 | Label join; |
20411 | masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(), |
20412 | &onSuccess, /*onSuccess=*/true, superSTV, |
20413 | scratch1, scratch2); |
20414 | masm.move32(Imm32(0), result); |
20415 | masm.jump(&join); |
20416 | masm.bind(&onSuccess); |
20417 | masm.move32(Imm32(1), result); |
20418 | masm.bind(&join); |
20419 | } |
20420 | |
20421 | void CodeGenerator::visitWasmRefIsSubtypeOfAbstractAndBranch( |
20422 | LWasmRefIsSubtypeOfAbstractAndBranch* ins) { |
20423 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20423; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20424 | Register ref = ToRegister(ins->ref()); |
20425 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
20426 | Label* onSuccess = getJumpLabelForBranch(ins->ifTrue()); |
20427 | Label* onFail = getJumpLabelForBranch(ins->ifFalse()); |
20428 | masm.branchWasmRefIsSubtype( |
20429 | ref, ins->sourceType(), ins->destType(), onSuccess, /*onSuccess=*/true, |
20430 | Register::Invalid(), scratch1, Register::Invalid()); |
20431 | masm.jump(onFail); |
20432 | } |
20433 | |
20434 | void CodeGenerator::visitWasmRefIsSubtypeOfConcreteAndBranch( |
20435 | LWasmRefIsSubtypeOfConcreteAndBranch* ins) { |
20436 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20436); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20436; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20437 | Register ref = ToRegister(ins->ref()); |
20438 | Register superSTV = ToRegister(ins->superSTV()); |
20439 | Register scratch1 = ToRegister(ins->temp0()); |
20440 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
20441 | Label* onSuccess = getJumpLabelForBranch(ins->ifTrue()); |
20442 | Label* onFail = getJumpLabelForBranch(ins->ifFalse()); |
20443 | masm.branchWasmRefIsSubtype(ref, ins->sourceType(), ins->destType(), |
20444 | onSuccess, /*onSuccess=*/true, superSTV, scratch1, |
20445 | scratch2); |
20446 | masm.jump(onFail); |
20447 | } |
20448 | |
20449 | void CodeGenerator::callWasmStructAllocFun( |
20450 | LInstruction* lir, wasm::SymbolicAddress fun, Register typeDefData, |
20451 | Register output, const wasm::TrapSiteDesc& trapSiteDesc) { |
20452 | MOZ_ASSERT(fun == wasm::SymbolicAddress::StructNewIL_true ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm:: SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress ::StructNewOOL_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { *((volatile int*)__null) = 20455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20453 | fun == wasm::SymbolicAddress::StructNewIL_false ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm:: SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress ::StructNewOOL_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { *((volatile int*)__null) = 20455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20454 | fun == wasm::SymbolicAddress::StructNewOOL_true ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm:: SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress ::StructNewOOL_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { *((volatile int*)__null) = 20455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20455 | fun == wasm::SymbolicAddress::StructNewOOL_false)do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm:: SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress ::StructNewOOL_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { *((volatile int*)__null) = 20455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20456 | MOZ_ASSERT(wasm::SASigStructNewIL_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20457; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20457 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20457; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20458 | MOZ_ASSERT(wasm::SASigStructNewIL_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_false. failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20459); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20459; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20459 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_false. failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20459); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20459; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20460 | MOZ_ASSERT(wasm::SASigStructNewOOL_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_true. failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20461); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20461; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20461 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_true. failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20461); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20461; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20462 | MOZ_ASSERT(wasm::SASigStructNewOOL_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_false .failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20463); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20463; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20463 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_false .failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20463); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20463; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20464 | |
20465 | masm.Push(InstanceReg); |
20466 | int32_t framePushedAfterInstance = masm.framePushed(); |
20467 | saveLive(lir); |
20468 | |
20469 | masm.setupWasmABICall(); |
20470 | masm.passABIArg(InstanceReg); |
20471 | masm.passABIArg(typeDefData); |
20472 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
20473 | CodeOffset offset = |
20474 | masm.callWithABI(trapSiteDesc.bytecodeOffset, fun, |
20475 | mozilla::Some(instanceOffset), ABIType::General); |
20476 | masm.storeCallPointerResult(output); |
20477 | |
20478 | markSafepointAt(offset.offset(), lir); |
20479 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance); |
20480 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall); |
20481 | |
20482 | restoreLive(lir); |
20483 | masm.Pop(InstanceReg); |
20484 | #if JS_CODEGEN_ARM64 |
20485 | masm.syncStackPtr(); |
20486 | #endif |
20487 | |
20488 | masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr, |
20489 | trapSiteDesc); |
20490 | } |
20491 | |
20492 | // Out-of-line path to allocate wasm GC structs |
20493 | class OutOfLineWasmNewStruct : public OutOfLineCodeBase<CodeGenerator> { |
20494 | LInstruction* lir_; |
20495 | wasm::SymbolicAddress fun_; |
20496 | Register typeDefData_; |
20497 | Register output_; |
20498 | wasm::TrapSiteDesc trapSiteDesc_; |
20499 | |
20500 | public: |
20501 | OutOfLineWasmNewStruct(LInstruction* lir, wasm::SymbolicAddress fun, |
20502 | Register typeDefData, Register output, |
20503 | const wasm::TrapSiteDesc& trapSiteDesc) |
20504 | : lir_(lir), |
20505 | fun_(fun), |
20506 | typeDefData_(typeDefData), |
20507 | output_(output), |
20508 | trapSiteDesc_(trapSiteDesc) {} |
20509 | |
20510 | void accept(CodeGenerator* codegen) override { |
20511 | codegen->visitOutOfLineWasmNewStruct(this); |
20512 | } |
20513 | |
20514 | LInstruction* lir() const { return lir_; } |
20515 | wasm::SymbolicAddress fun() const { return fun_; } |
20516 | Register typeDefData() const { return typeDefData_; } |
20517 | Register output() const { return output_; } |
20518 | const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } |
20519 | }; |
20520 | |
20521 | void CodeGenerator::visitOutOfLineWasmNewStruct(OutOfLineWasmNewStruct* ool) { |
20522 | callWasmStructAllocFun(ool->lir(), ool->fun(), ool->typeDefData(), |
20523 | ool->output(), ool->trapSiteDesc()); |
20524 | masm.jump(ool->rejoin()); |
20525 | } |
20526 | |
20527 | void CodeGenerator::visitWasmNewStructObject(LWasmNewStructObject* lir) { |
20528 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20528); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20528; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20529 | |
20530 | MWasmNewStructObject* mir = lir->mir(); |
20531 | |
20532 | Register typeDefData = ToRegister(lir->typeDefData()); |
20533 | Register output = ToRegister(lir->output()); |
20534 | |
20535 | if (mir->isOutline()) { |
20536 | wasm::SymbolicAddress fun = mir->zeroFields() |
20537 | ? wasm::SymbolicAddress::StructNewOOL_true |
20538 | : wasm::SymbolicAddress::StructNewOOL_false; |
20539 | callWasmStructAllocFun(lir, fun, typeDefData, output, mir->trapSiteDesc()); |
20540 | } else { |
20541 | wasm::SymbolicAddress fun = mir->zeroFields() |
20542 | ? wasm::SymbolicAddress::StructNewIL_true |
20543 | : wasm::SymbolicAddress::StructNewIL_false; |
20544 | |
20545 | Register instance = ToRegister(lir->instance()); |
20546 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20546); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20546; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20547 | |
20548 | auto* ool = new (alloc()) OutOfLineWasmNewStruct( |
20549 | lir, fun, typeDefData, output, mir->trapSiteDesc()); |
20550 | addOutOfLineCode(ool, lir->mir()); |
20551 | |
20552 | Register temp1 = ToRegister(lir->temp0()); |
20553 | Register temp2 = ToRegister(lir->temp1()); |
20554 | masm.wasmNewStructObject(instance, output, typeDefData, temp1, temp2, |
20555 | ool->entry(), mir->allocKind(), mir->zeroFields()); |
20556 | |
20557 | masm.bind(ool->rejoin()); |
20558 | } |
20559 | } |
20560 | |
20561 | void CodeGenerator::callWasmArrayAllocFun( |
20562 | LInstruction* lir, wasm::SymbolicAddress fun, Register numElements, |
20563 | Register typeDefData, Register output, |
20564 | const wasm::TrapSiteDesc& trapSiteDesc) { |
20565 | MOZ_ASSERT(fun == wasm::SymbolicAddress::ArrayNew_true ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" ")"); do { *((volatile int*)__null) = 20566; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20566 | fun == wasm::SymbolicAddress::ArrayNew_false)do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" ")"); do { *((volatile int*)__null) = 20566; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20567 | MOZ_ASSERT(wasm::SASigArrayNew_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20568; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20568 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20568; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20569 | MOZ_ASSERT(wasm::SASigArrayNew_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20570; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
20570 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20570; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20571 | |
20572 | masm.Push(InstanceReg); |
20573 | int32_t framePushedAfterInstance = masm.framePushed(); |
20574 | saveLive(lir); |
20575 | |
20576 | masm.setupWasmABICall(); |
20577 | masm.passABIArg(InstanceReg); |
20578 | masm.passABIArg(numElements); |
20579 | masm.passABIArg(typeDefData); |
20580 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
20581 | CodeOffset offset = |
20582 | masm.callWithABI(trapSiteDesc.bytecodeOffset, fun, |
20583 | mozilla::Some(instanceOffset), ABIType::General); |
20584 | masm.storeCallPointerResult(output); |
20585 | |
20586 | markSafepointAt(offset.offset(), lir); |
20587 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance); |
20588 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall); |
20589 | |
20590 | restoreLive(lir); |
20591 | masm.Pop(InstanceReg); |
20592 | #if JS_CODEGEN_ARM64 |
20593 | masm.syncStackPtr(); |
20594 | #endif |
20595 | |
20596 | masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr, |
20597 | trapSiteDesc); |
20598 | } |
20599 | |
20600 | // Out-of-line path to allocate wasm GC arrays |
20601 | class OutOfLineWasmNewArray : public OutOfLineCodeBase<CodeGenerator> { |
20602 | LInstruction* lir_; |
20603 | wasm::SymbolicAddress fun_; |
20604 | Register numElementsReg_; |
20605 | mozilla::Maybe<uint32_t> numElements_; |
20606 | Register typeDefData_; |
20607 | Register output_; |
20608 | wasm::TrapSiteDesc trapSiteDesc_; |
20609 | |
20610 | public: |
20611 | OutOfLineWasmNewArray(LInstruction* lir, wasm::SymbolicAddress fun, |
20612 | Register numElementsReg, |
20613 | mozilla::Maybe<uint32_t> numElements, |
20614 | Register typeDefData, Register output, |
20615 | const wasm::TrapSiteDesc& trapSiteDesc) |
20616 | : lir_(lir), |
20617 | fun_(fun), |
20618 | numElementsReg_(numElementsReg), |
20619 | numElements_(numElements), |
20620 | typeDefData_(typeDefData), |
20621 | output_(output), |
20622 | trapSiteDesc_(trapSiteDesc) {} |
20623 | |
20624 | void accept(CodeGenerator* codegen) override { |
20625 | codegen->visitOutOfLineWasmNewArray(this); |
20626 | } |
20627 | |
20628 | LInstruction* lir() const { return lir_; } |
20629 | wasm::SymbolicAddress fun() const { return fun_; } |
20630 | Register numElementsReg() const { return numElementsReg_; } |
20631 | mozilla::Maybe<uint32_t> numElements() const { return numElements_; } |
20632 | Register typeDefData() const { return typeDefData_; } |
20633 | Register output() const { return output_; } |
20634 | const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } |
20635 | }; |
20636 | |
20637 | void CodeGenerator::visitOutOfLineWasmNewArray(OutOfLineWasmNewArray* ool) { |
20638 | if (ool->numElements().isSome()) { |
20639 | masm.move32(Imm32(ool->numElements().value()), ool->numElementsReg()); |
20640 | } |
20641 | callWasmArrayAllocFun(ool->lir(), ool->fun(), ool->numElementsReg(), |
20642 | ool->typeDefData(), ool->output(), ool->trapSiteDesc()); |
20643 | masm.jump(ool->rejoin()); |
20644 | } |
20645 | |
20646 | void CodeGenerator::visitWasmNewArrayObject(LWasmNewArrayObject* lir) { |
20647 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20647); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20647; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20648 | |
20649 | MWasmNewArrayObject* mir = lir->mir(); |
20650 | |
20651 | Register typeDefData = ToRegister(lir->typeDefData()); |
20652 | Register output = ToRegister(lir->output()); |
20653 | Register temp1 = ToRegister(lir->temp0()); |
20654 | Register temp2 = ToRegister(lir->temp1()); |
20655 | |
20656 | wasm::SymbolicAddress fun = mir->zeroFields() |
20657 | ? wasm::SymbolicAddress::ArrayNew_true |
20658 | : wasm::SymbolicAddress::ArrayNew_false; |
20659 | |
20660 | if (lir->numElements()->isConstant()) { |
20661 | // numElements is constant, so we can do optimized code generation. |
20662 | uint32_t numElements = lir->numElements()->toConstant()->toInt32(); |
20663 | CheckedUint32 storageBytes = |
20664 | WasmArrayObject::calcStorageBytesChecked(mir->elemSize(), numElements); |
20665 | if (!storageBytes.isValid() || |
20666 | storageBytes.value() > WasmArrayObject_MaxInlineBytes) { |
20667 | // Too much array data to store inline. Immediately perform an instance |
20668 | // call to handle the out-of-line storage (or the trap). |
20669 | masm.move32(Imm32(numElements), temp1); |
20670 | callWasmArrayAllocFun(lir, fun, temp1, typeDefData, output, |
20671 | mir->trapSiteDesc()); |
20672 | } else { |
20673 | // storageBytes is small enough to be stored inline in WasmArrayObject. |
20674 | // Attempt a nursery allocation and fall back to an instance call if it |
20675 | // fails. |
20676 | Register instance = ToRegister(lir->instance()); |
20677 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20677; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20678 | |
20679 | auto ool = new (alloc()) |
20680 | OutOfLineWasmNewArray(lir, fun, temp1, mozilla::Some(numElements), |
20681 | typeDefData, output, mir->trapSiteDesc()); |
20682 | addOutOfLineCode(ool, lir->mir()); |
20683 | |
20684 | masm.wasmNewArrayObjectFixed(instance, output, typeDefData, temp1, temp2, |
20685 | ool->entry(), numElements, |
20686 | storageBytes.value(), mir->zeroFields()); |
20687 | |
20688 | masm.bind(ool->rejoin()); |
20689 | } |
20690 | } else { |
20691 | // numElements is dynamic. Attempt a dynamic inline-storage nursery |
20692 | // allocation and fall back to an instance call if it fails. |
20693 | Register instance = ToRegister(lir->instance()); |
20694 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20694); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20694; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20695 | Register numElements = ToRegister(lir->numElements()); |
20696 | |
20697 | auto ool = new (alloc()) |
20698 | OutOfLineWasmNewArray(lir, fun, numElements, mozilla::Nothing(), |
20699 | typeDefData, output, mir->trapSiteDesc()); |
20700 | addOutOfLineCode(ool, lir->mir()); |
20701 | |
20702 | masm.wasmNewArrayObject(instance, output, numElements, typeDefData, temp1, |
20703 | ool->entry(), mir->elemSize(), mir->zeroFields()); |
20704 | |
20705 | masm.bind(ool->rejoin()); |
20706 | } |
20707 | } |
20708 | |
20709 | void CodeGenerator::visitWasmHeapReg(LWasmHeapReg* ins) { |
20710 | #ifdef WASM_HAS_HEAPREG1 |
20711 | masm.movePtr(HeapReg, ToRegister(ins->output())); |
20712 | #else |
20713 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20713); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 20713; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
20714 | #endif |
20715 | } |
20716 | |
20717 | void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) { |
20718 | const MWasmBoundsCheck* mir = ins->mir(); |
20719 | Register ptr = ToRegister(ins->ptr()); |
20720 | Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit()); |
20721 | // When there are no spectre mitigations in place, branching out-of-line to |
20722 | // the trap is a big performance win, but with mitigations it's trickier. See |
20723 | // bug 1680243. |
20724 | if (JitOptions.spectreIndexMasking) { |
20725 | Label ok; |
20726 | masm.wasmBoundsCheck32(Assembler::Below, ptr, boundsCheckLimit, &ok); |
20727 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
20728 | masm.bind(&ok); |
20729 | } else { |
20730 | OutOfLineAbortingWasmTrap* ool = new (alloc()) |
20731 | OutOfLineAbortingWasmTrap(mir->trapSiteDesc(), wasm::Trap::OutOfBounds); |
20732 | addOutOfLineCode(ool, mir); |
20733 | masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptr, boundsCheckLimit, |
20734 | ool->entry()); |
20735 | } |
20736 | } |
20737 | |
20738 | void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) { |
20739 | const MWasmBoundsCheck* mir = ins->mir(); |
20740 | Register64 ptr = ToRegister64(ins->ptr()); |
20741 | Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit()); |
20742 | // See above. |
20743 | if (JitOptions.spectreIndexMasking) { |
20744 | Label ok; |
20745 | masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok); |
20746 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
20747 | masm.bind(&ok); |
20748 | } else { |
20749 | OutOfLineAbortingWasmTrap* ool = new (alloc()) |
20750 | OutOfLineAbortingWasmTrap(mir->trapSiteDesc(), wasm::Trap::OutOfBounds); |
20751 | addOutOfLineCode(ool, mir); |
20752 | masm.wasmBoundsCheck64(Assembler::AboveOrEqual, ptr, boundsCheckLimit, |
20753 | ool->entry()); |
20754 | } |
20755 | } |
20756 | |
20757 | void CodeGenerator::visitWasmBoundsCheckRange32(LWasmBoundsCheckRange32* ins) { |
20758 | const MWasmBoundsCheckRange32* mir = ins->mir(); |
20759 | Register index = ToRegister(ins->index()); |
20760 | Register length = ToRegister(ins->length()); |
20761 | Register limit = ToRegister(ins->limit()); |
20762 | Register tmp = ToRegister(ins->temp0()); |
20763 | |
20764 | masm.wasmBoundsCheckRange32(index, length, limit, tmp, mir->trapSiteDesc()); |
20765 | } |
20766 | |
20767 | void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) { |
20768 | const MWasmAlignmentCheck* mir = ins->mir(); |
20769 | Register ptr = ToRegister(ins->ptr()); |
20770 | OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap( |
20771 | mir->trapSiteDesc(), wasm::Trap::UnalignedAccess); |
20772 | addOutOfLineCode(ool, mir); |
20773 | masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1), |
20774 | ool->entry()); |
20775 | } |
20776 | |
20777 | void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) { |
20778 | const MWasmAlignmentCheck* mir = ins->mir(); |
20779 | Register64 ptr = ToRegister64(ins->ptr()); |
20780 | #ifdef JS_64BIT1 |
20781 | Register r = ptr.reg; |
20782 | #else |
20783 | Register r = ptr.low; |
20784 | #endif |
20785 | OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap( |
20786 | mir->trapSiteDesc(), wasm::Trap::UnalignedAccess); |
20787 | addOutOfLineCode(ool, mir); |
20788 | masm.branchTestPtr(Assembler::NonZero, r, Imm32(mir->byteSize() - 1), |
20789 | ool->entry()); |
20790 | } |
20791 | |
20792 | void CodeGenerator::visitWasmLoadInstance(LWasmLoadInstance* ins) { |
20793 | switch (ins->mir()->type()) { |
20794 | case MIRType::WasmAnyRef: |
20795 | case MIRType::Pointer: |
20796 | masm.loadPtr(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
20797 | ToRegister(ins->output())); |
20798 | break; |
20799 | case MIRType::Int32: |
20800 | masm.load32(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
20801 | ToRegister(ins->output())); |
20802 | break; |
20803 | default: |
20804 | MOZ_CRASH("MIRType not supported in WasmLoadInstance")do { do { } while (false); MOZ_ReportCrash("" "MIRType not supported in WasmLoadInstance" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20804); AnnotateMozCrashReason("MOZ_CRASH(" "MIRType not supported in WasmLoadInstance" ")"); do { *((volatile int*)__null) = 20804; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
20805 | } |
20806 | } |
20807 | |
20808 | void CodeGenerator::visitWasmLoadInstance64(LWasmLoadInstance64* ins) { |
20809 | MOZ_ASSERT(ins->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->type() == MIRType::Int64)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ins->mir()->type() == MIRType::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ins->mir()->type() == MIRType::Int64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20809); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 20809; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20810 | masm.load64(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
20811 | ToOutRegister64(ins)); |
20812 | } |
20813 | |
20814 | void CodeGenerator::incrementWarmUpCounter(AbsoluteAddress warmUpCount, |
20815 | JSScript* script, Register tmp) { |
20816 | // The code depends on the JitScript* not being discarded without also |
20817 | // invalidating Ion code. Assert this. |
20818 | #ifdef DEBUG1 |
20819 | Label ok; |
20820 | masm.movePtr(ImmGCPtr(script), tmp); |
20821 | masm.loadJitScript(tmp, tmp); |
20822 | masm.branchPtr(Assembler::Equal, tmp, ImmPtr(script->jitScript()), &ok); |
20823 | masm.assumeUnreachable("Didn't find JitScript?"); |
20824 | masm.bind(&ok); |
20825 | #endif |
20826 | |
20827 | masm.load32(warmUpCount, tmp); |
20828 | masm.add32(Imm32(1), tmp); |
20829 | masm.store32(tmp, warmUpCount); |
20830 | } |
20831 | |
20832 | void CodeGenerator::visitIncrementWarmUpCounter(LIncrementWarmUpCounter* ins) { |
20833 | Register tmp = ToRegister(ins->temp0()); |
20834 | |
20835 | AbsoluteAddress warmUpCount = |
20836 | AbsoluteAddress(ins->mir()->script()->jitScript()) |
20837 | .offset(JitScript::offsetOfWarmUpCount()); |
20838 | incrementWarmUpCounter(warmUpCount, ins->mir()->script(), tmp); |
20839 | } |
20840 | |
20841 | void CodeGenerator::visitLexicalCheck(LLexicalCheck* ins) { |
20842 | ValueOperand inputValue = ToValue(ins, LLexicalCheck::InputIndex); |
20843 | Label bail; |
20844 | masm.branchTestMagicValue(Assembler::Equal, inputValue, |
20845 | JS_UNINITIALIZED_LEXICAL, &bail); |
20846 | bailoutFrom(&bail, ins->snapshot()); |
20847 | } |
20848 | |
20849 | void CodeGenerator::visitThrowRuntimeLexicalError( |
20850 | LThrowRuntimeLexicalError* ins) { |
20851 | pushArg(Imm32(ins->mir()->errorNumber())); |
20852 | |
20853 | using Fn = bool (*)(JSContext*, unsigned); |
20854 | callVM<Fn, jit::ThrowRuntimeLexicalError>(ins); |
20855 | } |
20856 | |
20857 | void CodeGenerator::visitThrowMsg(LThrowMsg* ins) { |
20858 | pushArg(Imm32(static_cast<int32_t>(ins->mir()->throwMsgKind()))); |
20859 | |
20860 | using Fn = bool (*)(JSContext*, unsigned); |
20861 | callVM<Fn, js::ThrowMsgOperation>(ins); |
20862 | } |
20863 | |
20864 | void CodeGenerator::visitGlobalDeclInstantiation( |
20865 | LGlobalDeclInstantiation* ins) { |
20866 | pushArg(ImmPtr(ins->mir()->resumePoint()->pc())); |
20867 | pushArg(ImmGCPtr(ins->mir()->block()->info().script())); |
20868 | |
20869 | using Fn = bool (*)(JSContext*, HandleScript, const jsbytecode*); |
20870 | callVM<Fn, GlobalDeclInstantiationFromIon>(ins); |
20871 | } |
20872 | |
20873 | void CodeGenerator::visitDebugger(LDebugger* ins) { |
20874 | Register cx = ToRegister(ins->temp0()); |
20875 | |
20876 | masm.loadJSContext(cx); |
20877 | using Fn = bool (*)(JSContext* cx); |
20878 | masm.setupAlignedABICall(); |
20879 | masm.passABIArg(cx); |
20880 | masm.callWithABI<Fn, GlobalHasLiveOnDebuggerStatement>(); |
20881 | |
20882 | Label bail; |
20883 | masm.branchIfTrueBool(ReturnReg, &bail); |
20884 | bailoutFrom(&bail, ins->snapshot()); |
20885 | } |
20886 | |
20887 | void CodeGenerator::visitNewTarget(LNewTarget* ins) { |
20888 | ValueOperand output = ToOutValue(ins); |
20889 | |
20890 | // if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)] |
20891 | Label notConstructing, done; |
20892 | Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
20893 | masm.branchTestPtr(Assembler::Zero, calleeToken, |
20894 | Imm32(CalleeToken_FunctionConstructing), ¬Constructing); |
20895 | |
20896 | Register argvLen = output.scratchReg(); |
20897 | masm.loadNumActualArgs(FramePointer, argvLen); |
20898 | |
20899 | Label useNFormals; |
20900 | |
20901 | size_t numFormalArgs = ins->mirRaw()->block()->info().nargs(); |
20902 | masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs), &useNFormals); |
20903 | |
20904 | size_t argsOffset = JitFrameLayout::offsetOfActualArgs(); |
20905 | { |
20906 | BaseValueIndex newTarget(FramePointer, argvLen, argsOffset); |
20907 | masm.loadValue(newTarget, output); |
20908 | masm.jump(&done); |
20909 | } |
20910 | |
20911 | masm.bind(&useNFormals); |
20912 | |
20913 | { |
20914 | Address newTarget(FramePointer, |
20915 | argsOffset + (numFormalArgs * sizeof(Value))); |
20916 | masm.loadValue(newTarget, output); |
20917 | masm.jump(&done); |
20918 | } |
20919 | |
20920 | // else output = undefined |
20921 | masm.bind(¬Constructing); |
20922 | masm.moveValue(UndefinedValue(), output); |
20923 | masm.bind(&done); |
20924 | } |
20925 | |
20926 | void CodeGenerator::visitCheckReturn(LCheckReturn* ins) { |
20927 | ValueOperand returnValue = ToValue(ins, LCheckReturn::ReturnValueIndex); |
20928 | ValueOperand thisValue = ToValue(ins, LCheckReturn::ThisValueIndex); |
20929 | ValueOperand output = ToOutValue(ins); |
20930 | |
20931 | using Fn = bool (*)(JSContext*, HandleValue); |
20932 | OutOfLineCode* ool = oolCallVM<Fn, ThrowBadDerivedReturnOrUninitializedThis>( |
20933 | ins, ArgList(returnValue), StoreNothing()); |
20934 | |
20935 | Label noChecks; |
20936 | masm.branchTestObject(Assembler::Equal, returnValue, &noChecks); |
20937 | masm.branchTestUndefined(Assembler::NotEqual, returnValue, ool->entry()); |
20938 | masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry()); |
20939 | masm.moveValue(thisValue, output); |
20940 | masm.jump(ool->rejoin()); |
20941 | masm.bind(&noChecks); |
20942 | masm.moveValue(returnValue, output); |
20943 | masm.bind(ool->rejoin()); |
20944 | } |
20945 | |
20946 | void CodeGenerator::visitCheckIsObj(LCheckIsObj* ins) { |
20947 | ValueOperand value = ToValue(ins, LCheckIsObj::ValueIndex); |
20948 | Register output = ToRegister(ins->output()); |
20949 | |
20950 | using Fn = bool (*)(JSContext*, CheckIsObjectKind); |
20951 | OutOfLineCode* ool = oolCallVM<Fn, ThrowCheckIsObject>( |
20952 | ins, ArgList(Imm32(ins->mir()->checkKind())), StoreNothing()); |
20953 | |
20954 | masm.fallibleUnboxObject(value, output, ool->entry()); |
20955 | masm.bind(ool->rejoin()); |
20956 | } |
20957 | |
20958 | void CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) { |
20959 | ValueOperand checkValue = ToValue(ins, LCheckObjCoercible::ValueIndex); |
20960 | |
20961 | using Fn = bool (*)(JSContext*, HandleValue); |
20962 | OutOfLineCode* ool = oolCallVM<Fn, ThrowObjectCoercible>( |
20963 | ins, ArgList(checkValue), StoreNothing()); |
20964 | masm.branchTestNull(Assembler::Equal, checkValue, ool->entry()); |
20965 | masm.branchTestUndefined(Assembler::Equal, checkValue, ool->entry()); |
20966 | masm.bind(ool->rejoin()); |
20967 | } |
20968 | |
20969 | void CodeGenerator::visitCheckClassHeritage(LCheckClassHeritage* ins) { |
20970 | ValueOperand heritage = ToValue(ins, LCheckClassHeritage::HeritageIndex); |
20971 | Register temp0 = ToRegister(ins->temp0()); |
20972 | Register temp1 = ToRegister(ins->temp1()); |
20973 | |
20974 | using Fn = bool (*)(JSContext*, HandleValue); |
20975 | OutOfLineCode* ool = oolCallVM<Fn, CheckClassHeritageOperation>( |
20976 | ins, ArgList(heritage), StoreNothing()); |
20977 | |
20978 | masm.branchTestNull(Assembler::Equal, heritage, ool->rejoin()); |
20979 | masm.fallibleUnboxObject(heritage, temp0, ool->entry()); |
20980 | |
20981 | masm.isConstructor(temp0, temp1, ool->entry()); |
20982 | masm.branchTest32(Assembler::Zero, temp1, temp1, ool->entry()); |
20983 | |
20984 | masm.bind(ool->rejoin()); |
20985 | } |
20986 | |
20987 | void CodeGenerator::visitCheckThis(LCheckThis* ins) { |
20988 | ValueOperand thisValue = ToValue(ins, LCheckThis::ValueIndex); |
20989 | |
20990 | using Fn = bool (*)(JSContext*); |
20991 | OutOfLineCode* ool = |
20992 | oolCallVM<Fn, ThrowUninitializedThis>(ins, ArgList(), StoreNothing()); |
20993 | masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry()); |
20994 | masm.bind(ool->rejoin()); |
20995 | } |
20996 | |
20997 | void CodeGenerator::visitCheckThisReinit(LCheckThisReinit* ins) { |
20998 | ValueOperand thisValue = ToValue(ins, LCheckThisReinit::ThisValueIndex); |
20999 | |
21000 | using Fn = bool (*)(JSContext*); |
21001 | OutOfLineCode* ool = |
21002 | oolCallVM<Fn, ThrowInitializedThis>(ins, ArgList(), StoreNothing()); |
21003 | masm.branchTestMagic(Assembler::NotEqual, thisValue, ool->entry()); |
21004 | masm.bind(ool->rejoin()); |
21005 | } |
21006 | |
21007 | void CodeGenerator::visitGenerator(LGenerator* lir) { |
21008 | Register callee = ToRegister(lir->callee()); |
21009 | Register environmentChain = ToRegister(lir->environmentChain()); |
21010 | Register argsObject = ToRegister(lir->argsObject()); |
21011 | |
21012 | pushArg(argsObject); |
21013 | pushArg(environmentChain); |
21014 | pushArg(ImmGCPtr(current->mir()->info().script())); |
21015 | pushArg(callee); |
21016 | |
21017 | using Fn = JSObject* (*)(JSContext* cx, HandleFunction, HandleScript, |
21018 | HandleObject, HandleObject); |
21019 | callVM<Fn, CreateGenerator>(lir); |
21020 | } |
21021 | |
21022 | void CodeGenerator::visitAsyncResolve(LAsyncResolve* lir) { |
21023 | Register generator = ToRegister(lir->generator()); |
21024 | ValueOperand value = ToValue(lir, LAsyncResolve::ValueIndex); |
21025 | |
21026 | pushArg(value); |
21027 | pushArg(generator); |
21028 | |
21029 | using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>, |
21030 | HandleValue); |
21031 | callVM<Fn, js::AsyncFunctionResolve>(lir); |
21032 | } |
21033 | |
21034 | void CodeGenerator::visitAsyncReject(LAsyncReject* lir) { |
21035 | Register generator = ToRegister(lir->generator()); |
21036 | ValueOperand reason = ToValue(lir, LAsyncReject::ReasonIndex); |
21037 | ValueOperand stack = ToValue(lir, LAsyncReject::StackIndex); |
21038 | |
21039 | pushArg(stack); |
21040 | pushArg(reason); |
21041 | pushArg(generator); |
21042 | |
21043 | using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>, |
21044 | HandleValue, HandleValue); |
21045 | callVM<Fn, js::AsyncFunctionReject>(lir); |
21046 | } |
21047 | |
21048 | void CodeGenerator::visitAsyncAwait(LAsyncAwait* lir) { |
21049 | ValueOperand value = ToValue(lir, LAsyncAwait::ValueIndex); |
21050 | Register generator = ToRegister(lir->generator()); |
21051 | |
21052 | pushArg(value); |
21053 | pushArg(generator); |
21054 | |
21055 | using Fn = |
21056 | JSObject* (*)(JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj, |
21057 | HandleValue value); |
21058 | callVM<Fn, js::AsyncFunctionAwait>(lir); |
21059 | } |
21060 | |
21061 | void CodeGenerator::visitCanSkipAwait(LCanSkipAwait* lir) { |
21062 | ValueOperand value = ToValue(lir, LCanSkipAwait::ValueIndex); |
21063 | |
21064 | pushArg(value); |
21065 | |
21066 | using Fn = bool (*)(JSContext*, HandleValue, bool* canSkip); |
21067 | callVM<Fn, js::CanSkipAwait>(lir); |
21068 | } |
21069 | |
21070 | void CodeGenerator::visitMaybeExtractAwaitValue(LMaybeExtractAwaitValue* lir) { |
21071 | ValueOperand value = ToValue(lir, LMaybeExtractAwaitValue::ValueIndex); |
21072 | ValueOperand output = ToOutValue(lir); |
21073 | Register canSkip = ToRegister(lir->canSkip()); |
21074 | |
21075 | Label cantExtract, finished; |
21076 | masm.branchIfFalseBool(canSkip, &cantExtract); |
21077 | |
21078 | pushArg(value); |
21079 | |
21080 | using Fn = bool (*)(JSContext*, HandleValue, MutableHandleValue); |
21081 | callVM<Fn, js::ExtractAwaitValue>(lir); |
21082 | masm.jump(&finished); |
21083 | masm.bind(&cantExtract); |
21084 | |
21085 | masm.moveValue(value, output); |
21086 | |
21087 | masm.bind(&finished); |
21088 | } |
21089 | |
21090 | void CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) { |
21091 | ValueOperand checkValue = ToValue(ins, LDebugCheckSelfHosted::ValueIndex); |
21092 | pushArg(checkValue); |
21093 | using Fn = bool (*)(JSContext*, HandleValue); |
21094 | callVM<Fn, js::Debug_CheckSelfHosted>(ins); |
21095 | } |
21096 | |
21097 | void CodeGenerator::visitRandom(LRandom* ins) { |
21098 | using mozilla::non_crypto::XorShift128PlusRNG; |
21099 | |
21100 | FloatRegister output = ToFloatRegister(ins->output()); |
21101 | Register rngReg = ToRegister(ins->temp0()); |
21102 | |
21103 | Register64 temp1 = ToRegister64(ins->temp1()); |
21104 | Register64 temp2 = ToRegister64(ins->temp2()); |
21105 | |
21106 | const XorShift128PlusRNG* rng = gen->realm->addressOfRandomNumberGenerator(); |
21107 | masm.movePtr(ImmPtr(rng), rngReg); |
21108 | |
21109 | masm.randomDouble(rngReg, output, temp1, temp2); |
21110 | if (js::SupportDifferentialTesting()) { |
21111 | masm.loadConstantDouble(0.0, output); |
21112 | } |
21113 | } |
21114 | |
21115 | void CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins) { |
21116 | Register input = ToRegister(ins->input()); |
21117 | Register output = ToRegister(ins->output()); |
21118 | |
21119 | switch (ins->mir()->mode()) { |
21120 | case MSignExtendInt32::Byte: |
21121 | masm.move8SignExtend(input, output); |
21122 | break; |
21123 | case MSignExtendInt32::Half: |
21124 | masm.move16SignExtend(input, output); |
21125 | break; |
21126 | } |
21127 | } |
21128 | |
21129 | void CodeGenerator::visitSignExtendIntPtr(LSignExtendIntPtr* ins) { |
21130 | Register input = ToRegister(ins->input()); |
21131 | Register output = ToRegister(ins->output()); |
21132 | |
21133 | switch (ins->mir()->mode()) { |
21134 | case MSignExtendIntPtr::Byte: |
21135 | masm.move8SignExtendToPtr(input, output); |
21136 | break; |
21137 | case MSignExtendIntPtr::Half: |
21138 | masm.move16SignExtendToPtr(input, output); |
21139 | break; |
21140 | case MSignExtendIntPtr::Word: |
21141 | masm.move32SignExtendToPtr(input, output); |
21142 | break; |
21143 | } |
21144 | } |
21145 | |
21146 | void CodeGenerator::visitRotate(LRotate* ins) { |
21147 | MRotate* mir = ins->mir(); |
21148 | Register input = ToRegister(ins->input()); |
21149 | Register dest = ToRegister(ins->output()); |
21150 | |
21151 | const LAllocation* count = ins->count(); |
21152 | if (count->isConstant()) { |
21153 | int32_t c = ToInt32(count) & 0x1F; |
21154 | if (mir->isLeftRotate()) { |
21155 | masm.rotateLeft(Imm32(c), input, dest); |
21156 | } else { |
21157 | masm.rotateRight(Imm32(c), input, dest); |
21158 | } |
21159 | } else { |
21160 | Register creg = ToRegister(count); |
21161 | if (mir->isLeftRotate()) { |
21162 | masm.rotateLeft(creg, input, dest); |
21163 | } else { |
21164 | masm.rotateRight(creg, input, dest); |
21165 | } |
21166 | } |
21167 | } |
21168 | |
21169 | void CodeGenerator::visitReinterpretCast(LReinterpretCast* lir) { |
21170 | MReinterpretCast* ins = lir->mir(); |
21171 | |
21172 | MIRType to = ins->type(); |
21173 | mozilla::DebugOnly<MIRType> from = ins->input()->type(); |
21174 | |
21175 | switch (to) { |
21176 | case MIRType::Int32: |
21177 | MOZ_ASSERT(from == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(from == MIRType::Float32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(from == MIRType::Float32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("from == MIRType::Float32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21177); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from == MIRType::Float32" ")"); do { *((volatile int*)__null) = 21177; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21178 | masm.moveFloat32ToGPR(ToFloatRegister(lir->input()), |
21179 | ToRegister(lir->output())); |
21180 | break; |
21181 | case MIRType::Float32: |
21182 | MOZ_ASSERT(from == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(from == MIRType::Int32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(from == MIRType::Int32))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("from == MIRType::Int32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from == MIRType::Int32" ")"); do { *((volatile int*)__null) = 21182; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21183 | masm.moveGPRToFloat32(ToRegister(lir->input()), |
21184 | ToFloatRegister(lir->output())); |
21185 | break; |
21186 | case MIRType::Double: |
21187 | case MIRType::Int64: |
21188 | MOZ_CRASH("not handled by this LIR opcode")do { do { } while (false); MOZ_ReportCrash("" "not handled by this LIR opcode" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21188); AnnotateMozCrashReason("MOZ_CRASH(" "not handled by this LIR opcode" ")"); do { *((volatile int*)__null) = 21188; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
21189 | default: |
21190 | MOZ_CRASH("unexpected ReinterpretCast")do { do { } while (false); MOZ_ReportCrash("" "unexpected ReinterpretCast" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21190); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected ReinterpretCast" ")"); do { *((volatile int*)__null) = 21190; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
21191 | } |
21192 | } |
21193 | |
21194 | void CodeGenerator::visitReinterpretCastFromI64(LReinterpretCastFromI64* lir) { |
21195 | MOZ_ASSERT(lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Double)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Double))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Double" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21195); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double" ")"); do { *((volatile int*)__null) = 21195; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21196 | MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->input()->type() == MIRType::Int64 )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->input()->type() == MIRType::Int64 ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->input()->type() == MIRType::Int64", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21196); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 21196; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21197 | masm.moveGPR64ToDouble(ToRegister64(lir->input()), |
21198 | ToFloatRegister(lir->output())); |
21199 | } |
21200 | |
21201 | void CodeGenerator::visitReinterpretCastToI64(LReinterpretCastToI64* lir) { |
21202 | MOZ_ASSERT(lir->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Int64)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21202); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 21202; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21203 | MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->input()->type() == MIRType::Double )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->input()->type() == MIRType::Double ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->input()->type() == MIRType::Double", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21203); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Double" ")"); do { *((volatile int*)__null) = 21203; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21204 | masm.moveDoubleToGPR64(ToFloatRegister(lir->input()), ToOutRegister64(lir)); |
21205 | } |
21206 | |
21207 | class OutOfLineNaNToZero : public OutOfLineCodeBase<CodeGenerator> { |
21208 | LNaNToZero* lir_; |
21209 | |
21210 | public: |
21211 | explicit OutOfLineNaNToZero(LNaNToZero* lir) : lir_(lir) {} |
21212 | |
21213 | void accept(CodeGenerator* codegen) override { |
21214 | codegen->visitOutOfLineNaNToZero(this); |
21215 | } |
21216 | LNaNToZero* lir() const { return lir_; } |
21217 | }; |
21218 | |
21219 | void CodeGenerator::visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool) { |
21220 | FloatRegister output = ToFloatRegister(ool->lir()->output()); |
21221 | masm.loadConstantDouble(0.0, output); |
21222 | masm.jump(ool->rejoin()); |
21223 | } |
21224 | |
21225 | void CodeGenerator::visitNaNToZero(LNaNToZero* lir) { |
21226 | FloatRegister input = ToFloatRegister(lir->input()); |
21227 | |
21228 | OutOfLineNaNToZero* ool = new (alloc()) OutOfLineNaNToZero(lir); |
21229 | addOutOfLineCode(ool, lir->mir()); |
21230 | |
21231 | if (lir->mir()->operandIsNeverNegativeZero()) { |
21232 | masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry()); |
21233 | } else { |
21234 | FloatRegister scratch = ToFloatRegister(lir->temp0()); |
21235 | masm.loadConstantDouble(0.0, scratch); |
21236 | masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, |
21237 | ool->entry()); |
21238 | } |
21239 | masm.bind(ool->rejoin()); |
21240 | } |
21241 | |
21242 | void CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) { |
21243 | Register obj = ToRegister(lir->object()); |
21244 | Register output = ToRegister(lir->output()); |
21245 | Register temp = ToRegister(lir->temp0()); |
21246 | |
21247 | masm.setIsPackedArray(obj, output, temp); |
21248 | } |
21249 | |
21250 | void CodeGenerator::visitGuardArrayIsPacked(LGuardArrayIsPacked* lir) { |
21251 | Register array = ToRegister(lir->array()); |
21252 | Register temp0 = ToRegister(lir->temp0()); |
21253 | Register temp1 = ToRegister(lir->temp1()); |
21254 | |
21255 | Label bail; |
21256 | masm.branchArrayIsNotPacked(array, temp0, temp1, &bail); |
21257 | bailoutFrom(&bail, lir->snapshot()); |
21258 | } |
21259 | |
21260 | void CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) { |
21261 | Register target = ToRegister(lir->target()); |
21262 | ValueOperand out = ToOutValue(lir); |
21263 | Register scratch = out.scratchReg(); |
21264 | |
21265 | using Fn = bool (*)(JSContext*, HandleObject, MutableHandleValue); |
21266 | OutOfLineCode* ool = oolCallVM<Fn, jit::GetPrototypeOf>(lir, ArgList(target), |
21267 | StoreValueTo(out)); |
21268 | |
21269 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21269); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 21269; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21270 | |
21271 | masm.loadObjProto(target, scratch); |
21272 | |
21273 | Label hasProto; |
21274 | masm.branchPtr(Assembler::Above, scratch, ImmWord(1), &hasProto); |
21275 | |
21276 | // Call into the VM for lazy prototypes. |
21277 | masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), ool->entry()); |
21278 | |
21279 | masm.moveValue(NullValue(), out); |
21280 | masm.jump(ool->rejoin()); |
21281 | |
21282 | masm.bind(&hasProto); |
21283 | masm.tagValue(JSVAL_TYPE_OBJECT, scratch, out); |
21284 | |
21285 | masm.bind(ool->rejoin()); |
21286 | } |
21287 | |
21288 | void CodeGenerator::visitObjectWithProto(LObjectWithProto* lir) { |
21289 | pushArg(ToValue(lir, LObjectWithProto::PrototypeIndex)); |
21290 | |
21291 | using Fn = PlainObject* (*)(JSContext*, HandleValue); |
21292 | callVM<Fn, js::ObjectWithProtoOperation>(lir); |
21293 | } |
21294 | |
21295 | void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) { |
21296 | Register obj = ToRegister(lir->input()); |
21297 | Register output = ToRegister(lir->output()); |
21298 | |
21299 | masm.loadObjProto(obj, output); |
21300 | |
21301 | #ifdef DEBUG1 |
21302 | // We shouldn't encounter a null or lazy proto. |
21303 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21303); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 21303; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21304 | |
21305 | Label done; |
21306 | masm.branchPtr(Assembler::Above, output, ImmWord(1), &done); |
21307 | masm.assumeUnreachable("Unexpected null or lazy proto in MObjectStaticProto"); |
21308 | masm.bind(&done); |
21309 | #endif |
21310 | } |
21311 | |
21312 | void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) { |
21313 | pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind()))); |
21314 | |
21315 | using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind); |
21316 | callVM<Fn, js::BuiltinObjectOperation>(lir); |
21317 | } |
21318 | |
21319 | void CodeGenerator::visitSuperFunction(LSuperFunction* lir) { |
21320 | Register callee = ToRegister(lir->callee()); |
21321 | ValueOperand out = ToOutValue(lir); |
21322 | Register temp = ToRegister(lir->temp0()); |
21323 | |
21324 | #ifdef DEBUG1 |
21325 | Label classCheckDone; |
21326 | masm.branchTestObjIsFunction(Assembler::Equal, callee, temp, callee, |
21327 | &classCheckDone); |
21328 | masm.assumeUnreachable("Unexpected non-JSFunction callee in JSOp::SuperFun"); |
21329 | masm.bind(&classCheckDone); |
21330 | #endif |
21331 | |
21332 | // Load prototype of callee |
21333 | masm.loadObjProto(callee, temp); |
21334 | |
21335 | #ifdef DEBUG1 |
21336 | // We won't encounter a lazy proto, because |callee| is guaranteed to be a |
21337 | // JSFunction and only proxy objects can have a lazy proto. |
21338 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21338); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 21338; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21339 | |
21340 | Label proxyCheckDone; |
21341 | masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone); |
21342 | masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperFun"); |
21343 | masm.bind(&proxyCheckDone); |
21344 | #endif |
21345 | |
21346 | Label nullProto, done; |
21347 | masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto); |
21348 | |
21349 | // Box prototype and return |
21350 | masm.tagValue(JSVAL_TYPE_OBJECT, temp, out); |
21351 | masm.jump(&done); |
21352 | |
21353 | masm.bind(&nullProto); |
21354 | masm.moveValue(NullValue(), out); |
21355 | |
21356 | masm.bind(&done); |
21357 | } |
21358 | |
21359 | void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) { |
21360 | Register func = ToRegister(lir->function()); |
21361 | ValueOperand homeObject = ToValue(lir, LInitHomeObject::HomeObjectIndex); |
21362 | |
21363 | masm.assertFunctionIsExtended(func); |
21364 | |
21365 | Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot()); |
21366 | |
21367 | emitPreBarrier(addr); |
21368 | masm.storeValue(homeObject, addr); |
21369 | } |
21370 | |
21371 | void CodeGenerator::visitIsTypedArrayConstructor( |
21372 | LIsTypedArrayConstructor* lir) { |
21373 | Register object = ToRegister(lir->object()); |
21374 | Register output = ToRegister(lir->output()); |
21375 | |
21376 | masm.setIsDefinitelyTypedArrayConstructor(object, output); |
21377 | } |
21378 | |
21379 | void CodeGenerator::visitLoadValueTag(LLoadValueTag* lir) { |
21380 | ValueOperand value = ToValue(lir, LLoadValueTag::ValueIndex); |
21381 | Register output = ToRegister(lir->output()); |
21382 | |
21383 | Register tag = masm.extractTag(value, output); |
21384 | if (tag != output) { |
21385 | masm.mov(tag, output); |
21386 | } |
21387 | } |
21388 | |
21389 | void CodeGenerator::visitGuardTagNotEqual(LGuardTagNotEqual* lir) { |
21390 | Register lhs = ToRegister(lir->lhs()); |
21391 | Register rhs = ToRegister(lir->rhs()); |
21392 | |
21393 | bailoutCmp32(Assembler::Equal, lhs, rhs, lir->snapshot()); |
21394 | |
21395 | // If both lhs and rhs are numbers, can't use tag comparison to do inequality |
21396 | // comparison |
21397 | Label done; |
21398 | masm.branchTestNumber(Assembler::NotEqual, lhs, &done); |
21399 | masm.branchTestNumber(Assembler::NotEqual, rhs, &done); |
21400 | bailout(lir->snapshot()); |
21401 | |
21402 | masm.bind(&done); |
21403 | } |
21404 | |
21405 | void CodeGenerator::visitLoadWrapperTarget(LLoadWrapperTarget* lir) { |
21406 | Register object = ToRegister(lir->object()); |
21407 | Register output = ToRegister(lir->output()); |
21408 | |
21409 | masm.loadPtr(Address(object, ProxyObject::offsetOfReservedSlots()), output); |
21410 | |
21411 | // Bail for revoked proxies. |
21412 | Label bail; |
21413 | Address targetAddr(output, |
21414 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()); |
21415 | if (lir->mir()->fallible()) { |
21416 | masm.fallibleUnboxObject(targetAddr, output, &bail); |
21417 | bailoutFrom(&bail, lir->snapshot()); |
21418 | } else { |
21419 | masm.unboxObject(targetAddr, output); |
21420 | } |
21421 | } |
21422 | |
21423 | void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) { |
21424 | Register object = ToRegister(lir->object()); |
21425 | Register temp0 = ToRegister(lir->temp0()); |
21426 | Register temp1 = ToRegister(lir->temp1()); |
21427 | Register temp2 = ToRegister(lir->temp2()); |
21428 | |
21429 | masm.movePropertyKey(lir->mir()->propId(), temp1); |
21430 | masm.movePtr(ImmGCPtr(lir->mir()->getterSetter()), temp2); |
21431 | |
21432 | using Fn = bool (*)(JSContext* cx, JSObject* obj, jsid id, |
21433 | GetterSetter* getterSetter); |
21434 | masm.setupAlignedABICall(); |
21435 | masm.loadJSContext(temp0); |
21436 | masm.passABIArg(temp0); |
21437 | masm.passABIArg(object); |
21438 | masm.passABIArg(temp1); |
21439 | masm.passABIArg(temp2); |
21440 | masm.callWithABI<Fn, ObjectHasGetterSetterPure>(); |
21441 | |
21442 | bailoutIfFalseBool(ReturnReg, lir->snapshot()); |
21443 | } |
21444 | |
21445 | void CodeGenerator::visitGuardIsExtensible(LGuardIsExtensible* lir) { |
21446 | Register object = ToRegister(lir->object()); |
21447 | Register temp = ToRegister(lir->temp0()); |
21448 | |
21449 | Label bail; |
21450 | masm.branchIfObjectNotExtensible(object, temp, &bail); |
21451 | bailoutFrom(&bail, lir->snapshot()); |
21452 | } |
21453 | |
21454 | void CodeGenerator::visitGuardInt32IsNonNegative( |
21455 | LGuardInt32IsNonNegative* lir) { |
21456 | Register index = ToRegister(lir->index()); |
21457 | |
21458 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
21459 | } |
21460 | |
21461 | void CodeGenerator::visitGuardInt32Range(LGuardInt32Range* lir) { |
21462 | Register input = ToRegister(lir->input()); |
21463 | |
21464 | bailoutCmp32(Assembler::LessThan, input, Imm32(lir->mir()->minimum()), |
21465 | lir->snapshot()); |
21466 | bailoutCmp32(Assembler::GreaterThan, input, Imm32(lir->mir()->maximum()), |
21467 | lir->snapshot()); |
21468 | } |
21469 | |
21470 | void CodeGenerator::visitGuardIndexIsNotDenseElement( |
21471 | LGuardIndexIsNotDenseElement* lir) { |
21472 | Register object = ToRegister(lir->object()); |
21473 | Register index = ToRegister(lir->index()); |
21474 | Register temp = ToRegister(lir->temp0()); |
21475 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
21476 | |
21477 | // Load obj->elements. |
21478 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
21479 | |
21480 | // Ensure index >= initLength or the element is a hole. |
21481 | Label notDense; |
21482 | Address capacity(temp, ObjectElements::offsetOfInitializedLength()); |
21483 | masm.spectreBoundsCheck32(index, capacity, spectreTemp, ¬Dense); |
21484 | |
21485 | BaseValueIndex element(temp, index); |
21486 | masm.branchTestMagic(Assembler::Equal, element, ¬Dense); |
21487 | |
21488 | bailout(lir->snapshot()); |
21489 | |
21490 | masm.bind(¬Dense); |
21491 | } |
21492 | |
21493 | void CodeGenerator::visitGuardIndexIsValidUpdateOrAdd( |
21494 | LGuardIndexIsValidUpdateOrAdd* lir) { |
21495 | Register object = ToRegister(lir->object()); |
21496 | Register index = ToRegister(lir->index()); |
21497 | Register temp = ToRegister(lir->temp0()); |
21498 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
21499 | |
21500 | // Load obj->elements. |
21501 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
21502 | |
21503 | Label success; |
21504 | |
21505 | // If length is writable, branch to &success. All indices are writable. |
21506 | Address flags(temp, ObjectElements::offsetOfFlags()); |
21507 | masm.branchTest32(Assembler::Zero, flags, |
21508 | Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH), |
21509 | &success); |
21510 | |
21511 | // Otherwise, ensure index is in bounds. |
21512 | Label bail; |
21513 | Address length(temp, ObjectElements::offsetOfLength()); |
21514 | masm.spectreBoundsCheck32(index, length, spectreTemp, &bail); |
21515 | masm.bind(&success); |
21516 | |
21517 | bailoutFrom(&bail, lir->snapshot()); |
21518 | } |
21519 | |
21520 | void CodeGenerator::visitCallAddOrUpdateSparseElement( |
21521 | LCallAddOrUpdateSparseElement* lir) { |
21522 | Register object = ToRegister(lir->object()); |
21523 | Register index = ToRegister(lir->index()); |
21524 | ValueOperand value = ToValue(lir, LCallAddOrUpdateSparseElement::ValueIndex); |
21525 | |
21526 | pushArg(Imm32(lir->mir()->strict())); |
21527 | pushArg(value); |
21528 | pushArg(index); |
21529 | pushArg(object); |
21530 | |
21531 | using Fn = |
21532 | bool (*)(JSContext*, Handle<NativeObject*>, int32_t, HandleValue, bool); |
21533 | callVM<Fn, js::AddOrUpdateSparseElementHelper>(lir); |
21534 | } |
21535 | |
21536 | void CodeGenerator::visitCallGetSparseElement(LCallGetSparseElement* lir) { |
21537 | Register object = ToRegister(lir->object()); |
21538 | Register index = ToRegister(lir->index()); |
21539 | |
21540 | pushArg(index); |
21541 | pushArg(object); |
21542 | |
21543 | using Fn = |
21544 | bool (*)(JSContext*, Handle<NativeObject*>, int32_t, MutableHandleValue); |
21545 | callVM<Fn, js::GetSparseElementHelper>(lir); |
21546 | } |
21547 | |
21548 | void CodeGenerator::visitCallNativeGetElement(LCallNativeGetElement* lir) { |
21549 | Register object = ToRegister(lir->object()); |
21550 | Register index = ToRegister(lir->index()); |
21551 | |
21552 | pushArg(index); |
21553 | pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(object))); |
21554 | pushArg(object); |
21555 | |
21556 | using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t, |
21557 | MutableHandleValue); |
21558 | callVM<Fn, js::NativeGetElement>(lir); |
21559 | } |
21560 | |
21561 | void CodeGenerator::visitCallNativeGetElementSuper( |
21562 | LCallNativeGetElementSuper* lir) { |
21563 | Register object = ToRegister(lir->object()); |
21564 | Register index = ToRegister(lir->index()); |
21565 | ValueOperand receiver = |
21566 | ToValue(lir, LCallNativeGetElementSuper::ReceiverIndex); |
21567 | |
21568 | pushArg(index); |
21569 | pushArg(receiver); |
21570 | pushArg(object); |
21571 | |
21572 | using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t, |
21573 | MutableHandleValue); |
21574 | callVM<Fn, js::NativeGetElement>(lir); |
21575 | } |
21576 | |
21577 | void CodeGenerator::visitCallObjectHasSparseElement( |
21578 | LCallObjectHasSparseElement* lir) { |
21579 | Register object = ToRegister(lir->object()); |
21580 | Register index = ToRegister(lir->index()); |
21581 | Register temp0 = ToRegister(lir->temp0()); |
21582 | Register temp1 = ToRegister(lir->temp1()); |
21583 | Register output = ToRegister(lir->output()); |
21584 | |
21585 | masm.reserveStack(sizeof(Value)); |
21586 | masm.moveStackPtrTo(temp1); |
21587 | |
21588 | using Fn = bool (*)(JSContext*, NativeObject*, int32_t, Value*); |
21589 | masm.setupAlignedABICall(); |
21590 | masm.loadJSContext(temp0); |
21591 | masm.passABIArg(temp0); |
21592 | masm.passABIArg(object); |
21593 | masm.passABIArg(index); |
21594 | masm.passABIArg(temp1); |
21595 | masm.callWithABI<Fn, HasNativeElementPure>(); |
21596 | masm.storeCallPointerResult(temp0); |
21597 | |
21598 | Label bail, ok; |
21599 | uint32_t framePushed = masm.framePushed(); |
21600 | masm.branchIfTrueBool(temp0, &ok); |
21601 | masm.adjustStack(sizeof(Value)); |
21602 | masm.jump(&bail); |
21603 | |
21604 | masm.bind(&ok); |
21605 | masm.setFramePushed(framePushed); |
21606 | masm.unboxBoolean(Address(masm.getStackPointer(), 0), output); |
21607 | masm.adjustStack(sizeof(Value)); |
21608 | |
21609 | bailoutFrom(&bail, lir->snapshot()); |
21610 | } |
21611 | |
21612 | void CodeGenerator::visitBigIntAsIntN(LBigIntAsIntN* ins) { |
21613 | Register bits = ToRegister(ins->bits()); |
21614 | Register input = ToRegister(ins->input()); |
21615 | |
21616 | pushArg(bits); |
21617 | pushArg(input); |
21618 | |
21619 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t); |
21620 | callVM<Fn, jit::BigIntAsIntN>(ins); |
21621 | } |
21622 | |
21623 | void CodeGenerator::visitBigIntAsUintN(LBigIntAsUintN* ins) { |
21624 | Register bits = ToRegister(ins->bits()); |
21625 | Register input = ToRegister(ins->input()); |
21626 | |
21627 | pushArg(bits); |
21628 | pushArg(input); |
21629 | |
21630 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t); |
21631 | callVM<Fn, jit::BigIntAsUintN>(ins); |
21632 | } |
21633 | |
21634 | void CodeGenerator::visitGuardNonGCThing(LGuardNonGCThing* ins) { |
21635 | ValueOperand input = ToValue(ins, LGuardNonGCThing::InputIndex); |
21636 | |
21637 | Label bail; |
21638 | masm.branchTestGCThing(Assembler::Equal, input, &bail); |
21639 | bailoutFrom(&bail, ins->snapshot()); |
21640 | } |
21641 | |
21642 | void CodeGenerator::visitToHashableNonGCThing(LToHashableNonGCThing* ins) { |
21643 | ValueOperand input = ToValue(ins, LToHashableNonGCThing::InputIndex); |
21644 | FloatRegister tempFloat = ToFloatRegister(ins->temp0()); |
21645 | ValueOperand output = ToOutValue(ins); |
21646 | |
21647 | masm.toHashableNonGCThing(input, output, tempFloat); |
21648 | } |
21649 | |
21650 | void CodeGenerator::visitToHashableString(LToHashableString* ins) { |
21651 | Register input = ToRegister(ins->input()); |
21652 | Register output = ToRegister(ins->output()); |
21653 | |
21654 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
21655 | auto* ool = oolCallVM<Fn, js::AtomizeString>(ins, ArgList(input), |
21656 | StoreRegisterTo(output)); |
21657 | |
21658 | Label isAtom; |
21659 | masm.branchTest32(Assembler::NonZero, |
21660 | Address(input, JSString::offsetOfFlags()), |
21661 | Imm32(JSString::ATOM_BIT), &isAtom); |
21662 | |
21663 | masm.tryFastAtomize(input, output, output, ool->entry()); |
21664 | masm.jump(ool->rejoin()); |
21665 | masm.bind(&isAtom); |
21666 | masm.movePtr(input, output); |
21667 | masm.bind(ool->rejoin()); |
21668 | } |
21669 | |
21670 | void CodeGenerator::visitToHashableValue(LToHashableValue* ins) { |
21671 | ValueOperand input = ToValue(ins, LToHashableValue::InputIndex); |
21672 | FloatRegister tempFloat = ToFloatRegister(ins->temp0()); |
21673 | ValueOperand output = ToOutValue(ins); |
21674 | |
21675 | Register str = output.scratchReg(); |
21676 | |
21677 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
21678 | auto* ool = |
21679 | oolCallVM<Fn, js::AtomizeString>(ins, ArgList(str), StoreRegisterTo(str)); |
21680 | |
21681 | masm.toHashableValue(input, output, tempFloat, ool->entry(), ool->rejoin()); |
21682 | } |
21683 | |
21684 | void CodeGenerator::visitHashNonGCThing(LHashNonGCThing* ins) { |
21685 | ValueOperand input = ToValue(ins, LHashNonGCThing::InputIndex); |
21686 | Register temp = ToRegister(ins->temp0()); |
21687 | Register output = ToRegister(ins->output()); |
21688 | |
21689 | masm.prepareHashNonGCThing(input, output, temp); |
21690 | } |
21691 | |
21692 | void CodeGenerator::visitHashString(LHashString* ins) { |
21693 | Register input = ToRegister(ins->input()); |
21694 | Register temp = ToRegister(ins->temp0()); |
21695 | Register output = ToRegister(ins->output()); |
21696 | |
21697 | masm.prepareHashString(input, output, temp); |
21698 | } |
21699 | |
21700 | void CodeGenerator::visitHashSymbol(LHashSymbol* ins) { |
21701 | Register input = ToRegister(ins->input()); |
21702 | Register output = ToRegister(ins->output()); |
21703 | |
21704 | masm.prepareHashSymbol(input, output); |
21705 | } |
21706 | |
21707 | void CodeGenerator::visitHashBigInt(LHashBigInt* ins) { |
21708 | Register input = ToRegister(ins->input()); |
21709 | Register temp0 = ToRegister(ins->temp0()); |
21710 | Register temp1 = ToRegister(ins->temp1()); |
21711 | Register temp2 = ToRegister(ins->temp2()); |
21712 | Register output = ToRegister(ins->output()); |
21713 | |
21714 | masm.prepareHashBigInt(input, output, temp0, temp1, temp2); |
21715 | } |
21716 | |
21717 | void CodeGenerator::visitHashObject(LHashObject* ins) { |
21718 | Register setObj = ToRegister(ins->setObject()); |
21719 | ValueOperand input = ToValue(ins, LHashObject::InputIndex); |
21720 | Register temp0 = ToRegister(ins->temp0()); |
21721 | Register temp1 = ToRegister(ins->temp1()); |
21722 | Register temp2 = ToRegister(ins->temp2()); |
21723 | Register temp3 = ToRegister(ins->temp3()); |
21724 | Register output = ToRegister(ins->output()); |
21725 | |
21726 | masm.prepareHashObject(setObj, input, output, temp0, temp1, temp2, temp3); |
21727 | } |
21728 | |
21729 | void CodeGenerator::visitHashValue(LHashValue* ins) { |
21730 | Register setObj = ToRegister(ins->setObject()); |
21731 | ValueOperand input = ToValue(ins, LHashValue::InputIndex); |
21732 | Register temp0 = ToRegister(ins->temp0()); |
21733 | Register temp1 = ToRegister(ins->temp1()); |
21734 | Register temp2 = ToRegister(ins->temp2()); |
21735 | Register temp3 = ToRegister(ins->temp3()); |
21736 | Register output = ToRegister(ins->output()); |
21737 | |
21738 | masm.prepareHashValue(setObj, input, output, temp0, temp1, temp2, temp3); |
21739 | } |
21740 | |
21741 | void CodeGenerator::visitSetObjectHasNonBigInt(LSetObjectHasNonBigInt* ins) { |
21742 | Register setObj = ToRegister(ins->setObject()); |
21743 | ValueOperand input = ToValue(ins, LSetObjectHasNonBigInt::InputIndex); |
21744 | Register hash = ToRegister(ins->hash()); |
21745 | Register temp0 = ToRegister(ins->temp0()); |
21746 | Register temp1 = ToRegister(ins->temp1()); |
21747 | Register output = ToRegister(ins->output()); |
21748 | |
21749 | masm.setObjectHasNonBigInt(setObj, input, hash, output, temp0, temp1); |
21750 | } |
21751 | |
21752 | void CodeGenerator::visitSetObjectHasBigInt(LSetObjectHasBigInt* ins) { |
21753 | Register setObj = ToRegister(ins->setObject()); |
21754 | ValueOperand input = ToValue(ins, LSetObjectHasBigInt::InputIndex); |
21755 | Register hash = ToRegister(ins->hash()); |
21756 | Register temp0 = ToRegister(ins->temp0()); |
21757 | Register temp1 = ToRegister(ins->temp1()); |
21758 | Register temp2 = ToRegister(ins->temp2()); |
21759 | Register temp3 = ToRegister(ins->temp3()); |
21760 | Register output = ToRegister(ins->output()); |
21761 | |
21762 | masm.setObjectHasBigInt(setObj, input, hash, output, temp0, temp1, temp2, |
21763 | temp3); |
21764 | } |
21765 | |
21766 | void CodeGenerator::visitSetObjectHasValue(LSetObjectHasValue* ins) { |
21767 | Register setObj = ToRegister(ins->setObject()); |
21768 | ValueOperand input = ToValue(ins, LSetObjectHasValue::InputIndex); |
21769 | Register hash = ToRegister(ins->hash()); |
21770 | Register temp0 = ToRegister(ins->temp0()); |
21771 | Register temp1 = ToRegister(ins->temp1()); |
21772 | Register temp2 = ToRegister(ins->temp2()); |
21773 | Register temp3 = ToRegister(ins->temp3()); |
21774 | Register output = ToRegister(ins->output()); |
21775 | |
21776 | masm.setObjectHasValue(setObj, input, hash, output, temp0, temp1, temp2, |
21777 | temp3); |
21778 | } |
21779 | |
21780 | void CodeGenerator::visitSetObjectHasValueVMCall( |
21781 | LSetObjectHasValueVMCall* ins) { |
21782 | pushArg(ToValue(ins, LSetObjectHasValueVMCall::InputIndex)); |
21783 | pushArg(ToRegister(ins->setObject())); |
21784 | |
21785 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*); |
21786 | callVM<Fn, jit::SetObjectHas>(ins); |
21787 | } |
21788 | |
21789 | void CodeGenerator::visitSetObjectDelete(LSetObjectDelete* ins) { |
21790 | pushArg(ToValue(ins, LSetObjectDelete::KeyIndex)); |
21791 | pushArg(ToRegister(ins->setObject())); |
21792 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*); |
21793 | callVM<Fn, jit::SetObjectDelete>(ins); |
21794 | } |
21795 | |
21796 | void CodeGenerator::visitSetObjectAdd(LSetObjectAdd* ins) { |
21797 | pushArg(ToValue(ins, LSetObjectAdd::KeyIndex)); |
21798 | pushArg(ToRegister(ins->setObject())); |
21799 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue); |
21800 | callVM<Fn, jit::SetObjectAdd>(ins); |
21801 | } |
21802 | |
21803 | void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) { |
21804 | Register setObj = ToRegister(ins->setObject()); |
21805 | Register output = ToRegister(ins->output()); |
21806 | |
21807 | masm.loadSetObjectSize(setObj, output); |
21808 | } |
21809 | |
21810 | void CodeGenerator::visitMapObjectHasNonBigInt(LMapObjectHasNonBigInt* ins) { |
21811 | Register mapObj = ToRegister(ins->mapObject()); |
21812 | ValueOperand input = ToValue(ins, LMapObjectHasNonBigInt::InputIndex); |
21813 | Register hash = ToRegister(ins->hash()); |
21814 | Register temp0 = ToRegister(ins->temp0()); |
21815 | Register temp1 = ToRegister(ins->temp1()); |
21816 | Register output = ToRegister(ins->output()); |
21817 | |
21818 | masm.mapObjectHasNonBigInt(mapObj, input, hash, output, temp0, temp1); |
21819 | } |
21820 | |
21821 | void CodeGenerator::visitMapObjectHasBigInt(LMapObjectHasBigInt* ins) { |
21822 | Register mapObj = ToRegister(ins->mapObject()); |
21823 | ValueOperand input = ToValue(ins, LMapObjectHasBigInt::InputIndex); |
21824 | Register hash = ToRegister(ins->hash()); |
21825 | Register temp0 = ToRegister(ins->temp0()); |
21826 | Register temp1 = ToRegister(ins->temp1()); |
21827 | Register temp2 = ToRegister(ins->temp2()); |
21828 | Register temp3 = ToRegister(ins->temp3()); |
21829 | Register output = ToRegister(ins->output()); |
21830 | |
21831 | masm.mapObjectHasBigInt(mapObj, input, hash, output, temp0, temp1, temp2, |
21832 | temp3); |
21833 | } |
21834 | |
21835 | void CodeGenerator::visitMapObjectHasValue(LMapObjectHasValue* ins) { |
21836 | Register mapObj = ToRegister(ins->mapObject()); |
21837 | ValueOperand input = ToValue(ins, LMapObjectHasValue::InputIndex); |
21838 | Register hash = ToRegister(ins->hash()); |
21839 | Register temp0 = ToRegister(ins->temp0()); |
21840 | Register temp1 = ToRegister(ins->temp1()); |
21841 | Register temp2 = ToRegister(ins->temp2()); |
21842 | Register temp3 = ToRegister(ins->temp3()); |
21843 | Register output = ToRegister(ins->output()); |
21844 | |
21845 | masm.mapObjectHasValue(mapObj, input, hash, output, temp0, temp1, temp2, |
21846 | temp3); |
21847 | } |
21848 | |
21849 | void CodeGenerator::visitMapObjectHasValueVMCall( |
21850 | LMapObjectHasValueVMCall* ins) { |
21851 | pushArg(ToValue(ins, LMapObjectHasValueVMCall::InputIndex)); |
21852 | pushArg(ToRegister(ins->mapObject())); |
21853 | |
21854 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*); |
21855 | callVM<Fn, jit::MapObjectHas>(ins); |
21856 | } |
21857 | |
21858 | void CodeGenerator::visitMapObjectGetNonBigInt(LMapObjectGetNonBigInt* ins) { |
21859 | Register mapObj = ToRegister(ins->mapObject()); |
21860 | ValueOperand input = ToValue(ins, LMapObjectGetNonBigInt::InputIndex); |
21861 | Register hash = ToRegister(ins->hash()); |
21862 | Register temp0 = ToRegister(ins->temp0()); |
21863 | Register temp1 = ToRegister(ins->temp1()); |
21864 | ValueOperand output = ToOutValue(ins); |
21865 | |
21866 | masm.mapObjectGetNonBigInt(mapObj, input, hash, output, temp0, temp1, |
21867 | output.scratchReg()); |
21868 | } |
21869 | |
21870 | void CodeGenerator::visitMapObjectGetBigInt(LMapObjectGetBigInt* ins) { |
21871 | Register mapObj = ToRegister(ins->mapObject()); |
21872 | ValueOperand input = ToValue(ins, LMapObjectGetBigInt::InputIndex); |
21873 | Register hash = ToRegister(ins->hash()); |
21874 | Register temp0 = ToRegister(ins->temp0()); |
21875 | Register temp1 = ToRegister(ins->temp1()); |
21876 | Register temp2 = ToRegister(ins->temp2()); |
21877 | Register temp3 = ToRegister(ins->temp3()); |
21878 | ValueOperand output = ToOutValue(ins); |
21879 | |
21880 | masm.mapObjectGetBigInt(mapObj, input, hash, output, temp0, temp1, temp2, |
21881 | temp3, output.scratchReg()); |
21882 | } |
21883 | |
21884 | void CodeGenerator::visitMapObjectGetValue(LMapObjectGetValue* ins) { |
21885 | Register mapObj = ToRegister(ins->map()); |
21886 | ValueOperand input = ToValue(ins, LMapObjectGetValue::ValueIndex); |
21887 | Register hash = ToRegister(ins->hash()); |
21888 | Register temp0 = ToRegister(ins->temp0()); |
21889 | Register temp1 = ToRegister(ins->temp1()); |
21890 | Register temp2 = ToRegister(ins->temp2()); |
21891 | Register temp3 = ToRegister(ins->temp3()); |
21892 | ValueOperand output = ToOutValue(ins); |
21893 | |
21894 | masm.mapObjectGetValue(mapObj, input, hash, output, temp0, temp1, temp2, |
21895 | temp3, output.scratchReg()); |
21896 | } |
21897 | |
21898 | void CodeGenerator::visitMapObjectGetValueVMCall( |
21899 | LMapObjectGetValueVMCall* ins) { |
21900 | pushArg(ToValue(ins, LMapObjectGetValueVMCall::InputIndex)); |
21901 | pushArg(ToRegister(ins->mapObject())); |
21902 | |
21903 | using Fn = |
21904 | bool (*)(JSContext*, Handle<MapObject*>, HandleValue, MutableHandleValue); |
21905 | callVM<Fn, jit::MapObjectGet>(ins); |
21906 | } |
21907 | |
21908 | void CodeGenerator::visitMapObjectDelete(LMapObjectDelete* ins) { |
21909 | pushArg(ToValue(ins, LMapObjectDelete::KeyIndex)); |
21910 | pushArg(ToRegister(ins->mapObject())); |
21911 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*); |
21912 | callVM<Fn, jit::MapObjectDelete>(ins); |
21913 | } |
21914 | |
21915 | void CodeGenerator::visitMapObjectSet(LMapObjectSet* ins) { |
21916 | pushArg(ToValue(ins, LMapObjectSet::ValueIndex)); |
21917 | pushArg(ToValue(ins, LMapObjectSet::KeyIndex)); |
21918 | pushArg(ToRegister(ins->mapObject())); |
21919 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, HandleValue); |
21920 | callVM<Fn, jit::MapObjectSet>(ins); |
21921 | } |
21922 | |
21923 | void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) { |
21924 | Register mapObj = ToRegister(ins->mapObject()); |
21925 | Register output = ToRegister(ins->output()); |
21926 | |
21927 | masm.loadMapObjectSize(mapObj, output); |
21928 | } |
21929 | |
21930 | void CodeGenerator::visitDateFillLocalTimeSlots(LDateFillLocalTimeSlots* ins) { |
21931 | Register date = ToRegister(ins->date()); |
21932 | Register temp = ToRegister(ins->temp0()); |
21933 | |
21934 | masm.dateFillLocalTimeSlots(date, temp, liveVolatileRegs(ins)); |
21935 | } |
21936 | |
21937 | void CodeGenerator::visitDateHoursFromSecondsIntoYear( |
21938 | LDateHoursFromSecondsIntoYear* ins) { |
21939 | auto secondsIntoYear = |
21940 | ToValue(ins, LDateHoursFromSecondsIntoYear::SecondsIntoYearIndex); |
21941 | auto output = ToOutValue(ins); |
21942 | Register temp0 = ToRegister(ins->temp0()); |
21943 | Register temp1 = ToRegister(ins->temp1()); |
21944 | |
21945 | masm.dateHoursFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
21946 | } |
21947 | |
21948 | void CodeGenerator::visitDateMinutesFromSecondsIntoYear( |
21949 | LDateMinutesFromSecondsIntoYear* ins) { |
21950 | auto secondsIntoYear = |
21951 | ToValue(ins, LDateMinutesFromSecondsIntoYear::SecondsIntoYearIndex); |
21952 | auto output = ToOutValue(ins); |
21953 | Register temp0 = ToRegister(ins->temp0()); |
21954 | Register temp1 = ToRegister(ins->temp1()); |
21955 | |
21956 | masm.dateMinutesFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
21957 | } |
21958 | |
21959 | void CodeGenerator::visitDateSecondsFromSecondsIntoYear( |
21960 | LDateSecondsFromSecondsIntoYear* ins) { |
21961 | auto secondsIntoYear = |
21962 | ToValue(ins, LDateSecondsFromSecondsIntoYear::SecondsIntoYearIndex); |
21963 | auto output = ToOutValue(ins); |
21964 | Register temp0 = ToRegister(ins->temp0()); |
21965 | Register temp1 = ToRegister(ins->temp1()); |
21966 | |
21967 | masm.dateSecondsFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
21968 | } |
21969 | |
21970 | template <size_t NumDefs> |
21971 | void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) { |
21972 | wasm::JitCallStackArgVector stackArgs; |
21973 | masm.propagateOOM(stackArgs.reserve(lir->numOperands())); |
21974 | if (masm.oom()) { |
21975 | return; |
21976 | } |
21977 | |
21978 | MIonToWasmCall* mir = lir->mir(); |
21979 | const wasm::FuncExport& funcExport = mir->funcExport(); |
21980 | const wasm::FuncType& sig = |
21981 | mir->instance()->code().codeMeta().getFuncType(funcExport.funcIndex()); |
21982 | |
21983 | WasmABIArgGenerator abi; |
21984 | for (size_t i = 0; i < lir->numOperands(); i++) { |
21985 | MIRType argMir; |
21986 | switch (sig.args()[i].kind()) { |
21987 | case wasm::ValType::I32: |
21988 | case wasm::ValType::I64: |
21989 | case wasm::ValType::F32: |
21990 | case wasm::ValType::F64: |
21991 | argMir = sig.args()[i].toMIRType(); |
21992 | break; |
21993 | case wasm::ValType::V128: |
21994 | MOZ_CRASH("unexpected argument type when calling from ion to wasm")do { do { } while (false); MOZ_ReportCrash("" "unexpected argument type when calling from ion to wasm" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21994); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected argument type when calling from ion to wasm" ")"); do { *((volatile int*)__null) = 21994; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
21995 | case wasm::ValType::Ref: |
21996 | // temporarilyUnsupportedReftypeForEntry() restricts args to externref |
21997 | MOZ_RELEASE_ASSERT(sig.args()[i].refType().isExtern())do { static_assert( mozilla::detail::AssertionConditionType< decltype(sig.args()[i].refType().isExtern())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(sig.args()[i].refType().isExtern ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("sig.args()[i].refType().isExtern()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21997); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "sig.args()[i].refType().isExtern()" ")"); do { *((volatile int*)__null) = 21997; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21998 | // Argument is boxed on the JS side to an anyref, so passed as a |
21999 | // pointer here. |
22000 | argMir = sig.args()[i].toMIRType(); |
22001 | break; |
22002 | } |
22003 | |
22004 | ABIArg arg = abi.next(argMir); |
22005 | switch (arg.kind()) { |
22006 | case ABIArg::GPR: |
22007 | case ABIArg::FPU: { |
22008 | MOZ_ASSERT(ToAnyRegister(lir->getOperand(i)) == arg.reg())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToAnyRegister(lir->getOperand(i)) == arg.reg())> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToAnyRegister(lir->getOperand(i)) == arg.reg()))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToAnyRegister(lir->getOperand(i)) == arg.reg()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToAnyRegister(lir->getOperand(i)) == arg.reg()" ")"); do { *((volatile int*)__null) = 22008; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22009 | stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg()); |
22010 | break; |
22011 | } |
22012 | case ABIArg::Stack: { |
22013 | const LAllocation* larg = lir->getOperand(i); |
22014 | if (larg->isConstant()) { |
22015 | stackArgs.infallibleEmplaceBack(ToInt32(larg)); |
22016 | } else if (larg->isGeneralReg()) { |
22017 | stackArgs.infallibleEmplaceBack(ToRegister(larg)); |
22018 | } else if (larg->isFloatReg()) { |
22019 | stackArgs.infallibleEmplaceBack(ToFloatRegister(larg)); |
22020 | } else { |
22021 | // Always use the stack pointer here because GenerateDirectCallFromJit |
22022 | // depends on this. |
22023 | Address addr = ToAddress<BaseRegForAddress::SP>(larg); |
22024 | stackArgs.infallibleEmplaceBack(addr); |
22025 | } |
22026 | break; |
22027 | } |
22028 | #ifdef JS_CODEGEN_REGISTER_PAIR |
22029 | case ABIArg::GPR_PAIR: { |
22030 | MOZ_CRASH(do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22031); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls" ")"); do { *((volatile int*)__null) = 22031; __attribute__(( nomerge)) ::abort(); } while (false); } while (false) |
22031 | "no way to pass i64, and wasm uses hardfp for function calls")do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22031); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls" ")"); do { *((volatile int*)__null) = 22031; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
22032 | } |
22033 | #endif |
22034 | case ABIArg::Uninitialized: { |
22035 | MOZ_CRASH("Uninitialized ABIArg kind")do { do { } while (false); MOZ_ReportCrash("" "Uninitialized ABIArg kind" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22035); AnnotateMozCrashReason("MOZ_CRASH(" "Uninitialized ABIArg kind" ")"); do { *((volatile int*)__null) = 22035; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
22036 | } |
22037 | } |
22038 | } |
22039 | |
22040 | const wasm::ValTypeVector& results = sig.results(); |
22041 | if (results.length() == 0) { |
22042 | MOZ_ASSERT(lir->mir()->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Value)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Value))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Value" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22042); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value" ")"); do { *((volatile int*)__null) = 22042; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22043 | } else { |
22044 | MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented")do { static_assert( mozilla::detail::AssertionConditionType< decltype(results.length() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(results.length() == 1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("results.length() == 1" " (" "multi-value return unimplemented" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 1" ") (" "multi-value return unimplemented" ")"); do { *((volatile int*)__null) = 22044; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
22045 | switch (results[0].kind()) { |
22046 | case wasm::ValType::I32: |
22047 | MOZ_ASSERT(lir->mir()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Int32)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int32" ")"); do { *((volatile int*)__null) = 22047; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22048 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22048); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 22048; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22049 | break; |
22050 | case wasm::ValType::I64: |
22051 | MOZ_ASSERT(lir->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Int64)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22051); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 22051; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22052 | MOZ_ASSERT(ToOutRegister64(lir) == ReturnReg64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutRegister64(lir) == ReturnReg64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutRegister64(lir) == ReturnReg64 ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutRegister64(lir) == ReturnReg64", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22052); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutRegister64(lir) == ReturnReg64" ")"); do { *((volatile int*)__null) = 22052; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22053 | break; |
22054 | case wasm::ValType::F32: |
22055 | MOZ_ASSERT(lir->mir()->type() == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Float32)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Float32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Float32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32" ")"); do { *((volatile int*)__null) = 22055; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22056 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnFloat32Reg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnFloat32Reg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnFloat32Reg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnFloat32Reg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg" ")"); do { *((volatile int*)__null) = 22056; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22057 | break; |
22058 | case wasm::ValType::F64: |
22059 | MOZ_ASSERT(lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Double)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Double))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Double" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22059); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double" ")"); do { *((volatile int*)__null) = 22059; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22060 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22060); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 22060; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22061 | break; |
22062 | case wasm::ValType::V128: |
22063 | MOZ_CRASH("unexpected return type when calling from ion to wasm")do { do { } while (false); MOZ_ReportCrash("" "unexpected return type when calling from ion to wasm" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22063); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected return type when calling from ion to wasm" ")"); do { *((volatile int*)__null) = 22063; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
22064 | case wasm::ValType::Ref: |
22065 | // The wasm stubs layer unboxes anything that needs to be unboxed |
22066 | // and leaves it in a Value. A FuncRef/EqRef we could in principle |
22067 | // leave it as a raw object pointer but for now it complicates the |
22068 | // API to do so. |
22069 | MOZ_ASSERT(lir->mir()->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Value)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Value))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Value" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value" ")"); do { *((volatile int*)__null) = 22069; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22070 | break; |
22071 | } |
22072 | } |
22073 | |
22074 | WasmInstanceObject* instObj = lir->mir()->instanceObject(); |
22075 | |
22076 | Register scratch = ToRegister(lir->temp()); |
22077 | |
22078 | uint32_t callOffset; |
22079 | ensureOsiSpace(); |
22080 | GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs, |
22081 | scratch, &callOffset); |
22082 | |
22083 | // Add the instance object to the constant pool, so it is transferred to |
22084 | // the owning IonScript and so that it gets traced as long as the IonScript |
22085 | // lives. |
22086 | |
22087 | uint32_t unused; |
22088 | masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused)); |
22089 | |
22090 | markSafepointAt(callOffset, lir); |
22091 | } |
22092 | |
22093 | void CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) { |
22094 | emitIonToWasmCallBase(lir); |
22095 | } |
22096 | void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) { |
22097 | emitIonToWasmCallBase(lir); |
22098 | } |
22099 | void CodeGenerator::visitIonToWasmCallI64(LIonToWasmCallI64* lir) { |
22100 | emitIonToWasmCallBase(lir); |
22101 | } |
22102 | |
22103 | void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) { |
22104 | masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output())); |
22105 | } |
22106 | |
22107 | void CodeGenerator::visitWasmFence(LWasmFence* lir) { |
22108 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 22108; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22109 | masm.memoryBarrier(MembarFull); |
22110 | } |
22111 | |
22112 | void CodeGenerator::visitWasmAnyRefFromJSValue(LWasmAnyRefFromJSValue* lir) { |
22113 | ValueOperand input = ToValue(lir, LWasmAnyRefFromJSValue::InputIndex); |
22114 | Register output = ToRegister(lir->output()); |
22115 | FloatRegister tempFloat = ToFloatRegister(lir->temp0()); |
22116 | |
22117 | using Fn = JSObject* (*)(JSContext* cx, HandleValue value); |
22118 | OutOfLineCode* oolBoxValue = oolCallVM<Fn, wasm::AnyRef::boxValue>( |
22119 | lir, ArgList(input), StoreRegisterTo(output)); |
22120 | masm.convertValueToWasmAnyRef(input, output, tempFloat, oolBoxValue->entry()); |
22121 | masm.bind(oolBoxValue->rejoin()); |
22122 | } |
22123 | |
22124 | void CodeGenerator::visitWasmAnyRefFromJSObject(LWasmAnyRefFromJSObject* lir) { |
22125 | Register input = ToRegister(lir->input()); |
22126 | Register output = ToRegister(lir->output()); |
22127 | masm.convertObjectToWasmAnyRef(input, output); |
22128 | } |
22129 | |
22130 | void CodeGenerator::visitWasmAnyRefFromJSString(LWasmAnyRefFromJSString* lir) { |
22131 | Register input = ToRegister(lir->input()); |
22132 | Register output = ToRegister(lir->output()); |
22133 | masm.convertStringToWasmAnyRef(input, output); |
22134 | } |
22135 | |
22136 | void CodeGenerator::visitWasmAnyRefIsJSString(LWasmAnyRefIsJSString* lir) { |
22137 | Register input = ToRegister(lir->input()); |
22138 | Register output = ToRegister(lir->output()); |
22139 | Register temp = ToRegister(lir->temp0()); |
22140 | Label fallthrough; |
22141 | Label isJSString; |
22142 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
22143 | masm.move32(Imm32(0), output); |
22144 | masm.jump(&fallthrough); |
22145 | masm.bind(&isJSString); |
22146 | masm.move32(Imm32(1), output); |
22147 | masm.bind(&fallthrough); |
22148 | } |
22149 | |
22150 | void CodeGenerator::visitWasmTrapIfAnyRefIsNotJSString( |
22151 | LWasmTrapIfAnyRefIsNotJSString* lir) { |
22152 | Register input = ToRegister(lir->input()); |
22153 | Register temp = ToRegister(lir->temp0()); |
22154 | Label isJSString; |
22155 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
22156 | masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc()); |
22157 | masm.bind(&isJSString); |
22158 | } |
22159 | |
22160 | void CodeGenerator::visitWasmAnyRefJSStringLength( |
22161 | LWasmAnyRefJSStringLength* lir) { |
22162 | Register input = ToRegister(lir->input()); |
22163 | Register output = ToRegister(lir->output()); |
22164 | Register temp = ToRegister(lir->temp0()); |
22165 | Label isJSString; |
22166 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
22167 | masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc()); |
22168 | masm.bind(&isJSString); |
22169 | masm.untagWasmAnyRef(input, temp, wasm::AnyRefTag::String); |
22170 | masm.loadStringLength(temp, output); |
22171 | } |
22172 | |
22173 | void CodeGenerator::visitWasmNewI31Ref(LWasmNewI31Ref* lir) { |
22174 | if (lir->value()->isConstant()) { |
22175 | // i31ref are often created with constants. If that's the case we will |
22176 | // do the operation statically here. This is similar to what is done |
22177 | // in masm.truncate32ToWasmI31Ref. |
22178 | Register output = ToRegister(lir->output()); |
22179 | uint32_t value = |
22180 | static_cast<uint32_t>(lir->value()->toConstant()->toInt32()); |
22181 | uintptr_t ptr = wasm::AnyRef::fromUint32Truncate(value).rawValue(); |
22182 | masm.movePtr(ImmWord(ptr), output); |
22183 | } else { |
22184 | Register value = ToRegister(lir->value()); |
22185 | Register output = ToRegister(lir->output()); |
22186 | masm.truncate32ToWasmI31Ref(value, output); |
22187 | } |
22188 | } |
22189 | |
22190 | void CodeGenerator::visitWasmI31RefGet(LWasmI31RefGet* lir) { |
22191 | Register value = ToRegister(lir->value()); |
22192 | Register output = ToRegister(lir->output()); |
22193 | if (lir->mir()->wideningOp() == wasm::FieldWideningOp::Signed) { |
22194 | masm.convertWasmI31RefTo32Signed(value, output); |
22195 | } else { |
22196 | masm.convertWasmI31RefTo32Unsigned(value, output); |
22197 | } |
22198 | } |
22199 | |
22200 | #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT1 |
22201 | void CodeGenerator::visitAddDisposableResource(LAddDisposableResource* lir) { |
22202 | Register environment = ToRegister(lir->environment()); |
22203 | ValueOperand resource = ToValue(lir, LAddDisposableResource::ResourceIndex); |
22204 | ValueOperand method = ToValue(lir, LAddDisposableResource::MethodIndex); |
22205 | Register needsClosure = ToRegister(lir->needsClosure()); |
22206 | uint8_t hint = lir->hint(); |
22207 | |
22208 | pushArg(Imm32(hint)); |
22209 | pushArg(needsClosure); |
22210 | pushArg(method); |
22211 | pushArg(resource); |
22212 | pushArg(environment); |
22213 | |
22214 | using Fn = bool (*)(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, |
22215 | JS::Handle<JS::Value>, bool, UsingHint); |
22216 | callVM<Fn, js::AddDisposableResourceToCapability>(lir); |
22217 | } |
22218 | |
22219 | void CodeGenerator::visitTakeDisposeCapability(LTakeDisposeCapability* lir) { |
22220 | Register environment = ToRegister(lir->environment()); |
22221 | ValueOperand output = ToOutValue(lir); |
22222 | |
22223 | Address capabilityAddr( |
22224 | environment, DisposableEnvironmentObject::offsetOfDisposeCapability()); |
22225 | emitPreBarrier(capabilityAddr); |
22226 | masm.loadValue(capabilityAddr, output); |
22227 | masm.storeValue(JS::UndefinedValue(), capabilityAddr); |
22228 | } |
22229 | |
22230 | void CodeGenerator::visitCreateSuppressedError(LCreateSuppressedError* lir) { |
22231 | ValueOperand error = ToValue(lir, LCreateSuppressedError::ErrorIndex); |
22232 | ValueOperand suppressed = |
22233 | ToValue(lir, LCreateSuppressedError::SuppressedIndex); |
22234 | |
22235 | pushArg(suppressed); |
22236 | pushArg(error); |
22237 | |
22238 | using Fn = ErrorObject* (*)(JSContext*, JS::Handle<JS::Value>, |
22239 | JS::Handle<JS::Value>); |
22240 | callVM<Fn, js::CreateSuppressedError>(lir); |
22241 | } |
22242 | #endif |
22243 | |
22244 | #ifdef FUZZING_JS_FUZZILLI |
22245 | void CodeGenerator::emitFuzzilliHashObject(LInstruction* lir, Register obj, |
22246 | Register output) { |
22247 | using Fn = void (*)(JSContext* cx, JSObject* obj, uint32_t* out); |
22248 | OutOfLineCode* ool = oolCallVM<Fn, FuzzilliHashObjectInl>( |
22249 | lir, ArgList(obj), StoreRegisterTo(output)); |
22250 | |
22251 | masm.jump(ool->entry()); |
22252 | masm.bind(ool->rejoin()); |
22253 | } |
22254 | |
22255 | void CodeGenerator::emitFuzzilliHashBigInt(LInstruction* lir, Register bigInt, |
22256 | Register output) { |
22257 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
22258 | volatileRegs.takeUnchecked(output); |
22259 | |
22260 | masm.PushRegsInMask(volatileRegs); |
22261 | |
22262 | using Fn = uint32_t (*)(BigInt* bigInt); |
22263 | masm.setupUnalignedABICall(output); |
22264 | masm.passABIArg(bigInt); |
22265 | masm.callWithABI<Fn, js::FuzzilliHashBigInt>(); |
22266 | masm.storeCallInt32Result(output); |
22267 | |
22268 | masm.PopRegsInMask(volatileRegs); |
22269 | } |
22270 | |
22271 | void CodeGenerator::visitFuzzilliHashV(LFuzzilliHashV* ins) { |
22272 | ValueOperand value = ToValue(ins, 0); |
22273 | |
22274 | FloatRegister scratchFloat = ToFloatRegister(ins->getTemp(1)); |
22275 | Register scratch = ToRegister(ins->getTemp(0)); |
22276 | Register output = ToRegister(ins->output()); |
22277 | MOZ_ASSERT(scratch != output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch != output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(scratch != output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch != output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output" ")"); do { *((volatile int*)__null) = 22277; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22278 | |
22279 | Label hashDouble, done; |
22280 | |
22281 | Label isInt32, isDouble, isNull, isUndefined, isBoolean, isBigInt, isObject; |
22282 | { |
22283 | ScratchTagScope tag(masm, value); |
22284 | masm.splitTagForTest(value, tag); |
22285 | |
22286 | masm.branchTestInt32(Assembler::Equal, tag, &isInt32); |
22287 | masm.branchTestDouble(Assembler::Equal, tag, &isDouble); |
22288 | masm.branchTestNull(Assembler::Equal, tag, &isNull); |
22289 | masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined); |
22290 | masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean); |
22291 | masm.branchTestBigInt(Assembler::Equal, tag, &isBigInt); |
22292 | masm.branchTestObject(Assembler::Equal, tag, &isObject); |
22293 | |
22294 | // Symbol or String. |
22295 | masm.move32(Imm32(0), output); |
22296 | masm.jump(&done); |
22297 | } |
22298 | |
22299 | masm.bind(&isInt32); |
22300 | { |
22301 | masm.unboxInt32(value, scratch); |
22302 | masm.convertInt32ToDouble(scratch, scratchFloat); |
22303 | masm.jump(&hashDouble); |
22304 | } |
22305 | |
22306 | masm.bind(&isDouble); |
22307 | { |
22308 | masm.unboxDouble(value, scratchFloat); |
22309 | masm.jump(&hashDouble); |
22310 | } |
22311 | |
22312 | masm.bind(&isNull); |
22313 | { |
22314 | masm.loadConstantDouble(1.0, scratchFloat); |
22315 | masm.jump(&hashDouble); |
22316 | } |
22317 | |
22318 | masm.bind(&isUndefined); |
22319 | { |
22320 | masm.loadConstantDouble(2.0, scratchFloat); |
22321 | masm.jump(&hashDouble); |
22322 | } |
22323 | |
22324 | masm.bind(&isBoolean); |
22325 | { |
22326 | masm.unboxBoolean(value, scratch); |
22327 | masm.add32(Imm32(3), scratch); |
22328 | masm.convertInt32ToDouble(scratch, scratchFloat); |
22329 | masm.jump(&hashDouble); |
22330 | } |
22331 | |
22332 | masm.bind(&isBigInt); |
22333 | { |
22334 | masm.unboxBigInt(value, scratch); |
22335 | emitFuzzilliHashBigInt(ins, scratch, output); |
22336 | masm.jump(&done); |
22337 | } |
22338 | |
22339 | masm.bind(&isObject); |
22340 | { |
22341 | masm.unboxObject(value, scratch); |
22342 | emitFuzzilliHashObject(ins, scratch, output); |
22343 | masm.jump(&done); |
22344 | } |
22345 | |
22346 | masm.bind(&hashDouble); |
22347 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
22348 | |
22349 | masm.bind(&done); |
22350 | } |
22351 | |
22352 | void CodeGenerator::visitFuzzilliHashT(LFuzzilliHashT* ins) { |
22353 | const LAllocation* value = ins->value(); |
22354 | MIRType mirType = ins->mir()->getOperand(0)->type(); |
22355 | |
22356 | Register scratch = ToTempRegisterOrInvalid(ins->getTemp(0)); |
22357 | FloatRegister scratchFloat = ToTempFloatRegisterOrInvalid(ins->getTemp(1)); |
22358 | |
22359 | Register output = ToRegister(ins->output()); |
22360 | MOZ_ASSERT(scratch != output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch != output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(scratch != output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch != output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22360); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output" ")"); do { *((volatile int*)__null) = 22360; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
22361 | |
22362 | switch (mirType) { |
22363 | case MIRType::Undefined: { |
22364 | masm.loadConstantDouble(2.0, scratchFloat); |
22365 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
22366 | break; |
22367 | } |
22368 | |
22369 | case MIRType::Null: { |
22370 | masm.loadConstantDouble(1.0, scratchFloat); |
22371 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
22372 | break; |
22373 | } |
22374 | |
22375 | case MIRType::Int32: { |
22376 | masm.move32(ToRegister(value), scratch); |
22377 | masm.convertInt32ToDouble(scratch, scratchFloat); |
22378 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
22379 | break; |
22380 | } |
22381 | |
22382 | case MIRType::Double: { |
22383 | masm.moveDouble(ToFloatRegister(value), scratchFloat); |
22384 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
22385 | break; |
22386 | } |
22387 | |
22388 | case MIRType::Float32: { |
22389 | masm.convertFloat32ToDouble(ToFloatRegister(value), scratchFloat); |
22390 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
22391 | break; |
22392 | } |
22393 | |
22394 | case MIRType::Boolean: { |
22395 | masm.move32(ToRegister(value), scratch); |
22396 | masm.add32(Imm32(3), scratch); |
22397 | masm.convertInt32ToDouble(scratch, scratchFloat); |
22398 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
22399 | break; |
22400 | } |
22401 | |
22402 | case MIRType::BigInt: { |
22403 | emitFuzzilliHashBigInt(ins, ToRegister(value), output); |
22404 | break; |
22405 | } |
22406 | |
22407 | case MIRType::Object: { |
22408 | emitFuzzilliHashObject(ins, ToRegister(value), output); |
22409 | break; |
22410 | } |
22411 | |
22412 | default: |
22413 | MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22413); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type" ")"); do { *((volatile int*)__null) = 22413; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
22414 | } |
22415 | } |
22416 | |
22417 | void CodeGenerator::visitFuzzilliHashStore(LFuzzilliHashStore* ins) { |
22418 | Register value = ToRegister(ins->value()); |
22419 | Register temp0 = ToRegister(ins->getTemp(0)); |
22420 | Register temp1 = ToRegister(ins->getTemp(1)); |
22421 | |
22422 | masm.fuzzilliStoreHash(value, temp0, temp1); |
22423 | } |
22424 | #endif |
22425 | |
22426 | static_assert(!std::is_polymorphic_v<CodeGenerator>, |
22427 | "CodeGenerator should not have any virtual methods"); |
22428 | |
22429 | } // namespace jit |
22430 | } // namespace js |