File: | var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp |
Warning: | line 12469, column 8 Value stored to 'extractObject' during its initialization is never read |
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/JSScript-inl.h" |
96 | #include "wasm/WasmInstance-inl.h" |
97 | |
98 | using namespace js; |
99 | using namespace js::jit; |
100 | |
101 | using JS::GenericNaN; |
102 | using mozilla::AssertedCast; |
103 | using mozilla::CheckedUint32; |
104 | using mozilla::DebugOnly; |
105 | using mozilla::FloatingPoint; |
106 | using mozilla::Maybe; |
107 | using mozilla::NegativeInfinity; |
108 | using mozilla::PositiveInfinity; |
109 | |
110 | using JS::ExpandoAndGeneration; |
111 | |
112 | namespace js { |
113 | namespace jit { |
114 | |
115 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
116 | template <class Op> |
117 | static void HandleRegisterDump(Op op, MacroAssembler& masm, |
118 | LiveRegisterSet liveRegs, Register activation, |
119 | Register scratch) { |
120 | const size_t baseOffset = JitActivation::offsetOfRegs(); |
121 | |
122 | // Handle live GPRs. |
123 | for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) { |
124 | Register reg = *iter; |
125 | Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
126 | |
127 | if (reg == activation) { |
128 | // To use the original value of the activation register (that's |
129 | // now on top of the stack), we need the scratch register. |
130 | masm.push(scratch); |
131 | masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch); |
132 | op(scratch, dump); |
133 | masm.pop(scratch); |
134 | } else { |
135 | op(reg, dump); |
136 | } |
137 | } |
138 | |
139 | // Handle live FPRs. |
140 | for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) { |
141 | FloatRegister reg = *iter; |
142 | Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
143 | op(reg, dump); |
144 | } |
145 | } |
146 | |
147 | class StoreOp { |
148 | MacroAssembler& masm; |
149 | |
150 | public: |
151 | explicit StoreOp(MacroAssembler& masm) : masm(masm) {} |
152 | |
153 | void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); } |
154 | void operator()(FloatRegister reg, Address dump) { |
155 | if (reg.isDouble()) { |
156 | masm.storeDouble(reg, dump); |
157 | } else if (reg.isSingle()) { |
158 | masm.storeFloat32(reg, dump); |
159 | } else if (reg.isSimd128()) { |
160 | MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 160); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { *((volatile int*)__null) = 160; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
161 | } else { |
162 | MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 162); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type." ")"); do { *((volatile int*)__null) = 162; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
163 | } |
164 | } |
165 | }; |
166 | |
167 | class VerifyOp { |
168 | MacroAssembler& masm; |
169 | Label* failure_; |
170 | |
171 | public: |
172 | VerifyOp(MacroAssembler& masm, Label* failure) |
173 | : masm(masm), failure_(failure) {} |
174 | |
175 | void operator()(Register reg, Address dump) { |
176 | masm.branchPtr(Assembler::NotEqual, dump, reg, failure_); |
177 | } |
178 | void operator()(FloatRegister reg, Address dump) { |
179 | if (reg.isDouble()) { |
180 | ScratchDoubleScope scratch(masm); |
181 | masm.loadDouble(dump, scratch); |
182 | masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_); |
183 | } else if (reg.isSingle()) { |
184 | ScratchFloat32Scope scratch(masm); |
185 | masm.loadFloat32(dump, scratch); |
186 | masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_); |
187 | } else if (reg.isSimd128()) { |
188 | MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 188); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { *((volatile int*)__null) = 188; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
189 | } else { |
190 | MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 190); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type." ")"); do { *((volatile int*)__null) = 190; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
191 | } |
192 | } |
193 | }; |
194 | |
195 | void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) { |
196 | // Ensure the live registers stored by callVM did not change between |
197 | // the call and this OsiPoint. Try-catch relies on this invariant. |
198 | |
199 | // Load pointer to the JitActivation in a scratch register. |
200 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
201 | Register scratch = allRegs.takeAny(); |
202 | masm.push(scratch); |
203 | masm.loadJitActivation(scratch); |
204 | |
205 | // If we should not check registers (because the instruction did not call |
206 | // into the VM, or a GC happened), we're done. |
207 | Label failure, done; |
208 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
209 | masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done); |
210 | |
211 | // Having more than one VM function call made in one visit function at |
212 | // runtime is a sec-ciritcal error, because if we conservatively assume that |
213 | // one of the function call can re-enter Ion, then the invalidation process |
214 | // will potentially add a call at a random location, by patching the code |
215 | // before the return address. |
216 | masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure); |
217 | |
218 | // Set checkRegs to 0, so that we don't try to verify registers after we |
219 | // return from this script to the caller. |
220 | masm.store32(Imm32(0), checkRegs); |
221 | |
222 | // Ignore clobbered registers. Some instructions (like LValueToInt32) modify |
223 | // temps after calling into the VM. This is fine because no other |
224 | // instructions (including this OsiPoint) will depend on them. Also |
225 | // backtracking can also use the same register for an input and an output. |
226 | // These are marked as clobbered and shouldn't get checked. |
227 | LiveRegisterSet liveRegs; |
228 | liveRegs.set() = RegisterSet::Intersect( |
229 | safepoint->liveRegs().set(), |
230 | RegisterSet::Not(safepoint->clobberedRegs().set())); |
231 | |
232 | VerifyOp op(masm, &failure); |
233 | HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
234 | |
235 | masm.jump(&done); |
236 | |
237 | // Do not profile the callWithABI that occurs below. This is to avoid a |
238 | // rare corner case that occurs when profiling interacts with itself: |
239 | // |
240 | // When slow profiling assertions are turned on, FunctionBoundary ops |
241 | // (which update the profiler pseudo-stack) may emit a callVM, which |
242 | // forces them to have an osi point associated with them. The |
243 | // FunctionBoundary for inline function entry is added to the caller's |
244 | // graph with a PC from the caller's code, but during codegen it modifies |
245 | // Gecko Profiler instrumentation to add the callee as the current top-most |
246 | // script. When codegen gets to the OSIPoint, and the callWithABI below is |
247 | // emitted, the codegen thinks that the current frame is the callee, but |
248 | // the PC it's using from the OSIPoint refers to the caller. This causes |
249 | // the profiler instrumentation of the callWithABI below to ASSERT, since |
250 | // the script and pc are mismatched. To avoid this, we simply omit |
251 | // instrumentation for these callWithABIs. |
252 | |
253 | // Any live register captured by a safepoint (other than temp registers) |
254 | // must remain unchanged between the call and the OsiPoint instruction. |
255 | masm.bind(&failure); |
256 | masm.assumeUnreachable("Modified registers between VM call and OsiPoint"); |
257 | |
258 | masm.bind(&done); |
259 | masm.pop(scratch); |
260 | } |
261 | |
262 | bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) { |
263 | if (!checkOsiPointRegisters) { |
264 | return false; |
265 | } |
266 | |
267 | if (safepoint->liveRegs().emptyGeneral() && |
268 | safepoint->liveRegs().emptyFloat()) { |
269 | return false; // No registers to check. |
270 | } |
271 | |
272 | return true; |
273 | } |
274 | |
275 | void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) { |
276 | if (!shouldVerifyOsiPointRegs(safepoint)) { |
277 | return; |
278 | } |
279 | |
280 | // Set checkRegs to 0. If we perform a VM call, the instruction |
281 | // will set it to 1. |
282 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
283 | Register scratch = allRegs.takeAny(); |
284 | masm.push(scratch); |
285 | masm.loadJitActivation(scratch); |
286 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
287 | masm.store32(Imm32(0), checkRegs); |
288 | masm.pop(scratch); |
289 | } |
290 | |
291 | static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) { |
292 | // Store a copy of all live registers before performing the call. |
293 | // When we reach the OsiPoint, we can use this to check nothing |
294 | // modified them in the meantime. |
295 | |
296 | // Load pointer to the JitActivation in a scratch register. |
297 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
298 | Register scratch = allRegs.takeAny(); |
299 | masm.push(scratch); |
300 | masm.loadJitActivation(scratch); |
301 | |
302 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
303 | masm.add32(Imm32(1), checkRegs); |
304 | |
305 | StoreOp op(masm); |
306 | HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
307 | |
308 | masm.pop(scratch); |
309 | } |
310 | #endif // CHECK_OSIPOINT_REGISTERS |
311 | |
312 | // Before doing any call to Cpp, you should ensure that volatile |
313 | // registers are evicted by the register allocator. |
314 | void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) { |
315 | TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id); |
316 | const VMFunctionData& fun = GetVMFunction(id); |
317 | |
318 | // Stack is: |
319 | // ... frame ... |
320 | // [args] |
321 | #ifdef DEBUG1 |
322 | MOZ_ASSERT(pushedArgs_ == fun.explicitArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(pushedArgs_ == fun.explicitArgs)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pushedArgs_ == fun.explicitArgs ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pushedArgs_ == fun.explicitArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 322); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs" ")"); do { *((volatile int*)__null) = 322; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
323 | pushedArgs_ = 0; |
324 | #endif |
325 | |
326 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
327 | if (shouldVerifyOsiPointRegs(ins->safepoint())) { |
328 | StoreAllLiveRegs(masm, ins->safepoint()->liveRegs()); |
329 | } |
330 | #endif |
331 | |
332 | #ifdef DEBUG1 |
333 | if (ins->mirRaw()) { |
334 | MOZ_ASSERT(ins->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mirRaw()->isInstruction())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mirRaw()->isInstruction ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ins->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()" ")"); do { *((volatile int*)__null) = 334; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
335 | MInstruction* mir = ins->mirRaw()->toInstruction(); |
336 | MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint())do { if (mir->needsResumePoint()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(mir->resumePoint ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mir->resumePoint()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("mir->resumePoint()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 336); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()" ")"); do { *((volatile int*)__null) = 336; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
337 | |
338 | // If this MIR instruction has an overridden AliasSet, set the JitRuntime's |
339 | // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call |
340 | // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because |
341 | // interrupt callbacks can call JS (chrome JS or shell testing functions). |
342 | bool isWhitelisted = mir->isInterruptCheck() || mir->isCheckOverRecursed(); |
343 | if (!mir->hasDefaultAliasSet() && !isWhitelisted) { |
344 | const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode(); |
345 | masm.move32(Imm32(1), ReturnReg); |
346 | masm.store32(ReturnReg, AbsoluteAddress(addr)); |
347 | } |
348 | } |
349 | #endif |
350 | |
351 | // Push an exit frame descriptor. |
352 | masm.PushFrameDescriptor(FrameType::IonJS); |
353 | |
354 | // Call the wrapper function. The wrapper is in charge to unwind the stack |
355 | // when returning from the call. Failures are handled with exceptions based |
356 | // on the return value of the C functions. To guard the outcome of the |
357 | // returned value, use another LIR instruction. |
358 | ensureOsiSpace(); |
359 | uint32_t callOffset = masm.callJit(code); |
360 | markSafepointAt(callOffset, ins); |
361 | |
362 | #ifdef DEBUG1 |
363 | // Reset the disallowArbitraryCode flag after the call. |
364 | { |
365 | const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode(); |
366 | masm.push(ReturnReg); |
367 | masm.move32(Imm32(0), ReturnReg); |
368 | masm.store32(ReturnReg, AbsoluteAddress(addr)); |
369 | masm.pop(ReturnReg); |
370 | } |
371 | #endif |
372 | |
373 | // Pop rest of the exit frame and the arguments left on the stack. |
374 | int framePop = |
375 | sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall(); |
376 | masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop); |
377 | |
378 | // Stack is: |
379 | // ... frame ... |
380 | } |
381 | |
382 | template <typename Fn, Fn fn> |
383 | void CodeGenerator::callVM(LInstruction* ins) { |
384 | VMFunctionId id = VMFunctionToId<Fn, fn>::id; |
385 | callVMInternal(id, ins); |
386 | } |
387 | |
388 | // ArgSeq store arguments for OutOfLineCallVM. |
389 | // |
390 | // OutOfLineCallVM are created with "oolCallVM" function. The third argument of |
391 | // this function is an instance of a class which provides a "generate" in charge |
392 | // of pushing the argument, with "pushArg", for a VMFunction. |
393 | // |
394 | // Such list of arguments can be created by using the "ArgList" function which |
395 | // creates one instance of "ArgSeq", where the type of the arguments are |
396 | // inferred from the type of the arguments. |
397 | // |
398 | // The list of arguments must be written in the same order as if you were |
399 | // calling the function in C++. |
400 | // |
401 | // Example: |
402 | // ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs())) |
403 | |
404 | template <typename... ArgTypes> |
405 | class ArgSeq { |
406 | std::tuple<std::remove_reference_t<ArgTypes>...> args_; |
407 | |
408 | template <std::size_t... ISeq> |
409 | inline void generate(CodeGenerator* codegen, |
410 | std::index_sequence<ISeq...>) const { |
411 | // Arguments are pushed in reverse order, from last argument to first |
412 | // argument. |
413 | (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...); |
414 | } |
415 | |
416 | public: |
417 | explicit ArgSeq(ArgTypes&&... args) |
418 | : args_(std::forward<ArgTypes>(args)...) {} |
419 | |
420 | inline void generate(CodeGenerator* codegen) const { |
421 | generate(codegen, std::index_sequence_for<ArgTypes...>{}); |
422 | } |
423 | |
424 | #ifdef DEBUG1 |
425 | static constexpr size_t numArgs = sizeof...(ArgTypes); |
426 | #endif |
427 | }; |
428 | |
429 | template <typename... ArgTypes> |
430 | inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) { |
431 | return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...); |
432 | } |
433 | |
434 | // Store wrappers, to generate the right move of data after the VM call. |
435 | |
436 | struct StoreNothing { |
437 | inline void generate(CodeGenerator* codegen) const {} |
438 | inline LiveRegisterSet clobbered() const { |
439 | return LiveRegisterSet(); // No register gets clobbered |
440 | } |
441 | }; |
442 | |
443 | class StoreRegisterTo { |
444 | private: |
445 | Register out_; |
446 | |
447 | public: |
448 | explicit StoreRegisterTo(Register out) : out_(out) {} |
449 | |
450 | inline void generate(CodeGenerator* codegen) const { |
451 | // It's okay to use storePointerResultTo here - the VMFunction wrapper |
452 | // ensures the upper bytes are zero for bool/int32 return values. |
453 | codegen->storePointerResultTo(out_); |
454 | } |
455 | inline LiveRegisterSet clobbered() const { |
456 | LiveRegisterSet set; |
457 | set.add(out_); |
458 | return set; |
459 | } |
460 | }; |
461 | |
462 | class StoreFloatRegisterTo { |
463 | private: |
464 | FloatRegister out_; |
465 | |
466 | public: |
467 | explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {} |
468 | |
469 | inline void generate(CodeGenerator* codegen) const { |
470 | codegen->storeFloatResultTo(out_); |
471 | } |
472 | inline LiveRegisterSet clobbered() const { |
473 | LiveRegisterSet set; |
474 | set.add(out_); |
475 | return set; |
476 | } |
477 | }; |
478 | |
479 | template <typename Output> |
480 | class StoreValueTo_ { |
481 | private: |
482 | Output out_; |
483 | |
484 | public: |
485 | explicit StoreValueTo_(const Output& out) : out_(out) {} |
486 | |
487 | inline void generate(CodeGenerator* codegen) const { |
488 | codegen->storeResultValueTo(out_); |
489 | } |
490 | inline LiveRegisterSet clobbered() const { |
491 | LiveRegisterSet set; |
492 | set.add(out_); |
493 | return set; |
494 | } |
495 | }; |
496 | |
497 | template <typename Output> |
498 | StoreValueTo_<Output> StoreValueTo(const Output& out) { |
499 | return StoreValueTo_<Output>(out); |
500 | } |
501 | |
502 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
503 | class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> { |
504 | private: |
505 | LInstruction* lir_; |
506 | ArgSeq args_; |
507 | StoreOutputTo out_; |
508 | |
509 | public: |
510 | OutOfLineCallVM(LInstruction* lir, const ArgSeq& args, |
511 | const StoreOutputTo& out) |
512 | : lir_(lir), args_(args), out_(out) {} |
513 | |
514 | void accept(CodeGenerator* codegen) override { |
515 | codegen->visitOutOfLineCallVM(this); |
516 | } |
517 | |
518 | LInstruction* lir() const { return lir_; } |
519 | const ArgSeq& args() const { return args_; } |
520 | const StoreOutputTo& out() const { return out_; } |
521 | }; |
522 | |
523 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
524 | OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args, |
525 | const StoreOutputTo& out) { |
526 | MOZ_ASSERT(lir->mirRaw())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mirRaw())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mirRaw()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mirRaw()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 526); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()" ")"); do { *((volatile int*)__null) = 526; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
527 | MOZ_ASSERT(lir->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mirRaw()->isInstruction())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mirRaw()->isInstruction ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 527); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()" ")"); do { *((volatile int*)__null) = 527; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
528 | |
529 | #ifdef DEBUG1 |
530 | VMFunctionId id = VMFunctionToId<Fn, fn>::id; |
531 | const VMFunctionData& fun = GetVMFunction(id); |
532 | MOZ_ASSERT(fun.explicitArgs == args.numArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.explicitArgs == args.numArgs)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.explicitArgs == args.numArgs ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "fun.explicitArgs == args.numArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs" ")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
533 | MOZ_ASSERT(fun.returnsData() !=do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo , StoreNothing>))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v <StoreOutputTo, StoreNothing>)))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" ")"); do { *((volatile int*)__null) = 534; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
534 | (std::is_same_v<StoreOutputTo, StoreNothing>))do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo , StoreNothing>))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v <StoreOutputTo, StoreNothing>)))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" ")"); do { *((volatile int*)__null) = 534; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
535 | #endif |
536 | |
537 | OutOfLineCode* ool = new (alloc()) |
538 | OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out); |
539 | addOutOfLineCode(ool, lir->mirRaw()->toInstruction()); |
540 | return ool; |
541 | } |
542 | |
543 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
544 | void CodeGenerator::visitOutOfLineCallVM( |
545 | OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) { |
546 | LInstruction* lir = ool->lir(); |
547 | |
548 | #ifdef JS_JITSPEW1 |
549 | JitSpewStart(JitSpew_Codegen, " # LIR=%s", |
550 | lir->opName()); |
551 | if (const char* extra = lir->getExtraName()) { |
552 | JitSpewCont(JitSpew_Codegen, ":%s", extra); |
553 | } |
554 | JitSpewFin(JitSpew_Codegen); |
555 | #endif |
556 | perfSpewer_.recordInstruction(masm, lir); |
557 | saveLive(lir); |
558 | ool->args().generate(this); |
559 | callVM<Fn, fn>(lir); |
560 | ool->out().generate(this); |
561 | restoreLiveIgnore(lir, ool->out().clobbered()); |
562 | masm.jump(ool->rejoin()); |
563 | } |
564 | |
565 | class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> { |
566 | private: |
567 | LInstruction* lir_; |
568 | size_t cacheIndex_; |
569 | size_t cacheInfoIndex_; |
570 | |
571 | public: |
572 | OutOfLineICFallback(LInstruction* lir, size_t cacheIndex, |
573 | size_t cacheInfoIndex) |
574 | : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {} |
575 | |
576 | void bind(MacroAssembler* masm) override { |
577 | // The binding of the initial jump is done in |
578 | // CodeGenerator::visitOutOfLineICFallback. |
579 | } |
580 | |
581 | size_t cacheIndex() const { return cacheIndex_; } |
582 | size_t cacheInfoIndex() const { return cacheInfoIndex_; } |
583 | LInstruction* lir() const { return lir_; } |
584 | |
585 | void accept(CodeGenerator* codegen) override { |
586 | codegen->visitOutOfLineICFallback(this); |
587 | } |
588 | }; |
589 | |
590 | void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) { |
591 | if (cacheIndex == SIZE_MAX(18446744073709551615UL)) { |
592 | masm.setOOM(); |
593 | return; |
594 | } |
595 | |
596 | DataPtr<IonIC> cache(this, cacheIndex); |
597 | MInstruction* mir = lir->mirRaw()->toInstruction(); |
598 | cache->setScriptedLocation(mir->block()->info().script(), |
599 | mir->resumePoint()->pc()); |
600 | |
601 | Register temp = cache->scratchRegisterForEntryJump(); |
602 | icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp); |
603 | masm.jump(Address(temp, 0)); |
604 | |
605 | MOZ_ASSERT(!icInfo_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!icInfo_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!icInfo_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!icInfo_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!icInfo_.empty()" ")"); do { *((volatile int*)__null) = 605; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
606 | |
607 | OutOfLineICFallback* ool = |
608 | new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1); |
609 | addOutOfLineCode(ool, mir); |
610 | |
611 | masm.bind(ool->rejoin()); |
612 | cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset())); |
613 | } |
614 | |
615 | void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) { |
616 | LInstruction* lir = ool->lir(); |
617 | size_t cacheIndex = ool->cacheIndex(); |
618 | size_t cacheInfoIndex = ool->cacheInfoIndex(); |
619 | |
620 | DataPtr<IonIC> ic(this, cacheIndex); |
621 | |
622 | // Register the location of the OOL path in the IC. |
623 | ic->setFallbackOffset(CodeOffset(masm.currentOffset())); |
624 | |
625 | switch (ic->kind()) { |
626 | case CacheKind::GetProp: |
627 | case CacheKind::GetElem: { |
628 | IonGetPropertyIC* getPropIC = ic->asGetPropertyIC(); |
629 | |
630 | saveLive(lir); |
631 | |
632 | pushArg(getPropIC->id()); |
633 | pushArg(getPropIC->value()); |
634 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
635 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
636 | |
637 | using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*, |
638 | HandleValue, HandleValue, MutableHandleValue); |
639 | callVM<Fn, IonGetPropertyIC::update>(lir); |
640 | |
641 | StoreValueTo(getPropIC->output()).generate(this); |
642 | restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered()); |
643 | |
644 | masm.jump(ool->rejoin()); |
645 | return; |
646 | } |
647 | case CacheKind::GetPropSuper: |
648 | case CacheKind::GetElemSuper: { |
649 | IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC(); |
650 | |
651 | saveLive(lir); |
652 | |
653 | pushArg(getPropSuperIC->id()); |
654 | pushArg(getPropSuperIC->receiver()); |
655 | pushArg(getPropSuperIC->object()); |
656 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
657 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
658 | |
659 | using Fn = |
660 | bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject, |
661 | HandleValue, HandleValue, MutableHandleValue); |
662 | callVM<Fn, IonGetPropSuperIC::update>(lir); |
663 | |
664 | StoreValueTo(getPropSuperIC->output()).generate(this); |
665 | restoreLiveIgnore(lir, |
666 | StoreValueTo(getPropSuperIC->output()).clobbered()); |
667 | |
668 | masm.jump(ool->rejoin()); |
669 | return; |
670 | } |
671 | case CacheKind::SetProp: |
672 | case CacheKind::SetElem: { |
673 | IonSetPropertyIC* setPropIC = ic->asSetPropertyIC(); |
674 | |
675 | saveLive(lir); |
676 | |
677 | pushArg(setPropIC->rhs()); |
678 | pushArg(setPropIC->id()); |
679 | pushArg(setPropIC->object()); |
680 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
681 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
682 | |
683 | using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*, |
684 | HandleObject, HandleValue, HandleValue); |
685 | callVM<Fn, IonSetPropertyIC::update>(lir); |
686 | |
687 | restoreLive(lir); |
688 | |
689 | masm.jump(ool->rejoin()); |
690 | return; |
691 | } |
692 | case CacheKind::GetName: { |
693 | IonGetNameIC* getNameIC = ic->asGetNameIC(); |
694 | |
695 | saveLive(lir); |
696 | |
697 | pushArg(getNameIC->environment()); |
698 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
699 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
700 | |
701 | using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject, |
702 | MutableHandleValue); |
703 | callVM<Fn, IonGetNameIC::update>(lir); |
704 | |
705 | StoreValueTo(getNameIC->output()).generate(this); |
706 | restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered()); |
707 | |
708 | masm.jump(ool->rejoin()); |
709 | return; |
710 | } |
711 | case CacheKind::BindName: { |
712 | IonBindNameIC* bindNameIC = ic->asBindNameIC(); |
713 | |
714 | saveLive(lir); |
715 | |
716 | pushArg(bindNameIC->environment()); |
717 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
718 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
719 | |
720 | using Fn = |
721 | JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject); |
722 | callVM<Fn, IonBindNameIC::update>(lir); |
723 | |
724 | StoreRegisterTo(bindNameIC->output()).generate(this); |
725 | restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered()); |
726 | |
727 | masm.jump(ool->rejoin()); |
728 | return; |
729 | } |
730 | case CacheKind::GetIterator: { |
731 | IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC(); |
732 | |
733 | saveLive(lir); |
734 | |
735 | pushArg(getIteratorIC->value()); |
736 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
737 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
738 | |
739 | using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*, |
740 | HandleValue); |
741 | callVM<Fn, IonGetIteratorIC::update>(lir); |
742 | |
743 | StoreRegisterTo(getIteratorIC->output()).generate(this); |
744 | restoreLiveIgnore(lir, |
745 | StoreRegisterTo(getIteratorIC->output()).clobbered()); |
746 | |
747 | masm.jump(ool->rejoin()); |
748 | return; |
749 | } |
750 | case CacheKind::OptimizeSpreadCall: { |
751 | auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC(); |
752 | |
753 | saveLive(lir); |
754 | |
755 | pushArg(optimizeSpreadCallIC->value()); |
756 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
757 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
758 | |
759 | using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*, |
760 | HandleValue, MutableHandleValue); |
761 | callVM<Fn, IonOptimizeSpreadCallIC::update>(lir); |
762 | |
763 | StoreValueTo(optimizeSpreadCallIC->output()).generate(this); |
764 | restoreLiveIgnore( |
765 | lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered()); |
766 | |
767 | masm.jump(ool->rejoin()); |
768 | return; |
769 | } |
770 | case CacheKind::In: { |
771 | IonInIC* inIC = ic->asInIC(); |
772 | |
773 | saveLive(lir); |
774 | |
775 | pushArg(inIC->object()); |
776 | pushArg(inIC->key()); |
777 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
778 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
779 | |
780 | using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue, |
781 | HandleObject, bool*); |
782 | callVM<Fn, IonInIC::update>(lir); |
783 | |
784 | StoreRegisterTo(inIC->output()).generate(this); |
785 | restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered()); |
786 | |
787 | masm.jump(ool->rejoin()); |
788 | return; |
789 | } |
790 | case CacheKind::HasOwn: { |
791 | IonHasOwnIC* hasOwnIC = ic->asHasOwnIC(); |
792 | |
793 | saveLive(lir); |
794 | |
795 | pushArg(hasOwnIC->id()); |
796 | pushArg(hasOwnIC->value()); |
797 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
798 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
799 | |
800 | using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue, |
801 | HandleValue, int32_t*); |
802 | callVM<Fn, IonHasOwnIC::update>(lir); |
803 | |
804 | StoreRegisterTo(hasOwnIC->output()).generate(this); |
805 | restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered()); |
806 | |
807 | masm.jump(ool->rejoin()); |
808 | return; |
809 | } |
810 | case CacheKind::CheckPrivateField: { |
811 | IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC(); |
812 | |
813 | saveLive(lir); |
814 | |
815 | pushArg(checkPrivateFieldIC->id()); |
816 | pushArg(checkPrivateFieldIC->value()); |
817 | |
818 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
819 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
820 | |
821 | using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*, |
822 | HandleValue, HandleValue, bool*); |
823 | callVM<Fn, IonCheckPrivateFieldIC::update>(lir); |
824 | |
825 | StoreRegisterTo(checkPrivateFieldIC->output()).generate(this); |
826 | restoreLiveIgnore( |
827 | lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered()); |
828 | |
829 | masm.jump(ool->rejoin()); |
830 | return; |
831 | } |
832 | case CacheKind::InstanceOf: { |
833 | IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC(); |
834 | |
835 | saveLive(lir); |
836 | |
837 | pushArg(hasInstanceOfIC->rhs()); |
838 | pushArg(hasInstanceOfIC->lhs()); |
839 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
840 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
841 | |
842 | using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*, |
843 | HandleValue lhs, HandleObject rhs, bool* res); |
844 | callVM<Fn, IonInstanceOfIC::update>(lir); |
845 | |
846 | StoreRegisterTo(hasInstanceOfIC->output()).generate(this); |
847 | restoreLiveIgnore(lir, |
848 | StoreRegisterTo(hasInstanceOfIC->output()).clobbered()); |
849 | |
850 | masm.jump(ool->rejoin()); |
851 | return; |
852 | } |
853 | case CacheKind::UnaryArith: { |
854 | IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC(); |
855 | |
856 | saveLive(lir); |
857 | |
858 | pushArg(unaryArithIC->input()); |
859 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
860 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
861 | |
862 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
863 | IonUnaryArithIC* stub, HandleValue val, |
864 | MutableHandleValue res); |
865 | callVM<Fn, IonUnaryArithIC::update>(lir); |
866 | |
867 | StoreValueTo(unaryArithIC->output()).generate(this); |
868 | restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered()); |
869 | |
870 | masm.jump(ool->rejoin()); |
871 | return; |
872 | } |
873 | case CacheKind::ToPropertyKey: { |
874 | IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC(); |
875 | |
876 | saveLive(lir); |
877 | |
878 | pushArg(toPropertyKeyIC->input()); |
879 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
880 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
881 | |
882 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
883 | IonToPropertyKeyIC* ic, HandleValue val, |
884 | MutableHandleValue res); |
885 | callVM<Fn, IonToPropertyKeyIC::update>(lir); |
886 | |
887 | StoreValueTo(toPropertyKeyIC->output()).generate(this); |
888 | restoreLiveIgnore(lir, |
889 | StoreValueTo(toPropertyKeyIC->output()).clobbered()); |
890 | |
891 | masm.jump(ool->rejoin()); |
892 | return; |
893 | } |
894 | case CacheKind::BinaryArith: { |
895 | IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC(); |
896 | |
897 | saveLive(lir); |
898 | |
899 | pushArg(binaryArithIC->rhs()); |
900 | pushArg(binaryArithIC->lhs()); |
901 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
902 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
903 | |
904 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
905 | IonBinaryArithIC* stub, HandleValue lhs, |
906 | HandleValue rhs, MutableHandleValue res); |
907 | callVM<Fn, IonBinaryArithIC::update>(lir); |
908 | |
909 | StoreValueTo(binaryArithIC->output()).generate(this); |
910 | restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered()); |
911 | |
912 | masm.jump(ool->rejoin()); |
913 | return; |
914 | } |
915 | case CacheKind::Compare: { |
916 | IonCompareIC* compareIC = ic->asCompareIC(); |
917 | |
918 | saveLive(lir); |
919 | |
920 | pushArg(compareIC->rhs()); |
921 | pushArg(compareIC->lhs()); |
922 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
923 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
924 | |
925 | using Fn = |
926 | bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub, |
927 | HandleValue lhs, HandleValue rhs, bool* res); |
928 | callVM<Fn, IonCompareIC::update>(lir); |
929 | |
930 | StoreRegisterTo(compareIC->output()).generate(this); |
931 | restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered()); |
932 | |
933 | masm.jump(ool->rejoin()); |
934 | return; |
935 | } |
936 | case CacheKind::CloseIter: { |
937 | IonCloseIterIC* closeIterIC = ic->asCloseIterIC(); |
938 | |
939 | saveLive(lir); |
940 | |
941 | pushArg(closeIterIC->iter()); |
942 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
943 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
944 | |
945 | using Fn = |
946 | bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject); |
947 | callVM<Fn, IonCloseIterIC::update>(lir); |
948 | |
949 | restoreLive(lir); |
950 | |
951 | masm.jump(ool->rejoin()); |
952 | return; |
953 | } |
954 | case CacheKind::OptimizeGetIterator: { |
955 | auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC(); |
956 | |
957 | saveLive(lir); |
958 | |
959 | pushArg(optimizeGetIteratorIC->value()); |
960 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
961 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
962 | |
963 | using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*, |
964 | HandleValue, bool* res); |
965 | callVM<Fn, IonOptimizeGetIteratorIC::update>(lir); |
966 | |
967 | StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this); |
968 | restoreLiveIgnore( |
969 | lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered()); |
970 | |
971 | masm.jump(ool->rejoin()); |
972 | return; |
973 | } |
974 | case CacheKind::Call: |
975 | case CacheKind::TypeOf: |
976 | case CacheKind::TypeOfEq: |
977 | case CacheKind::ToBool: |
978 | case CacheKind::GetIntrinsic: |
979 | case CacheKind::NewArray: |
980 | case CacheKind::NewObject: |
981 | MOZ_CRASH("Unsupported IC")do { do { } while (false); MOZ_ReportCrash("" "Unsupported IC" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 981); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported IC" ")" ); do { *((volatile int*)__null) = 981; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
982 | } |
983 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 983); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 983; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
984 | } |
985 | |
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 | zoneStubsToReadBarrier_(0) {} |
997 | |
998 | CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); } |
999 | |
1000 | void CodeGenerator::visitValueToInt32(LValueToInt32* lir) { |
1001 | ValueOperand operand = ToValue(lir, LValueToInt32::Input); |
1002 | Register output = ToRegister(lir->output()); |
1003 | FloatRegister temp = ToFloatRegister(lir->tempFloat()); |
1004 | |
1005 | Label fails; |
1006 | if (lir->mode() == LValueToInt32::TRUNCATE) { |
1007 | OutOfLineCode* oolDouble = oolTruncateDouble(temp, output, lir->mir()); |
1008 | |
1009 | // We can only handle strings in truncation contexts, like bitwise |
1010 | // operations. |
1011 | Register stringReg = ToRegister(lir->temp()); |
1012 | using Fn = bool (*)(JSContext*, JSString*, double*); |
1013 | auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg), |
1014 | StoreFloatRegisterTo(temp)); |
1015 | Label* stringEntry = oolString->entry(); |
1016 | Label* stringRejoin = oolString->rejoin(); |
1017 | |
1018 | masm.truncateValueToInt32(operand, stringEntry, stringRejoin, |
1019 | oolDouble->entry(), stringReg, temp, output, |
1020 | &fails); |
1021 | masm.bind(oolDouble->rejoin()); |
1022 | } else { |
1023 | MOZ_ASSERT(lir->mode() == LValueToInt32::NORMAL)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mode() == LValueToInt32::NORMAL)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mode() == LValueToInt32::NORMAL))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mode() == LValueToInt32::NORMAL" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1023); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mode() == LValueToInt32::NORMAL" ")"); do { *((volatile int*)__null) = 1023; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1024 | masm.convertValueToInt32(operand, temp, output, &fails, |
1025 | lir->mirNormal()->needsNegativeZeroCheck(), |
1026 | lir->mirNormal()->conversion()); |
1027 | } |
1028 | |
1029 | bailoutFrom(&fails, lir->snapshot()); |
1030 | } |
1031 | |
1032 | void CodeGenerator::visitValueToDouble(LValueToDouble* lir) { |
1033 | ValueOperand operand = ToValue(lir, LValueToDouble::InputIndex); |
1034 | FloatRegister output = ToFloatRegister(lir->output()); |
1035 | |
1036 | Label fail; |
1037 | masm.convertValueToDouble(operand, output, &fail); |
1038 | bailoutFrom(&fail, lir->snapshot()); |
1039 | } |
1040 | |
1041 | void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) { |
1042 | ValueOperand operand = ToValue(lir, LValueToFloat32::InputIndex); |
1043 | FloatRegister output = ToFloatRegister(lir->output()); |
1044 | |
1045 | Label fail; |
1046 | masm.convertValueToFloat32(operand, output, &fail); |
1047 | bailoutFrom(&fail, lir->snapshot()); |
1048 | } |
1049 | |
1050 | void CodeGenerator::visitValueToFloat16(LValueToFloat16* lir) { |
1051 | ValueOperand operand = ToValue(lir, LValueToFloat16::InputIndex); |
1052 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
1053 | FloatRegister output = ToFloatRegister(lir->output()); |
1054 | |
1055 | LiveRegisterSet volatileRegs; |
1056 | if (!MacroAssembler::SupportsFloat64To16()) { |
1057 | volatileRegs = liveVolatileRegs(lir); |
1058 | } |
1059 | |
1060 | Label fail; |
1061 | masm.convertValueToFloat16(operand, output, temp, volatileRegs, &fail); |
1062 | bailoutFrom(&fail, lir->snapshot()); |
1063 | } |
1064 | |
1065 | void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) { |
1066 | ValueOperand operand = ToValue(lir, LValueToBigInt::InputIndex); |
1067 | Register output = ToRegister(lir->output()); |
1068 | |
1069 | using Fn = BigInt* (*)(JSContext*, HandleValue); |
1070 | auto* ool = |
1071 | oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output)); |
1072 | |
1073 | Register tag = masm.extractTag(operand, output); |
1074 | |
1075 | Label notBigInt, done; |
1076 | masm.branchTestBigInt(Assembler::NotEqual, tag, ¬BigInt); |
1077 | masm.unboxBigInt(operand, output); |
1078 | masm.jump(&done); |
1079 | masm.bind(¬BigInt); |
1080 | |
1081 | masm.branchTestBoolean(Assembler::Equal, tag, ool->entry()); |
1082 | masm.branchTestString(Assembler::Equal, tag, ool->entry()); |
1083 | |
1084 | // ToBigInt(object) can have side-effects; all other types throw a TypeError. |
1085 | bailout(lir->snapshot()); |
1086 | |
1087 | masm.bind(ool->rejoin()); |
1088 | masm.bind(&done); |
1089 | } |
1090 | |
1091 | void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) { |
1092 | masm.convertInt32ToDouble(ToRegister(lir->input()), |
1093 | ToFloatRegister(lir->output())); |
1094 | } |
1095 | |
1096 | void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) { |
1097 | masm.convertFloat32ToDouble(ToFloatRegister(lir->input()), |
1098 | ToFloatRegister(lir->output())); |
1099 | } |
1100 | |
1101 | void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) { |
1102 | masm.convertDoubleToFloat32(ToFloatRegister(lir->input()), |
1103 | ToFloatRegister(lir->output())); |
1104 | } |
1105 | |
1106 | void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) { |
1107 | masm.convertInt32ToFloat32(ToRegister(lir->input()), |
1108 | ToFloatRegister(lir->output())); |
1109 | } |
1110 | |
1111 | void CodeGenerator::visitDoubleToFloat16(LDoubleToFloat16* lir) { |
1112 | LiveRegisterSet volatileRegs; |
1113 | if (!MacroAssembler::SupportsFloat64To16()) { |
1114 | volatileRegs = liveVolatileRegs(lir); |
1115 | } |
1116 | masm.convertDoubleToFloat16( |
1117 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
1118 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
1119 | } |
1120 | |
1121 | void CodeGenerator::visitFloat32ToFloat16(LFloat32ToFloat16* lir) { |
1122 | LiveRegisterSet volatileRegs; |
1123 | if (!MacroAssembler::SupportsFloat32To16()) { |
1124 | volatileRegs = liveVolatileRegs(lir); |
1125 | } |
1126 | masm.convertFloat32ToFloat16( |
1127 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
1128 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
1129 | } |
1130 | |
1131 | void CodeGenerator::visitInt32ToFloat16(LInt32ToFloat16* lir) { |
1132 | LiveRegisterSet volatileRegs; |
1133 | if (!MacroAssembler::SupportsFloat32To16()) { |
1134 | volatileRegs = liveVolatileRegs(lir); |
1135 | } |
1136 | masm.convertInt32ToFloat16( |
1137 | ToRegister(lir->input()), ToFloatRegister(lir->output()), |
1138 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
1139 | } |
1140 | |
1141 | void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) { |
1142 | Label fail; |
1143 | FloatRegister input = ToFloatRegister(lir->input()); |
1144 | Register output = ToRegister(lir->output()); |
1145 | masm.convertDoubleToInt32(input, output, &fail, |
1146 | lir->mir()->needsNegativeZeroCheck()); |
1147 | bailoutFrom(&fail, lir->snapshot()); |
1148 | } |
1149 | |
1150 | void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) { |
1151 | Label fail; |
1152 | FloatRegister input = ToFloatRegister(lir->input()); |
1153 | Register output = ToRegister(lir->output()); |
1154 | masm.convertFloat32ToInt32(input, output, &fail, |
1155 | lir->mir()->needsNegativeZeroCheck()); |
1156 | bailoutFrom(&fail, lir->snapshot()); |
1157 | } |
1158 | |
1159 | void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) { |
1160 | #ifdef JS_64BIT1 |
1161 | // This LIR instruction is only used if the input can be negative. |
1162 | MOZ_ASSERT(lir->mir()->canBeNegative())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->canBeNegative())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->canBeNegative ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->canBeNegative()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1162); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->canBeNegative()" ")"); do { *((volatile int*)__null) = 1162; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1163 | |
1164 | Register output = ToRegister(lir->output()); |
1165 | const LAllocation* input = lir->input(); |
1166 | if (input->isRegister()) { |
1167 | masm.move32SignExtendToPtr(ToRegister(input), output); |
1168 | } else { |
1169 | masm.load32SignExtendToPtr(ToAddress(input), output); |
1170 | } |
1171 | #else |
1172 | MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1172); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { *((volatile int*)__null) = 1172; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
1173 | #endif |
1174 | } |
1175 | |
1176 | void CodeGenerator::visitNonNegativeIntPtrToInt32( |
1177 | LNonNegativeIntPtrToInt32* lir) { |
1178 | #ifdef JS_64BIT1 |
1179 | Register output = ToRegister(lir->output()); |
1180 | MOZ_ASSERT(ToRegister(lir->input()) == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->input()) == output)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(ToRegister(lir->input()) == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->input()) == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1180); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"); do { *((volatile int*)__null) = 1180; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1181 | |
1182 | Label bail; |
1183 | masm.guardNonNegativeIntPtrToInt32(output, &bail); |
1184 | bailoutFrom(&bail, lir->snapshot()); |
1185 | #else |
1186 | MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1186); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { *((volatile int*)__null) = 1186; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
1187 | #endif |
1188 | } |
1189 | |
1190 | void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) { |
1191 | Register input = ToRegister(lir->input()); |
1192 | FloatRegister output = ToFloatRegister(lir->output()); |
1193 | masm.convertIntPtrToDouble(input, output); |
1194 | } |
1195 | |
1196 | void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) { |
1197 | Register output = ToRegister(lir->output()); |
1198 | MOZ_ASSERT(ToRegister(lir->input()) == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->input()) == output)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(ToRegister(lir->input()) == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->input()) == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"); do { *((volatile int*)__null) = 1198; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1199 | |
1200 | uint32_t byteSize = lir->mir()->byteSize(); |
1201 | |
1202 | #ifdef DEBUG1 |
1203 | Label ok; |
1204 | masm.branchTestPtr(Assembler::NotSigned, output, output, &ok); |
1205 | masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength"); |
1206 | masm.bind(&ok); |
1207 | #endif |
1208 | |
1209 | Label bail; |
1210 | masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail); |
1211 | bailoutFrom(&bail, lir->snapshot()); |
1212 | } |
1213 | |
1214 | void CodeGenerator::emitOOLTestObject(Register objreg, |
1215 | Label* ifEmulatesUndefined, |
1216 | Label* ifDoesntEmulateUndefined, |
1217 | Register scratch) { |
1218 | saveVolatile(scratch); |
1219 | #if defined(DEBUG1) || defined(FUZZING) |
1220 | masm.loadPtr(AbsoluteAddress( |
1221 | gen->runtime->addressOfHasSeenObjectEmulateUndefinedFuse()), |
1222 | scratch); |
1223 | using Fn = bool (*)(JSObject* obj, size_t fuseValue); |
1224 | masm.setupAlignedABICall(); |
1225 | masm.passABIArg(objreg); |
1226 | masm.passABIArg(scratch); |
1227 | masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>(); |
1228 | #else |
1229 | using Fn = bool (*)(JSObject* obj); |
1230 | masm.setupAlignedABICall(); |
1231 | masm.passABIArg(objreg); |
1232 | masm.callWithABI<Fn, js::EmulatesUndefined>(); |
1233 | #endif |
1234 | masm.storeCallPointerResult(scratch); |
1235 | restoreVolatile(scratch); |
1236 | |
1237 | masm.branchIfTrueBool(scratch, ifEmulatesUndefined); |
1238 | masm.jump(ifDoesntEmulateUndefined); |
1239 | } |
1240 | |
1241 | // Base out-of-line code generator for all tests of the truthiness of an |
1242 | // object, where the object might not be truthy. (Recall that per spec all |
1243 | // objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class |
1244 | // flag to permit objects to look like |undefined| in certain contexts, |
1245 | // including in object truthiness testing.) We check truthiness inline except |
1246 | // when we're testing it on a proxy, in which case out-of-line code will call |
1247 | // EmulatesUndefined for a conclusive answer. |
1248 | class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> { |
1249 | Register objreg_; |
1250 | Register scratch_; |
1251 | |
1252 | Label* ifEmulatesUndefined_; |
1253 | Label* ifDoesntEmulateUndefined_; |
1254 | |
1255 | #ifdef DEBUG1 |
1256 | bool initialized() { return ifEmulatesUndefined_ != nullptr; } |
1257 | #endif |
1258 | |
1259 | public: |
1260 | OutOfLineTestObject() |
1261 | : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {} |
1262 | |
1263 | void accept(CodeGenerator* codegen) final { |
1264 | MOZ_ASSERT(initialized())do { static_assert( mozilla::detail::AssertionConditionType< decltype(initialized())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(initialized()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("initialized()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1264); AnnotateMozCrashReason("MOZ_ASSERT" "(" "initialized()" ")"); do { *((volatile int*)__null) = 1264; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1265 | codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_, |
1266 | ifDoesntEmulateUndefined_, scratch_); |
1267 | } |
1268 | |
1269 | // Specify the register where the object to be tested is found, labels to |
1270 | // jump to if the object is truthy or falsy, and a scratch register for |
1271 | // use in the out-of-line path. |
1272 | void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined, |
1273 | Label* ifDoesntEmulateUndefined, Register scratch) { |
1274 | MOZ_ASSERT(!initialized())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!initialized())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!initialized()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!initialized()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1274); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!initialized()" ")"); do { *((volatile int*)__null) = 1274; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1275 | MOZ_ASSERT(ifEmulatesUndefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ifEmulatesUndefined)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ifEmulatesUndefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ifEmulatesUndefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ifEmulatesUndefined" ")"); do { *((volatile int*)__null) = 1275; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1276 | objreg_ = objreg; |
1277 | scratch_ = scratch; |
1278 | ifEmulatesUndefined_ = ifEmulatesUndefined; |
1279 | ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined; |
1280 | } |
1281 | }; |
1282 | |
1283 | // A subclass of OutOfLineTestObject containing two extra labels, for use when |
1284 | // the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line |
1285 | // code. The user should bind these labels in inline code, and specify them as |
1286 | // targets via setInputAndTargets, as appropriate. |
1287 | class OutOfLineTestObjectWithLabels : public OutOfLineTestObject { |
1288 | Label label1_; |
1289 | Label label2_; |
1290 | |
1291 | public: |
1292 | OutOfLineTestObjectWithLabels() = default; |
1293 | |
1294 | Label* label1() { return &label1_; } |
1295 | Label* label2() { return &label2_; } |
1296 | }; |
1297 | |
1298 | void CodeGenerator::testObjectEmulatesUndefinedKernel( |
1299 | Register objreg, Label* ifEmulatesUndefined, |
1300 | Label* ifDoesntEmulateUndefined, Register scratch, |
1301 | OutOfLineTestObject* ool) { |
1302 | ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, |
1303 | scratch); |
1304 | |
1305 | // Perform a fast-path check of the object's class flags if the object's |
1306 | // not a proxy. Let out-of-line code handle the slow cases that require |
1307 | // saving registers, making a function call, and restoring registers. |
1308 | masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(), |
1309 | ifEmulatesUndefined); |
1310 | } |
1311 | |
1312 | void CodeGenerator::branchTestObjectEmulatesUndefined( |
1313 | Register objreg, Label* ifEmulatesUndefined, |
1314 | Label* ifDoesntEmulateUndefined, Register scratch, |
1315 | OutOfLineTestObject* ool) { |
1316 | MOZ_ASSERT(!ifDoesntEmulateUndefined->bound(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ifDoesntEmulateUndefined->bound())>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(!ifDoesntEmulateUndefined->bound()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!ifDoesntEmulateUndefined->bound()" " (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")"); do { *((volatile int*)__null) = 1317; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
1317 | "ifDoesntEmulateUndefined will be bound to the fallthrough path")do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ifDoesntEmulateUndefined->bound())>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(!ifDoesntEmulateUndefined->bound()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!ifDoesntEmulateUndefined->bound()" " (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")"); do { *((volatile int*)__null) = 1317; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1318 | |
1319 | testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, |
1320 | ifDoesntEmulateUndefined, scratch, ool); |
1321 | masm.bind(ifDoesntEmulateUndefined); |
1322 | } |
1323 | |
1324 | void CodeGenerator::testObjectEmulatesUndefined(Register objreg, |
1325 | Label* ifEmulatesUndefined, |
1326 | Label* ifDoesntEmulateUndefined, |
1327 | Register scratch, |
1328 | OutOfLineTestObject* ool) { |
1329 | testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, |
1330 | ifDoesntEmulateUndefined, scratch, ool); |
1331 | masm.jump(ifDoesntEmulateUndefined); |
1332 | } |
1333 | |
1334 | void CodeGenerator::testValueTruthyForType( |
1335 | JSValueType type, ScratchTagScope& tag, const ValueOperand& value, |
1336 | Register tempToUnbox, Register temp, FloatRegister floatTemp, |
1337 | Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool, |
1338 | bool skipTypeTest) { |
1339 | #ifdef DEBUG1 |
1340 | if (skipTypeTest) { |
1341 | Label expected; |
1342 | masm.branchTestType(Assembler::Equal, tag, type, &expected); |
1343 | masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType"); |
1344 | masm.bind(&expected); |
1345 | } |
1346 | #endif |
1347 | |
1348 | // Handle irregular types first. |
1349 | switch (type) { |
1350 | case JSVAL_TYPE_UNDEFINED: |
1351 | case JSVAL_TYPE_NULL: |
1352 | // Undefined and null are falsy. |
1353 | if (!skipTypeTest) { |
1354 | masm.branchTestType(Assembler::Equal, tag, type, ifFalsy); |
1355 | } else { |
1356 | masm.jump(ifFalsy); |
1357 | } |
1358 | return; |
1359 | case JSVAL_TYPE_SYMBOL: |
1360 | // Symbols are truthy. |
1361 | if (!skipTypeTest) { |
1362 | masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy); |
1363 | } else { |
1364 | masm.jump(ifTruthy); |
1365 | } |
1366 | return; |
1367 | case JSVAL_TYPE_OBJECT: { |
1368 | Label notObject; |
1369 | if (!skipTypeTest) { |
1370 | masm.branchTestObject(Assembler::NotEqual, tag, ¬Object); |
1371 | } |
1372 | ScratchTagScopeRelease _(&tag); |
1373 | Register objreg = masm.extractObject(value, tempToUnbox); |
1374 | testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool); |
1375 | masm.bind(¬Object); |
1376 | return; |
1377 | } |
1378 | default: |
1379 | break; |
1380 | } |
1381 | |
1382 | // Check the type of the value (unless this is the last possible type). |
1383 | Label differentType; |
1384 | if (!skipTypeTest) { |
1385 | masm.branchTestType(Assembler::NotEqual, tag, type, &differentType); |
1386 | } |
1387 | |
1388 | // Branch if the value is falsy. |
1389 | ScratchTagScopeRelease _(&tag); |
1390 | switch (type) { |
1391 | case JSVAL_TYPE_BOOLEAN: { |
1392 | masm.branchTestBooleanTruthy(false, value, ifFalsy); |
1393 | break; |
1394 | } |
1395 | case JSVAL_TYPE_INT32: { |
1396 | masm.branchTestInt32Truthy(false, value, ifFalsy); |
1397 | break; |
1398 | } |
1399 | case JSVAL_TYPE_STRING: { |
1400 | masm.branchTestStringTruthy(false, value, ifFalsy); |
1401 | break; |
1402 | } |
1403 | case JSVAL_TYPE_BIGINT: { |
1404 | masm.branchTestBigIntTruthy(false, value, ifFalsy); |
1405 | break; |
1406 | } |
1407 | case JSVAL_TYPE_DOUBLE: { |
1408 | masm.unboxDouble(value, floatTemp); |
1409 | masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy); |
1410 | break; |
1411 | } |
1412 | default: |
1413 | MOZ_CRASH("Unexpected value type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected value type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1413); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected value type" ")"); do { *((volatile int*)__null) = 1413; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
1414 | } |
1415 | |
1416 | // If we reach this point, the value is truthy. We fall through for |
1417 | // truthy on the last test; otherwise, branch. |
1418 | if (!skipTypeTest) { |
1419 | masm.jump(ifTruthy); |
1420 | } |
1421 | |
1422 | masm.bind(&differentType); |
1423 | } |
1424 | |
1425 | void CodeGenerator::testValueTruthy(const ValueOperand& value, |
1426 | Register tempToUnbox, Register temp, |
1427 | FloatRegister floatTemp, |
1428 | const TypeDataList& observedTypes, |
1429 | Label* ifTruthy, Label* ifFalsy, |
1430 | OutOfLineTestObject* ool) { |
1431 | ScratchTagScope tag(masm, value); |
1432 | masm.splitTagForTest(value, tag); |
1433 | |
1434 | const std::initializer_list<JSValueType> defaultOrder = { |
1435 | JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, |
1436 | JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING, |
1437 | JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT}; |
1438 | |
1439 | mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder); |
1440 | |
1441 | // Generate tests for previously observed types first. |
1442 | // The TypeDataList is sorted by descending frequency. |
1443 | for (auto& observed : observedTypes) { |
1444 | JSValueType type = observed.type(); |
1445 | remaining -= type; |
1446 | |
1447 | testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp, |
1448 | ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false); |
1449 | } |
1450 | |
1451 | // Generate tests for remaining types. |
1452 | for (auto type : defaultOrder) { |
1453 | if (!remaining.contains(type)) { |
1454 | continue; |
1455 | } |
1456 | remaining -= type; |
1457 | |
1458 | // We don't need a type test for the last possible type. |
1459 | bool skipTypeTest = remaining.isEmpty(); |
1460 | testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp, |
1461 | ifTruthy, ifFalsy, ool, skipTypeTest); |
1462 | } |
1463 | MOZ_ASSERT(remaining.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(remaining.isEmpty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(remaining.isEmpty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("remaining.isEmpty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1463); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { *((volatile int*)__null) = 1463; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1464 | |
1465 | // We fall through if the final test is truthy. |
1466 | } |
1467 | |
1468 | void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) { |
1469 | Label* ifTrueLabel = getJumpLabelForBranch(lir->ifTrue()); |
1470 | Label* ifFalseLabel = getJumpLabelForBranch(lir->ifFalse()); |
1471 | Register input = ToRegister(lir->input()); |
1472 | |
1473 | if (isNextBlock(lir->ifFalse()->lir())) { |
1474 | masm.branchIfBigIntIsNonZero(input, ifTrueLabel); |
1475 | } else if (isNextBlock(lir->ifTrue()->lir())) { |
1476 | masm.branchIfBigIntIsZero(input, ifFalseLabel); |
1477 | } else { |
1478 | masm.branchIfBigIntIsZero(input, ifFalseLabel); |
1479 | jumpToBlock(lir->ifTrue()); |
1480 | } |
1481 | } |
1482 | |
1483 | void CodeGenerator::assertObjectDoesNotEmulateUndefined( |
1484 | Register input, Register temp, const MInstruction* mir) { |
1485 | #if defined(DEBUG1) || defined(FUZZING) |
1486 | // Validate that the object indeed doesn't have the emulates undefined flag. |
1487 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
1488 | addOutOfLineCode(ool, mir); |
1489 | |
1490 | Label* doesNotEmulateUndefined = ool->label1(); |
1491 | Label* emulatesUndefined = ool->label2(); |
1492 | |
1493 | testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined, |
1494 | temp, ool); |
1495 | masm.bind(emulatesUndefined); |
1496 | masm.assumeUnreachable( |
1497 | "Found an object emulating undefined while the fuse is intact"); |
1498 | masm.bind(doesNotEmulateUndefined); |
1499 | #endif |
1500 | } |
1501 | |
1502 | void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) { |
1503 | Label* truthy = getJumpLabelForBranch(lir->ifTruthy()); |
1504 | Label* falsy = getJumpLabelForBranch(lir->ifFalsy()); |
1505 | Register input = ToRegister(lir->input()); |
1506 | |
1507 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
1508 | if (intact) { |
1509 | assertObjectDoesNotEmulateUndefined(input, ToRegister(lir->temp()), |
1510 | lir->mir()); |
1511 | // Bug 1874905: It would be fantastic if this could be optimized out |
1512 | masm.jump(truthy); |
1513 | } else { |
1514 | auto* ool = new (alloc()) OutOfLineTestObject(); |
1515 | addOutOfLineCode(ool, lir->mir()); |
1516 | |
1517 | testObjectEmulatesUndefined(input, falsy, truthy, ToRegister(lir->temp()), |
1518 | ool); |
1519 | } |
1520 | } |
1521 | |
1522 | void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) { |
1523 | auto* ool = new (alloc()) OutOfLineTestObject(); |
1524 | addOutOfLineCode(ool, lir->mir()); |
1525 | |
1526 | Label* truthy = getJumpLabelForBranch(lir->ifTruthy()); |
1527 | Label* falsy = getJumpLabelForBranch(lir->ifFalsy()); |
1528 | |
1529 | ValueOperand input = ToValue(lir, LTestVAndBranch::Input); |
1530 | Register tempToUnbox = ToTempUnboxRegister(lir->temp1()); |
1531 | Register temp = ToRegister(lir->temp2()); |
1532 | FloatRegister floatTemp = ToFloatRegister(lir->tempFloat()); |
1533 | const TypeDataList& observedTypes = lir->mir()->observedTypes(); |
1534 | |
1535 | testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy, |
1536 | falsy, ool); |
1537 | masm.jump(truthy); |
1538 | } |
1539 | |
1540 | void CodeGenerator::visitBooleanToString(LBooleanToString* lir) { |
1541 | Register input = ToRegister(lir->input()); |
1542 | Register output = ToRegister(lir->output()); |
1543 | const JSAtomState& names = gen->runtime->names(); |
1544 | Label true_, done; |
1545 | |
1546 | masm.branchTest32(Assembler::NonZero, input, input, &true_); |
1547 | masm.movePtr(ImmGCPtr(names.false_), output); |
1548 | masm.jump(&done); |
1549 | |
1550 | masm.bind(&true_); |
1551 | masm.movePtr(ImmGCPtr(names.true_), output); |
1552 | |
1553 | masm.bind(&done); |
1554 | } |
1555 | |
1556 | void CodeGenerator::visitIntToString(LIntToString* lir) { |
1557 | Register input = ToRegister(lir->input()); |
1558 | Register output = ToRegister(lir->output()); |
1559 | |
1560 | using Fn = JSLinearString* (*)(JSContext*, int); |
1561 | OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>( |
1562 | lir, ArgList(input), StoreRegisterTo(output)); |
1563 | |
1564 | masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(), |
1565 | ool->entry()); |
1566 | |
1567 | masm.bind(ool->rejoin()); |
1568 | } |
1569 | |
1570 | void CodeGenerator::visitDoubleToString(LDoubleToString* lir) { |
1571 | FloatRegister input = ToFloatRegister(lir->input()); |
1572 | Register temp = ToRegister(lir->temp0()); |
1573 | Register output = ToRegister(lir->output()); |
1574 | |
1575 | using Fn = JSString* (*)(JSContext*, double); |
1576 | OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>( |
1577 | lir, ArgList(input), StoreRegisterTo(output)); |
1578 | |
1579 | // Try double to integer conversion and run integer to string code. |
1580 | masm.convertDoubleToInt32(input, temp, ool->entry(), false); |
1581 | masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(), |
1582 | ool->entry()); |
1583 | |
1584 | masm.bind(ool->rejoin()); |
1585 | } |
1586 | |
1587 | void CodeGenerator::visitValueToString(LValueToString* lir) { |
1588 | ValueOperand input = ToValue(lir, LValueToString::InputIndex); |
1589 | Register output = ToRegister(lir->output()); |
1590 | |
1591 | using Fn = JSString* (*)(JSContext*, HandleValue); |
1592 | OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>( |
1593 | lir, ArgList(input), StoreRegisterTo(output)); |
1594 | |
1595 | Label done; |
1596 | Register tag = masm.extractTag(input, output); |
1597 | const JSAtomState& names = gen->runtime->names(); |
1598 | |
1599 | // String |
1600 | { |
1601 | Label notString; |
1602 | masm.branchTestString(Assembler::NotEqual, tag, ¬String); |
1603 | masm.unboxString(input, output); |
1604 | masm.jump(&done); |
1605 | masm.bind(¬String); |
1606 | } |
1607 | |
1608 | // Integer |
1609 | { |
1610 | Label notInteger; |
1611 | masm.branchTestInt32(Assembler::NotEqual, tag, ¬Integer); |
1612 | Register unboxed = ToTempUnboxRegister(lir->temp0()); |
1613 | unboxed = masm.extractInt32(input, unboxed); |
1614 | masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(), |
1615 | ool->entry()); |
1616 | masm.jump(&done); |
1617 | masm.bind(¬Integer); |
1618 | } |
1619 | |
1620 | // Double |
1621 | { |
1622 | // Note: no fastpath. Need two extra registers and can only convert doubles |
1623 | // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT. |
1624 | masm.branchTestDouble(Assembler::Equal, tag, ool->entry()); |
1625 | } |
1626 | |
1627 | // Undefined |
1628 | { |
1629 | Label notUndefined; |
1630 | masm.branchTestUndefined(Assembler::NotEqual, tag, ¬Undefined); |
1631 | masm.movePtr(ImmGCPtr(names.undefined), output); |
1632 | masm.jump(&done); |
1633 | masm.bind(¬Undefined); |
1634 | } |
1635 | |
1636 | // Null |
1637 | { |
1638 | Label notNull; |
1639 | masm.branchTestNull(Assembler::NotEqual, tag, ¬Null); |
1640 | masm.movePtr(ImmGCPtr(names.null), output); |
1641 | masm.jump(&done); |
1642 | masm.bind(¬Null); |
1643 | } |
1644 | |
1645 | // Boolean |
1646 | { |
1647 | Label notBoolean, true_; |
1648 | masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean); |
1649 | masm.branchTestBooleanTruthy(true, input, &true_); |
1650 | masm.movePtr(ImmGCPtr(names.false_), output); |
1651 | masm.jump(&done); |
1652 | masm.bind(&true_); |
1653 | masm.movePtr(ImmGCPtr(names.true_), output); |
1654 | masm.jump(&done); |
1655 | masm.bind(¬Boolean); |
1656 | } |
1657 | |
1658 | // Objects/symbols are only possible when |mir->mightHaveSideEffects()|. |
1659 | if (lir->mir()->mightHaveSideEffects()) { |
1660 | // Object |
1661 | if (lir->mir()->supportSideEffects()) { |
1662 | masm.branchTestObject(Assembler::Equal, tag, ool->entry()); |
1663 | } else { |
1664 | // Bail. |
1665 | MOZ_ASSERT(lir->mir()->needsSnapshot())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->needsSnapshot())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->needsSnapshot ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->needsSnapshot()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1665); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()" ")"); do { *((volatile int*)__null) = 1665; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1666 | Label bail; |
1667 | masm.branchTestObject(Assembler::Equal, tag, &bail); |
1668 | bailoutFrom(&bail, lir->snapshot()); |
1669 | } |
1670 | |
1671 | // Symbol |
1672 | if (lir->mir()->supportSideEffects()) { |
1673 | masm.branchTestSymbol(Assembler::Equal, tag, ool->entry()); |
1674 | } else { |
1675 | // Bail. |
1676 | MOZ_ASSERT(lir->mir()->needsSnapshot())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->needsSnapshot())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->needsSnapshot ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->needsSnapshot()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1676); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()" ")"); do { *((volatile int*)__null) = 1676; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1677 | Label bail; |
1678 | masm.branchTestSymbol(Assembler::Equal, tag, &bail); |
1679 | bailoutFrom(&bail, lir->snapshot()); |
1680 | } |
1681 | } |
1682 | |
1683 | // BigInt |
1684 | { |
1685 | // No fastpath currently implemented. |
1686 | masm.branchTestBigInt(Assembler::Equal, tag, ool->entry()); |
1687 | } |
1688 | |
1689 | masm.assumeUnreachable("Unexpected type for LValueToString."); |
1690 | |
1691 | masm.bind(&done); |
1692 | masm.bind(ool->rejoin()); |
1693 | } |
1694 | |
1695 | using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**); |
1696 | |
1697 | static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder, |
1698 | size_t offset, Register buffer, |
1699 | LiveGeneralRegisterSet& liveVolatiles, |
1700 | StoreBufferMutationFn fun) { |
1701 | Label callVM; |
1702 | Label exit; |
1703 | |
1704 | // Call into the VM to barrier the write. The only registers that need to |
1705 | // be preserved are those in liveVolatiles, so once they are saved on the |
1706 | // stack all volatile registers are available for use. |
1707 | masm.bind(&callVM); |
1708 | masm.PushRegsInMask(liveVolatiles); |
1709 | |
1710 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
1711 | regs.takeUnchecked(buffer); |
1712 | regs.takeUnchecked(holder); |
1713 | Register addrReg = regs.takeAny(); |
1714 | |
1715 | masm.computeEffectiveAddress(Address(holder, offset), addrReg); |
1716 | |
1717 | bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>(); |
1718 | if (needExtraReg) { |
1719 | masm.push(holder); |
1720 | masm.setupUnalignedABICall(holder); |
1721 | } else { |
1722 | masm.setupUnalignedABICall(regs.takeAny()); |
1723 | } |
1724 | masm.passABIArg(buffer); |
1725 | masm.passABIArg(addrReg); |
1726 | masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun), |
1727 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
1728 | |
1729 | if (needExtraReg) { |
1730 | masm.pop(holder); |
1731 | } |
1732 | masm.PopRegsInMask(liveVolatiles); |
1733 | masm.bind(&exit); |
1734 | } |
1735 | |
1736 | // Warning: this function modifies prev and next. |
1737 | static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder, |
1738 | size_t offset, Register prev, Register next, |
1739 | LiveGeneralRegisterSet& liveVolatiles) { |
1740 | Label exit; |
1741 | Label checkRemove, putCell; |
1742 | |
1743 | // if (next && (buffer = next->storeBuffer())) |
1744 | // but we never pass in nullptr for next. |
1745 | Register storebuffer = next; |
1746 | masm.loadStoreBuffer(next, storebuffer); |
1747 | masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove); |
1748 | |
1749 | // if (prev && prev->storeBuffer()) |
1750 | masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell); |
1751 | masm.loadStoreBuffer(prev, prev); |
1752 | masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit); |
1753 | |
1754 | // buffer->putCell(cellp) |
1755 | masm.bind(&putCell); |
1756 | EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles, |
1757 | JSString::addCellAddressToStoreBuffer); |
1758 | masm.jump(&exit); |
1759 | |
1760 | // if (prev && (buffer = prev->storeBuffer())) |
1761 | masm.bind(&checkRemove); |
1762 | masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit); |
1763 | masm.loadStoreBuffer(prev, storebuffer); |
1764 | masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit); |
1765 | EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles, |
1766 | JSString::removeCellAddressFromStoreBuffer); |
1767 | |
1768 | masm.bind(&exit); |
1769 | } |
1770 | |
1771 | void CodeGenerator::visitRegExp(LRegExp* lir) { |
1772 | Register output = ToRegister(lir->output()); |
1773 | Register temp = ToRegister(lir->temp0()); |
1774 | JSObject* source = lir->mir()->source(); |
1775 | |
1776 | using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>); |
1777 | OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>( |
1778 | lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output)); |
1779 | if (lir->mir()->hasShared()) { |
1780 | TemplateObject templateObject(source); |
1781 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
1782 | ool->entry()); |
1783 | } else { |
1784 | masm.jump(ool->entry()); |
1785 | } |
1786 | masm.bind(ool->rejoin()); |
1787 | } |
1788 | |
1789 | static constexpr int32_t RegExpPairsVectorStartOffset( |
1790 | int32_t inputOutputDataStartOffset) { |
1791 | return inputOutputDataStartOffset + int32_t(InputOutputDataSize) + |
1792 | int32_t(sizeof(MatchPairs)); |
1793 | } |
1794 | |
1795 | static Address RegExpPairCountAddress(MacroAssembler& masm, |
1796 | int32_t inputOutputDataStartOffset) { |
1797 | return Address(FramePointer, inputOutputDataStartOffset + |
1798 | int32_t(InputOutputDataSize) + |
1799 | MatchPairs::offsetOfPairCount()); |
1800 | } |
1801 | |
1802 | static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp, |
1803 | Register input, Register lastIndex, |
1804 | Register staticsReg, Register temp1, |
1805 | Register temp2, gc::Heap initialStringHeap, |
1806 | LiveGeneralRegisterSet& volatileRegs) { |
1807 | Address pendingInputAddress(staticsReg, |
1808 | RegExpStatics::offsetOfPendingInput()); |
1809 | Address matchesInputAddress(staticsReg, |
1810 | RegExpStatics::offsetOfMatchesInput()); |
1811 | Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource()); |
1812 | Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex()); |
1813 | |
1814 | masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String); |
1815 | masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String); |
1816 | masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String); |
1817 | |
1818 | if (initialStringHeap == gc::Heap::Default) { |
1819 | // Writing into RegExpStatics tenured memory; must post-barrier. |
1820 | if (staticsReg.volatile_()) { |
1821 | volatileRegs.add(staticsReg); |
1822 | } |
1823 | |
1824 | masm.loadPtr(pendingInputAddress, temp1); |
1825 | masm.storePtr(input, pendingInputAddress); |
1826 | masm.movePtr(input, temp2); |
1827 | EmitPostWriteBarrierS(masm, staticsReg, |
1828 | RegExpStatics::offsetOfPendingInput(), |
1829 | temp1 /* prev */, temp2 /* next */, volatileRegs); |
1830 | |
1831 | masm.loadPtr(matchesInputAddress, temp1); |
1832 | masm.storePtr(input, matchesInputAddress); |
1833 | masm.movePtr(input, temp2); |
1834 | EmitPostWriteBarrierS(masm, staticsReg, |
1835 | RegExpStatics::offsetOfMatchesInput(), |
1836 | temp1 /* prev */, temp2 /* next */, volatileRegs); |
1837 | } else { |
1838 | masm.debugAssertGCThingIsTenured(input, temp1); |
1839 | masm.storePtr(input, pendingInputAddress); |
1840 | masm.storePtr(input, matchesInputAddress); |
1841 | } |
1842 | |
1843 | masm.storePtr(lastIndex, |
1844 | Address(staticsReg, RegExpStatics::offsetOfLazyIndex())); |
1845 | masm.store32( |
1846 | Imm32(1), |
1847 | Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation())); |
1848 | |
1849 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset( |
1850 | RegExpObject::SHARED_SLOT)), |
1851 | temp1, JSVAL_TYPE_PRIVATE_GCTHING); |
1852 | masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2); |
1853 | masm.storePtr(temp2, lazySourceAddress); |
1854 | static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size"); |
1855 | masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2); |
1856 | masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags())); |
1857 | } |
1858 | |
1859 | // Prepare an InputOutputData and optional MatchPairs which space has been |
1860 | // allocated for on the stack, and try to execute a RegExp on a string input. |
1861 | // If the RegExp was successfully executed and matched the input, fallthrough. |
1862 | // Otherwise, jump to notFound or failure. |
1863 | // |
1864 | // inputOutputDataStartOffset is the offset relative to the frame pointer |
1865 | // register. This offset is negative for the RegExpExecTest stub. |
1866 | static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp, |
1867 | Register input, Register lastIndex, |
1868 | Register temp1, Register temp2, |
1869 | Register temp3, |
1870 | int32_t inputOutputDataStartOffset, |
1871 | gc::Heap initialStringHeap, Label* notFound, |
1872 | Label* failure) { |
1873 | JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp"); |
1874 | |
1875 | using irregexp::InputOutputData; |
1876 | |
1877 | /* |
1878 | * [SMDOC] Stack layout for PrepareAndExecuteRegExp |
1879 | * |
1880 | * Before this function is called, the caller is responsible for |
1881 | * allocating enough stack space for the following data: |
1882 | * |
1883 | * inputOutputDataStartOffset +-----> +---------------+ |
1884 | * |InputOutputData| |
1885 | * inputStartAddress +----------> inputStart| |
1886 | * inputEndAddress +----------> inputEnd| |
1887 | * startIndexAddress +----------> startIndex| |
1888 | * matchesAddress +----------> matches|-----+ |
1889 | * +---------------+ | |
1890 | * matchPairs(Address|Offset) +-----> +---------------+ <--+ |
1891 | * | MatchPairs | |
1892 | * pairCountAddress +----------> count | |
1893 | * pairsPointerAddress +----------> pairs |-----+ |
1894 | * +---------------+ | |
1895 | * pairsArray(Address|Offset) +-----> +---------------+ <--+ |
1896 | * | MatchPair | |
1897 | * firstMatchStartAddress +----------> start | <--+ |
1898 | * | limit | | |
1899 | * +---------------+ | |
1900 | * . | |
1901 | * . Reserved space for |
1902 | * . RegExpObject::MaxPairCount |
1903 | * . MatchPair objects |
1904 | * . | |
1905 | * +---------------+ | |
1906 | * | MatchPair | | |
1907 | * | start | | |
1908 | * | limit | <--+ |
1909 | * +---------------+ |
1910 | */ |
1911 | |
1912 | int32_t ioOffset = inputOutputDataStartOffset; |
1913 | int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData)); |
1914 | int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs)); |
1915 | |
1916 | Address inputStartAddress(FramePointer, |
1917 | ioOffset + InputOutputData::offsetOfInputStart()); |
1918 | Address inputEndAddress(FramePointer, |
1919 | ioOffset + InputOutputData::offsetOfInputEnd()); |
1920 | Address startIndexAddress(FramePointer, |
1921 | ioOffset + InputOutputData::offsetOfStartIndex()); |
1922 | Address matchesAddress(FramePointer, |
1923 | ioOffset + InputOutputData::offsetOfMatches()); |
1924 | |
1925 | Address matchPairsAddress(FramePointer, matchPairsOffset); |
1926 | Address pairCountAddress(FramePointer, |
1927 | matchPairsOffset + MatchPairs::offsetOfPairCount()); |
1928 | Address pairsPointerAddress(FramePointer, |
1929 | matchPairsOffset + MatchPairs::offsetOfPairs()); |
1930 | |
1931 | Address pairsArrayAddress(FramePointer, pairsArrayOffset); |
1932 | Address firstMatchStartAddress(FramePointer, |
1933 | pairsArrayOffset + MatchPair::offsetOfStart()); |
1934 | |
1935 | // First, fill in a skeletal MatchPairs instance on the stack. This will be |
1936 | // passed to the OOL stub in the caller if we aren't able to execute the |
1937 | // RegExp inline, and that stub needs to be able to determine whether the |
1938 | // execution finished successfully. |
1939 | |
1940 | // Initialize MatchPairs::pairCount to 1. The correct value can only |
1941 | // be determined after loading the RegExpShared. If the RegExpShared |
1942 | // has Kind::Atom, this is the correct pairCount. |
1943 | masm.store32(Imm32(1), pairCountAddress); |
1944 | |
1945 | // Initialize MatchPairs::pairs pointer |
1946 | masm.computeEffectiveAddress(pairsArrayAddress, temp1); |
1947 | masm.storePtr(temp1, pairsPointerAddress); |
1948 | |
1949 | // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch |
1950 | masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress); |
1951 | |
1952 | // Determine the set of volatile inputs to save when calling into C++ or |
1953 | // regexp code. |
1954 | LiveGeneralRegisterSet volatileRegs; |
1955 | if (lastIndex.volatile_()) { |
1956 | volatileRegs.add(lastIndex); |
1957 | } |
1958 | if (input.volatile_()) { |
1959 | volatileRegs.add(input); |
1960 | } |
1961 | if (regexp.volatile_()) { |
1962 | volatileRegs.add(regexp); |
1963 | } |
1964 | |
1965 | // Ensure the input string is not a rope. |
1966 | Label isLinear; |
1967 | masm.branchIfNotRope(input, &isLinear); |
1968 | { |
1969 | masm.PushRegsInMask(volatileRegs); |
1970 | |
1971 | using Fn = JSLinearString* (*)(JSString*); |
1972 | masm.setupUnalignedABICall(temp1); |
1973 | masm.passABIArg(input); |
1974 | masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>(); |
1975 | |
1976 | MOZ_ASSERT(!volatileRegs.has(temp1))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!volatileRegs.has(temp1))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!volatileRegs.has(temp1)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!volatileRegs.has(temp1)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { *((volatile int*)__null) = 1976; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
1977 | masm.storeCallPointerResult(temp1); |
1978 | masm.PopRegsInMask(volatileRegs); |
1979 | |
1980 | masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure); |
1981 | } |
1982 | masm.bind(&isLinear); |
1983 | |
1984 | // Load the RegExpShared. |
1985 | Register regexpReg = temp1; |
1986 | Address sharedSlot = Address( |
1987 | regexp, NativeObject::getFixedSlotOffset(RegExpObject::SHARED_SLOT)); |
1988 | masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure); |
1989 | masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING); |
1990 | |
1991 | // Handle Atom matches |
1992 | Label notAtom, checkSuccess; |
1993 | masm.branchPtr(Assembler::Equal, |
1994 | Address(regexpReg, RegExpShared::offsetOfPatternAtom()), |
1995 | ImmWord(0), ¬Atom); |
1996 | { |
1997 | masm.computeEffectiveAddress(matchPairsAddress, temp3); |
1998 | |
1999 | masm.PushRegsInMask(volatileRegs); |
2000 | using Fn = |
2001 | RegExpRunStatus (*)(RegExpShared* re, const JSLinearString* input, |
2002 | size_t start, MatchPairs* matchPairs); |
2003 | masm.setupUnalignedABICall(temp2); |
2004 | masm.passABIArg(regexpReg); |
2005 | masm.passABIArg(input); |
2006 | masm.passABIArg(lastIndex); |
2007 | masm.passABIArg(temp3); |
2008 | masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>(); |
2009 | |
2010 | MOZ_ASSERT(!volatileRegs.has(temp1))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!volatileRegs.has(temp1))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!volatileRegs.has(temp1)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!volatileRegs.has(temp1)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2010); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { *((volatile int*)__null) = 2010; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2011 | masm.storeCallInt32Result(temp1); |
2012 | masm.PopRegsInMask(volatileRegs); |
2013 | |
2014 | masm.jump(&checkSuccess); |
2015 | } |
2016 | masm.bind(¬Atom); |
2017 | |
2018 | // Don't handle regexps with too many capture pairs. |
2019 | masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2); |
2020 | masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount), |
2021 | failure); |
2022 | |
2023 | // Fill in the pair count in the MatchPairs on the stack. |
2024 | masm.store32(temp2, pairCountAddress); |
2025 | |
2026 | // Load code pointer and length of input (in bytes). |
2027 | // Store the input start in the InputOutputData. |
2028 | Register codePointer = temp1; // Note: temp1 was previously regexpReg. |
2029 | Register byteLength = temp3; |
2030 | { |
2031 | Label isLatin1, done; |
2032 | masm.loadStringLength(input, byteLength); |
2033 | |
2034 | masm.branchLatin1String(input, &isLatin1); |
2035 | |
2036 | // Two-byte input |
2037 | masm.loadStringChars(input, temp2, CharEncoding::TwoByte); |
2038 | masm.storePtr(temp2, inputStartAddress); |
2039 | masm.loadPtr( |
2040 | Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)), |
2041 | codePointer); |
2042 | masm.lshiftPtr(Imm32(1), byteLength); |
2043 | masm.jump(&done); |
2044 | |
2045 | // Latin1 input |
2046 | masm.bind(&isLatin1); |
2047 | masm.loadStringChars(input, temp2, CharEncoding::Latin1); |
2048 | masm.storePtr(temp2, inputStartAddress); |
2049 | masm.loadPtr( |
2050 | Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)), |
2051 | codePointer); |
2052 | |
2053 | masm.bind(&done); |
2054 | |
2055 | // Store end pointer |
2056 | masm.addPtr(byteLength, temp2); |
2057 | masm.storePtr(temp2, inputEndAddress); |
2058 | } |
2059 | |
2060 | // Guard that the RegExpShared has been compiled for this type of input. |
2061 | // If it has not been compiled, we fall back to the OOL case, which will |
2062 | // do a VM call into the interpreter. |
2063 | // TODO: add an interpreter trampoline? |
2064 | masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure); |
2065 | masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer); |
2066 | |
2067 | // Finish filling in the InputOutputData instance on the stack |
2068 | masm.computeEffectiveAddress(matchPairsAddress, temp2); |
2069 | masm.storePtr(temp2, matchesAddress); |
2070 | masm.storePtr(lastIndex, startIndexAddress); |
2071 | |
2072 | // Execute the RegExp. |
2073 | masm.computeEffectiveAddress( |
2074 | Address(FramePointer, inputOutputDataStartOffset), temp2); |
2075 | masm.PushRegsInMask(volatileRegs); |
2076 | masm.setupUnalignedABICall(temp3); |
2077 | masm.passABIArg(temp2); |
2078 | masm.callWithABI(codePointer); |
2079 | masm.storeCallInt32Result(temp1); |
2080 | masm.PopRegsInMask(volatileRegs); |
2081 | |
2082 | masm.bind(&checkSuccess); |
2083 | masm.branch32(Assembler::Equal, temp1, |
2084 | Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound); |
2085 | masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)), |
2086 | failure); |
2087 | |
2088 | // Lazily update the RegExpStatics. |
2089 | size_t offset = GlobalObjectData::offsetOfRegExpRealm() + |
2090 | RegExpRealm::offsetOfRegExpStatics(); |
2091 | masm.loadGlobalObjectData(temp1); |
2092 | masm.loadPtr(Address(temp1, offset), temp1); |
2093 | UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3, |
2094 | initialStringHeap, volatileRegs); |
2095 | |
2096 | return true; |
2097 | } |
2098 | |
2099 | static void EmitInitDependentStringBase(MacroAssembler& masm, |
2100 | Register dependent, Register base, |
2101 | Register temp1, Register temp2, |
2102 | bool needsPostBarrier) { |
2103 | // Determine the base string to use and store it in temp2. |
2104 | Label notDependent, markedDependedOn; |
2105 | masm.load32(Address(base, JSString::offsetOfFlags()), temp1); |
2106 | masm.branchTest32(Assembler::Zero, temp1, Imm32(JSString::DEPENDENT_BIT), |
2107 | ¬Dependent); |
2108 | { |
2109 | // The base is also a dependent string. Load its base to prevent chains of |
2110 | // dependent strings in most cases. This must either be an atom or already |
2111 | // have the DEPENDED_ON_BIT set. |
2112 | masm.loadDependentStringBase(base, temp2); |
2113 | masm.jump(&markedDependedOn); |
2114 | } |
2115 | masm.bind(¬Dependent); |
2116 | { |
2117 | // The base is not a dependent string. Set the DEPENDED_ON_BIT if it's not |
2118 | // an atom. |
2119 | masm.movePtr(base, temp2); |
2120 | masm.branchTest32(Assembler::NonZero, temp1, Imm32(JSString::ATOM_BIT), |
2121 | &markedDependedOn); |
2122 | masm.or32(Imm32(JSString::DEPENDED_ON_BIT), temp1); |
2123 | masm.store32(temp1, Address(temp2, JSString::offsetOfFlags())); |
2124 | } |
2125 | masm.bind(&markedDependedOn); |
2126 | |
2127 | #ifdef DEBUG1 |
2128 | // Assert the base has the DEPENDED_ON_BIT set or is an atom. |
2129 | Label isAppropriatelyMarked; |
2130 | masm.branchTest32(Assembler::NonZero, |
2131 | Address(temp2, JSString::offsetOfFlags()), |
2132 | Imm32(JSString::ATOM_BIT | JSString::DEPENDED_ON_BIT), |
2133 | &isAppropriatelyMarked); |
2134 | masm.assumeUnreachable("Base string is missing DEPENDED_ON_BIT"); |
2135 | masm.bind(&isAppropriatelyMarked); |
2136 | #endif |
2137 | masm.storeDependentStringBase(temp2, dependent); |
2138 | |
2139 | // Post-barrier the base store. The base is still in temp2. |
2140 | if (needsPostBarrier) { |
2141 | Label done; |
2142 | masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done); |
2143 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done); |
2144 | |
2145 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
2146 | regsToSave.takeUnchecked(temp1); |
2147 | regsToSave.takeUnchecked(temp2); |
2148 | |
2149 | masm.PushRegsInMask(regsToSave); |
2150 | |
2151 | masm.mov(ImmPtr(masm.runtime()), temp1); |
2152 | |
2153 | using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell); |
2154 | masm.setupUnalignedABICall(temp2); |
2155 | masm.passABIArg(temp1); |
2156 | masm.passABIArg(dependent); |
2157 | masm.callWithABI<Fn, PostWriteBarrier>(); |
2158 | |
2159 | masm.PopRegsInMask(regsToSave); |
2160 | |
2161 | masm.bind(&done); |
2162 | } else { |
2163 | #ifdef DEBUG1 |
2164 | Label done; |
2165 | masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done); |
2166 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done); |
2167 | masm.assumeUnreachable("Missing post barrier for dependent string base"); |
2168 | masm.bind(&done); |
2169 | #endif |
2170 | } |
2171 | } |
2172 | |
2173 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
2174 | Register len, Register byteOpScratch, |
2175 | CharEncoding encoding, |
2176 | size_t maximumLength = SIZE_MAX(18446744073709551615UL)); |
2177 | |
2178 | class CreateDependentString { |
2179 | CharEncoding encoding_; |
2180 | Register string_; |
2181 | Register temp1_; |
2182 | Register temp2_; |
2183 | Label* failure_; |
2184 | |
2185 | enum class FallbackKind : uint8_t { |
2186 | InlineString, |
2187 | FatInlineString, |
2188 | NotInlineString, |
2189 | Count |
2190 | }; |
2191 | mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)> |
2192 | fallbacks_, joins_; |
2193 | |
2194 | public: |
2195 | CreateDependentString(CharEncoding encoding, Register string, Register temp1, |
2196 | Register temp2, Label* failure) |
2197 | : encoding_(encoding), |
2198 | string_(string), |
2199 | temp1_(temp1), |
2200 | temp2_(temp2), |
2201 | failure_(failure) {} |
2202 | |
2203 | Register string() const { return string_; } |
2204 | CharEncoding encoding() const { return encoding_; } |
2205 | |
2206 | // Generate code that creates DependentString. |
2207 | // Caller should call generateFallback after masm.ret(), to generate |
2208 | // fallback path. |
2209 | void generate(MacroAssembler& masm, const JSAtomState& names, |
2210 | CompileRuntime* runtime, Register base, |
2211 | BaseIndex startIndexAddress, BaseIndex limitIndexAddress, |
2212 | gc::Heap initialStringHeap); |
2213 | |
2214 | // Generate fallback path for creating DependentString. |
2215 | void generateFallback(MacroAssembler& masm); |
2216 | }; |
2217 | |
2218 | void CreateDependentString::generate(MacroAssembler& masm, |
2219 | const JSAtomState& names, |
2220 | CompileRuntime* runtime, Register base, |
2221 | BaseIndex startIndexAddress, |
2222 | BaseIndex limitIndexAddress, |
2223 | gc::Heap initialStringHeap) { |
2224 | JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)", |
2225 | (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
2226 | |
2227 | auto newGCString = [&](FallbackKind kind) { |
2228 | uint32_t flags = kind == FallbackKind::InlineString |
2229 | ? JSString::INIT_THIN_INLINE_FLAGS |
2230 | : kind == FallbackKind::FatInlineString |
2231 | ? JSString::INIT_FAT_INLINE_FLAGS |
2232 | : JSString::INIT_DEPENDENT_FLAGS; |
2233 | if (encoding_ == CharEncoding::Latin1) { |
2234 | flags |= JSString::LATIN1_CHARS_BIT; |
2235 | } |
2236 | |
2237 | if (kind != FallbackKind::FatInlineString) { |
2238 | masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]); |
2239 | } else { |
2240 | masm.newGCFatInlineString(string_, temp2_, initialStringHeap, |
2241 | &fallbacks_[kind]); |
2242 | } |
2243 | masm.bind(&joins_[kind]); |
2244 | masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags())); |
2245 | }; |
2246 | |
2247 | // Compute the string length. |
2248 | masm.load32(startIndexAddress, temp2_); |
2249 | masm.load32(limitIndexAddress, temp1_); |
2250 | masm.sub32(temp2_, temp1_); |
2251 | |
2252 | Label done, nonEmpty; |
2253 | |
2254 | // Zero length matches use the empty string. |
2255 | masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty); |
2256 | masm.movePtr(ImmGCPtr(names.empty_), string_); |
2257 | masm.jump(&done); |
2258 | |
2259 | masm.bind(&nonEmpty); |
2260 | |
2261 | // Complete matches use the base string. |
2262 | Label nonBaseStringMatch; |
2263 | masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch); |
2264 | masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()), |
2265 | temp1_, &nonBaseStringMatch); |
2266 | masm.movePtr(base, string_); |
2267 | masm.jump(&done); |
2268 | |
2269 | masm.bind(&nonBaseStringMatch); |
2270 | |
2271 | Label notInline; |
2272 | |
2273 | int32_t maxInlineLength = encoding_ == CharEncoding::Latin1 |
2274 | ? JSFatInlineString::MAX_LENGTH_LATIN1 |
2275 | : JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
2276 | masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), ¬Inline); |
2277 | { |
2278 | // Make a thin or fat inline string. |
2279 | Label stringAllocated, fatInline; |
2280 | |
2281 | int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1 |
2282 | ? JSThinInlineString::MAX_LENGTH_LATIN1 |
2283 | : JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
2284 | masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength), |
2285 | &fatInline); |
2286 | if (encoding_ == CharEncoding::Latin1) { |
2287 | // One character Latin-1 strings can be loaded directly from the |
2288 | // static strings table. |
2289 | Label thinInline; |
2290 | masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline); |
2291 | { |
2292 | static_assert( |
2293 | StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR, |
2294 | "Latin-1 strings can be loaded from static strings"); |
2295 | |
2296 | masm.loadStringChars(base, temp1_, encoding_); |
2297 | masm.loadChar(temp1_, temp2_, temp1_, encoding_); |
2298 | |
2299 | masm.lookupStaticString(temp1_, string_, runtime->staticStrings()); |
2300 | |
2301 | masm.jump(&done); |
2302 | } |
2303 | masm.bind(&thinInline); |
2304 | } |
2305 | { |
2306 | newGCString(FallbackKind::InlineString); |
2307 | masm.jump(&stringAllocated); |
2308 | } |
2309 | masm.bind(&fatInline); |
2310 | { newGCString(FallbackKind::FatInlineString); } |
2311 | masm.bind(&stringAllocated); |
2312 | |
2313 | masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); |
2314 | |
2315 | masm.push(string_); |
2316 | masm.push(base); |
2317 | |
2318 | MOZ_ASSERT(startIndexAddress.base == FramePointer,do { static_assert( mozilla::detail::AssertionConditionType< decltype(startIndexAddress.base == FramePointer)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(startIndexAddress.base == FramePointer))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("startIndexAddress.base == FramePointer" " (" "startIndexAddress is still valid after stack pushes" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2319); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") (" "startIndexAddress is still valid after stack pushes" ")" ); do { *((volatile int*)__null) = 2319; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
2319 | "startIndexAddress is still valid after stack pushes")do { static_assert( mozilla::detail::AssertionConditionType< decltype(startIndexAddress.base == FramePointer)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(startIndexAddress.base == FramePointer))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("startIndexAddress.base == FramePointer" " (" "startIndexAddress is still valid after stack pushes" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2319); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") (" "startIndexAddress is still valid after stack pushes" ")" ); do { *((volatile int*)__null) = 2319; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2320 | |
2321 | // Load chars pointer for the new string. |
2322 | masm.loadInlineStringCharsForStore(string_, string_); |
2323 | |
2324 | // Load the source characters pointer. |
2325 | masm.loadStringChars(base, temp2_, encoding_); |
2326 | masm.load32(startIndexAddress, base); |
2327 | masm.addToCharPtr(temp2_, base, encoding_); |
2328 | |
2329 | CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_); |
2330 | |
2331 | masm.pop(base); |
2332 | masm.pop(string_); |
2333 | |
2334 | masm.jump(&done); |
2335 | } |
2336 | |
2337 | masm.bind(¬Inline); |
2338 | |
2339 | { |
2340 | // Make a dependent string. |
2341 | // Warning: string may be tenured (if the fallback case is hit), so |
2342 | // stores into it must be post barriered. |
2343 | newGCString(FallbackKind::NotInlineString); |
2344 | |
2345 | masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); |
2346 | |
2347 | masm.loadNonInlineStringChars(base, temp1_, encoding_); |
2348 | masm.load32(startIndexAddress, temp2_); |
2349 | masm.addToCharPtr(temp1_, temp2_, encoding_); |
2350 | masm.storeNonInlineStringChars(temp1_, string_); |
2351 | |
2352 | EmitInitDependentStringBase(masm, string_, base, temp1_, temp2_, |
2353 | /* needsPostBarrier = */ true); |
2354 | } |
2355 | |
2356 | masm.bind(&done); |
2357 | } |
2358 | |
2359 | void CreateDependentString::generateFallback(MacroAssembler& masm) { |
2360 | JitSpew(JitSpew_Codegen, |
2361 | "# Emitting CreateDependentString fallback (encoding=%s)", |
2362 | (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
2363 | |
2364 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
2365 | regsToSave.takeUnchecked(string_); |
2366 | regsToSave.takeUnchecked(temp2_); |
2367 | |
2368 | for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) { |
2369 | masm.bind(&fallbacks_[kind]); |
2370 | |
2371 | masm.PushRegsInMask(regsToSave); |
2372 | |
2373 | using Fn = void* (*)(JSContext* cx); |
2374 | masm.setupUnalignedABICall(string_); |
2375 | masm.loadJSContext(string_); |
2376 | masm.passABIArg(string_); |
2377 | if (kind == FallbackKind::FatInlineString) { |
2378 | masm.callWithABI<Fn, AllocateFatInlineString>(); |
2379 | } else { |
2380 | masm.callWithABI<Fn, AllocateDependentString>(); |
2381 | } |
2382 | masm.storeCallPointerResult(string_); |
2383 | |
2384 | masm.PopRegsInMask(regsToSave); |
2385 | |
2386 | masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_); |
2387 | |
2388 | masm.jump(&joins_[kind]); |
2389 | } |
2390 | } |
2391 | |
2392 | // Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar, |
2393 | // but RegExpExecMatch also has to load and update .lastIndex for global/sticky |
2394 | // regular expressions. |
2395 | static JitCode* GenerateRegExpMatchStubShared(JSContext* cx, |
2396 | gc::Heap initialStringHeap, |
2397 | bool isExecMatch) { |
2398 | if (isExecMatch) { |
2399 | JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub"); |
2400 | } else { |
2401 | JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub"); |
2402 | } |
2403 | |
2404 | // |initialStringHeap| could be stale after a GC. |
2405 | JS::AutoCheckCannotGC nogc(cx); |
2406 | |
2407 | Register regexp = RegExpMatcherRegExpReg; |
2408 | Register input = RegExpMatcherStringReg; |
2409 | Register lastIndex = RegExpMatcherLastIndexReg; |
2410 | ValueOperand result = JSReturnOperand; |
2411 | |
2412 | // We are free to clobber all registers, as LRegExpMatcher is a call |
2413 | // instruction. |
2414 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
2415 | regs.take(input); |
2416 | regs.take(regexp); |
2417 | regs.take(lastIndex); |
2418 | |
2419 | Register temp1 = regs.takeAny(); |
2420 | Register temp2 = regs.takeAny(); |
2421 | Register temp3 = regs.takeAny(); |
2422 | Register maybeTemp4 = InvalidReg; |
2423 | if (!regs.empty()) { |
2424 | // There are not enough registers on x86. |
2425 | maybeTemp4 = regs.takeAny(); |
2426 | } |
2427 | Register maybeTemp5 = InvalidReg; |
2428 | if (!regs.empty()) { |
2429 | // There are not enough registers on x86. |
2430 | maybeTemp5 = regs.takeAny(); |
2431 | } |
2432 | |
2433 | Address flagsSlot(regexp, RegExpObject::offsetOfFlags()); |
2434 | Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex()); |
2435 | |
2436 | TempAllocator temp(&cx->tempLifoAlloc()); |
2437 | JitContext jcx(cx); |
2438 | StackMacroAssembler masm(cx, temp); |
2439 | AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared"); |
2440 | |
2441 | #ifdef JS_USE_LINK_REGISTER |
2442 | masm.pushReturnAddress(); |
2443 | #endif |
2444 | masm.push(FramePointer); |
2445 | masm.moveStackPtrTo(FramePointer); |
2446 | |
2447 | Label notFoundZeroLastIndex; |
2448 | if (isExecMatch) { |
2449 | masm.loadRegExpLastIndex(regexp, input, lastIndex, ¬FoundZeroLastIndex); |
2450 | } |
2451 | |
2452 | // The InputOutputData is placed above the frame pointer and return address on |
2453 | // the stack. |
2454 | int32_t inputOutputDataStartOffset = 2 * sizeof(void*); |
2455 | |
2456 | Label notFound, oolEntry; |
2457 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
2458 | temp3, inputOutputDataStartOffset, |
2459 | initialStringHeap, ¬Found, &oolEntry)) { |
2460 | return nullptr; |
2461 | } |
2462 | |
2463 | // If a regexp has named captures, fall back to the OOL stub, which |
2464 | // will end up calling CreateRegExpMatchResults. |
2465 | Register shared = temp2; |
2466 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset( |
2467 | RegExpObject::SHARED_SLOT)), |
2468 | shared, JSVAL_TYPE_PRIVATE_GCTHING); |
2469 | masm.branchPtr(Assembler::NotEqual, |
2470 | Address(shared, RegExpShared::offsetOfGroupsTemplate()), |
2471 | ImmWord(0), &oolEntry); |
2472 | |
2473 | // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub. |
2474 | masm.branchTest32(Assembler::NonZero, |
2475 | Address(shared, RegExpShared::offsetOfFlags()), |
2476 | Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry); |
2477 | |
2478 | Address pairCountAddress = |
2479 | RegExpPairCountAddress(masm, inputOutputDataStartOffset); |
2480 | |
2481 | // Construct the result. |
2482 | Register object = temp1; |
2483 | { |
2484 | // In most cases, the array will have just 1-2 elements, so we optimize for |
2485 | // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots |
2486 | // because two slots are used for the elements header). |
2487 | |
2488 | // Load the array length in temp2 and the shape in temp3. |
2489 | Label allocated; |
2490 | masm.load32(pairCountAddress, temp2); |
2491 | size_t offset = GlobalObjectData::offsetOfRegExpRealm() + |
2492 | RegExpRealm::offsetOfNormalMatchResultShape(); |
2493 | masm.loadGlobalObjectData(temp3); |
2494 | masm.loadPtr(Address(temp3, offset), temp3); |
2495 | |
2496 | auto emitAllocObject = [&](size_t elementCapacity) { |
2497 | gc::AllocKind kind = GuessArrayGCKind(elementCapacity); |
2498 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(kind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(kind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2498); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 2498; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2499 | kind = ForegroundToBackgroundAllocKind(kind); |
2500 | |
2501 | #ifdef DEBUG1 |
2502 | // Assert all of the available slots are used for |elementCapacity| |
2503 | // elements. |
2504 | size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity; |
2505 | MOZ_ASSERT(usedSlots == GetGCKindSlots(kind))do { static_assert( mozilla::detail::AssertionConditionType< decltype(usedSlots == GetGCKindSlots(kind))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(usedSlots == GetGCKindSlots( kind)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("usedSlots == GetGCKindSlots(kind)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)" ")"); do { *((volatile int*)__null) = 2505; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2506 | #endif |
2507 | |
2508 | constexpr size_t numUsedDynamicSlots = |
2509 | RegExpRealm::MatchResultObjectSlotSpan; |
2510 | constexpr size_t numDynamicSlots = |
2511 | RegExpRealm::MatchResultObjectNumDynamicSlots; |
2512 | constexpr size_t arrayLength = 1; |
2513 | masm.createArrayWithFixedElements(object, temp3, temp2, temp3, |
2514 | arrayLength, elementCapacity, |
2515 | numUsedDynamicSlots, numDynamicSlots, |
2516 | kind, gc::Heap::Default, &oolEntry); |
2517 | }; |
2518 | |
2519 | Label moreThan2; |
2520 | masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2); |
2521 | emitAllocObject(2); |
2522 | masm.jump(&allocated); |
2523 | |
2524 | Label moreThan6; |
2525 | masm.bind(&moreThan2); |
2526 | masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6); |
2527 | emitAllocObject(6); |
2528 | masm.jump(&allocated); |
2529 | |
2530 | masm.bind(&moreThan6); |
2531 | static_assert(RegExpObject::MaxPairCount == 14); |
2532 | emitAllocObject(RegExpObject::MaxPairCount); |
2533 | |
2534 | masm.bind(&allocated); |
2535 | } |
2536 | |
2537 | // clang-format off |
2538 | /* |
2539 | * [SMDOC] Stack layout for the RegExpMatcher stub |
2540 | * |
2541 | * +---------------+ |
2542 | * FramePointer +-----> |Caller-FramePtr| |
2543 | * +---------------+ |
2544 | * |Return-Address | |
2545 | * +---------------+ |
2546 | * inputOutputDataStartOffset +-----> +---------------+ |
2547 | * |InputOutputData| |
2548 | * +---------------+ |
2549 | * +---------------+ |
2550 | * | MatchPairs | |
2551 | * pairsCountAddress +-----------> count | |
2552 | * | pairs | |
2553 | * | | |
2554 | * +---------------+ |
2555 | * pairsVectorStartOffset +-----> +---------------+ |
2556 | * | MatchPair | |
2557 | * matchPairStart +------------> start | <-------+ |
2558 | * matchPairLimit +------------> limit | | Reserved space for |
2559 | * +---------------+ | `RegExpObject::MaxPairCount` |
2560 | * . | MatchPair objects. |
2561 | * . | |
2562 | * . | `count` objects will be |
2563 | * +---------------+ | initialized and can be |
2564 | * | MatchPair | | accessed below. |
2565 | * | start | <-------+ |
2566 | * | limit | |
2567 | * +---------------+ |
2568 | */ |
2569 | // clang-format on |
2570 | |
2571 | static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t), |
2572 | "MatchPair consists of two int32 values representing the start" |
2573 | "and the end offset of the match"); |
2574 | |
2575 | int32_t pairsVectorStartOffset = |
2576 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
2577 | |
2578 | // Incremented by one below for each match pair. |
2579 | Register matchIndex = temp2; |
2580 | masm.move32(Imm32(0), matchIndex); |
2581 | |
2582 | // The element in which to store the result of the current match. |
2583 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
2584 | BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset); |
2585 | |
2586 | // The current match pair's "start" and "limit" member. |
2587 | BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight, |
2588 | pairsVectorStartOffset + MatchPair::offsetOfStart()); |
2589 | BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight, |
2590 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
2591 | |
2592 | Label* depStrFailure = &oolEntry; |
2593 | Label restoreRegExpAndLastIndex; |
2594 | |
2595 | Register temp4; |
2596 | if (maybeTemp4 == InvalidReg) { |
2597 | depStrFailure = &restoreRegExpAndLastIndex; |
2598 | |
2599 | // We don't have enough registers for a fourth temporary. Reuse |regexp| |
2600 | // as a temporary. We restore its value at |restoreRegExpAndLastIndex|. |
2601 | masm.push(regexp); |
2602 | temp4 = regexp; |
2603 | } else { |
2604 | temp4 = maybeTemp4; |
2605 | } |
2606 | |
2607 | Register temp5; |
2608 | if (maybeTemp5 == InvalidReg) { |
2609 | depStrFailure = &restoreRegExpAndLastIndex; |
2610 | |
2611 | // We don't have enough registers for a fifth temporary. Reuse |lastIndex| |
2612 | // as a temporary. We restore its value at |restoreRegExpAndLastIndex|. |
2613 | masm.push(lastIndex); |
2614 | temp5 = lastIndex; |
2615 | } else { |
2616 | temp5 = maybeTemp5; |
2617 | } |
2618 | |
2619 | auto maybeRestoreRegExpAndLastIndex = [&]() { |
2620 | if (maybeTemp5 == InvalidReg) { |
2621 | masm.pop(lastIndex); |
2622 | } |
2623 | if (maybeTemp4 == InvalidReg) { |
2624 | masm.pop(regexp); |
2625 | } |
2626 | }; |
2627 | |
2628 | // Loop to construct the match strings. There are two different loops, |
2629 | // depending on whether the input is a Two-Byte or a Latin-1 string. |
2630 | CreateDependentString depStrs[]{ |
2631 | {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure}, |
2632 | {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure}, |
2633 | }; |
2634 | |
2635 | { |
2636 | Label isLatin1, done; |
2637 | masm.branchLatin1String(input, &isLatin1); |
2638 | |
2639 | for (auto& depStr : depStrs) { |
2640 | if (depStr.encoding() == CharEncoding::Latin1) { |
2641 | masm.bind(&isLatin1); |
2642 | } |
2643 | |
2644 | Label matchLoop; |
2645 | masm.bind(&matchLoop); |
2646 | |
2647 | static_assert(MatchPair::NoMatch == -1, |
2648 | "MatchPair::start is negative if no match was found"); |
2649 | |
2650 | Label isUndefined, storeDone; |
2651 | masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0), |
2652 | &isUndefined); |
2653 | { |
2654 | depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()), |
2655 | input, matchPairStart, matchPairLimit, |
2656 | initialStringHeap); |
2657 | |
2658 | // Storing into nursery-allocated results object's elements; no post |
2659 | // barrier. |
2660 | masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement); |
2661 | masm.jump(&storeDone); |
2662 | } |
2663 | masm.bind(&isUndefined); |
2664 | { masm.storeValue(UndefinedValue(), objectMatchElement); } |
2665 | masm.bind(&storeDone); |
2666 | |
2667 | masm.add32(Imm32(1), matchIndex); |
2668 | masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, |
2669 | &done); |
2670 | masm.jump(&matchLoop); |
2671 | } |
2672 | |
2673 | #ifdef DEBUG1 |
2674 | masm.assumeUnreachable("The match string loop doesn't fall through."); |
2675 | #endif |
2676 | |
2677 | masm.bind(&done); |
2678 | } |
2679 | |
2680 | maybeRestoreRegExpAndLastIndex(); |
2681 | |
2682 | // Fill in the rest of the output object. |
2683 | masm.store32( |
2684 | matchIndex, |
2685 | Address(object, |
2686 | elementsOffset + ObjectElements::offsetOfInitializedLength())); |
2687 | masm.store32( |
2688 | matchIndex, |
2689 | Address(object, elementsOffset + ObjectElements::offsetOfLength())); |
2690 | |
2691 | Address firstMatchPairStartAddress( |
2692 | FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart()); |
2693 | Address firstMatchPairLimitAddress( |
2694 | FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
2695 | |
2696 | static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0, |
2697 | "First slot holds the 'index' property"); |
2698 | static_assert(RegExpRealm::MatchResultObjectInputSlot == 1, |
2699 | "Second slot holds the 'input' property"); |
2700 | |
2701 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
2702 | |
2703 | masm.load32(firstMatchPairStartAddress, temp3); |
2704 | masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0)); |
2705 | |
2706 | // No post barrier needed (address is within nursery object.) |
2707 | masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value))); |
2708 | |
2709 | // For the ExecMatch stub, if the regular expression is global or sticky, we |
2710 | // have to update its .lastIndex slot. |
2711 | if (isExecMatch) { |
2712 | MOZ_ASSERT(object != lastIndex)do { static_assert( mozilla::detail::AssertionConditionType< decltype(object != lastIndex)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(object != lastIndex))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("object != lastIndex" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2712); AnnotateMozCrashReason("MOZ_ASSERT" "(" "object != lastIndex" ")"); do { *((volatile int*)__null) = 2712; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2713 | Label notGlobalOrSticky; |
2714 | masm.branchTest32(Assembler::Zero, flagsSlot, |
2715 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
2716 | ¬GlobalOrSticky); |
2717 | masm.load32(firstMatchPairLimitAddress, lastIndex); |
2718 | masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot); |
2719 | masm.bind(¬GlobalOrSticky); |
2720 | } |
2721 | |
2722 | // All done! |
2723 | masm.tagValue(JSVAL_TYPE_OBJECT, object, result); |
2724 | masm.pop(FramePointer); |
2725 | masm.ret(); |
2726 | |
2727 | masm.bind(¬Found); |
2728 | if (isExecMatch) { |
2729 | Label notGlobalOrSticky; |
2730 | masm.branchTest32(Assembler::Zero, flagsSlot, |
2731 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
2732 | ¬GlobalOrSticky); |
2733 | masm.bind(¬FoundZeroLastIndex); |
2734 | masm.storeValue(Int32Value(0), lastIndexSlot); |
2735 | masm.bind(¬GlobalOrSticky); |
2736 | } |
2737 | masm.moveValue(NullValue(), result); |
2738 | masm.pop(FramePointer); |
2739 | masm.ret(); |
2740 | |
2741 | // Fallback paths for CreateDependentString. |
2742 | for (auto& depStr : depStrs) { |
2743 | depStr.generateFallback(masm); |
2744 | } |
2745 | |
2746 | // Fall-through to the ool entry after restoring the registers. |
2747 | masm.bind(&restoreRegExpAndLastIndex); |
2748 | maybeRestoreRegExpAndLastIndex(); |
2749 | |
2750 | // Use an undefined value to signal to the caller that the OOL stub needs to |
2751 | // be called. |
2752 | masm.bind(&oolEntry); |
2753 | masm.moveValue(UndefinedValue(), result); |
2754 | masm.pop(FramePointer); |
2755 | masm.ret(); |
2756 | |
2757 | Linker linker(masm); |
2758 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
2759 | if (!code) { |
2760 | return nullptr; |
2761 | } |
2762 | |
2763 | const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub"; |
2764 | CollectPerfSpewerJitCodeProfile(code, name); |
2765 | #ifdef MOZ_VTUNE1 |
2766 | vtune::MarkStub(code, name); |
2767 | #endif |
2768 | |
2769 | return code; |
2770 | } |
2771 | |
2772 | JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) { |
2773 | return GenerateRegExpMatchStubShared(cx, initialStringHeap, |
2774 | /* isExecMatch = */ false); |
2775 | } |
2776 | |
2777 | JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) { |
2778 | return GenerateRegExpMatchStubShared(cx, initialStringHeap, |
2779 | /* isExecMatch = */ true); |
2780 | } |
2781 | |
2782 | class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator> { |
2783 | LRegExpMatcher* lir_; |
2784 | |
2785 | public: |
2786 | explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir) : lir_(lir) {} |
2787 | |
2788 | void accept(CodeGenerator* codegen) override { |
2789 | codegen->visitOutOfLineRegExpMatcher(this); |
2790 | } |
2791 | |
2792 | LRegExpMatcher* lir() const { return lir_; } |
2793 | }; |
2794 | |
2795 | void CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool) { |
2796 | LRegExpMatcher* lir = ool->lir(); |
2797 | Register lastIndex = ToRegister(lir->lastIndex()); |
2798 | Register input = ToRegister(lir->string()); |
2799 | Register regexp = ToRegister(lir->regexp()); |
2800 | |
2801 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
2802 | regs.take(lastIndex); |
2803 | regs.take(input); |
2804 | regs.take(regexp); |
2805 | Register temp = regs.takeAny(); |
2806 | |
2807 | masm.computeEffectiveAddress( |
2808 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
2809 | |
2810 | pushArg(temp); |
2811 | pushArg(lastIndex); |
2812 | pushArg(input); |
2813 | pushArg(regexp); |
2814 | |
2815 | // We are not using oolCallVM because we are in a Call, and that live |
2816 | // registers are already saved by the the register allocator. |
2817 | using Fn = |
2818 | bool (*)(JSContext*, HandleObject regexp, HandleString input, |
2819 | int32_t lastIndex, MatchPairs* pairs, MutableHandleValue output); |
2820 | callVM<Fn, RegExpMatcherRaw>(lir); |
2821 | |
2822 | masm.jump(ool->rejoin()); |
2823 | } |
2824 | |
2825 | void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) { |
2826 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2826); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg" ")"); do { *((volatile int*)__null) = 2826; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2827 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpMatcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpMatcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpMatcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2827); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg" ")"); do { *((volatile int*)__null) = 2827; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2828 | MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2828); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg" ")"); do { *((volatile int*)__null) = 2828; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2829 | MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir) == JSReturnOperand)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutValue(lir) == JSReturnOperand ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutValue(lir) == JSReturnOperand", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2829); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand" ")"); do { *((volatile int*)__null) = 2829; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2830 | |
2831 | #if defined(JS_NUNBOX32) |
2832 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type); |
2833 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data); |
2834 | static_assert(RegExpMatcherStringReg != JSReturnReg_Type); |
2835 | static_assert(RegExpMatcherStringReg != JSReturnReg_Data); |
2836 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type); |
2837 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data); |
2838 | #elif defined(JS_PUNBOX641) |
2839 | static_assert(RegExpMatcherRegExpReg != JSReturnReg); |
2840 | static_assert(RegExpMatcherStringReg != JSReturnReg); |
2841 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg); |
2842 | #endif |
2843 | |
2844 | masm.reserveStack(RegExpReservedStack); |
2845 | |
2846 | OutOfLineRegExpMatcher* ool = new (alloc()) OutOfLineRegExpMatcher(lir); |
2847 | addOutOfLineCode(ool, lir->mir()); |
2848 | |
2849 | const JitZone* jitZone = gen->realm->zone()->jitZone(); |
2850 | JitCode* regExpMatcherStub = |
2851 | jitZone->regExpMatcherStubNoBarrier(&zoneStubsToReadBarrier_); |
2852 | masm.call(regExpMatcherStub); |
2853 | masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry()); |
2854 | masm.bind(ool->rejoin()); |
2855 | |
2856 | masm.freeStack(RegExpReservedStack); |
2857 | } |
2858 | |
2859 | class OutOfLineRegExpExecMatch : public OutOfLineCodeBase<CodeGenerator> { |
2860 | LRegExpExecMatch* lir_; |
2861 | |
2862 | public: |
2863 | explicit OutOfLineRegExpExecMatch(LRegExpExecMatch* lir) : lir_(lir) {} |
2864 | |
2865 | void accept(CodeGenerator* codegen) override { |
2866 | codegen->visitOutOfLineRegExpExecMatch(this); |
2867 | } |
2868 | |
2869 | LRegExpExecMatch* lir() const { return lir_; } |
2870 | }; |
2871 | |
2872 | void CodeGenerator::visitOutOfLineRegExpExecMatch( |
2873 | OutOfLineRegExpExecMatch* ool) { |
2874 | LRegExpExecMatch* lir = ool->lir(); |
2875 | Register input = ToRegister(lir->string()); |
2876 | Register regexp = ToRegister(lir->regexp()); |
2877 | |
2878 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
2879 | regs.take(input); |
2880 | regs.take(regexp); |
2881 | Register temp = regs.takeAny(); |
2882 | |
2883 | masm.computeEffectiveAddress( |
2884 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
2885 | |
2886 | pushArg(temp); |
2887 | pushArg(input); |
2888 | pushArg(regexp); |
2889 | |
2890 | // We are not using oolCallVM because we are in a Call and live registers have |
2891 | // already been saved by the register allocator. |
2892 | using Fn = |
2893 | bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input, |
2894 | MatchPairs* pairs, MutableHandleValue output); |
2895 | callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir); |
2896 | masm.jump(ool->rejoin()); |
2897 | } |
2898 | |
2899 | void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) { |
2900 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2900); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg" ")"); do { *((volatile int*)__null) = 2900; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2901 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpMatcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpMatcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpMatcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2901); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg" ")"); do { *((volatile int*)__null) = 2901; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2902 | MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir) == JSReturnOperand)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutValue(lir) == JSReturnOperand ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutValue(lir) == JSReturnOperand", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2902); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand" ")"); do { *((volatile int*)__null) = 2902; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
2903 | |
2904 | #if defined(JS_NUNBOX32) |
2905 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type); |
2906 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data); |
2907 | static_assert(RegExpMatcherStringReg != JSReturnReg_Type); |
2908 | static_assert(RegExpMatcherStringReg != JSReturnReg_Data); |
2909 | #elif defined(JS_PUNBOX641) |
2910 | static_assert(RegExpMatcherRegExpReg != JSReturnReg); |
2911 | static_assert(RegExpMatcherStringReg != JSReturnReg); |
2912 | #endif |
2913 | |
2914 | masm.reserveStack(RegExpReservedStack); |
2915 | |
2916 | auto* ool = new (alloc()) OutOfLineRegExpExecMatch(lir); |
2917 | addOutOfLineCode(ool, lir->mir()); |
2918 | |
2919 | const JitZone* jitZone = gen->realm->zone()->jitZone(); |
2920 | JitCode* regExpExecMatchStub = |
2921 | jitZone->regExpExecMatchStubNoBarrier(&zoneStubsToReadBarrier_); |
2922 | masm.call(regExpExecMatchStub); |
2923 | masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry()); |
2924 | |
2925 | masm.bind(ool->rejoin()); |
2926 | masm.freeStack(RegExpReservedStack); |
2927 | } |
2928 | |
2929 | JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) { |
2930 | JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub"); |
2931 | |
2932 | Register regexp = RegExpSearcherRegExpReg; |
2933 | Register input = RegExpSearcherStringReg; |
2934 | Register lastIndex = RegExpSearcherLastIndexReg; |
2935 | Register result = ReturnReg; |
2936 | |
2937 | // We are free to clobber all registers, as LRegExpSearcher is a call |
2938 | // instruction. |
2939 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
2940 | regs.take(input); |
2941 | regs.take(regexp); |
2942 | regs.take(lastIndex); |
2943 | |
2944 | Register temp1 = regs.takeAny(); |
2945 | Register temp2 = regs.takeAny(); |
2946 | Register temp3 = regs.takeAny(); |
2947 | |
2948 | TempAllocator temp(&cx->tempLifoAlloc()); |
2949 | JitContext jcx(cx); |
2950 | StackMacroAssembler masm(cx, temp); |
2951 | AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub"); |
2952 | |
2953 | #ifdef JS_USE_LINK_REGISTER |
2954 | masm.pushReturnAddress(); |
2955 | #endif |
2956 | masm.push(FramePointer); |
2957 | masm.moveStackPtrTo(FramePointer); |
2958 | |
2959 | #ifdef DEBUG1 |
2960 | // Store sentinel value to cx->regExpSearcherLastLimit. |
2961 | // See comment in RegExpSearcherImpl. |
2962 | masm.loadJSContext(temp1); |
2963 | masm.store32(Imm32(RegExpSearcherLastLimitSentinel), |
2964 | Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit())); |
2965 | #endif |
2966 | |
2967 | // The InputOutputData is placed above the frame pointer and return address on |
2968 | // the stack. |
2969 | int32_t inputOutputDataStartOffset = 2 * sizeof(void*); |
2970 | |
2971 | Label notFound, oolEntry; |
2972 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
2973 | temp3, inputOutputDataStartOffset, |
2974 | initialStringHeap, ¬Found, &oolEntry)) { |
2975 | return nullptr; |
2976 | } |
2977 | |
2978 | // clang-format off |
2979 | /* |
2980 | * [SMDOC] Stack layout for the RegExpSearcher stub |
2981 | * |
2982 | * +---------------+ |
2983 | * FramePointer +-----> |Caller-FramePtr| |
2984 | * +---------------+ |
2985 | * |Return-Address | |
2986 | * +---------------+ |
2987 | * inputOutputDataStartOffset +-----> +---------------+ |
2988 | * |InputOutputData| |
2989 | * +---------------+ |
2990 | * +---------------+ |
2991 | * | MatchPairs | |
2992 | * | count | |
2993 | * | pairs | |
2994 | * | | |
2995 | * +---------------+ |
2996 | * pairsVectorStartOffset +-----> +---------------+ |
2997 | * | MatchPair | |
2998 | * matchPairStart +------------> start | <-------+ |
2999 | * matchPairLimit +------------> limit | | Reserved space for |
3000 | * +---------------+ | `RegExpObject::MaxPairCount` |
3001 | * . | MatchPair objects. |
3002 | * . | |
3003 | * . | Only a single object will |
3004 | * +---------------+ | be initialized and can be |
3005 | * | MatchPair | | accessed below. |
3006 | * | start | <-------+ |
3007 | * | limit | |
3008 | * +---------------+ |
3009 | */ |
3010 | // clang-format on |
3011 | |
3012 | int32_t pairsVectorStartOffset = |
3013 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
3014 | Address matchPairStart(FramePointer, |
3015 | pairsVectorStartOffset + MatchPair::offsetOfStart()); |
3016 | Address matchPairLimit(FramePointer, |
3017 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
3018 | |
3019 | // Store match limit to cx->regExpSearcherLastLimit and return the index. |
3020 | masm.load32(matchPairLimit, result); |
3021 | masm.loadJSContext(input); |
3022 | masm.store32(result, |
3023 | Address(input, JSContext::offsetOfRegExpSearcherLastLimit())); |
3024 | masm.load32(matchPairStart, result); |
3025 | masm.pop(FramePointer); |
3026 | masm.ret(); |
3027 | |
3028 | masm.bind(¬Found); |
3029 | masm.move32(Imm32(RegExpSearcherResultNotFound), result); |
3030 | masm.pop(FramePointer); |
3031 | masm.ret(); |
3032 | |
3033 | masm.bind(&oolEntry); |
3034 | masm.move32(Imm32(RegExpSearcherResultFailed), result); |
3035 | masm.pop(FramePointer); |
3036 | masm.ret(); |
3037 | |
3038 | Linker linker(masm); |
3039 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
3040 | if (!code) { |
3041 | return nullptr; |
3042 | } |
3043 | |
3044 | CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub"); |
3045 | #ifdef MOZ_VTUNE1 |
3046 | vtune::MarkStub(code, "RegExpSearcherStub"); |
3047 | #endif |
3048 | |
3049 | return code; |
3050 | } |
3051 | |
3052 | class OutOfLineRegExpSearcher : public OutOfLineCodeBase<CodeGenerator> { |
3053 | LRegExpSearcher* lir_; |
3054 | |
3055 | public: |
3056 | explicit OutOfLineRegExpSearcher(LRegExpSearcher* lir) : lir_(lir) {} |
3057 | |
3058 | void accept(CodeGenerator* codegen) override { |
3059 | codegen->visitOutOfLineRegExpSearcher(this); |
3060 | } |
3061 | |
3062 | LRegExpSearcher* lir() const { return lir_; } |
3063 | }; |
3064 | |
3065 | void CodeGenerator::visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool) { |
3066 | LRegExpSearcher* lir = ool->lir(); |
3067 | Register lastIndex = ToRegister(lir->lastIndex()); |
3068 | Register input = ToRegister(lir->string()); |
3069 | Register regexp = ToRegister(lir->regexp()); |
3070 | |
3071 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
3072 | regs.take(lastIndex); |
3073 | regs.take(input); |
3074 | regs.take(regexp); |
3075 | Register temp = regs.takeAny(); |
3076 | |
3077 | masm.computeEffectiveAddress( |
3078 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
3079 | |
3080 | pushArg(temp); |
3081 | pushArg(lastIndex); |
3082 | pushArg(input); |
3083 | pushArg(regexp); |
3084 | |
3085 | // We are not using oolCallVM because we are in a Call, and that live |
3086 | // registers are already saved by the the register allocator. |
3087 | using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input, |
3088 | int32_t lastIndex, MatchPairs* pairs, int32_t* result); |
3089 | callVM<Fn, RegExpSearcherRaw>(lir); |
3090 | |
3091 | masm.jump(ool->rejoin()); |
3092 | } |
3093 | |
3094 | void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) { |
3095 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3095); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg" ")"); do { *((volatile int*)__null) = 3095; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3096 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpSearcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpSearcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpSearcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpSearcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg" ")"); do { *((volatile int*)__null) = 3096; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3097 | MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3097); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg" ")"); do { *((volatile int*)__null) = 3097; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3098 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3098); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 3098; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3099 | |
3100 | static_assert(RegExpSearcherRegExpReg != ReturnReg); |
3101 | static_assert(RegExpSearcherStringReg != ReturnReg); |
3102 | static_assert(RegExpSearcherLastIndexReg != ReturnReg); |
3103 | |
3104 | masm.reserveStack(RegExpReservedStack); |
3105 | |
3106 | OutOfLineRegExpSearcher* ool = new (alloc()) OutOfLineRegExpSearcher(lir); |
3107 | addOutOfLineCode(ool, lir->mir()); |
3108 | |
3109 | const JitZone* jitZone = gen->realm->zone()->jitZone(); |
3110 | JitCode* regExpSearcherStub = |
3111 | jitZone->regExpSearcherStubNoBarrier(&zoneStubsToReadBarrier_); |
3112 | masm.call(regExpSearcherStub); |
3113 | masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed), |
3114 | ool->entry()); |
3115 | masm.bind(ool->rejoin()); |
3116 | |
3117 | masm.freeStack(RegExpReservedStack); |
3118 | } |
3119 | |
3120 | void CodeGenerator::visitRegExpSearcherLastLimit( |
3121 | LRegExpSearcherLastLimit* lir) { |
3122 | Register result = ToRegister(lir->output()); |
3123 | Register scratch = ToRegister(lir->temp0()); |
3124 | |
3125 | masm.loadAndClearRegExpSearcherLastLimit(result, scratch); |
3126 | } |
3127 | |
3128 | JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) { |
3129 | JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub"); |
3130 | |
3131 | Register regexp = RegExpExecTestRegExpReg; |
3132 | Register input = RegExpExecTestStringReg; |
3133 | Register result = ReturnReg; |
3134 | |
3135 | TempAllocator temp(&cx->tempLifoAlloc()); |
3136 | JitContext jcx(cx); |
3137 | StackMacroAssembler masm(cx, temp); |
3138 | AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub"); |
3139 | |
3140 | #ifdef JS_USE_LINK_REGISTER |
3141 | masm.pushReturnAddress(); |
3142 | #endif |
3143 | masm.push(FramePointer); |
3144 | masm.moveStackPtrTo(FramePointer); |
3145 | |
3146 | // We are free to clobber all registers, as LRegExpExecTest is a call |
3147 | // instruction. |
3148 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
3149 | regs.take(input); |
3150 | regs.take(regexp); |
3151 | |
3152 | // Ensure lastIndex != result. |
3153 | regs.take(result); |
3154 | Register lastIndex = regs.takeAny(); |
3155 | regs.add(result); |
3156 | Register temp1 = regs.takeAny(); |
3157 | Register temp2 = regs.takeAny(); |
3158 | Register temp3 = regs.takeAny(); |
3159 | |
3160 | Address flagsSlot(regexp, RegExpObject::offsetOfFlags()); |
3161 | Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex()); |
3162 | |
3163 | masm.reserveStack(RegExpReservedStack); |
3164 | |
3165 | // Load lastIndex and skip RegExp execution if needed. |
3166 | Label notFoundZeroLastIndex; |
3167 | masm.loadRegExpLastIndex(regexp, input, lastIndex, ¬FoundZeroLastIndex); |
3168 | |
3169 | // In visitRegExpMatcher and visitRegExpSearcher, we reserve stack space |
3170 | // before calling the stub. For RegExpExecTest we call the stub before |
3171 | // reserving stack space, so the offset of the InputOutputData relative to the |
3172 | // frame pointer is negative. |
3173 | constexpr int32_t inputOutputDataStartOffset = -int32_t(RegExpReservedStack); |
3174 | |
3175 | // On ARM64, load/store instructions can encode an immediate offset in the |
3176 | // range [-256, 4095]. If we ever fail this assertion, it would be more |
3177 | // efficient to store the data above the frame pointer similar to |
3178 | // RegExpMatcher and RegExpSearcher. |
3179 | static_assert(inputOutputDataStartOffset >= -256); |
3180 | |
3181 | Label notFound, oolEntry; |
3182 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
3183 | temp3, inputOutputDataStartOffset, |
3184 | initialStringHeap, ¬Found, &oolEntry)) { |
3185 | return nullptr; |
3186 | } |
3187 | |
3188 | // Set `result` to true/false to indicate found/not-found, or to |
3189 | // RegExpExecTestResultFailed if we have to retry in C++. If the regular |
3190 | // expression is global or sticky, we also have to update its .lastIndex slot. |
3191 | |
3192 | Label done; |
3193 | int32_t pairsVectorStartOffset = |
3194 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
3195 | Address matchPairLimit(FramePointer, |
3196 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
3197 | |
3198 | masm.move32(Imm32(1), result); |
3199 | masm.branchTest32(Assembler::Zero, flagsSlot, |
3200 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
3201 | &done); |
3202 | masm.load32(matchPairLimit, lastIndex); |
3203 | masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot); |
3204 | masm.jump(&done); |
3205 | |
3206 | masm.bind(¬Found); |
3207 | masm.move32(Imm32(0), result); |
3208 | masm.branchTest32(Assembler::Zero, flagsSlot, |
3209 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
3210 | &done); |
3211 | masm.storeValue(Int32Value(0), lastIndexSlot); |
3212 | masm.jump(&done); |
3213 | |
3214 | masm.bind(¬FoundZeroLastIndex); |
3215 | masm.move32(Imm32(0), result); |
3216 | masm.storeValue(Int32Value(0), lastIndexSlot); |
3217 | masm.jump(&done); |
3218 | |
3219 | masm.bind(&oolEntry); |
3220 | masm.move32(Imm32(RegExpExecTestResultFailed), result); |
3221 | |
3222 | masm.bind(&done); |
3223 | masm.freeStack(RegExpReservedStack); |
3224 | masm.pop(FramePointer); |
3225 | masm.ret(); |
3226 | |
3227 | Linker linker(masm); |
3228 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
3229 | if (!code) { |
3230 | return nullptr; |
3231 | } |
3232 | |
3233 | CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub"); |
3234 | #ifdef MOZ_VTUNE1 |
3235 | vtune::MarkStub(code, "RegExpExecTestStub"); |
3236 | #endif |
3237 | |
3238 | return code; |
3239 | } |
3240 | |
3241 | class OutOfLineRegExpExecTest : public OutOfLineCodeBase<CodeGenerator> { |
3242 | LRegExpExecTest* lir_; |
3243 | |
3244 | public: |
3245 | explicit OutOfLineRegExpExecTest(LRegExpExecTest* lir) : lir_(lir) {} |
3246 | |
3247 | void accept(CodeGenerator* codegen) override { |
3248 | codegen->visitOutOfLineRegExpExecTest(this); |
3249 | } |
3250 | |
3251 | LRegExpExecTest* lir() const { return lir_; } |
3252 | }; |
3253 | |
3254 | void CodeGenerator::visitOutOfLineRegExpExecTest(OutOfLineRegExpExecTest* ool) { |
3255 | LRegExpExecTest* lir = ool->lir(); |
3256 | Register input = ToRegister(lir->string()); |
3257 | Register regexp = ToRegister(lir->regexp()); |
3258 | |
3259 | pushArg(input); |
3260 | pushArg(regexp); |
3261 | |
3262 | // We are not using oolCallVM because we are in a Call and live registers have |
3263 | // already been saved by the register allocator. |
3264 | using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp, |
3265 | HandleString input, bool* result); |
3266 | callVM<Fn, RegExpBuiltinExecTestFromJit>(lir); |
3267 | |
3268 | masm.jump(ool->rejoin()); |
3269 | } |
3270 | |
3271 | void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) { |
3272 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg" ")"); do { *((volatile int*)__null) = 3272; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3273 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpExecTestStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpExecTestStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpExecTestStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpExecTestStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3273); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg" ")"); do { *((volatile int*)__null) = 3273; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3274 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3274); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 3274; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3275 | |
3276 | static_assert(RegExpExecTestRegExpReg != ReturnReg); |
3277 | static_assert(RegExpExecTestStringReg != ReturnReg); |
3278 | |
3279 | auto* ool = new (alloc()) OutOfLineRegExpExecTest(lir); |
3280 | addOutOfLineCode(ool, lir->mir()); |
3281 | |
3282 | const JitZone* jitZone = gen->realm->zone()->jitZone(); |
3283 | JitCode* regExpExecTestStub = |
3284 | jitZone->regExpExecTestStubNoBarrier(&zoneStubsToReadBarrier_); |
3285 | masm.call(regExpExecTestStub); |
3286 | |
3287 | masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed), |
3288 | ool->entry()); |
3289 | |
3290 | masm.bind(ool->rejoin()); |
3291 | } |
3292 | |
3293 | void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) { |
3294 | Register regexp = ToRegister(ins->regexp()); |
3295 | Register input = ToRegister(ins->input()); |
3296 | Register output = ToRegister(ins->output()); |
3297 | |
3298 | using Fn = |
3299 | bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*); |
3300 | auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>( |
3301 | ins, ArgList(regexp, input), StoreRegisterTo(output)); |
3302 | |
3303 | // Load RegExpShared in |output|. |
3304 | Label vmCall; |
3305 | masm.loadParsedRegExpShared(regexp, output, ool->entry()); |
3306 | |
3307 | // Return true iff pairCount > 1. |
3308 | Label returnTrue; |
3309 | masm.branch32(Assembler::Above, |
3310 | Address(output, RegExpShared::offsetOfPairCount()), Imm32(1), |
3311 | &returnTrue); |
3312 | masm.move32(Imm32(0), output); |
3313 | masm.jump(ool->rejoin()); |
3314 | |
3315 | masm.bind(&returnTrue); |
3316 | masm.move32(Imm32(1), output); |
3317 | |
3318 | masm.bind(ool->rejoin()); |
3319 | } |
3320 | |
3321 | class OutOfLineRegExpPrototypeOptimizable |
3322 | : public OutOfLineCodeBase<CodeGenerator> { |
3323 | LRegExpPrototypeOptimizable* ins_; |
3324 | |
3325 | public: |
3326 | explicit OutOfLineRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins) |
3327 | : ins_(ins) {} |
3328 | |
3329 | void accept(CodeGenerator* codegen) override { |
3330 | codegen->visitOutOfLineRegExpPrototypeOptimizable(this); |
3331 | } |
3332 | LRegExpPrototypeOptimizable* ins() const { return ins_; } |
3333 | }; |
3334 | |
3335 | void CodeGenerator::visitRegExpPrototypeOptimizable( |
3336 | LRegExpPrototypeOptimizable* ins) { |
3337 | Register object = ToRegister(ins->object()); |
3338 | Register output = ToRegister(ins->output()); |
3339 | Register temp = ToRegister(ins->temp0()); |
3340 | |
3341 | OutOfLineRegExpPrototypeOptimizable* ool = |
3342 | new (alloc()) OutOfLineRegExpPrototypeOptimizable(ins); |
3343 | addOutOfLineCode(ool, ins->mir()); |
3344 | |
3345 | const GlobalObject* global = gen->realm->maybeGlobal(); |
3346 | MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType< decltype(global)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("global", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3346); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")" ); do { *((volatile int*)__null) = 3346; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3347 | masm.branchIfNotRegExpPrototypeOptimizable(object, temp, global, |
3348 | ool->entry()); |
3349 | masm.move32(Imm32(0x1), output); |
3350 | |
3351 | masm.bind(ool->rejoin()); |
3352 | } |
3353 | |
3354 | void CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable( |
3355 | OutOfLineRegExpPrototypeOptimizable* ool) { |
3356 | LRegExpPrototypeOptimizable* ins = ool->ins(); |
3357 | Register object = ToRegister(ins->object()); |
3358 | Register output = ToRegister(ins->output()); |
3359 | |
3360 | saveVolatile(output); |
3361 | |
3362 | using Fn = bool (*)(JSContext* cx, JSObject* proto); |
3363 | masm.setupAlignedABICall(); |
3364 | masm.loadJSContext(output); |
3365 | masm.passABIArg(output); |
3366 | masm.passABIArg(object); |
3367 | masm.callWithABI<Fn, RegExpPrototypeOptimizableRaw>(); |
3368 | masm.storeCallBoolResult(output); |
3369 | |
3370 | restoreVolatile(output); |
3371 | |
3372 | masm.jump(ool->rejoin()); |
3373 | } |
3374 | |
3375 | class OutOfLineRegExpInstanceOptimizable |
3376 | : public OutOfLineCodeBase<CodeGenerator> { |
3377 | LRegExpInstanceOptimizable* ins_; |
3378 | |
3379 | public: |
3380 | explicit OutOfLineRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins) |
3381 | : ins_(ins) {} |
3382 | |
3383 | void accept(CodeGenerator* codegen) override { |
3384 | codegen->visitOutOfLineRegExpInstanceOptimizable(this); |
3385 | } |
3386 | LRegExpInstanceOptimizable* ins() const { return ins_; } |
3387 | }; |
3388 | |
3389 | void CodeGenerator::visitRegExpInstanceOptimizable( |
3390 | LRegExpInstanceOptimizable* ins) { |
3391 | Register object = ToRegister(ins->object()); |
3392 | Register output = ToRegister(ins->output()); |
3393 | Register temp = ToRegister(ins->temp0()); |
3394 | |
3395 | OutOfLineRegExpInstanceOptimizable* ool = |
3396 | new (alloc()) OutOfLineRegExpInstanceOptimizable(ins); |
3397 | addOutOfLineCode(ool, ins->mir()); |
3398 | |
3399 | const GlobalObject* global = gen->realm->maybeGlobal(); |
3400 | MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType< decltype(global)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("global", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3400); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")" ); do { *((volatile int*)__null) = 3400; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3401 | masm.branchIfNotRegExpInstanceOptimizable(object, temp, global, ool->entry()); |
3402 | masm.move32(Imm32(0x1), output); |
3403 | |
3404 | masm.bind(ool->rejoin()); |
3405 | } |
3406 | |
3407 | void CodeGenerator::visitOutOfLineRegExpInstanceOptimizable( |
3408 | OutOfLineRegExpInstanceOptimizable* ool) { |
3409 | LRegExpInstanceOptimizable* ins = ool->ins(); |
3410 | Register object = ToRegister(ins->object()); |
3411 | Register proto = ToRegister(ins->proto()); |
3412 | Register output = ToRegister(ins->output()); |
3413 | |
3414 | saveVolatile(output); |
3415 | |
3416 | using Fn = bool (*)(JSContext* cx, JSObject* obj, JSObject* proto); |
3417 | masm.setupAlignedABICall(); |
3418 | masm.loadJSContext(output); |
3419 | masm.passABIArg(output); |
3420 | masm.passABIArg(object); |
3421 | masm.passABIArg(proto); |
3422 | masm.callWithABI<Fn, RegExpInstanceOptimizableRaw>(); |
3423 | masm.storeCallBoolResult(output); |
3424 | |
3425 | restoreVolatile(output); |
3426 | |
3427 | masm.jump(ool->rejoin()); |
3428 | } |
3429 | |
3430 | static void FindFirstDollarIndex(MacroAssembler& masm, Register str, |
3431 | Register len, Register temp0, Register temp1, |
3432 | Register output, CharEncoding encoding) { |
3433 | #ifdef DEBUG1 |
3434 | Label ok; |
3435 | masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); |
3436 | masm.assumeUnreachable("Length should be greater than 0."); |
3437 | masm.bind(&ok); |
3438 | #endif |
3439 | |
3440 | Register chars = temp0; |
3441 | masm.loadStringChars(str, chars, encoding); |
3442 | |
3443 | masm.move32(Imm32(0), output); |
3444 | |
3445 | Label start, done; |
3446 | masm.bind(&start); |
3447 | |
3448 | Register currentChar = temp1; |
3449 | masm.loadChar(chars, output, currentChar, encoding); |
3450 | masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done); |
3451 | |
3452 | masm.add32(Imm32(1), output); |
3453 | masm.branch32(Assembler::NotEqual, output, len, &start); |
3454 | |
3455 | masm.move32(Imm32(-1), output); |
3456 | |
3457 | masm.bind(&done); |
3458 | } |
3459 | |
3460 | void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) { |
3461 | Register str = ToRegister(ins->str()); |
3462 | Register output = ToRegister(ins->output()); |
3463 | Register temp0 = ToRegister(ins->temp0()); |
3464 | Register temp1 = ToRegister(ins->temp1()); |
3465 | Register len = ToRegister(ins->temp2()); |
3466 | |
3467 | using Fn = bool (*)(JSContext*, JSString*, int32_t*); |
3468 | OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>( |
3469 | ins, ArgList(str), StoreRegisterTo(output)); |
3470 | |
3471 | masm.branchIfRope(str, ool->entry()); |
3472 | masm.loadStringLength(str, len); |
3473 | |
3474 | Label isLatin1, done; |
3475 | masm.branchLatin1String(str, &isLatin1); |
3476 | { |
3477 | FindFirstDollarIndex(masm, str, len, temp0, temp1, output, |
3478 | CharEncoding::TwoByte); |
3479 | masm.jump(&done); |
3480 | } |
3481 | masm.bind(&isLatin1); |
3482 | { |
3483 | FindFirstDollarIndex(masm, str, len, temp0, temp1, output, |
3484 | CharEncoding::Latin1); |
3485 | } |
3486 | masm.bind(&done); |
3487 | masm.bind(ool->rejoin()); |
3488 | } |
3489 | |
3490 | void CodeGenerator::visitStringReplace(LStringReplace* lir) { |
3491 | if (lir->replacement()->isConstant()) { |
3492 | pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString())); |
3493 | } else { |
3494 | pushArg(ToRegister(lir->replacement())); |
3495 | } |
3496 | |
3497 | if (lir->pattern()->isConstant()) { |
3498 | pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString())); |
3499 | } else { |
3500 | pushArg(ToRegister(lir->pattern())); |
3501 | } |
3502 | |
3503 | if (lir->string()->isConstant()) { |
3504 | pushArg(ImmGCPtr(lir->string()->toConstant()->toString())); |
3505 | } else { |
3506 | pushArg(ToRegister(lir->string())); |
3507 | } |
3508 | |
3509 | using Fn = |
3510 | JSString* (*)(JSContext*, HandleString, HandleString, HandleString); |
3511 | if (lir->mir()->isFlatReplacement()) { |
3512 | callVM<Fn, StringFlatReplaceString>(lir); |
3513 | } else { |
3514 | callVM<Fn, StringReplace>(lir); |
3515 | } |
3516 | } |
3517 | |
3518 | void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) { |
3519 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
3520 | TypedOrValueRegister lhs = |
3521 | TypedOrValueRegister(ToValue(lir, LBinaryValueCache::LhsIndex)); |
3522 | TypedOrValueRegister rhs = |
3523 | TypedOrValueRegister(ToValue(lir, LBinaryValueCache::RhsIndex)); |
3524 | ValueOperand output = ToOutValue(lir); |
3525 | |
3526 | JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc()); |
3527 | |
3528 | switch (jsop) { |
3529 | case JSOp::Add: |
3530 | case JSOp::Sub: |
3531 | case JSOp::Mul: |
3532 | case JSOp::Div: |
3533 | case JSOp::Mod: |
3534 | case JSOp::Pow: |
3535 | case JSOp::BitAnd: |
3536 | case JSOp::BitOr: |
3537 | case JSOp::BitXor: |
3538 | case JSOp::Lsh: |
3539 | case JSOp::Rsh: |
3540 | case JSOp::Ursh: { |
3541 | IonBinaryArithIC ic(liveRegs, lhs, rhs, output); |
3542 | addIC(lir, allocateIC(ic)); |
3543 | return; |
3544 | } |
3545 | default: |
3546 | MOZ_CRASH("Unsupported jsop in MBinaryValueCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryValueCache" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3546); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache" ")"); do { *((volatile int*)__null) = 3546; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
3547 | } |
3548 | } |
3549 | |
3550 | void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) { |
3551 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
3552 | TypedOrValueRegister lhs = |
3553 | TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::LhsIndex)); |
3554 | TypedOrValueRegister rhs = |
3555 | TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::RhsIndex)); |
3556 | Register output = ToRegister(lir->output()); |
3557 | |
3558 | JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc()); |
3559 | |
3560 | switch (jsop) { |
3561 | case JSOp::Lt: |
3562 | case JSOp::Le: |
3563 | case JSOp::Gt: |
3564 | case JSOp::Ge: |
3565 | case JSOp::Eq: |
3566 | case JSOp::Ne: |
3567 | case JSOp::StrictEq: |
3568 | case JSOp::StrictNe: { |
3569 | IonCompareIC ic(liveRegs, lhs, rhs, output); |
3570 | addIC(lir, allocateIC(ic)); |
3571 | return; |
3572 | } |
3573 | default: |
3574 | MOZ_CRASH("Unsupported jsop in MBinaryBoolCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryBoolCache" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3574); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache" ")"); do { *((volatile int*)__null) = 3574; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
3575 | } |
3576 | } |
3577 | |
3578 | void CodeGenerator::visitUnaryCache(LUnaryCache* lir) { |
3579 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
3580 | TypedOrValueRegister input = |
3581 | TypedOrValueRegister(ToValue(lir, LUnaryCache::InputIndex)); |
3582 | ValueOperand output = ToOutValue(lir); |
3583 | |
3584 | IonUnaryArithIC ic(liveRegs, input, output); |
3585 | addIC(lir, allocateIC(ic)); |
3586 | } |
3587 | |
3588 | void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) { |
3589 | pushArg(ImmPtr(lir->mir()->module())); |
3590 | |
3591 | using Fn = JSObject* (*)(JSContext*, HandleObject); |
3592 | callVM<Fn, js::GetOrCreateModuleMetaObject>(lir); |
3593 | } |
3594 | |
3595 | void CodeGenerator::visitDynamicImport(LDynamicImport* lir) { |
3596 | pushArg(ToValue(lir, LDynamicImport::OptionsIndex)); |
3597 | pushArg(ToValue(lir, LDynamicImport::SpecifierIndex)); |
3598 | pushArg(ImmGCPtr(current->mir()->info().script())); |
3599 | |
3600 | using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue); |
3601 | callVM<Fn, js::StartDynamicModuleImport>(lir); |
3602 | } |
3603 | |
3604 | void CodeGenerator::visitLambda(LLambda* lir) { |
3605 | Register envChain = ToRegister(lir->environmentChain()); |
3606 | Register output = ToRegister(lir->output()); |
3607 | Register tempReg = ToRegister(lir->temp0()); |
3608 | |
3609 | JSFunction* fun = lir->mir()->templateFunction(); |
3610 | |
3611 | using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject); |
3612 | OutOfLineCode* ool = oolCallVM<Fn, js::Lambda>( |
3613 | lir, ArgList(ImmGCPtr(fun), envChain), StoreRegisterTo(output)); |
3614 | |
3615 | TemplateObject templateObject(fun); |
3616 | masm.createGCObject(output, tempReg, templateObject, gc::Heap::Default, |
3617 | ool->entry()); |
3618 | |
3619 | masm.storeValue(JSVAL_TYPE_OBJECT, envChain, |
3620 | Address(output, JSFunction::offsetOfEnvironment())); |
3621 | // No post barrier needed because output is guaranteed to be allocated in |
3622 | // the nursery. |
3623 | |
3624 | masm.bind(ool->rejoin()); |
3625 | } |
3626 | |
3627 | void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) { |
3628 | Register envChain = ToRegister(lir->envChain()); |
3629 | Register prototype = ToRegister(lir->prototype()); |
3630 | |
3631 | pushArg(prototype); |
3632 | pushArg(envChain); |
3633 | pushArg(ImmGCPtr(lir->mir()->function())); |
3634 | |
3635 | using Fn = |
3636 | JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject); |
3637 | callVM<Fn, js::FunWithProtoOperation>(lir); |
3638 | } |
3639 | |
3640 | void CodeGenerator::visitSetFunName(LSetFunName* lir) { |
3641 | pushArg(Imm32(lir->mir()->prefixKind())); |
3642 | pushArg(ToValue(lir, LSetFunName::NameIndex)); |
3643 | pushArg(ToRegister(lir->fun())); |
3644 | |
3645 | using Fn = |
3646 | bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind); |
3647 | callVM<Fn, js::SetFunctionName>(lir); |
3648 | } |
3649 | |
3650 | void CodeGenerator::visitOsiPoint(LOsiPoint* lir) { |
3651 | // Note: markOsiPoint ensures enough space exists between the last |
3652 | // LOsiPoint and this one to patch adjacent call instructions. |
3653 | |
3654 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3654); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 3654; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3655 | |
3656 | uint32_t osiCallPointOffset = markOsiPoint(lir); |
3657 | |
3658 | LSafepoint* safepoint = lir->associatedSafepoint(); |
3659 | MOZ_ASSERT(!safepoint->osiCallPointOffset())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!safepoint->osiCallPointOffset())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!safepoint->osiCallPointOffset ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!safepoint->osiCallPointOffset()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3659); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()" ")"); do { *((volatile int*)__null) = 3659; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3660 | safepoint->setOsiCallPointOffset(osiCallPointOffset); |
3661 | |
3662 | #ifdef DEBUG1 |
3663 | // There should be no movegroups or other instructions between |
3664 | // an instruction and its OsiPoint. This is necessary because |
3665 | // we use the OsiPoint's snapshot from within VM calls. |
3666 | for (LInstructionReverseIterator iter(current->rbegin(lir)); |
3667 | iter != current->rend(); iter++) { |
3668 | if (*iter == lir) { |
3669 | continue; |
3670 | } |
3671 | MOZ_ASSERT(!iter->isMoveGroup())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!iter->isMoveGroup())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!iter->isMoveGroup()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!iter->isMoveGroup()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3671); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!iter->isMoveGroup()" ")"); do { *((volatile int*)__null) = 3671; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3672 | MOZ_ASSERT(iter->safepoint() == safepoint)do { static_assert( mozilla::detail::AssertionConditionType< decltype(iter->safepoint() == safepoint)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(iter->safepoint() == safepoint ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "iter->safepoint() == safepoint", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3672); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint" ")"); do { *((volatile int*)__null) = 3672; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3673 | break; |
3674 | } |
3675 | #endif |
3676 | |
3677 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
3678 | if (shouldVerifyOsiPointRegs(safepoint)) { |
3679 | verifyOsiPointRegs(safepoint); |
3680 | } |
3681 | #endif |
3682 | } |
3683 | |
3684 | void CodeGenerator::visitPhi(LPhi* lir) { |
3685 | MOZ_CRASH("Unexpected LPhi in CodeGenerator")do { do { } while (false); MOZ_ReportCrash("" "Unexpected LPhi in CodeGenerator" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3685); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator" ")"); do { *((volatile int*)__null) = 3685; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
3686 | } |
3687 | |
3688 | void CodeGenerator::visitGoto(LGoto* lir) { jumpToBlock(lir->target()); } |
3689 | |
3690 | void CodeGenerator::visitTableSwitch(LTableSwitch* ins) { |
3691 | MTableSwitch* mir = ins->mir(); |
3692 | Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); |
3693 | const LAllocation* temp; |
3694 | |
3695 | if (mir->getOperand(0)->type() != MIRType::Int32) { |
3696 | temp = ins->tempInt()->output(); |
3697 | |
3698 | // The input is a double, so try and convert it to an integer. |
3699 | // If it does not fit in an integer, take the default case. |
3700 | masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp), |
3701 | defaultcase, false); |
3702 | } else { |
3703 | temp = ins->index(); |
3704 | } |
3705 | |
3706 | emitTableSwitchDispatch(mir, ToRegister(temp), |
3707 | ToRegisterOrInvalid(ins->tempPointer())); |
3708 | } |
3709 | |
3710 | void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) { |
3711 | MTableSwitch* mir = ins->mir(); |
3712 | Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); |
3713 | |
3714 | Register index = ToRegister(ins->tempInt()); |
3715 | ValueOperand value = ToValue(ins, LTableSwitchV::InputValue); |
3716 | Register tag = masm.extractTag(value, index); |
3717 | masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase); |
3718 | |
3719 | Label unboxInt, isInt; |
3720 | masm.branchTestInt32(Assembler::Equal, tag, &unboxInt); |
3721 | { |
3722 | FloatRegister floatIndex = ToFloatRegister(ins->tempFloat()); |
3723 | masm.unboxDouble(value, floatIndex); |
3724 | masm.convertDoubleToInt32(floatIndex, index, defaultcase, false); |
3725 | masm.jump(&isInt); |
3726 | } |
3727 | |
3728 | masm.bind(&unboxInt); |
3729 | masm.unboxInt32(value, index); |
3730 | |
3731 | masm.bind(&isInt); |
3732 | |
3733 | emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer())); |
3734 | } |
3735 | |
3736 | void CodeGenerator::visitParameter(LParameter* lir) {} |
3737 | |
3738 | void CodeGenerator::visitCallee(LCallee* lir) { |
3739 | Register callee = ToRegister(lir->output()); |
3740 | Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
3741 | |
3742 | masm.loadFunctionFromCalleeToken(ptr, callee); |
3743 | } |
3744 | |
3745 | void CodeGenerator::visitIsConstructing(LIsConstructing* lir) { |
3746 | Register output = ToRegister(lir->output()); |
3747 | Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
3748 | masm.loadPtr(calleeToken, output); |
3749 | |
3750 | // We must be inside a function. |
3751 | MOZ_ASSERT(current->mir()->info().script()->function())do { static_assert( mozilla::detail::AssertionConditionType< decltype(current->mir()->info().script()->function() )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(current->mir()->info().script()->function() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "current->mir()->info().script()->function()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3751); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()" ")"); do { *((volatile int*)__null) = 3751; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3752 | |
3753 | // The low bit indicates whether this call is constructing, just clear the |
3754 | // other bits. |
3755 | static_assert(CalleeToken_Function == 0x0, |
3756 | "CalleeTokenTag value should match"); |
3757 | static_assert(CalleeToken_FunctionConstructing == 0x1, |
3758 | "CalleeTokenTag value should match"); |
3759 | masm.andPtr(Imm32(0x1), output); |
3760 | } |
3761 | |
3762 | void CodeGenerator::visitReturn(LReturn* lir) { |
3763 | #if defined(JS_NUNBOX32) |
3764 | DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX); |
3765 | DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX); |
3766 | MOZ_ASSERT(ToRegister(type) == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(type) == JSReturnReg_Type)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToRegister(type) == JSReturnReg_Type ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(type) == JSReturnReg_Type", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3766); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type" ")"); do { *((volatile int*)__null) = 3766; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3767 | MOZ_ASSERT(ToRegister(payload) == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(payload) == JSReturnReg_Data)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(payload) == JSReturnReg_Data))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(payload) == JSReturnReg_Data" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3767); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data" ")"); do { *((volatile int*)__null) = 3767; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3768 | #elif defined(JS_PUNBOX641) |
3769 | DebugOnly<LAllocation*> result = lir->getOperand(0); |
3770 | MOZ_ASSERT(ToRegister(result) == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(result) == JSReturnReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToRegister(result) == JSReturnReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(result) == JSReturnReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3770); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg" ")"); do { *((volatile int*)__null) = 3770; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3771 | #endif |
3772 | // Don't emit a jump to the return label if this is the last block, as |
3773 | // it'll fall through to the epilogue. |
3774 | // |
3775 | // This is -not- true however for a Generator-return, which may appear in the |
3776 | // middle of the last block, so we should always emit the jump there. |
3777 | if (current->mir() != *gen->graph().poBegin() || lir->isGenerator()) { |
3778 | masm.jump(&returnLabel_); |
3779 | } |
3780 | } |
3781 | |
3782 | void CodeGenerator::visitOsrEntry(LOsrEntry* lir) { |
3783 | Register temp = ToRegister(lir->temp()); |
3784 | |
3785 | // Remember the OSR entry offset into the code buffer. |
3786 | masm.flushBuffer(); |
3787 | setOsrEntryOffset(masm.size()); |
3788 | |
3789 | // Allocate the full frame for this function |
3790 | // Note we have a new entry here. So we reset MacroAssembler::framePushed() |
3791 | // to 0, before reserving the stack. |
3792 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3792); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 3792; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3793 | masm.setFramePushed(0); |
3794 | |
3795 | // The Baseline code ensured both the frame pointer and stack pointer point to |
3796 | // the JitFrameLayout on the stack. |
3797 | |
3798 | // If profiling, save the current frame pointer to a per-thread global field. |
3799 | if (isProfilerInstrumentationEnabled()) { |
3800 | masm.profilerEnterFrame(FramePointer, temp); |
3801 | } |
3802 | |
3803 | masm.reserveStack(frameSize()); |
3804 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3804); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 3804; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3805 | |
3806 | // Ensure that the Ion frames is properly aligned. |
3807 | masm.assertStackAlignment(JitStackAlignment, 0); |
3808 | } |
3809 | |
3810 | void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) { |
3811 | const LAllocation* frame = lir->getOperand(0); |
3812 | const LDefinition* object = lir->getDef(0); |
3813 | |
3814 | const ptrdiff_t frameOffset = |
3815 | BaselineFrame::reverseOffsetOfEnvironmentChain(); |
3816 | |
3817 | masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
3818 | } |
3819 | |
3820 | void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) { |
3821 | const LAllocation* frame = lir->getOperand(0); |
3822 | const LDefinition* object = lir->getDef(0); |
3823 | |
3824 | const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj(); |
3825 | |
3826 | masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
3827 | } |
3828 | |
3829 | void CodeGenerator::visitOsrValue(LOsrValue* value) { |
3830 | const LAllocation* frame = value->getOperand(0); |
3831 | const ValueOperand out = ToOutValue(value); |
3832 | |
3833 | const ptrdiff_t frameOffset = value->mir()->frameOffset(); |
3834 | |
3835 | masm.loadValue(Address(ToRegister(frame), frameOffset), out); |
3836 | } |
3837 | |
3838 | void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) { |
3839 | const LAllocation* frame = lir->getOperand(0); |
3840 | const ValueOperand out = ToOutValue(lir); |
3841 | |
3842 | Address flags = |
3843 | Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags()); |
3844 | Address retval = |
3845 | Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue()); |
3846 | |
3847 | masm.moveValue(UndefinedValue(), out); |
3848 | |
3849 | Label done; |
3850 | masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), |
3851 | &done); |
3852 | masm.loadValue(retval, out); |
3853 | masm.bind(&done); |
3854 | } |
3855 | |
3856 | void CodeGenerator::visitStackArgT(LStackArgT* lir) { |
3857 | const LAllocation* arg = lir->arg(); |
3858 | MIRType argType = lir->type(); |
3859 | uint32_t argslot = lir->argslot(); |
3860 | MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount())do { static_assert( mozilla::detail::AssertionConditionType< decltype(argslot - 1u < graph.argumentSlotCount())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argslot - 1u < graph.argumentSlotCount()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argslot - 1u < graph.argumentSlotCount()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3860); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()" ")"); do { *((volatile int*)__null) = 3860; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3861 | |
3862 | Address dest = AddressOfPassedArg(argslot); |
3863 | |
3864 | if (arg->isFloatReg()) { |
3865 | masm.boxDouble(ToFloatRegister(arg), dest); |
3866 | } else if (arg->isRegister()) { |
3867 | masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest); |
3868 | } else { |
3869 | masm.storeValue(arg->toConstant()->toJSValue(), dest); |
3870 | } |
3871 | } |
3872 | |
3873 | void CodeGenerator::visitStackArgV(LStackArgV* lir) { |
3874 | ValueOperand val = ToValue(lir, 0); |
3875 | uint32_t argslot = lir->argslot(); |
3876 | MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount())do { static_assert( mozilla::detail::AssertionConditionType< decltype(argslot - 1u < graph.argumentSlotCount())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argslot - 1u < graph.argumentSlotCount()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argslot - 1u < graph.argumentSlotCount()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3876); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()" ")"); do { *((volatile int*)__null) = 3876; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3877 | |
3878 | masm.storeValue(val, AddressOfPassedArg(argslot)); |
3879 | } |
3880 | |
3881 | void CodeGenerator::visitMoveGroup(LMoveGroup* group) { |
3882 | if (!group->numMoves()) { |
3883 | return; |
3884 | } |
3885 | |
3886 | MoveResolver& resolver = masm.moveResolver(); |
3887 | |
3888 | for (size_t i = 0; i < group->numMoves(); i++) { |
3889 | const LMove& move = group->getMove(i); |
3890 | |
3891 | LAllocation from = move.from(); |
3892 | LAllocation to = move.to(); |
3893 | LDefinition::Type type = move.type(); |
3894 | |
3895 | // No bogus moves. |
3896 | MOZ_ASSERT(from != to)do { static_assert( mozilla::detail::AssertionConditionType< decltype(from != to)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(from != to))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("from != to", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3896); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to" ")"); do { *((volatile int*)__null) = 3896; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3897 | MOZ_ASSERT(!from.isConstant())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!from.isConstant())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!from.isConstant()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!from.isConstant()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3897); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!from.isConstant()" ")"); do { *((volatile int*)__null) = 3897; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
3898 | MoveOp::Type moveType; |
3899 | switch (type) { |
3900 | case LDefinition::OBJECT: |
3901 | case LDefinition::SLOTS: |
3902 | case LDefinition::WASM_ANYREF: |
3903 | #ifdef JS_NUNBOX32 |
3904 | case LDefinition::TYPE: |
3905 | case LDefinition::PAYLOAD: |
3906 | #else |
3907 | case LDefinition::BOX: |
3908 | #endif |
3909 | case LDefinition::GENERAL: |
3910 | case LDefinition::STACKRESULTS: |
3911 | moveType = MoveOp::GENERAL; |
3912 | break; |
3913 | case LDefinition::INT32: |
3914 | moveType = MoveOp::INT32; |
3915 | break; |
3916 | case LDefinition::FLOAT32: |
3917 | moveType = MoveOp::FLOAT32; |
3918 | break; |
3919 | case LDefinition::DOUBLE: |
3920 | moveType = MoveOp::DOUBLE; |
3921 | break; |
3922 | case LDefinition::SIMD128: |
3923 | moveType = MoveOp::SIMD128; |
3924 | break; |
3925 | default: |
3926 | MOZ_CRASH("Unexpected move type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected move type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3926); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected move type" ")"); do { *((volatile int*)__null) = 3926; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
3927 | } |
3928 | |
3929 | masm.propagateOOM( |
3930 | resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType)); |
3931 | } |
3932 | |
3933 | masm.propagateOOM(resolver.resolve()); |
3934 | if (masm.oom()) { |
3935 | return; |
3936 | } |
3937 | |
3938 | MoveEmitter emitter(masm); |
3939 | |
3940 | #ifdef JS_CODEGEN_X86 |
3941 | if (group->maybeScratchRegister().isGeneralReg()) { |
3942 | emitter.setScratchRegister( |
3943 | group->maybeScratchRegister().toGeneralReg()->reg()); |
3944 | } else { |
3945 | resolver.sortMemoryToMemoryMoves(); |
3946 | } |
3947 | #endif |
3948 | |
3949 | emitter.emit(resolver); |
3950 | emitter.finish(); |
3951 | } |
3952 | |
3953 | void CodeGenerator::visitInteger(LInteger* lir) { |
3954 | masm.move32(Imm32(lir->i32()), ToRegister(lir->output())); |
3955 | } |
3956 | |
3957 | void CodeGenerator::visitInteger64(LInteger64* lir) { |
3958 | masm.move64(Imm64(lir->i64()), ToOutRegister64(lir)); |
3959 | } |
3960 | |
3961 | void CodeGenerator::visitPointer(LPointer* lir) { |
3962 | masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output())); |
3963 | } |
3964 | |
3965 | void CodeGenerator::visitNurseryObject(LNurseryObject* lir) { |
3966 | Register output = ToRegister(lir->output()); |
3967 | uint32_t nurseryIndex = lir->mir()->nurseryIndex(); |
3968 | |
3969 | // Load a pointer to the entry in IonScript's nursery objects list. |
3970 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output); |
3971 | masm.propagateOOM(ionNurseryObjectLabels_.emplaceBack(label, nurseryIndex)); |
3972 | |
3973 | // Load the JSObject*. |
3974 | masm.loadPtr(Address(output, 0), output); |
3975 | } |
3976 | |
3977 | void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) { |
3978 | // No-op. |
3979 | } |
3980 | |
3981 | void CodeGenerator::visitDebugEnterGCUnsafeRegion( |
3982 | LDebugEnterGCUnsafeRegion* lir) { |
3983 | Register temp = ToRegister(lir->temp0()); |
3984 | |
3985 | masm.loadJSContext(temp); |
3986 | |
3987 | Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion()); |
3988 | masm.add32(Imm32(1), inUnsafeRegion); |
3989 | |
3990 | Label ok; |
3991 | masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok); |
3992 | masm.assumeUnreachable("unbalanced enter/leave GC unsafe region"); |
3993 | masm.bind(&ok); |
3994 | } |
3995 | |
3996 | void CodeGenerator::visitDebugLeaveGCUnsafeRegion( |
3997 | LDebugLeaveGCUnsafeRegion* lir) { |
3998 | Register temp = ToRegister(lir->temp0()); |
3999 | |
4000 | masm.loadJSContext(temp); |
4001 | |
4002 | Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion()); |
4003 | masm.add32(Imm32(-1), inUnsafeRegion); |
4004 | |
4005 | Label ok; |
4006 | masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok); |
4007 | masm.assumeUnreachable("unbalanced enter/leave GC unsafe region"); |
4008 | masm.bind(&ok); |
4009 | } |
4010 | |
4011 | void CodeGenerator::visitSlots(LSlots* lir) { |
4012 | Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots()); |
4013 | masm.loadPtr(slots, ToRegister(lir->output())); |
4014 | } |
4015 | |
4016 | void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) { |
4017 | ValueOperand dest = ToOutValue(lir); |
4018 | Register base = ToRegister(lir->input()); |
4019 | int32_t offset = lir->mir()->slot() * sizeof(js::Value); |
4020 | |
4021 | masm.loadValue(Address(base, offset), dest); |
4022 | } |
4023 | |
4024 | static ConstantOrRegister ToConstantOrRegister(const LAllocation* value, |
4025 | MIRType valueType) { |
4026 | if (value->isConstant()) { |
4027 | return ConstantOrRegister(value->toConstant()->toJSValue()); |
4028 | } |
4029 | return TypedOrValueRegister(valueType, ToAnyRegister(value)); |
4030 | } |
4031 | |
4032 | void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) { |
4033 | Register base = ToRegister(lir->slots()); |
4034 | int32_t offset = lir->mir()->slot() * sizeof(js::Value); |
4035 | Address dest(base, offset); |
4036 | |
4037 | if (lir->mir()->needsBarrier()) { |
4038 | emitPreBarrier(dest); |
4039 | } |
4040 | |
4041 | MIRType valueType = lir->mir()->value()->type(); |
4042 | ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType); |
4043 | masm.storeUnboxedValue(value, valueType, dest); |
4044 | } |
4045 | |
4046 | void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) { |
4047 | Register base = ToRegister(lir->slots()); |
4048 | int32_t offset = lir->mir()->slot() * sizeof(Value); |
4049 | |
4050 | const ValueOperand value = ToValue(lir, LStoreDynamicSlotV::ValueIndex); |
4051 | |
4052 | if (lir->mir()->needsBarrier()) { |
4053 | emitPreBarrier(Address(base, offset)); |
4054 | } |
4055 | |
4056 | masm.storeValue(value, Address(base, offset)); |
4057 | } |
4058 | |
4059 | void CodeGenerator::visitElements(LElements* lir) { |
4060 | Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); |
4061 | masm.loadPtr(elements, ToRegister(lir->output())); |
4062 | } |
4063 | |
4064 | void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) { |
4065 | Address environment(ToRegister(lir->function()), |
4066 | JSFunction::offsetOfEnvironment()); |
4067 | masm.unboxObject(environment, ToRegister(lir->output())); |
4068 | } |
4069 | |
4070 | void CodeGenerator::visitHomeObject(LHomeObject* lir) { |
4071 | Register func = ToRegister(lir->function()); |
4072 | Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot()); |
4073 | |
4074 | masm.assertFunctionIsExtended(func); |
4075 | #ifdef DEBUG1 |
4076 | Label isObject; |
4077 | masm.branchTestObject(Assembler::Equal, homeObject, &isObject); |
4078 | masm.assumeUnreachable("[[HomeObject]] must be Object"); |
4079 | masm.bind(&isObject); |
4080 | #endif |
4081 | |
4082 | masm.unboxObject(homeObject, ToRegister(lir->output())); |
4083 | } |
4084 | |
4085 | void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) { |
4086 | Register homeObject = ToRegister(lir->homeObject()); |
4087 | ValueOperand output = ToOutValue(lir); |
4088 | Register temp = output.scratchReg(); |
4089 | |
4090 | masm.loadObjProto(homeObject, temp); |
4091 | |
4092 | #ifdef DEBUG1 |
4093 | // We won't encounter a lazy proto, because the prototype is guaranteed to |
4094 | // either be a JSFunction or a PlainObject, and only proxy objects can have a |
4095 | // lazy proto. |
4096 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 4096; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4097 | |
4098 | Label proxyCheckDone; |
4099 | masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone); |
4100 | masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase"); |
4101 | masm.bind(&proxyCheckDone); |
4102 | #endif |
4103 | |
4104 | Label nullProto, done; |
4105 | masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto); |
4106 | |
4107 | // Box prototype and return |
4108 | masm.tagValue(JSVAL_TYPE_OBJECT, temp, output); |
4109 | masm.jump(&done); |
4110 | |
4111 | masm.bind(&nullProto); |
4112 | masm.moveValue(NullValue(), output); |
4113 | |
4114 | masm.bind(&done); |
4115 | } |
4116 | |
4117 | template <class T> |
4118 | static T* ToConstantObject(MDefinition* def) { |
4119 | MOZ_ASSERT(def->isConstant())do { static_assert( mozilla::detail::AssertionConditionType< decltype(def->isConstant())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(def->isConstant()))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("def->isConstant()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->isConstant()" ")"); do { *((volatile int*)__null) = 4119; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4120 | return &def->toConstant()->toObject().as<T>(); |
4121 | } |
4122 | |
4123 | void CodeGenerator::visitNewLexicalEnvironmentObject( |
4124 | LNewLexicalEnvironmentObject* lir) { |
4125 | Register output = ToRegister(lir->output()); |
4126 | Register temp = ToRegister(lir->temp0()); |
4127 | |
4128 | auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>( |
4129 | lir->mir()->templateObj()); |
4130 | auto* scope = &templateObj->scope(); |
4131 | gc::Heap initialHeap = gc::Heap::Default; |
4132 | |
4133 | using Fn = |
4134 | BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>); |
4135 | auto* ool = |
4136 | oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>( |
4137 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
4138 | |
4139 | TemplateObject templateObject(templateObj); |
4140 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
4141 | |
4142 | masm.bind(ool->rejoin()); |
4143 | } |
4144 | |
4145 | void CodeGenerator::visitNewClassBodyEnvironmentObject( |
4146 | LNewClassBodyEnvironmentObject* lir) { |
4147 | Register output = ToRegister(lir->output()); |
4148 | Register temp = ToRegister(lir->temp0()); |
4149 | |
4150 | auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>( |
4151 | lir->mir()->templateObj()); |
4152 | auto* scope = &templateObj->scope(); |
4153 | gc::Heap initialHeap = gc::Heap::Default; |
4154 | |
4155 | using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*, |
4156 | Handle<ClassBodyScope*>); |
4157 | auto* ool = |
4158 | oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>( |
4159 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
4160 | |
4161 | TemplateObject templateObject(templateObj); |
4162 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
4163 | |
4164 | masm.bind(ool->rejoin()); |
4165 | } |
4166 | |
4167 | void CodeGenerator::visitNewVarEnvironmentObject( |
4168 | LNewVarEnvironmentObject* lir) { |
4169 | Register output = ToRegister(lir->output()); |
4170 | Register temp = ToRegister(lir->temp0()); |
4171 | |
4172 | auto* templateObj = |
4173 | ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj()); |
4174 | auto* scope = &templateObj->scope().as<VarScope>(); |
4175 | gc::Heap initialHeap = gc::Heap::Default; |
4176 | |
4177 | using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>); |
4178 | auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>( |
4179 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
4180 | |
4181 | TemplateObject templateObject(templateObj); |
4182 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
4183 | |
4184 | masm.bind(ool->rejoin()); |
4185 | } |
4186 | |
4187 | void CodeGenerator::visitGuardShape(LGuardShape* guard) { |
4188 | Register obj = ToRegister(guard->input()); |
4189 | Register temp = ToTempRegisterOrInvalid(guard->temp0()); |
4190 | Label bail; |
4191 | masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp, |
4192 | obj, &bail); |
4193 | bailoutFrom(&bail, guard->snapshot()); |
4194 | } |
4195 | |
4196 | void CodeGenerator::visitGuardFuse(LGuardFuse* guard) { |
4197 | auto fuseIndex = guard->mir()->fuseIndex(); |
4198 | switch (fuseIndex) { |
4199 | case RealmFuses::FuseIndex::OptimizeGetIteratorFuse: |
4200 | addOptimizeGetIteratorFuseDependency(); |
4201 | return; |
4202 | default: |
4203 | // validateAndRegisterFuseDependencies doesn't have |
4204 | // handling for this yet, actively check fuse instead. |
4205 | break; |
4206 | } |
4207 | |
4208 | Register temp = ToRegister(guard->temp0()); |
4209 | Label bail; |
4210 | |
4211 | // Bake specific fuse address for Ion code, because we won't share this code |
4212 | // across realms. |
4213 | GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex); |
4214 | masm.loadPtr(AbsoluteAddress(fuse->fuseRef()), temp); |
4215 | masm.branchPtr(Assembler::NotEqual, temp, ImmPtr(nullptr), &bail); |
4216 | |
4217 | bailoutFrom(&bail, guard->snapshot()); |
4218 | } |
4219 | |
4220 | void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) { |
4221 | Register obj = ToRegister(guard->object()); |
4222 | Register shapeList = ToRegister(guard->shapeList()); |
4223 | Register temp = ToRegister(guard->temp0()); |
4224 | Register temp2 = ToRegister(guard->temp1()); |
4225 | Register temp3 = ToRegister(guard->temp2()); |
4226 | Register spectre = ToTempRegisterOrInvalid(guard->temp3()); |
4227 | |
4228 | Label bail; |
4229 | masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp); |
4230 | masm.branchTestObjShapeList(Assembler::NotEqual, obj, temp, temp2, temp3, |
4231 | spectre, &bail); |
4232 | bailoutFrom(&bail, guard->snapshot()); |
4233 | } |
4234 | |
4235 | void CodeGenerator::visitGuardProto(LGuardProto* guard) { |
4236 | Register obj = ToRegister(guard->object()); |
4237 | Register expected = ToRegister(guard->expected()); |
4238 | Register temp = ToRegister(guard->temp0()); |
4239 | |
4240 | masm.loadObjProto(obj, temp); |
4241 | |
4242 | Label bail; |
4243 | masm.branchPtr(Assembler::NotEqual, temp, expected, &bail); |
4244 | bailoutFrom(&bail, guard->snapshot()); |
4245 | } |
4246 | |
4247 | void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) { |
4248 | Register obj = ToRegister(guard->input()); |
4249 | Register temp = ToRegister(guard->temp0()); |
4250 | |
4251 | masm.loadObjProto(obj, temp); |
4252 | |
4253 | Label bail; |
4254 | masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail); |
4255 | bailoutFrom(&bail, guard->snapshot()); |
4256 | } |
4257 | |
4258 | void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) { |
4259 | Register obj = ToRegister(guard->input()); |
4260 | Register temp = ToRegister(guard->temp0()); |
4261 | |
4262 | Label bail; |
4263 | masm.branchIfNonNativeObj(obj, temp, &bail); |
4264 | bailoutFrom(&bail, guard->snapshot()); |
4265 | } |
4266 | |
4267 | void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) { |
4268 | Register temp = ToRegister(guard->temp0()); |
4269 | Label bail; |
4270 | |
4271 | masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp); |
4272 | masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()), |
4273 | &bail); |
4274 | bailoutFrom(&bail, guard->snapshot()); |
4275 | } |
4276 | |
4277 | void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) { |
4278 | Register obj = ToRegister(guard->input()); |
4279 | Register temp = ToRegister(guard->temp0()); |
4280 | |
4281 | Label bail; |
4282 | masm.branchTestObjectIsProxy(false, obj, temp, &bail); |
4283 | bailoutFrom(&bail, guard->snapshot()); |
4284 | } |
4285 | |
4286 | void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) { |
4287 | Register obj = ToRegister(guard->input()); |
4288 | Register temp = ToRegister(guard->temp0()); |
4289 | |
4290 | Label bail; |
4291 | masm.branchTestObjectIsProxy(true, obj, temp, &bail); |
4292 | bailoutFrom(&bail, guard->snapshot()); |
4293 | } |
4294 | |
4295 | void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) { |
4296 | Register proxy = ToRegister(guard->proxy()); |
4297 | Register temp = ToRegister(guard->temp0()); |
4298 | |
4299 | Label bail; |
4300 | masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp, |
4301 | GetDOMProxyHandlerFamily(), &bail); |
4302 | bailoutFrom(&bail, guard->snapshot()); |
4303 | } |
4304 | |
4305 | void CodeGenerator::visitProxyGet(LProxyGet* lir) { |
4306 | Register proxy = ToRegister(lir->proxy()); |
4307 | Register temp = ToRegister(lir->temp0()); |
4308 | |
4309 | pushArg(lir->mir()->id(), temp); |
4310 | pushArg(proxy); |
4311 | |
4312 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue); |
4313 | callVM<Fn, ProxyGetProperty>(lir); |
4314 | } |
4315 | |
4316 | void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) { |
4317 | Register proxy = ToRegister(lir->proxy()); |
4318 | ValueOperand idVal = ToValue(lir, LProxyGetByValue::IdIndex); |
4319 | |
4320 | pushArg(idVal); |
4321 | pushArg(proxy); |
4322 | |
4323 | using Fn = |
4324 | bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue); |
4325 | callVM<Fn, ProxyGetPropertyByValue>(lir); |
4326 | } |
4327 | |
4328 | void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) { |
4329 | Register proxy = ToRegister(lir->proxy()); |
4330 | ValueOperand idVal = ToValue(lir, LProxyHasProp::IdIndex); |
4331 | |
4332 | pushArg(idVal); |
4333 | pushArg(proxy); |
4334 | |
4335 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*); |
4336 | if (lir->mir()->hasOwn()) { |
4337 | callVM<Fn, ProxyHasOwn>(lir); |
4338 | } else { |
4339 | callVM<Fn, ProxyHas>(lir); |
4340 | } |
4341 | } |
4342 | |
4343 | void CodeGenerator::visitProxySet(LProxySet* lir) { |
4344 | Register proxy = ToRegister(lir->proxy()); |
4345 | ValueOperand rhs = ToValue(lir, LProxySet::RhsIndex); |
4346 | Register temp = ToRegister(lir->temp0()); |
4347 | |
4348 | pushArg(Imm32(lir->mir()->strict())); |
4349 | pushArg(rhs); |
4350 | pushArg(lir->mir()->id(), temp); |
4351 | pushArg(proxy); |
4352 | |
4353 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool); |
4354 | callVM<Fn, ProxySetProperty>(lir); |
4355 | } |
4356 | |
4357 | void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) { |
4358 | Register proxy = ToRegister(lir->proxy()); |
4359 | ValueOperand idVal = ToValue(lir, LProxySetByValue::IdIndex); |
4360 | ValueOperand rhs = ToValue(lir, LProxySetByValue::RhsIndex); |
4361 | |
4362 | pushArg(Imm32(lir->mir()->strict())); |
4363 | pushArg(rhs); |
4364 | pushArg(idVal); |
4365 | pushArg(proxy); |
4366 | |
4367 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool); |
4368 | callVM<Fn, ProxySetPropertyByValue>(lir); |
4369 | } |
4370 | |
4371 | void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) { |
4372 | Register obj = ToRegister(lir->obj()); |
4373 | ValueOperand rhs = ToValue(lir, LCallSetArrayLength::RhsIndex); |
4374 | |
4375 | pushArg(Imm32(lir->mir()->strict())); |
4376 | pushArg(rhs); |
4377 | pushArg(obj); |
4378 | |
4379 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool); |
4380 | callVM<Fn, jit::SetArrayLength>(lir); |
4381 | } |
4382 | |
4383 | void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) { |
4384 | Register obj = ToRegister(lir->object()); |
4385 | Register temp0 = ToRegister(lir->temp0()); |
4386 | Register temp1 = ToRegister(lir->temp1()); |
4387 | Register temp2 = ToRegister(lir->temp2()); |
4388 | Register temp3 = ToRegister(lir->temp3()); |
4389 | ValueOperand output = ToOutValue(lir); |
4390 | |
4391 | Label cacheHit; |
4392 | masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2, |
4393 | output, &cacheHit); |
4394 | |
4395 | Label bail; |
4396 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
4397 | |
4398 | masm.Push(UndefinedValue()); |
4399 | masm.moveStackPtrTo(temp3); |
4400 | |
4401 | using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id, |
4402 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
4403 | masm.setupAlignedABICall(); |
4404 | masm.loadJSContext(temp0); |
4405 | masm.passABIArg(temp0); |
4406 | masm.passABIArg(obj); |
4407 | masm.movePropertyKey(lir->mir()->name(), temp1); |
4408 | masm.passABIArg(temp1); |
4409 | masm.passABIArg(temp2); |
4410 | masm.passABIArg(temp3); |
4411 | |
4412 | masm.callWithABI<Fn, GetNativeDataPropertyPure>(); |
4413 | |
4414 | MOZ_ASSERT(!output.aliases(ReturnReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!output.aliases(ReturnReg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!output.aliases(ReturnReg))) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!output.aliases(ReturnReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4414); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)" ")"); do { *((volatile int*)__null) = 4414; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4415 | masm.Pop(output); |
4416 | |
4417 | masm.branchIfFalseBool(ReturnReg, &bail); |
4418 | masm.bind(&cacheHit); |
4419 | |
4420 | bailoutFrom(&bail, lir->snapshot()); |
4421 | } |
4422 | |
4423 | void CodeGenerator::visitMegamorphicLoadSlotPermissive( |
4424 | LMegamorphicLoadSlotPermissive* lir) { |
4425 | Register obj = ToRegister(lir->object()); |
4426 | Register temp0 = ToRegister(lir->temp0()); |
4427 | Register temp1 = ToRegister(lir->temp1()); |
4428 | Register temp2 = ToRegister(lir->temp2()); |
4429 | ValueOperand output = ToOutValue(lir); |
4430 | |
4431 | Label cacheHit; |
4432 | masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2, |
4433 | output, &cacheHit); |
4434 | |
4435 | masm.movePropertyKey(lir->mir()->name(), temp1); |
4436 | pushArg(temp2); |
4437 | pushArg(temp1); |
4438 | pushArg(obj); |
4439 | |
4440 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, |
4441 | MegamorphicCacheEntry*, MutableHandleValue); |
4442 | callVM<Fn, GetPropMaybeCached>(lir); |
4443 | |
4444 | masm.bind(&cacheHit); |
4445 | } |
4446 | |
4447 | void CodeGenerator::visitMegamorphicLoadSlotByValue( |
4448 | LMegamorphicLoadSlotByValue* lir) { |
4449 | Register obj = ToRegister(lir->object()); |
4450 | ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex); |
4451 | Register temp0 = ToRegister(lir->temp0()); |
4452 | Register temp1 = ToRegister(lir->temp1()); |
4453 | Register temp2 = ToRegister(lir->temp2()); |
4454 | ValueOperand output = ToOutValue(lir); |
4455 | |
4456 | Label cacheHit, bail; |
4457 | masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2, |
4458 | output, &cacheHit); |
4459 | |
4460 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
4461 | |
4462 | // idVal will be in vp[0], result will be stored in vp[1]. |
4463 | masm.reserveStack(sizeof(Value)); |
4464 | masm.Push(idVal); |
4465 | masm.moveStackPtrTo(temp0); |
4466 | |
4467 | using Fn = bool (*)(JSContext* cx, JSObject* obj, |
4468 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
4469 | masm.setupAlignedABICall(); |
4470 | masm.loadJSContext(temp1); |
4471 | masm.passABIArg(temp1); |
4472 | masm.passABIArg(obj); |
4473 | masm.passABIArg(temp2); |
4474 | masm.passABIArg(temp0); |
4475 | masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>(); |
4476 | |
4477 | MOZ_ASSERT(!idVal.aliases(temp0))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!idVal.aliases(temp0))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!idVal.aliases(temp0)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("!idVal.aliases(temp0)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4477); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { *((volatile int*)__null) = 4477; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4478 | masm.storeCallPointerResult(temp0); |
4479 | masm.Pop(idVal); |
4480 | |
4481 | uint32_t framePushed = masm.framePushed(); |
4482 | Label ok; |
4483 | masm.branchIfTrueBool(temp0, &ok); |
4484 | masm.freeStack(sizeof(Value)); // Discard result Value. |
4485 | masm.jump(&bail); |
4486 | |
4487 | masm.bind(&ok); |
4488 | masm.setFramePushed(framePushed); |
4489 | masm.Pop(output); |
4490 | |
4491 | masm.bind(&cacheHit); |
4492 | |
4493 | bailoutFrom(&bail, lir->snapshot()); |
4494 | } |
4495 | |
4496 | void CodeGenerator::visitMegamorphicLoadSlotByValuePermissive( |
4497 | LMegamorphicLoadSlotByValuePermissive* lir) { |
4498 | Register obj = ToRegister(lir->object()); |
4499 | ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex); |
4500 | Register temp0 = ToRegister(lir->temp0()); |
4501 | Register temp1 = ToRegister(lir->temp1()); |
4502 | Register temp2 = ToRegister(lir->temp2()); |
4503 | ValueOperand output = ToOutValue(lir); |
4504 | |
4505 | Label cacheHit; |
4506 | masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2, |
4507 | output, &cacheHit); |
4508 | |
4509 | pushArg(temp2); |
4510 | pushArg(idVal); |
4511 | pushArg(obj); |
4512 | |
4513 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, |
4514 | MegamorphicCacheEntry*, MutableHandleValue); |
4515 | callVM<Fn, GetElemMaybeCached>(lir); |
4516 | |
4517 | masm.bind(&cacheHit); |
4518 | } |
4519 | |
4520 | void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) { |
4521 | Register obj = ToRegister(lir->object()); |
4522 | ValueOperand value = ToValue(lir, LMegamorphicStoreSlot::RhsIndex); |
4523 | |
4524 | Register temp0 = ToRegister(lir->temp0()); |
4525 | #ifndef JS_CODEGEN_X86 |
4526 | Register temp1 = ToRegister(lir->temp1()); |
4527 | Register temp2 = ToRegister(lir->temp2()); |
4528 | #endif |
4529 | |
4530 | Label cacheHit, done; |
4531 | #ifdef JS_CODEGEN_X86 |
4532 | masm.emitMegamorphicCachedSetSlot( |
4533 | lir->mir()->name(), obj, temp0, value, &cacheHit, |
4534 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
4535 | EmitPreBarrier(masm, addr, mirType); |
4536 | }); |
4537 | #else |
4538 | masm.emitMegamorphicCachedSetSlot( |
4539 | lir->mir()->name(), obj, temp0, temp1, temp2, value, &cacheHit, |
4540 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
4541 | EmitPreBarrier(masm, addr, mirType); |
4542 | }); |
4543 | #endif |
4544 | |
4545 | pushArg(Imm32(lir->mir()->strict())); |
4546 | pushArg(value); |
4547 | pushArg(lir->mir()->name(), temp0); |
4548 | pushArg(obj); |
4549 | |
4550 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool); |
4551 | callVM<Fn, SetPropertyMegamorphic<true>>(lir); |
4552 | |
4553 | masm.jump(&done); |
4554 | masm.bind(&cacheHit); |
4555 | |
4556 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done); |
4557 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done); |
4558 | |
4559 | saveVolatile(temp0); |
4560 | emitPostWriteBarrier(obj); |
4561 | restoreVolatile(temp0); |
4562 | |
4563 | masm.bind(&done); |
4564 | } |
4565 | |
4566 | void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) { |
4567 | Register obj = ToRegister(lir->object()); |
4568 | ValueOperand idVal = ToValue(lir, LMegamorphicHasProp::IdIndex); |
4569 | Register temp0 = ToRegister(lir->temp0()); |
4570 | Register temp1 = ToRegister(lir->temp1()); |
4571 | Register temp2 = ToRegister(lir->temp2()); |
4572 | Register output = ToRegister(lir->output()); |
4573 | |
4574 | Label bail, cacheHit; |
4575 | masm.emitMegamorphicCacheLookupExists(idVal, obj, temp0, temp1, temp2, output, |
4576 | &cacheHit, lir->mir()->hasOwn()); |
4577 | |
4578 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
4579 | |
4580 | // idVal will be in vp[0], result will be stored in vp[1]. |
4581 | masm.reserveStack(sizeof(Value)); |
4582 | masm.Push(idVal); |
4583 | masm.moveStackPtrTo(temp0); |
4584 | |
4585 | using Fn = bool (*)(JSContext* cx, JSObject* obj, |
4586 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
4587 | masm.setupAlignedABICall(); |
4588 | masm.loadJSContext(temp1); |
4589 | masm.passABIArg(temp1); |
4590 | masm.passABIArg(obj); |
4591 | masm.passABIArg(temp2); |
4592 | masm.passABIArg(temp0); |
4593 | if (lir->mir()->hasOwn()) { |
4594 | masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>(); |
4595 | } else { |
4596 | masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>(); |
4597 | } |
4598 | |
4599 | MOZ_ASSERT(!idVal.aliases(temp0))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!idVal.aliases(temp0))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!idVal.aliases(temp0)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("!idVal.aliases(temp0)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4599); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { *((volatile int*)__null) = 4599; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4600 | masm.storeCallPointerResult(temp0); |
4601 | masm.Pop(idVal); |
4602 | |
4603 | uint32_t framePushed = masm.framePushed(); |
4604 | Label ok; |
4605 | masm.branchIfTrueBool(temp0, &ok); |
4606 | masm.freeStack(sizeof(Value)); // Discard result Value. |
4607 | masm.jump(&bail); |
4608 | |
4609 | masm.bind(&ok); |
4610 | masm.setFramePushed(framePushed); |
4611 | masm.unboxBoolean(Address(masm.getStackPointer(), 0), output); |
4612 | masm.freeStack(sizeof(Value)); |
4613 | masm.bind(&cacheHit); |
4614 | |
4615 | bailoutFrom(&bail, lir->snapshot()); |
4616 | } |
4617 | |
4618 | void CodeGenerator::visitSmallObjectVariableKeyHasProp( |
4619 | LSmallObjectVariableKeyHasProp* lir) { |
4620 | Register id = ToRegister(lir->id()); |
4621 | Register output = ToRegister(lir->output()); |
4622 | |
4623 | #ifdef DEBUG1 |
4624 | Label isAtom; |
4625 | masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()), |
4626 | Imm32(JSString::ATOM_BIT), &isAtom); |
4627 | masm.assumeUnreachable("Expected atom input"); |
4628 | masm.bind(&isAtom); |
4629 | #endif |
4630 | |
4631 | SharedShape* shape = &lir->mir()->shape()->asShared(); |
4632 | |
4633 | Label done, success; |
4634 | for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) { |
4635 | masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()), |
4636 | &success); |
4637 | } |
4638 | masm.move32(Imm32(0), output); |
4639 | masm.jump(&done); |
4640 | masm.bind(&success); |
4641 | masm.move32(Imm32(1), output); |
4642 | masm.bind(&done); |
4643 | } |
4644 | |
4645 | void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared( |
4646 | LGuardIsNotArrayBufferMaybeShared* guard) { |
4647 | Register obj = ToRegister(guard->input()); |
4648 | Register temp = ToRegister(guard->temp0()); |
4649 | |
4650 | Label bail; |
4651 | masm.loadObjClassUnsafe(obj, temp); |
4652 | masm.branchPtr(Assembler::Equal, temp, |
4653 | ImmPtr(&FixedLengthArrayBufferObject::class_), &bail); |
4654 | masm.branchPtr(Assembler::Equal, temp, |
4655 | ImmPtr(&FixedLengthSharedArrayBufferObject::class_), &bail); |
4656 | masm.branchPtr(Assembler::Equal, temp, |
4657 | ImmPtr(&ResizableArrayBufferObject::class_), &bail); |
4658 | masm.branchPtr(Assembler::Equal, temp, |
4659 | ImmPtr(&GrowableSharedArrayBufferObject::class_), &bail); |
4660 | bailoutFrom(&bail, guard->snapshot()); |
4661 | } |
4662 | |
4663 | void CodeGenerator::visitGuardIsTypedArray(LGuardIsTypedArray* guard) { |
4664 | Register obj = ToRegister(guard->input()); |
4665 | Register temp = ToRegister(guard->temp0()); |
4666 | |
4667 | Label bail; |
4668 | masm.loadObjClassUnsafe(obj, temp); |
4669 | masm.branchIfClassIsNotTypedArray(temp, &bail); |
4670 | bailoutFrom(&bail, guard->snapshot()); |
4671 | } |
4672 | |
4673 | void CodeGenerator::visitGuardIsFixedLengthTypedArray( |
4674 | LGuardIsFixedLengthTypedArray* guard) { |
4675 | Register obj = ToRegister(guard->input()); |
4676 | Register temp = ToRegister(guard->temp0()); |
4677 | |
4678 | Label bail; |
4679 | masm.loadObjClassUnsafe(obj, temp); |
4680 | masm.branchIfClassIsNotFixedLengthTypedArray(temp, &bail); |
4681 | bailoutFrom(&bail, guard->snapshot()); |
4682 | } |
4683 | |
4684 | void CodeGenerator::visitGuardIsResizableTypedArray( |
4685 | LGuardIsResizableTypedArray* guard) { |
4686 | Register obj = ToRegister(guard->input()); |
4687 | Register temp = ToRegister(guard->temp0()); |
4688 | |
4689 | Label bail; |
4690 | masm.loadObjClassUnsafe(obj, temp); |
4691 | masm.branchIfClassIsNotResizableTypedArray(temp, &bail); |
4692 | bailoutFrom(&bail, guard->snapshot()); |
4693 | } |
4694 | |
4695 | void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) { |
4696 | Register obj = ToRegister(guard->input()); |
4697 | |
4698 | Label bail; |
4699 | |
4700 | Address handlerAddr(obj, ProxyObject::offsetOfHandler()); |
4701 | masm.branchPtr(Assembler::NotEqual, handlerAddr, |
4702 | ImmPtr(guard->mir()->handler()), &bail); |
4703 | |
4704 | bailoutFrom(&bail, guard->snapshot()); |
4705 | } |
4706 | |
4707 | void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) { |
4708 | Register input = ToRegister(guard->input()); |
4709 | Register expected = ToRegister(guard->expected()); |
4710 | |
4711 | Assembler::Condition cond = |
4712 | guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
4713 | bailoutCmpPtr(cond, input, expected, guard->snapshot()); |
4714 | } |
4715 | |
4716 | void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) { |
4717 | Register input = ToRegister(guard->input()); |
4718 | Register expected = ToRegister(guard->expected()); |
4719 | |
4720 | bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot()); |
4721 | } |
4722 | |
4723 | void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) { |
4724 | Register str = ToRegister(guard->str()); |
4725 | Register scratch = ToRegister(guard->temp0()); |
4726 | |
4727 | LiveRegisterSet volatileRegs = liveVolatileRegs(guard); |
4728 | volatileRegs.takeUnchecked(scratch); |
4729 | |
4730 | Label bail; |
4731 | masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs, |
4732 | &bail); |
4733 | bailoutFrom(&bail, guard->snapshot()); |
4734 | } |
4735 | |
4736 | void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) { |
4737 | Register symbol = ToRegister(guard->symbol()); |
4738 | |
4739 | bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()), |
4740 | guard->snapshot()); |
4741 | } |
4742 | |
4743 | void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) { |
4744 | Register num = ToRegister(guard->num()); |
4745 | |
4746 | bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()), |
4747 | guard->snapshot()); |
4748 | } |
4749 | |
4750 | void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) { |
4751 | Register str = ToRegister(lir->string()); |
4752 | Register output = ToRegister(lir->output()); |
4753 | |
4754 | Label vmCall, done; |
4755 | masm.loadStringIndexValue(str, output, &vmCall); |
4756 | masm.jump(&done); |
4757 | |
4758 | { |
4759 | masm.bind(&vmCall); |
4760 | |
4761 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
4762 | volatileRegs.takeUnchecked(output); |
4763 | masm.PushRegsInMask(volatileRegs); |
4764 | |
4765 | using Fn = int32_t (*)(JSString* str); |
4766 | masm.setupAlignedABICall(); |
4767 | masm.passABIArg(str); |
4768 | masm.callWithABI<Fn, GetIndexFromString>(); |
4769 | masm.storeCallInt32Result(output); |
4770 | |
4771 | masm.PopRegsInMask(volatileRegs); |
4772 | |
4773 | // GetIndexFromString returns a negative value on failure. |
4774 | bailoutTest32(Assembler::Signed, output, output, lir->snapshot()); |
4775 | } |
4776 | |
4777 | masm.bind(&done); |
4778 | } |
4779 | |
4780 | void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) { |
4781 | Register str = ToRegister(lir->string()); |
4782 | Register output = ToRegister(lir->output()); |
4783 | Register temp = ToRegister(lir->temp0()); |
4784 | |
4785 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
4786 | |
4787 | Label bail; |
4788 | masm.guardStringToInt32(str, output, temp, volatileRegs, &bail); |
4789 | bailoutFrom(&bail, lir->snapshot()); |
4790 | } |
4791 | |
4792 | void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) { |
4793 | Register str = ToRegister(lir->string()); |
4794 | FloatRegister output = ToFloatRegister(lir->output()); |
4795 | Register temp0 = ToRegister(lir->temp0()); |
4796 | Register temp1 = ToRegister(lir->temp1()); |
4797 | |
4798 | Label vmCall, done; |
4799 | // Use indexed value as fast path if possible. |
4800 | masm.loadStringIndexValue(str, temp0, &vmCall); |
4801 | masm.convertInt32ToDouble(temp0, output); |
4802 | masm.jump(&done); |
4803 | { |
4804 | masm.bind(&vmCall); |
4805 | |
4806 | // Reserve stack for holding the result value of the call. |
4807 | masm.reserveStack(sizeof(double)); |
4808 | masm.moveStackPtrTo(temp0); |
4809 | |
4810 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
4811 | volatileRegs.takeUnchecked(temp0); |
4812 | volatileRegs.takeUnchecked(temp1); |
4813 | masm.PushRegsInMask(volatileRegs); |
4814 | |
4815 | using Fn = bool (*)(JSContext* cx, JSString* str, double* result); |
4816 | masm.setupAlignedABICall(); |
4817 | masm.loadJSContext(temp1); |
4818 | masm.passABIArg(temp1); |
4819 | masm.passABIArg(str); |
4820 | masm.passABIArg(temp0); |
4821 | masm.callWithABI<Fn, StringToNumberPure>(); |
4822 | masm.storeCallPointerResult(temp0); |
4823 | |
4824 | masm.PopRegsInMask(volatileRegs); |
4825 | |
4826 | Label ok; |
4827 | masm.branchIfTrueBool(temp0, &ok); |
4828 | { |
4829 | // OOM path, recovered by StringToNumberPure. |
4830 | // |
4831 | // Use addToStackPtr instead of freeStack as freeStack tracks stack height |
4832 | // flow-insensitively, and using it here would confuse the stack height |
4833 | // tracking. |
4834 | masm.addToStackPtr(Imm32(sizeof(double))); |
4835 | bailout(lir->snapshot()); |
4836 | } |
4837 | masm.bind(&ok); |
4838 | masm.Pop(output); |
4839 | } |
4840 | masm.bind(&done); |
4841 | } |
4842 | |
4843 | void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) { |
4844 | Register obj = ToRegister(guard->input()); |
4845 | Register temp = ToRegister(guard->temp0()); |
4846 | |
4847 | // Load obj->elements. |
4848 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp); |
4849 | |
4850 | // Make sure there are no dense elements. |
4851 | Address initLength(temp, ObjectElements::offsetOfInitializedLength()); |
4852 | bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot()); |
4853 | } |
4854 | |
4855 | void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) { |
4856 | Register input = ToRegister(lir->input()); |
4857 | Register64 output = ToOutRegister64(lir); |
4858 | |
4859 | masm.move32To64ZeroExtend(input, output); |
4860 | } |
4861 | |
4862 | void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input, |
4863 | Register64 output) { |
4864 | Register temp = output.scratchReg(); |
4865 | |
4866 | saveLive(lir); |
4867 | |
4868 | masm.reserveStack(sizeof(uint64_t)); |
4869 | masm.moveStackPtrTo(temp); |
4870 | pushArg(temp); |
4871 | pushArg(input); |
4872 | |
4873 | using Fn = bool (*)(JSContext*, HandleString, uint64_t*); |
4874 | callVM<Fn, DoStringToInt64>(lir); |
4875 | |
4876 | masm.load64(Address(masm.getStackPointer(), 0), output); |
4877 | masm.freeStack(sizeof(uint64_t)); |
4878 | |
4879 | restoreLiveIgnore(lir, StoreValueTo(output).clobbered()); |
4880 | } |
4881 | |
4882 | void CodeGenerator::visitStringToInt64(LStringToInt64* lir) { |
4883 | Register input = ToRegister(lir->input()); |
4884 | Register64 output = ToOutRegister64(lir); |
4885 | |
4886 | emitStringToInt64(lir, input, output); |
4887 | } |
4888 | |
4889 | void CodeGenerator::visitValueToInt64(LValueToInt64* lir) { |
4890 | ValueOperand input = ToValue(lir, LValueToInt64::InputIndex); |
4891 | Register temp = ToRegister(lir->temp0()); |
4892 | Register64 output = ToOutRegister64(lir); |
4893 | |
4894 | int checks = 3; |
4895 | |
4896 | Label fail, done; |
4897 | // Jump to fail if this is the last check and we fail it, |
4898 | // otherwise to the next test. |
4899 | auto emitTestAndUnbox = [&](auto testAndUnbox) { |
4900 | MOZ_ASSERT(checks > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(checks > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(checks > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("checks > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4900); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks > 0" ")"); do { *((volatile int*)__null) = 4900; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4901 | |
4902 | checks--; |
4903 | Label notType; |
4904 | Label* target = checks ? ¬Type : &fail; |
4905 | |
4906 | testAndUnbox(target); |
4907 | |
4908 | if (checks) { |
4909 | masm.jump(&done); |
4910 | masm.bind(¬Type); |
4911 | } |
4912 | }; |
4913 | |
4914 | Register tag = masm.extractTag(input, temp); |
4915 | |
4916 | // BigInt. |
4917 | emitTestAndUnbox([&](Label* target) { |
4918 | masm.branchTestBigInt(Assembler::NotEqual, tag, target); |
4919 | masm.unboxBigInt(input, temp); |
4920 | masm.loadBigInt64(temp, output); |
4921 | }); |
4922 | |
4923 | // Boolean |
4924 | emitTestAndUnbox([&](Label* target) { |
4925 | masm.branchTestBoolean(Assembler::NotEqual, tag, target); |
4926 | masm.unboxBoolean(input, temp); |
4927 | masm.move32To64ZeroExtend(temp, output); |
4928 | }); |
4929 | |
4930 | // String |
4931 | emitTestAndUnbox([&](Label* target) { |
4932 | masm.branchTestString(Assembler::NotEqual, tag, target); |
4933 | masm.unboxString(input, temp); |
4934 | emitStringToInt64(lir, temp, output); |
4935 | }); |
4936 | |
4937 | MOZ_ASSERT(checks == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(checks == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(checks == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("checks == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4937); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0" ")"); do { *((volatile int*)__null) = 4937; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4938 | |
4939 | bailoutFrom(&fail, lir->snapshot()); |
4940 | masm.bind(&done); |
4941 | } |
4942 | |
4943 | void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) { |
4944 | Register operand = ToRegister(lir->input()); |
4945 | Register64 output = ToOutRegister64(lir); |
4946 | |
4947 | masm.loadBigInt64(operand, output); |
4948 | } |
4949 | |
4950 | OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir, |
4951 | Scalar::Type type, |
4952 | Register64 input, |
4953 | Register output) { |
4954 | #if JS_BITS_PER_WORD64 == 32 |
4955 | using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t); |
4956 | auto args = ArgList(input.low, input.high); |
4957 | #else |
4958 | using Fn = BigInt* (*)(JSContext*, uint64_t); |
4959 | auto args = ArgList(input); |
4960 | #endif |
4961 | |
4962 | if (type == Scalar::BigInt64) { |
4963 | return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args, |
4964 | StoreRegisterTo(output)); |
4965 | } |
4966 | MOZ_ASSERT(type == Scalar::BigUint64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == Scalar::BigUint64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == Scalar::BigUint64))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == Scalar::BigUint64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == Scalar::BigUint64" ")"); do { *((volatile int*)__null) = 4966; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
4967 | return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args, |
4968 | StoreRegisterTo(output)); |
4969 | } |
4970 | |
4971 | void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type, |
4972 | Register64 input, Register output, |
4973 | Register maybeTemp) { |
4974 | OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output); |
4975 | |
4976 | if (maybeTemp != InvalidReg) { |
4977 | masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry()); |
4978 | } else { |
4979 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
4980 | regs.take(input); |
4981 | regs.take(output); |
4982 | |
4983 | Register temp = regs.takeAny(); |
4984 | |
4985 | masm.push(temp); |
4986 | |
4987 | Label fail, ok; |
4988 | masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail); |
4989 | masm.pop(temp); |
4990 | masm.jump(&ok); |
4991 | masm.bind(&fail); |
4992 | masm.pop(temp); |
4993 | masm.jump(ool->entry()); |
4994 | masm.bind(&ok); |
4995 | } |
4996 | masm.initializeBigInt64(type, output, input); |
4997 | masm.bind(ool->rejoin()); |
4998 | } |
4999 | |
5000 | void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) { |
5001 | Register64 input = ToRegister64(lir->input()); |
5002 | Register temp = ToRegister(lir->temp0()); |
5003 | Register output = ToRegister(lir->output()); |
5004 | |
5005 | emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp); |
5006 | } |
5007 | |
5008 | void CodeGenerator::visitGuardValue(LGuardValue* lir) { |
5009 | ValueOperand input = ToValue(lir, LGuardValue::InputIndex); |
5010 | Value expected = lir->mir()->expected(); |
5011 | Label bail; |
5012 | masm.branchTestValue(Assembler::NotEqual, input, expected, &bail); |
5013 | bailoutFrom(&bail, lir->snapshot()); |
5014 | } |
5015 | |
5016 | void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) { |
5017 | ValueOperand input = ToValue(lir, LGuardNullOrUndefined::InputIndex); |
5018 | |
5019 | ScratchTagScope tag(masm, input); |
5020 | masm.splitTagForTest(input, tag); |
5021 | |
5022 | Label done; |
5023 | masm.branchTestNull(Assembler::Equal, tag, &done); |
5024 | |
5025 | Label bail; |
5026 | masm.branchTestUndefined(Assembler::NotEqual, tag, &bail); |
5027 | bailoutFrom(&bail, lir->snapshot()); |
5028 | |
5029 | masm.bind(&done); |
5030 | } |
5031 | |
5032 | void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) { |
5033 | ValueOperand input = ToValue(lir, LGuardIsNotObject::InputIndex); |
5034 | |
5035 | Label bail; |
5036 | masm.branchTestObject(Assembler::Equal, input, &bail); |
5037 | bailoutFrom(&bail, lir->snapshot()); |
5038 | } |
5039 | |
5040 | void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) { |
5041 | Register function = ToRegister(lir->function()); |
5042 | |
5043 | Label bail; |
5044 | if (uint16_t flags = lir->mir()->expectedFlags()) { |
5045 | masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail); |
5046 | } |
5047 | if (uint16_t flags = lir->mir()->unexpectedFlags()) { |
5048 | masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail); |
5049 | } |
5050 | bailoutFrom(&bail, lir->snapshot()); |
5051 | } |
5052 | |
5053 | void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor( |
5054 | LGuardFunctionIsNonBuiltinCtor* lir) { |
5055 | Register function = ToRegister(lir->function()); |
5056 | Register temp = ToRegister(lir->temp0()); |
5057 | |
5058 | Label bail; |
5059 | masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail); |
5060 | bailoutFrom(&bail, lir->snapshot()); |
5061 | } |
5062 | |
5063 | void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) { |
5064 | Register function = ToRegister(lir->function()); |
5065 | Register temp = ToRegister(lir->temp0()); |
5066 | |
5067 | Assembler::Condition cond = |
5068 | lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
5069 | |
5070 | Label bail; |
5071 | masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail); |
5072 | bailoutFrom(&bail, lir->snapshot()); |
5073 | } |
5074 | |
5075 | void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) { |
5076 | Register function = ToRegister(lir->function()); |
5077 | |
5078 | Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript()); |
5079 | bailoutCmpPtr(Assembler::NotEqual, scriptAddr, |
5080 | ImmGCPtr(lir->mir()->expected()), lir->snapshot()); |
5081 | } |
5082 | |
5083 | // Out-of-line path to update the store buffer. |
5084 | class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> { |
5085 | LInstruction* lir_; |
5086 | const LAllocation* object_; |
5087 | |
5088 | public: |
5089 | OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object) |
5090 | : lir_(lir), object_(object) {} |
5091 | |
5092 | void accept(CodeGenerator* codegen) override { |
5093 | codegen->visitOutOfLineCallPostWriteBarrier(this); |
5094 | } |
5095 | |
5096 | LInstruction* lir() const { return lir_; } |
5097 | const LAllocation* object() const { return object_; } |
5098 | }; |
5099 | |
5100 | static void EmitStoreBufferCheckForConstant(MacroAssembler& masm, |
5101 | const gc::TenuredCell* cell, |
5102 | AllocatableGeneralRegisterSet& regs, |
5103 | Label* exit, Label* callVM) { |
5104 | Register temp = regs.takeAny(); |
5105 | |
5106 | gc::Arena* arena = cell->arena(); |
5107 | |
5108 | Register cells = temp; |
5109 | masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells); |
5110 | |
5111 | size_t index = gc::ArenaCellSet::getCellIndex(cell); |
5112 | size_t word; |
5113 | uint32_t mask; |
5114 | gc::ArenaCellSet::getWordIndexAndMask(index, &word, &mask); |
5115 | size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t); |
5116 | |
5117 | masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask), |
5118 | exit); |
5119 | |
5120 | // Check whether this is the sentinel set and if so call the VM to allocate |
5121 | // one for this arena. |
5122 | masm.branchPtr(Assembler::Equal, |
5123 | Address(cells, gc::ArenaCellSet::offsetOfArena()), |
5124 | ImmPtr(nullptr), callVM); |
5125 | |
5126 | // Add the cell to the set. |
5127 | masm.or32(Imm32(mask), Address(cells, offset)); |
5128 | masm.jump(exit); |
5129 | |
5130 | regs.add(temp); |
5131 | } |
5132 | |
5133 | static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime, |
5134 | Register objreg, JSObject* maybeConstant, |
5135 | bool isGlobal, |
5136 | AllocatableGeneralRegisterSet& regs) { |
5137 | MOZ_ASSERT_IF(isGlobal, maybeConstant)do { if (isGlobal) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(maybeConstant)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(maybeConstant))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("maybeConstant", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeConstant" ")"); do { *((volatile int*)__null) = 5137; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
5138 | |
5139 | Label callVM; |
5140 | Label exit; |
5141 | |
5142 | Register temp = regs.takeAny(); |
5143 | |
5144 | // We already have a fast path to check whether a global is in the store |
5145 | // buffer. |
5146 | if (!isGlobal) { |
5147 | if (maybeConstant) { |
5148 | // Check store buffer bitmap directly for known object. |
5149 | EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs, |
5150 | &exit, &callVM); |
5151 | } else { |
5152 | // Check one element cache to avoid VM call. |
5153 | masm.branchPtr(Assembler::Equal, |
5154 | AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()), |
5155 | objreg, &exit); |
5156 | } |
5157 | } |
5158 | |
5159 | // Call into the VM to barrier the write. |
5160 | masm.bind(&callVM); |
5161 | |
5162 | Register runtimereg = temp; |
5163 | masm.mov(ImmPtr(runtime), runtimereg); |
5164 | |
5165 | masm.setupAlignedABICall(); |
5166 | masm.passABIArg(runtimereg); |
5167 | masm.passABIArg(objreg); |
5168 | if (isGlobal) { |
5169 | using Fn = void (*)(JSRuntime* rt, GlobalObject* obj); |
5170 | masm.callWithABI<Fn, PostGlobalWriteBarrier>(); |
5171 | } else { |
5172 | using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj); |
5173 | masm.callWithABI<Fn, PostWriteBarrier>(); |
5174 | } |
5175 | |
5176 | masm.bind(&exit); |
5177 | } |
5178 | |
5179 | void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) { |
5180 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
5181 | |
5182 | Register objreg; |
5183 | JSObject* object = nullptr; |
5184 | bool isGlobal = false; |
5185 | if (obj->isConstant()) { |
5186 | object = &obj->toConstant()->toObject(); |
5187 | isGlobal = isGlobalObject(object); |
5188 | objreg = regs.takeAny(); |
5189 | masm.movePtr(ImmGCPtr(object), objreg); |
5190 | } else { |
5191 | objreg = ToRegister(obj); |
5192 | regs.takeUnchecked(objreg); |
5193 | } |
5194 | |
5195 | EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs); |
5196 | } |
5197 | |
5198 | // Returns true if `def` might be allocated in the nursery. |
5199 | static bool ValueNeedsPostBarrier(MDefinition* def) { |
5200 | if (def->isBox()) { |
5201 | def = def->toBox()->input(); |
5202 | } |
5203 | if (def->type() == MIRType::Value) { |
5204 | return true; |
5205 | } |
5206 | return NeedsPostBarrier(def->type()); |
5207 | } |
5208 | |
5209 | class OutOfLineElementPostWriteBarrier |
5210 | : public OutOfLineCodeBase<CodeGenerator> { |
5211 | LiveRegisterSet liveVolatileRegs_; |
5212 | const LAllocation* index_; |
5213 | int32_t indexDiff_; |
5214 | Register obj_; |
5215 | Register scratch_; |
5216 | |
5217 | public: |
5218 | OutOfLineElementPostWriteBarrier(const LiveRegisterSet& liveVolatileRegs, |
5219 | Register obj, const LAllocation* index, |
5220 | Register scratch, int32_t indexDiff) |
5221 | : liveVolatileRegs_(liveVolatileRegs), |
5222 | index_(index), |
5223 | indexDiff_(indexDiff), |
5224 | obj_(obj), |
5225 | scratch_(scratch) {} |
5226 | |
5227 | void accept(CodeGenerator* codegen) override { |
5228 | codegen->visitOutOfLineElementPostWriteBarrier(this); |
5229 | } |
5230 | |
5231 | const LiveRegisterSet& liveVolatileRegs() const { return liveVolatileRegs_; } |
5232 | const LAllocation* index() const { return index_; } |
5233 | int32_t indexDiff() const { return indexDiff_; } |
5234 | |
5235 | Register object() const { return obj_; } |
5236 | Register scratch() const { return scratch_; } |
5237 | }; |
5238 | |
5239 | void CodeGenerator::emitElementPostWriteBarrier( |
5240 | MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj, |
5241 | const LAllocation* index, Register scratch, const ConstantOrRegister& val, |
5242 | int32_t indexDiff) { |
5243 | if (val.constant()) { |
5244 | MOZ_ASSERT_IF(val.value().isGCThing(),do { if (val.value().isGCThing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!IsInsideNursery (val.value().toGCThing()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(val.value() .toGCThing())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!IsInsideNursery(val.value().toGCThing())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())" ")"); do { *((volatile int*)__null) = 5245; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
5245 | !IsInsideNursery(val.value().toGCThing()))do { if (val.value().isGCThing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!IsInsideNursery (val.value().toGCThing()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(val.value() .toGCThing())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!IsInsideNursery(val.value().toGCThing())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())" ")"); do { *((volatile int*)__null) = 5245; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
5246 | return; |
5247 | } |
5248 | |
5249 | TypedOrValueRegister reg = val.reg(); |
5250 | if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) { |
5251 | return; |
5252 | } |
5253 | |
5254 | auto* ool = new (alloc()) OutOfLineElementPostWriteBarrier( |
5255 | liveVolatileRegs, obj, index, scratch, indexDiff); |
5256 | addOutOfLineCode(ool, mir); |
5257 | |
5258 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, ool->rejoin()); |
5259 | |
5260 | if (reg.hasValue()) { |
5261 | masm.branchValueIsNurseryCell(Assembler::Equal, reg.valueReg(), scratch, |
5262 | ool->entry()); |
5263 | } else { |
5264 | masm.branchPtrInNurseryChunk(Assembler::Equal, reg.typedReg().gpr(), |
5265 | scratch, ool->entry()); |
5266 | } |
5267 | |
5268 | masm.bind(ool->rejoin()); |
5269 | } |
5270 | |
5271 | void CodeGenerator::visitOutOfLineElementPostWriteBarrier( |
5272 | OutOfLineElementPostWriteBarrier* ool) { |
5273 | Register obj = ool->object(); |
5274 | Register scratch = ool->scratch(); |
5275 | const LAllocation* index = ool->index(); |
5276 | int32_t indexDiff = ool->indexDiff(); |
5277 | |
5278 | masm.PushRegsInMask(ool->liveVolatileRegs()); |
5279 | |
5280 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
5281 | regs.takeUnchecked(obj); |
5282 | regs.takeUnchecked(scratch); |
5283 | |
5284 | Register indexReg; |
5285 | if (index->isConstant()) { |
5286 | indexReg = regs.takeAny(); |
5287 | masm.move32(Imm32(ToInt32(index) + indexDiff), indexReg); |
5288 | } else { |
5289 | indexReg = ToRegister(index); |
5290 | regs.takeUnchecked(indexReg); |
5291 | if (indexDiff != 0) { |
5292 | masm.add32(Imm32(indexDiff), indexReg); |
5293 | } |
5294 | } |
5295 | |
5296 | masm.setupUnalignedABICall(scratch); |
5297 | masm.movePtr(ImmPtr(gen->runtime), scratch); |
5298 | masm.passABIArg(scratch); |
5299 | masm.passABIArg(obj); |
5300 | masm.passABIArg(indexReg); |
5301 | using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index); |
5302 | masm.callWithABI<Fn, PostWriteElementBarrier>(); |
5303 | |
5304 | // We don't need a sub32 here because indexReg must be in liveVolatileRegs |
5305 | // if indexDiff is not zero, so it will be restored below. |
5306 | MOZ_ASSERT_IF(indexDiff != 0, ool->liveVolatileRegs().has(indexReg))do { if (indexDiff != 0) { do { static_assert( mozilla::detail ::AssertionConditionType<decltype(ool->liveVolatileRegs ().has(indexReg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ool->liveVolatileRegs().has (indexReg)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ool->liveVolatileRegs().has(indexReg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5306); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ool->liveVolatileRegs().has(indexReg)" ")"); do { *((volatile int*)__null) = 5306; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
5307 | |
5308 | masm.PopRegsInMask(ool->liveVolatileRegs()); |
5309 | |
5310 | masm.jump(ool->rejoin()); |
5311 | } |
5312 | |
5313 | void CodeGenerator::emitPostWriteBarrier(Register objreg) { |
5314 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
5315 | regs.takeUnchecked(objreg); |
5316 | EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs); |
5317 | } |
5318 | |
5319 | void CodeGenerator::visitOutOfLineCallPostWriteBarrier( |
5320 | OutOfLineCallPostWriteBarrier* ool) { |
5321 | saveLiveVolatile(ool->lir()); |
5322 | const LAllocation* obj = ool->object(); |
5323 | emitPostWriteBarrier(obj); |
5324 | restoreLiveVolatile(ool->lir()); |
5325 | |
5326 | masm.jump(ool->rejoin()); |
5327 | } |
5328 | |
5329 | void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal, |
5330 | OutOfLineCode* ool) { |
5331 | // Check whether an object is a global that we have already barriered before |
5332 | // calling into the VM. |
5333 | // |
5334 | // We only check for the script's global, not other globals within the same |
5335 | // compartment, because we bake in a pointer to realm->globalWriteBarriered |
5336 | // and doing that would be invalid for other realms because they could be |
5337 | // collected before the Ion code is discarded. |
5338 | |
5339 | if (!maybeGlobal->isConstant()) { |
5340 | return; |
5341 | } |
5342 | |
5343 | JSObject* obj = &maybeGlobal->toConstant()->toObject(); |
5344 | if (gen->realm->maybeGlobal() != obj) { |
5345 | return; |
5346 | } |
5347 | |
5348 | const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered(); |
5349 | masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0), |
5350 | ool->rejoin()); |
5351 | } |
5352 | |
5353 | template <class LPostBarrierType, MIRType nurseryType> |
5354 | void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir, |
5355 | OutOfLineCode* ool) { |
5356 | static_assert(NeedsPostBarrier(nurseryType)); |
5357 | |
5358 | addOutOfLineCode(ool, lir->mir()); |
5359 | |
5360 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
5361 | |
5362 | if (lir->object()->isConstant()) { |
5363 | // Constant nursery objects cannot appear here, see |
5364 | // LIRGenerator::visitPostWriteElementBarrier. |
5365 | MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsInsideNursery(&lir->object()->toConstant ()->toObject()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&lir-> object()->toConstant()->toObject())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsInsideNursery(&lir->object()->toConstant()->toObject())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5365); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())" ")"); do { *((volatile int*)__null) = 5365; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5366 | } else { |
5367 | masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()), |
5368 | temp, ool->rejoin()); |
5369 | } |
5370 | |
5371 | maybeEmitGlobalBarrierCheck(lir->object(), ool); |
5372 | |
5373 | Register value = ToRegister(lir->value()); |
5374 | if constexpr (nurseryType == MIRType::Object) { |
5375 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5375); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 5375; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5376 | } else if constexpr (nurseryType == MIRType::String) { |
5377 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::String )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5377); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 5377; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5378 | } else { |
5379 | static_assert(nurseryType == MIRType::BigInt); |
5380 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::BigInt )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::BigInt ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::BigInt", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5380); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 5380; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5381 | } |
5382 | masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry()); |
5383 | |
5384 | masm.bind(ool->rejoin()); |
5385 | } |
5386 | |
5387 | template <class LPostBarrierType> |
5388 | void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir, |
5389 | OutOfLineCode* ool) { |
5390 | addOutOfLineCode(ool, lir->mir()); |
5391 | |
5392 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
5393 | |
5394 | if (lir->object()->isConstant()) { |
5395 | // Constant nursery objects cannot appear here, see |
5396 | // LIRGenerator::visitPostWriteElementBarrier. |
5397 | MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsInsideNursery(&lir->object()->toConstant ()->toObject()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&lir-> object()->toConstant()->toObject())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsInsideNursery(&lir->object()->toConstant()->toObject())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5397); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())" ")"); do { *((volatile int*)__null) = 5397; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5398 | } else { |
5399 | masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()), |
5400 | temp, ool->rejoin()); |
5401 | } |
5402 | |
5403 | maybeEmitGlobalBarrierCheck(lir->object(), ool); |
5404 | |
5405 | ValueOperand value = ToValue(lir, LPostBarrierType::ValueIndex); |
5406 | masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry()); |
5407 | |
5408 | masm.bind(ool->rejoin()); |
5409 | } |
5410 | |
5411 | void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) { |
5412 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
5413 | visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool); |
5414 | } |
5415 | |
5416 | void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) { |
5417 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
5418 | visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool); |
5419 | } |
5420 | |
5421 | void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) { |
5422 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
5423 | visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool); |
5424 | } |
5425 | |
5426 | void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) { |
5427 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
5428 | visitPostWriteBarrierCommonV(lir, ool); |
5429 | } |
5430 | |
5431 | // Out-of-line path to update the store buffer. |
5432 | class OutOfLineCallPostWriteElementBarrier |
5433 | : public OutOfLineCodeBase<CodeGenerator> { |
5434 | LInstruction* lir_; |
5435 | const LAllocation* object_; |
5436 | const LAllocation* index_; |
5437 | |
5438 | public: |
5439 | OutOfLineCallPostWriteElementBarrier(LInstruction* lir, |
5440 | const LAllocation* object, |
5441 | const LAllocation* index) |
5442 | : lir_(lir), object_(object), index_(index) {} |
5443 | |
5444 | void accept(CodeGenerator* codegen) override { |
5445 | codegen->visitOutOfLineCallPostWriteElementBarrier(this); |
5446 | } |
5447 | |
5448 | LInstruction* lir() const { return lir_; } |
5449 | |
5450 | const LAllocation* object() const { return object_; } |
5451 | |
5452 | const LAllocation* index() const { return index_; } |
5453 | }; |
5454 | |
5455 | void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier( |
5456 | OutOfLineCallPostWriteElementBarrier* ool) { |
5457 | saveLiveVolatile(ool->lir()); |
5458 | |
5459 | const LAllocation* obj = ool->object(); |
5460 | const LAllocation* index = ool->index(); |
5461 | |
5462 | Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj); |
5463 | Register indexreg = ToRegister(index); |
5464 | |
5465 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
5466 | regs.takeUnchecked(indexreg); |
5467 | |
5468 | if (obj->isConstant()) { |
5469 | objreg = regs.takeAny(); |
5470 | masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg); |
5471 | } else { |
5472 | regs.takeUnchecked(objreg); |
5473 | } |
5474 | |
5475 | Register runtimereg = regs.takeAny(); |
5476 | using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index); |
5477 | masm.setupAlignedABICall(); |
5478 | masm.mov(ImmPtr(gen->runtime), runtimereg); |
5479 | masm.passABIArg(runtimereg); |
5480 | masm.passABIArg(objreg); |
5481 | masm.passABIArg(indexreg); |
5482 | masm.callWithABI<Fn, PostWriteElementBarrier>(); |
5483 | |
5484 | restoreLiveVolatile(ool->lir()); |
5485 | |
5486 | masm.jump(ool->rejoin()); |
5487 | } |
5488 | |
5489 | void CodeGenerator::visitPostWriteElementBarrierO( |
5490 | LPostWriteElementBarrierO* lir) { |
5491 | auto ool = new (alloc()) |
5492 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
5493 | visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir, |
5494 | ool); |
5495 | } |
5496 | |
5497 | void CodeGenerator::visitPostWriteElementBarrierS( |
5498 | LPostWriteElementBarrierS* lir) { |
5499 | auto ool = new (alloc()) |
5500 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
5501 | visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir, |
5502 | ool); |
5503 | } |
5504 | |
5505 | void CodeGenerator::visitPostWriteElementBarrierBI( |
5506 | LPostWriteElementBarrierBI* lir) { |
5507 | auto ool = new (alloc()) |
5508 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
5509 | visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir, |
5510 | ool); |
5511 | } |
5512 | |
5513 | void CodeGenerator::visitPostWriteElementBarrierV( |
5514 | LPostWriteElementBarrierV* lir) { |
5515 | auto ool = new (alloc()) |
5516 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
5517 | visitPostWriteBarrierCommonV(lir, ool); |
5518 | } |
5519 | |
5520 | void CodeGenerator::visitAssertCanElidePostWriteBarrier( |
5521 | LAssertCanElidePostWriteBarrier* lir) { |
5522 | Register object = ToRegister(lir->object()); |
5523 | ValueOperand value = |
5524 | ToValue(lir, LAssertCanElidePostWriteBarrier::ValueIndex); |
5525 | Register temp = ToRegister(lir->temp0()); |
5526 | |
5527 | Label ok; |
5528 | masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok); |
5529 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok); |
5530 | |
5531 | masm.assumeUnreachable("Unexpected missing post write barrier"); |
5532 | |
5533 | masm.bind(&ok); |
5534 | } |
5535 | |
5536 | template <typename LCallIns> |
5537 | void CodeGenerator::emitCallNative(LCallIns* call, JSNative native, |
5538 | Register argContextReg, Register argUintNReg, |
5539 | Register argVpReg, Register tempReg, |
5540 | uint32_t unusedStack) { |
5541 | masm.checkStackAlignment(); |
5542 | |
5543 | // Native functions have the signature: |
5544 | // bool (*)(JSContext*, unsigned, Value* vp) |
5545 | // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward |
5546 | // are the function arguments. |
5547 | |
5548 | // Allocate space for the outparam, moving the StackPointer to what will be |
5549 | // &vp[1]. |
5550 | masm.adjustStack(unusedStack); |
5551 | |
5552 | // Push a Value containing the callee object: natives are allowed to access |
5553 | // their callee before setting the return value. The StackPointer is moved |
5554 | // to &vp[0]. |
5555 | // |
5556 | // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|. |
5557 | if constexpr (std::is_same_v<LCallIns, LCallClassHook>) { |
5558 | Register calleeReg = ToRegister(call->getCallee()); |
5559 | masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg))); |
5560 | |
5561 | // Enter the callee realm. |
5562 | if (call->mir()->maybeCrossRealm()) { |
5563 | masm.switchToObjectRealm(calleeReg, tempReg); |
5564 | } |
5565 | } else { |
5566 | WrappedFunction* target = call->mir()->getSingleTarget(); |
5567 | masm.Push(ObjectValue(*target->rawNativeJSFunction())); |
5568 | |
5569 | // Enter the callee realm. |
5570 | if (call->mir()->maybeCrossRealm()) { |
5571 | masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg); |
5572 | masm.switchToObjectRealm(tempReg, tempReg); |
5573 | } |
5574 | } |
5575 | |
5576 | // Preload arguments into registers. |
5577 | masm.loadJSContext(argContextReg); |
5578 | masm.moveStackPtrTo(argVpReg); |
5579 | |
5580 | // Initialize |NativeExitFrameLayout::argc_|. |
5581 | masm.Push(argUintNReg); |
5582 | |
5583 | // Construct native exit frame. |
5584 | // |
5585 | // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and |
5586 | // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|. |
5587 | // |
5588 | // The NativeExitFrameLayout is now fully initialized. |
5589 | uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg); |
5590 | masm.enterFakeExitFrameForNative(argContextReg, tempReg, |
5591 | call->mir()->isConstructing()); |
5592 | |
5593 | markSafepointAt(safepointOffset, call); |
5594 | |
5595 | // Construct and execute call. |
5596 | masm.setupAlignedABICall(); |
5597 | masm.passABIArg(argContextReg); |
5598 | masm.passABIArg(argUintNReg); |
5599 | masm.passABIArg(argVpReg); |
5600 | |
5601 | ensureOsiSpace(); |
5602 | // If we're using a simulator build, `native` will already point to the |
5603 | // simulator's call-redirection code for LCallClassHook. Load the address in |
5604 | // a register first so that we don't try to redirect it a second time. |
5605 | bool emittedCall = false; |
5606 | #ifdef JS_SIMULATOR |
5607 | if constexpr (std::is_same_v<LCallIns, LCallClassHook>) { |
5608 | masm.movePtr(ImmPtr(native), tempReg); |
5609 | masm.callWithABI(tempReg); |
5610 | emittedCall = true; |
5611 | } |
5612 | #endif |
5613 | if (!emittedCall) { |
5614 | masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General, |
5615 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
5616 | } |
5617 | |
5618 | // Test for failure. |
5619 | masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); |
5620 | |
5621 | // Exit the callee realm. |
5622 | if (call->mir()->maybeCrossRealm()) { |
5623 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
5624 | } |
5625 | |
5626 | // Load the outparam vp[0] into output register(s). |
5627 | masm.loadValue( |
5628 | Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), |
5629 | JSReturnOperand); |
5630 | |
5631 | // Until C++ code is instrumented against Spectre, prevent speculative |
5632 | // execution from returning any private data. |
5633 | if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() && |
5634 | call->mir()->hasLiveDefUses()) { |
5635 | masm.speculationBarrier(); |
5636 | } |
5637 | |
5638 | #ifdef DEBUG1 |
5639 | // Native constructors are guaranteed to return an Object value. |
5640 | if (call->mir()->isConstructing()) { |
5641 | Label notPrimitive; |
5642 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
5643 | ¬Primitive); |
5644 | masm.assumeUnreachable("native constructors don't return primitives"); |
5645 | masm.bind(¬Primitive); |
5646 | } |
5647 | #endif |
5648 | } |
5649 | |
5650 | template <typename LCallIns> |
5651 | void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) { |
5652 | uint32_t unusedStack = |
5653 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
5654 | |
5655 | // Registers used for callWithABI() argument-passing. |
5656 | const Register argContextReg = ToRegister(call->getArgContextReg()); |
5657 | const Register argUintNReg = ToRegister(call->getArgUintNReg()); |
5658 | const Register argVpReg = ToRegister(call->getArgVpReg()); |
5659 | |
5660 | // Misc. temporary registers. |
5661 | const Register tempReg = ToRegister(call->getTempReg()); |
5662 | |
5663 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
5664 | |
5665 | // Initialize the argc register. |
5666 | masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg); |
5667 | |
5668 | // Create the exit frame and call the native. |
5669 | emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg, |
5670 | unusedStack); |
5671 | |
5672 | // The next instruction is removing the footer of the exit frame, so there |
5673 | // is no need for leaveFakeExitFrame. |
5674 | |
5675 | // Move the StackPointer back to its original location, unwinding the native |
5676 | // exit frame. |
5677 | masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack); |
5678 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5678); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 5678; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5679 | } |
5680 | |
5681 | void CodeGenerator::visitCallNative(LCallNative* call) { |
5682 | WrappedFunction* target = call->getSingleTarget(); |
5683 | MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5683); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")" ); do { *((volatile int*)__null) = 5683; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5684 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5684); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 5684; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5685 | |
5686 | JSNative native = target->native(); |
5687 | if (call->ignoresReturnValue() && target->hasJitInfo()) { |
5688 | const JSJitInfo* jitInfo = target->jitInfo(); |
5689 | if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) { |
5690 | native = jitInfo->ignoresReturnValueMethod; |
5691 | } |
5692 | } |
5693 | emitCallNative(call, native); |
5694 | } |
5695 | |
5696 | void CodeGenerator::visitCallClassHook(LCallClassHook* call) { |
5697 | emitCallNative(call, call->mir()->target()); |
5698 | } |
5699 | |
5700 | static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv, |
5701 | DOMObjectKind kind) { |
5702 | // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This |
5703 | // will be in the first slot but may be fixed or non-fixed. |
5704 | MOZ_ASSERT(obj != priv)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj != priv)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj != priv))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj != priv", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5704); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv" ")"); do { *((volatile int*)__null) = 5704; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5705 | |
5706 | switch (kind) { |
5707 | case DOMObjectKind::Native: |
5708 | // If it's a native object, the value must be in a fixed slot. |
5709 | // See CanAttachDOMCall in CacheIR.cpp. |
5710 | masm.debugAssertObjHasFixedSlots(obj, priv); |
5711 | masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv); |
5712 | break; |
5713 | case DOMObjectKind::Proxy: { |
5714 | #ifdef DEBUG1 |
5715 | // Sanity check: it must be a DOM proxy. |
5716 | Label isDOMProxy; |
5717 | masm.branchTestProxyHandlerFamily( |
5718 | Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy); |
5719 | masm.assumeUnreachable("Expected a DOM proxy"); |
5720 | masm.bind(&isDOMProxy); |
5721 | #endif |
5722 | masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), priv); |
5723 | masm.loadPrivate( |
5724 | Address(priv, js::detail::ProxyReservedSlots::offsetOfSlot(0)), priv); |
5725 | break; |
5726 | } |
5727 | } |
5728 | } |
5729 | |
5730 | void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) { |
5731 | WrappedFunction* target = call->getSingleTarget(); |
5732 | MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5732); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")" ); do { *((volatile int*)__null) = 5732; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5733 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5733); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 5733; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5734 | MOZ_ASSERT(target->hasJitInfo())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->hasJitInfo())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->hasJitInfo()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("target->hasJitInfo()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5734); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitInfo()" ")"); do { *((volatile int*)__null) = 5734; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5735 | MOZ_ASSERT(call->mir()->isCallDOMNative())do { static_assert( mozilla::detail::AssertionConditionType< decltype(call->mir()->isCallDOMNative())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(call->mir()->isCallDOMNative ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("call->mir()->isCallDOMNative()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5735); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()" ")"); do { *((volatile int*)__null) = 5735; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5736 | |
5737 | int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
5738 | |
5739 | // Registers used for callWithABI() argument-passing. |
5740 | const Register argJSContext = ToRegister(call->getArgJSContext()); |
5741 | const Register argObj = ToRegister(call->getArgObj()); |
5742 | const Register argPrivate = ToRegister(call->getArgPrivate()); |
5743 | const Register argArgs = ToRegister(call->getArgArgs()); |
5744 | |
5745 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
5746 | |
5747 | masm.checkStackAlignment(); |
5748 | |
5749 | // DOM methods have the signature: |
5750 | // bool (*)(JSContext*, HandleObject, void* private, const |
5751 | // JSJitMethodCallArgs& args) |
5752 | // Where args is initialized from an argc and a vp, vp[0] is space for an |
5753 | // outparam and the callee, vp[1] is |this|, and vp[2] onward are the |
5754 | // function arguments. Note that args stores the argv, not the vp, and |
5755 | // argv == vp + 2. |
5756 | |
5757 | // Nestle the stack up against the pushed arguments, leaving StackPointer at |
5758 | // &vp[1] |
5759 | masm.adjustStack(unusedStack); |
5760 | // argObj is filled with the extracted object, then returned. |
5761 | Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj); |
5762 | MOZ_ASSERT(obj == argObj)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj == argObj)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj == argObj))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj == argObj", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5762); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj == argObj" ")"); do { *((volatile int*)__null) = 5762; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5763 | |
5764 | // Push a Value containing the callee object: natives are allowed to access |
5765 | // their callee before setting the return value. After this the StackPointer |
5766 | // points to &vp[0]. |
5767 | masm.Push(ObjectValue(*target->rawNativeJSFunction())); |
5768 | |
5769 | // Now compute the argv value. Since StackPointer is pointing to &vp[0] and |
5770 | // argv is &vp[2] we just need to add 2*sizeof(Value) to the current |
5771 | // StackPointer. |
5772 | static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0); |
5773 | static_assert(JSJitMethodCallArgsTraits::offsetOfArgc == |
5774 | IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv); |
5775 | masm.computeEffectiveAddress( |
5776 | Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs); |
5777 | |
5778 | LoadDOMPrivate(masm, obj, argPrivate, |
5779 | static_cast<MCallDOMNative*>(call->mir())->objectKind()); |
5780 | |
5781 | // Push argc from the call instruction into what will become the IonExitFrame |
5782 | masm.Push(Imm32(call->numActualArgs())); |
5783 | |
5784 | // Push our argv onto the stack |
5785 | masm.Push(argArgs); |
5786 | // And store our JSJitMethodCallArgs* in argArgs. |
5787 | masm.moveStackPtrTo(argArgs); |
5788 | |
5789 | // Push |this| object for passing HandleObject. We push after argc to |
5790 | // maintain the same sp-relative location of the object pointer with other |
5791 | // DOMExitFrames. |
5792 | masm.Push(argObj); |
5793 | masm.moveStackPtrTo(argObj); |
5794 | |
5795 | if (call->mir()->maybeCrossRealm()) { |
5796 | // We use argJSContext as scratch register here. |
5797 | masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext); |
5798 | masm.switchToObjectRealm(argJSContext, argJSContext); |
5799 | } |
5800 | |
5801 | // Construct native exit frame. |
5802 | uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext); |
5803 | masm.loadJSContext(argJSContext); |
5804 | masm.enterFakeExitFrame(argJSContext, argJSContext, |
5805 | ExitFrameType::IonDOMMethod); |
5806 | |
5807 | markSafepointAt(safepointOffset, call); |
5808 | |
5809 | // Construct and execute call. |
5810 | masm.setupAlignedABICall(); |
5811 | masm.loadJSContext(argJSContext); |
5812 | masm.passABIArg(argJSContext); |
5813 | masm.passABIArg(argObj); |
5814 | masm.passABIArg(argPrivate); |
5815 | masm.passABIArg(argArgs); |
5816 | ensureOsiSpace(); |
5817 | masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method), |
5818 | ABIType::General, |
5819 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
5820 | |
5821 | if (target->jitInfo()->isInfallible) { |
5822 | masm.loadValue(Address(masm.getStackPointer(), |
5823 | IonDOMMethodExitFrameLayout::offsetOfResult()), |
5824 | JSReturnOperand); |
5825 | } else { |
5826 | // Test for failure. |
5827 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
5828 | |
5829 | // Load the outparam vp[0] into output register(s). |
5830 | masm.loadValue(Address(masm.getStackPointer(), |
5831 | IonDOMMethodExitFrameLayout::offsetOfResult()), |
5832 | JSReturnOperand); |
5833 | } |
5834 | |
5835 | // Switch back to the current realm if needed. Note: if the DOM method threw |
5836 | // an exception, the exception handler will do this. |
5837 | if (call->mir()->maybeCrossRealm()) { |
5838 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
5839 | "Clobbering ReturnReg should not affect the return value"); |
5840 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
5841 | } |
5842 | |
5843 | // Until C++ code is instrumented against Spectre, prevent speculative |
5844 | // execution from returning any private data. |
5845 | if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) { |
5846 | masm.speculationBarrier(); |
5847 | } |
5848 | |
5849 | // The next instruction is removing the footer of the exit frame, so there |
5850 | // is no need for leaveFakeExitFrame. |
5851 | |
5852 | // Move the StackPointer back to its original location, unwinding the native |
5853 | // exit frame. |
5854 | masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack); |
5855 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 5855; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5856 | } |
5857 | |
5858 | void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) { |
5859 | pushArg(ImmGCPtr(lir->mir()->name())); |
5860 | |
5861 | using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue); |
5862 | callVM<Fn, GetIntrinsicValue>(lir); |
5863 | } |
5864 | |
5865 | void CodeGenerator::emitCallInvokeFunction( |
5866 | LInstruction* call, Register calleereg, bool constructing, |
5867 | bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) { |
5868 | // Nestle %esp up to the argument vector. |
5869 | // Each path must account for framePushed_ separately, for callVM to be valid. |
5870 | masm.freeStack(unusedStack); |
5871 | |
5872 | pushArg(masm.getStackPointer()); // argv. |
5873 | pushArg(Imm32(argc)); // argc. |
5874 | pushArg(Imm32(ignoresReturnValue)); |
5875 | pushArg(Imm32(constructing)); // constructing. |
5876 | pushArg(calleereg); // JSFunction*. |
5877 | |
5878 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
5879 | MutableHandleValue); |
5880 | callVM<Fn, jit::InvokeFunction>(call); |
5881 | |
5882 | // Un-nestle %esp from the argument vector. No prefix was pushed. |
5883 | masm.reserveStack(unusedStack); |
5884 | } |
5885 | |
5886 | void CodeGenerator::visitCallGeneric(LCallGeneric* call) { |
5887 | // The callee is passed straight through to the trampoline. |
5888 | MOZ_ASSERT(ToRegister(call->getCallee()) == IonGenericCallCalleeReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(call->getCallee()) == IonGenericCallCalleeReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(call->getCallee()) == IonGenericCallCalleeReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(call->getCallee()) == IonGenericCallCalleeReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5888); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg" ")"); do { *((volatile int*)__null) = 5888; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5889 | |
5890 | Register argcReg = ToRegister(call->getArgc()); |
5891 | uint32_t unusedStack = |
5892 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
5893 | |
5894 | // Known-target case is handled by LCallKnown. |
5895 | MOZ_ASSERT(!call->hasSingleTarget())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!call->hasSingleTarget())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!call->hasSingleTarget()) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!call->hasSingleTarget()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5895); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->hasSingleTarget()" ")"); do { *((volatile int*)__null) = 5895; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
5896 | |
5897 | masm.checkStackAlignment(); |
5898 | |
5899 | masm.move32(Imm32(call->numActualArgs()), argcReg); |
5900 | |
5901 | // Nestle the StackPointer up to the argument vector. |
5902 | masm.freeStack(unusedStack); |
5903 | ensureOsiSpace(); |
5904 | |
5905 | auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct |
5906 | : IonGenericCallKind::Call; |
5907 | |
5908 | TrampolinePtr genericCallStub = |
5909 | gen->jitRuntime()->getIonGenericCallStub(kind); |
5910 | uint32_t callOffset = masm.callJit(genericCallStub); |
5911 | markSafepointAt(callOffset, call); |
5912 | |
5913 | if (call->mir()->maybeCrossRealm()) { |
5914 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
5915 | "ReturnReg available as scratch after scripted calls"); |
5916 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
5917 | } |
5918 | |
5919 | // Restore stack pointer. |
5920 | masm.setFramePushed(frameSize()); |
5921 | emitRestoreStackPointerFromFP(); |
5922 | |
5923 | // If the return value of the constructing function is Primitive, |
5924 | // replace the return value with the Object from CreateThis. |
5925 | if (call->mir()->isConstructing()) { |
5926 | Label notPrimitive; |
5927 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
5928 | ¬Primitive); |
5929 | masm.loadValue(Address(masm.getStackPointer(), unusedStack), |
5930 | JSReturnOperand); |
5931 | #ifdef DEBUG1 |
5932 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
5933 | ¬Primitive); |
5934 | masm.assumeUnreachable("CreateThis creates an object"); |
5935 | #endif |
5936 | masm.bind(¬Primitive); |
5937 | } |
5938 | } |
5939 | |
5940 | void JitRuntime::generateIonGenericCallArgumentsShift( |
5941 | MacroAssembler& masm, Register argc, Register curr, Register end, |
5942 | Register scratch, Label* done) { |
5943 | static_assert(sizeof(Value) == 8); |
5944 | // There are |argc| Values on the stack. Shift them all down by 8 bytes, |
5945 | // overwriting the first value. |
5946 | |
5947 | // Initialize `curr` to the destination of the first copy, and `end` to the |
5948 | // final value of curr. |
5949 | masm.moveStackPtrTo(curr); |
5950 | masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end); |
5951 | |
5952 | Label loop; |
5953 | masm.bind(&loop); |
5954 | masm.branchPtr(Assembler::Equal, curr, end, done); |
5955 | masm.loadPtr(Address(curr, 8), scratch); |
5956 | masm.storePtr(scratch, Address(curr, 0)); |
5957 | masm.addPtr(Imm32(sizeof(uintptr_t)), curr); |
5958 | masm.jump(&loop); |
5959 | } |
5960 | |
5961 | void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm, |
5962 | IonGenericCallKind kind) { |
5963 | AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub"); |
5964 | ionGenericCallStubOffset_[kind] = startTrampolineCode(masm); |
5965 | |
5966 | // This code is tightly coupled with visitCallGeneric. |
5967 | // |
5968 | // Upon entry: |
5969 | // IonGenericCallCalleeReg contains a pointer to the callee object. |
5970 | // IonGenericCallArgcReg contains the number of actual args. |
5971 | // The arguments have been pushed onto the stack: |
5972 | // [newTarget] (iff isConstructing) |
5973 | // [argN] |
5974 | // ... |
5975 | // [arg1] |
5976 | // [arg0] |
5977 | // [this] |
5978 | // <return address> (if not JS_USE_LINK_REGISTER) |
5979 | // |
5980 | // This trampoline is responsible for entering the callee's realm, |
5981 | // massaging the stack into the right shape, and then performing a |
5982 | // tail call. We will return directly to the Ion code from the |
5983 | // callee. |
5984 | // |
5985 | // To do a tail call, we keep the return address in a register, even |
5986 | // on platforms that don't normally use a link register, and push it |
5987 | // just before jumping to the callee, after we are done setting up |
5988 | // the stack. |
5989 | // |
5990 | // The caller is responsible for switching back to the caller's |
5991 | // realm and cleaning up the stack. |
5992 | |
5993 | Register calleeReg = IonGenericCallCalleeReg; |
5994 | Register argcReg = IonGenericCallArgcReg; |
5995 | Register scratch = IonGenericCallScratch; |
5996 | Register scratch2 = IonGenericCallScratch2; |
5997 | |
5998 | #ifndef JS_USE_LINK_REGISTER |
5999 | Register returnAddrReg = IonGenericCallReturnAddrReg; |
6000 | masm.pop(returnAddrReg); |
6001 | #endif |
6002 | |
6003 | #ifdef JS_CODEGEN_ARM |
6004 | // The default second scratch register on arm is lr, which we need |
6005 | // preserved for tail calls. |
6006 | AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg); |
6007 | #endif |
6008 | |
6009 | bool isConstructing = kind == IonGenericCallKind::Construct; |
6010 | |
6011 | Label entry, notFunction, noJitEntry, vmCall; |
6012 | masm.bind(&entry); |
6013 | |
6014 | // Guard that the callee is actually a function. |
6015 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch, |
6016 | calleeReg, ¬Function); |
6017 | |
6018 | // Guard that the callee supports the [[Call]] or [[Construct]] operation. |
6019 | // If these tests fail, we will call into the VM to throw an exception. |
6020 | if (isConstructing) { |
6021 | masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR, |
6022 | Assembler::Zero, &vmCall); |
6023 | } else { |
6024 | masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor, |
6025 | calleeReg, scratch, &vmCall); |
6026 | } |
6027 | |
6028 | if (isConstructing) { |
6029 | // Use the slow path if CreateThis was unable to create the |this| object. |
6030 | Address thisAddr(masm.getStackPointer(), 0); |
6031 | masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall); |
6032 | } |
6033 | |
6034 | masm.switchToObjectRealm(calleeReg, scratch); |
6035 | |
6036 | // Load jitCodeRaw for callee if it exists. |
6037 | masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry); |
6038 | |
6039 | // **************************** |
6040 | // * Functions with jit entry * |
6041 | // **************************** |
6042 | masm.loadJitCodeRaw(calleeReg, scratch2); |
6043 | |
6044 | // Construct the JitFrameLayout. |
6045 | masm.PushCalleeToken(calleeReg, isConstructing); |
6046 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch); |
6047 | #ifndef JS_USE_LINK_REGISTER |
6048 | masm.push(returnAddrReg); |
6049 | #endif |
6050 | |
6051 | // Check whether we need a rectifier frame. |
6052 | Label noRectifier; |
6053 | masm.loadFunctionArgCount(calleeReg, scratch); |
6054 | masm.branch32(Assembler::BelowOrEqual, scratch, argcReg, &noRectifier); |
6055 | { |
6056 | // Tail-call the arguments rectifier. |
6057 | // Because all trampolines are created at the same time, |
6058 | // we can't create a TrampolinePtr for the arguments rectifier, |
6059 | // because it hasn't been linked yet. We can, however, directly |
6060 | // encode its offset. |
6061 | Label rectifier; |
6062 | bindLabelToOffset(&rectifier, argumentsRectifierOffset_); |
6063 | |
6064 | masm.jump(&rectifier); |
6065 | } |
6066 | |
6067 | // Tail call the jit entry. |
6068 | masm.bind(&noRectifier); |
6069 | masm.jump(scratch2); |
6070 | |
6071 | // ******************** |
6072 | // * Native functions * |
6073 | // ******************** |
6074 | masm.bind(&noJitEntry); |
6075 | if (!isConstructing) { |
6076 | generateIonGenericCallFunCall(masm, &entry, &vmCall); |
6077 | } |
6078 | generateIonGenericCallNativeFunction(masm, isConstructing); |
6079 | |
6080 | // ******************* |
6081 | // * Bound functions * |
6082 | // ******************* |
6083 | // TODO: support class hooks? |
6084 | masm.bind(¬Function); |
6085 | if (!isConstructing) { |
6086 | // TODO: support generic bound constructors? |
6087 | generateIonGenericCallBoundFunction(masm, &entry, &vmCall); |
6088 | } |
6089 | |
6090 | // ******************** |
6091 | // * Fallback VM call * |
6092 | // ******************** |
6093 | masm.bind(&vmCall); |
6094 | |
6095 | masm.push(masm.getStackPointer()); // argv |
6096 | masm.push(argcReg); // argc |
6097 | masm.push(Imm32(false)); // ignores return value |
6098 | masm.push(Imm32(isConstructing)); // constructing |
6099 | masm.push(calleeReg); // callee |
6100 | |
6101 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
6102 | MutableHandleValue); |
6103 | VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id; |
6104 | uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)]; |
6105 | Label invokeFunctionVMEntry; |
6106 | bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset); |
6107 | |
6108 | masm.pushFrameDescriptor(FrameType::IonJS); |
6109 | #ifndef JS_USE_LINK_REGISTER |
6110 | masm.push(returnAddrReg); |
6111 | #endif |
6112 | masm.jump(&invokeFunctionVMEntry); |
6113 | } |
6114 | |
6115 | void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm, |
6116 | bool isConstructing) { |
6117 | Register calleeReg = IonGenericCallCalleeReg; |
6118 | Register argcReg = IonGenericCallArgcReg; |
6119 | Register scratch = IonGenericCallScratch; |
6120 | Register scratch2 = IonGenericCallScratch2; |
6121 | Register contextReg = IonGenericCallScratch3; |
6122 | #ifndef JS_USE_LINK_REGISTER |
6123 | Register returnAddrReg = IonGenericCallReturnAddrReg; |
6124 | #endif |
6125 | |
6126 | // Push a value containing the callee, which will become argv[0]. |
6127 | masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg); |
6128 | |
6129 | // Load the callee address into calleeReg. |
6130 | #ifdef JS_SIMULATOR |
6131 | masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg); |
6132 | #else |
6133 | masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()), |
6134 | calleeReg); |
6135 | #endif |
6136 | |
6137 | // Load argv into scratch2. |
6138 | masm.moveStackPtrTo(scratch2); |
6139 | |
6140 | // Push argc. |
6141 | masm.push(argcReg); |
6142 | |
6143 | masm.loadJSContext(contextReg); |
6144 | |
6145 | // Construct native exit frame. Note that unlike other cases in this |
6146 | // trampoline, this code does not use a tail call. |
6147 | masm.pushFrameDescriptor(FrameType::IonJS); |
6148 | #ifdef JS_USE_LINK_REGISTER |
6149 | masm.pushReturnAddress(); |
6150 | #else |
6151 | masm.push(returnAddrReg); |
6152 | #endif |
6153 | |
6154 | masm.push(FramePointer); |
6155 | masm.moveStackPtrTo(FramePointer); |
6156 | masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing); |
6157 | |
6158 | masm.setupUnalignedABICall(scratch); |
6159 | masm.passABIArg(contextReg); // cx |
6160 | masm.passABIArg(argcReg); // argc |
6161 | masm.passABIArg(scratch2); // argv |
6162 | |
6163 | masm.callWithABI(calleeReg); |
6164 | |
6165 | // Test for failure. |
6166 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
6167 | |
6168 | masm.loadValue( |
6169 | Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), |
6170 | JSReturnOperand); |
6171 | |
6172 | // Leave the exit frame. |
6173 | masm.moveToStackPtr(FramePointer); |
6174 | masm.pop(FramePointer); |
6175 | |
6176 | // Return. |
6177 | masm.ret(); |
6178 | } |
6179 | |
6180 | void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm, |
6181 | Label* entry, Label* vmCall) { |
6182 | Register calleeReg = IonGenericCallCalleeReg; |
6183 | Register argcReg = IonGenericCallArgcReg; |
6184 | Register scratch = IonGenericCallScratch; |
6185 | Register scratch2 = IonGenericCallScratch2; |
6186 | Register scratch3 = IonGenericCallScratch3; |
6187 | |
6188 | Label notFunCall; |
6189 | masm.branchPtr(Assembler::NotEqual, |
6190 | Address(calleeReg, JSFunction::offsetOfNativeOrEnv()), |
6191 | ImmPtr(js::fun_call), ¬FunCall); |
6192 | |
6193 | // In general, we can implement fun_call by replacing calleeReg with |
6194 | // |this|, sliding all the other arguments down, and decrementing argc. |
6195 | // |
6196 | // *BEFORE* *AFTER* |
6197 | // [argN] argc = N+1 <padding> |
6198 | // ... [argN] argc = N |
6199 | // [arg1] ... |
6200 | // [arg0] [arg1] <- now arg0 |
6201 | // [this] <- top of stack (aligned) [arg0] <- now this |
6202 | // |
6203 | // The only exception is when argc is already 0, in which case instead |
6204 | // of shifting arguments down we replace [this] with UndefinedValue(): |
6205 | // |
6206 | // *BEFORE* *AFTER* |
6207 | // [this] argc = 0 [undef] argc = 0 |
6208 | // |
6209 | // After making this transformation, we can jump back to the beginning |
6210 | // of this trampoline to handle the inner call. |
6211 | |
6212 | // Guard that |this| is an object. If it is, replace calleeReg. |
6213 | masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall); |
6214 | masm.movePtr(scratch, calleeReg); |
6215 | |
6216 | Label hasArgs; |
6217 | masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs); |
6218 | |
6219 | // No arguments. Replace |this| with |undefined| and start from the top. |
6220 | masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0)); |
6221 | masm.jump(entry); |
6222 | |
6223 | masm.bind(&hasArgs); |
6224 | |
6225 | Label doneSliding; |
6226 | generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2, |
6227 | scratch3, &doneSliding); |
6228 | masm.bind(&doneSliding); |
6229 | masm.sub32(Imm32(1), argcReg); |
6230 | |
6231 | masm.jump(entry); |
6232 | |
6233 | masm.bind(¬FunCall); |
6234 | } |
6235 | |
6236 | void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm, |
6237 | Label* entry, |
6238 | Label* vmCall) { |
6239 | Register calleeReg = IonGenericCallCalleeReg; |
6240 | Register argcReg = IonGenericCallArgcReg; |
6241 | Register scratch = IonGenericCallScratch; |
6242 | Register scratch2 = IonGenericCallScratch2; |
6243 | Register scratch3 = IonGenericCallScratch3; |
6244 | |
6245 | masm.branchTestObjClass(Assembler::NotEqual, calleeReg, |
6246 | &BoundFunctionObject::class_, scratch, calleeReg, |
6247 | vmCall); |
6248 | |
6249 | Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot()); |
6250 | Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot()); |
6251 | Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot()); |
6252 | Address firstInlineArgSlot( |
6253 | calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg()); |
6254 | |
6255 | // Check that we won't be pushing too many arguments. |
6256 | masm.load32(flagsSlot, scratch); |
6257 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch); |
6258 | masm.add32(argcReg, scratch); |
6259 | masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall); |
6260 | |
6261 | // The stack is currently correctly aligned for a jit call. We will |
6262 | // be updating the `this` value and potentially adding additional |
6263 | // arguments. On platforms with 16-byte alignment, if the number of |
6264 | // bound arguments is odd, we have to move the arguments that are |
6265 | // currently on the stack. For example, with one bound argument: |
6266 | // |
6267 | // *BEFORE* *AFTER* |
6268 | // [argN] <padding> |
6269 | // ... [argN] | |
6270 | // [arg1] ... | These arguments have been |
6271 | // [arg0] [arg1] | shifted down 8 bytes. |
6272 | // [this] <- top of stack (aligned) [arg0] v |
6273 | // [bound0] <- one bound argument (odd) |
6274 | // [boundThis] <- top of stack (aligned) |
6275 | // |
6276 | Label poppedThis; |
6277 | if (JitStackValueAlignment > 1) { |
6278 | Label alreadyAligned; |
6279 | masm.branchTest32(Assembler::Zero, flagsSlot, |
6280 | Imm32(1 << BoundFunctionObject::NumBoundArgsShift), |
6281 | &alreadyAligned); |
6282 | |
6283 | // We have an odd number of bound arguments. Shift the existing arguments |
6284 | // down by 8 bytes. |
6285 | generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2, |
6286 | scratch3, &poppedThis); |
6287 | masm.bind(&alreadyAligned); |
6288 | } |
6289 | |
6290 | // Pop the current `this`. It will be replaced with the bound `this`. |
6291 | masm.freeStack(sizeof(Value)); |
6292 | masm.bind(&poppedThis); |
6293 | |
6294 | // Load the number of bound arguments in scratch |
6295 | masm.load32(flagsSlot, scratch); |
6296 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch); |
6297 | |
6298 | Label donePushingBoundArguments; |
6299 | masm.branch32(Assembler::Equal, scratch, Imm32(0), |
6300 | &donePushingBoundArguments); |
6301 | |
6302 | // Update argc to include bound arguments. |
6303 | masm.add32(scratch, argcReg); |
6304 | |
6305 | // Load &boundArgs[0] in scratch2. |
6306 | Label outOfLineBoundArguments, haveBoundArguments; |
6307 | masm.branch32(Assembler::Above, scratch, |
6308 | Imm32(BoundFunctionObject::MaxInlineBoundArgs), |
6309 | &outOfLineBoundArguments); |
6310 | masm.computeEffectiveAddress(firstInlineArgSlot, scratch2); |
6311 | masm.jump(&haveBoundArguments); |
6312 | |
6313 | masm.bind(&outOfLineBoundArguments); |
6314 | masm.unboxObject(firstInlineArgSlot, scratch2); |
6315 | masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2); |
6316 | |
6317 | masm.bind(&haveBoundArguments); |
6318 | |
6319 | // Load &boundArgs[numBoundArgs] in scratch. |
6320 | BaseObjectElementIndex lastBoundArg(scratch2, scratch); |
6321 | masm.computeEffectiveAddress(lastBoundArg, scratch); |
6322 | |
6323 | // Push the bound arguments, starting with the last one. |
6324 | // Copying pre-decrements scratch until scratch2 is reached. |
6325 | Label boundArgumentsLoop; |
6326 | masm.bind(&boundArgumentsLoop); |
6327 | masm.subPtr(Imm32(sizeof(Value)), scratch); |
6328 | masm.pushValue(Address(scratch, 0)); |
6329 | masm.branchPtr(Assembler::Above, scratch, scratch2, &boundArgumentsLoop); |
6330 | masm.bind(&donePushingBoundArguments); |
6331 | |
6332 | // Push the bound `this`. |
6333 | masm.pushValue(thisSlot); |
6334 | |
6335 | // Load the target in calleeReg. |
6336 | masm.unboxObject(targetSlot, calleeReg); |
6337 | |
6338 | // At this point, all preconditions for entering the trampoline are met: |
6339 | // - calleeReg contains a pointer to the callee object |
6340 | // - argcReg contains the number of actual args (now including bound args) |
6341 | // - the arguments are on the stack with the correct alignment. |
6342 | // Instead of generating more code, we can jump back to the entry point |
6343 | // of the trampoline to call the bound target. |
6344 | masm.jump(entry); |
6345 | } |
6346 | |
6347 | void CodeGenerator::visitCallKnown(LCallKnown* call) { |
6348 | Register calleereg = ToRegister(call->getFunction()); |
6349 | Register objreg = ToRegister(call->getTempObject()); |
6350 | uint32_t unusedStack = |
6351 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
6352 | WrappedFunction* target = call->getSingleTarget(); |
6353 | |
6354 | // Native single targets (except Wasm and TrampolineNative functions) are |
6355 | // handled by LCallNative. |
6356 | MOZ_ASSERT(target->hasJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->hasJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->hasJitEntry()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->hasJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6356); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitEntry()" ")"); do { *((volatile int*)__null) = 6356; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6357 | |
6358 | // Missing arguments must have been explicitly appended by WarpBuilder. |
6359 | DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing(); |
6360 | MOZ_ASSERT(target->nargs() <=do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->nargs() <= call->mir()->numStackArgs () - numNonArgsOnStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->nargs() <= call ->mir()->numStackArgs() - numNonArgsOnStack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" ")"); do { *((volatile int*)__null) = 6361; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
6361 | call->mir()->numStackArgs() - numNonArgsOnStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->nargs() <= call->mir()->numStackArgs () - numNonArgsOnStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->nargs() <= call ->mir()->numStackArgs() - numNonArgsOnStack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" ")"); do { *((volatile int*)__null) = 6361; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6362 | |
6363 | MOZ_ASSERT_IF(call->isConstructing(), target->isConstructor())do { if (call->isConstructing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(target->isConstructor ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(target->isConstructor()))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("target->isConstructor()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6363); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isConstructor()" ")"); do { *((volatile int*)__null) = 6363; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
6364 | |
6365 | masm.checkStackAlignment(); |
6366 | |
6367 | if (target->isClassConstructor() && !call->isConstructing()) { |
6368 | emitCallInvokeFunction(call, calleereg, call->isConstructing(), |
6369 | call->ignoresReturnValue(), call->numActualArgs(), |
6370 | unusedStack); |
6371 | return; |
6372 | } |
6373 | |
6374 | MOZ_ASSERT_IF(target->isClassConstructor(), call->isConstructing())do { if (target->isClassConstructor()) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(call-> isConstructing())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(call->isConstructing()))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("call->isConstructing()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6374); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->isConstructing()" ")"); do { *((volatile int*)__null) = 6374; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
6375 | |
6376 | MOZ_ASSERT(!call->mir()->needsThisCheck())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!call->mir()->needsThisCheck())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!call->mir()->needsThisCheck ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!call->mir()->needsThisCheck()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->mir()->needsThisCheck()" ")"); do { *((volatile int*)__null) = 6376; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6377 | |
6378 | if (call->mir()->maybeCrossRealm()) { |
6379 | masm.switchToObjectRealm(calleereg, objreg); |
6380 | } |
6381 | |
6382 | masm.loadJitCodeRaw(calleereg, objreg); |
6383 | |
6384 | // Nestle the StackPointer up to the argument vector. |
6385 | masm.freeStack(unusedStack); |
6386 | |
6387 | // Construct the JitFrameLayout. |
6388 | masm.PushCalleeToken(calleereg, call->mir()->isConstructing()); |
6389 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, call->numActualArgs()); |
6390 | |
6391 | // Finally call the function in objreg. |
6392 | ensureOsiSpace(); |
6393 | uint32_t callOffset = masm.callJit(objreg); |
6394 | markSafepointAt(callOffset, call); |
6395 | |
6396 | if (call->mir()->maybeCrossRealm()) { |
6397 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
6398 | "ReturnReg available as scratch after scripted calls"); |
6399 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
6400 | } |
6401 | |
6402 | // Restore stack pointer: pop JitFrameLayout fields still left on the stack |
6403 | // and undo the earlier |freeStack(unusedStack)|. |
6404 | int prefixGarbage = |
6405 | sizeof(JitFrameLayout) - JitFrameLayout::bytesPoppedAfterCall(); |
6406 | masm.adjustStack(prefixGarbage - unusedStack); |
6407 | |
6408 | // If the return value of the constructing function is Primitive, |
6409 | // replace the return value with the Object from CreateThis. |
6410 | if (call->mir()->isConstructing()) { |
6411 | Label notPrimitive; |
6412 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
6413 | ¬Primitive); |
6414 | masm.loadValue(Address(masm.getStackPointer(), unusedStack), |
6415 | JSReturnOperand); |
6416 | #ifdef DEBUG1 |
6417 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
6418 | ¬Primitive); |
6419 | masm.assumeUnreachable("CreateThis creates an object"); |
6420 | #endif |
6421 | masm.bind(¬Primitive); |
6422 | } |
6423 | } |
6424 | |
6425 | template <typename T> |
6426 | void CodeGenerator::emitCallInvokeFunction(T* apply) { |
6427 | pushArg(masm.getStackPointer()); // argv. |
6428 | pushArg(ToRegister(apply->getArgc())); // argc. |
6429 | pushArg(Imm32(apply->mir()->ignoresReturnValue())); // ignoresReturnValue. |
6430 | pushArg(Imm32(apply->mir()->isConstructing())); // isConstructing. |
6431 | pushArg(ToRegister(apply->getFunction())); // JSFunction*. |
6432 | |
6433 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
6434 | MutableHandleValue); |
6435 | callVM<Fn, jit::InvokeFunction>(apply); |
6436 | } |
6437 | |
6438 | // Do not bailout after the execution of this function since the stack no longer |
6439 | // correspond to what is expected by the snapshots. |
6440 | void CodeGenerator::emitAllocateSpaceForApply(Register argcreg, |
6441 | Register scratch) { |
6442 | // Use scratch register to calculate stack space (including padding). |
6443 | masm.movePtr(argcreg, scratch); |
6444 | |
6445 | // Align the JitFrameLayout on the JitStackAlignment. |
6446 | if (JitStackValueAlignment > 1) { |
6447 | MOZ_ASSERT(frameSize() % JitStackAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6448; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
6448 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6448; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6449 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6449); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6449; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6450 | Label noPaddingNeeded; |
6451 | // If the number of arguments is odd, then we do not need any padding. |
6452 | // |
6453 | // Note: The |JitStackValueAlignment == 2| condition requires that the |
6454 | // overall number of values on the stack is even. When we have an odd number |
6455 | // of arguments, we don't need any padding, because the |thisValue| is |
6456 | // pushed after the arguments, so the overall number of values on the stack |
6457 | // is even. |
6458 | masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded); |
6459 | masm.addPtr(Imm32(1), scratch); |
6460 | masm.bind(&noPaddingNeeded); |
6461 | } |
6462 | |
6463 | // Reserve space for copying the arguments. |
6464 | NativeObject::elementsSizeMustNotOverflow(); |
6465 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
6466 | masm.subFromStackPtr(scratch); |
6467 | |
6468 | #ifdef DEBUG1 |
6469 | // Put a magic value in the space reserved for padding. Note, this code cannot |
6470 | // be merged with the previous test, as not all architectures can write below |
6471 | // their stack pointers. |
6472 | if (JitStackValueAlignment > 1) { |
6473 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6473; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6474 | Label noPaddingNeeded; |
6475 | // If the number of arguments is odd, then we do not need any padding. |
6476 | masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded); |
6477 | BaseValueIndex dstPtr(masm.getStackPointer(), argcreg); |
6478 | masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr); |
6479 | masm.bind(&noPaddingNeeded); |
6480 | } |
6481 | #endif |
6482 | } |
6483 | |
6484 | // Do not bailout after the execution of this function since the stack no longer |
6485 | // correspond to what is expected by the snapshots. |
6486 | void CodeGenerator::emitAllocateSpaceForConstructAndPushNewTarget( |
6487 | Register argcreg, Register newTargetAndScratch) { |
6488 | // Align the JitFrameLayout on the JitStackAlignment. Contrary to |
6489 | // |emitAllocateSpaceForApply()|, we're always pushing a magic value, because |
6490 | // we can't write to |newTargetAndScratch| before |new.target| has been pushed |
6491 | // onto the stack. |
6492 | if (JitStackValueAlignment > 1) { |
6493 | MOZ_ASSERT(frameSize() % JitStackAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6494); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6494; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
6494 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6494); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6494; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6495 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6495); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6495; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6496 | |
6497 | Label noPaddingNeeded; |
6498 | // If the number of arguments is even, then we do not need any padding. |
6499 | // |
6500 | // Note: The |JitStackValueAlignment == 2| condition requires that the |
6501 | // overall number of values on the stack is even. When we have an even |
6502 | // number of arguments, we don't need any padding, because |new.target| is |
6503 | // is pushed before the arguments and |thisValue| is pushed after all |
6504 | // arguments, so the overall number of values on the stack is even. |
6505 | masm.branchTestPtr(Assembler::Zero, argcreg, Imm32(1), &noPaddingNeeded); |
6506 | masm.pushValue(MagicValue(JS_ARG_POISON)); |
6507 | masm.bind(&noPaddingNeeded); |
6508 | } |
6509 | |
6510 | // Push |new.target| after the padding value, but before any arguments. |
6511 | masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch); |
6512 | |
6513 | // Use newTargetAndScratch to calculate stack space (including padding). |
6514 | masm.movePtr(argcreg, newTargetAndScratch); |
6515 | |
6516 | // Reserve space for copying the arguments. |
6517 | NativeObject::elementsSizeMustNotOverflow(); |
6518 | masm.lshiftPtr(Imm32(ValueShift), newTargetAndScratch); |
6519 | masm.subFromStackPtr(newTargetAndScratch); |
6520 | } |
6521 | |
6522 | // Destroys argvIndex and copyreg. |
6523 | void CodeGenerator::emitCopyValuesForApply(Register argvSrcBase, |
6524 | Register argvIndex, Register copyreg, |
6525 | size_t argvSrcOffset, |
6526 | size_t argvDstOffset) { |
6527 | Label loop; |
6528 | masm.bind(&loop); |
6529 | |
6530 | // As argvIndex is off by 1, and we use the decBranchPtr instruction to loop |
6531 | // back, we have to substract the size of the word which are copied. |
6532 | BaseValueIndex srcPtr(argvSrcBase, argvIndex, |
6533 | int32_t(argvSrcOffset) - sizeof(void*)); |
6534 | BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex, |
6535 | int32_t(argvDstOffset) - sizeof(void*)); |
6536 | masm.loadPtr(srcPtr, copyreg); |
6537 | masm.storePtr(copyreg, dstPtr); |
6538 | |
6539 | // Handle 32 bits architectures. |
6540 | if (sizeof(Value) == 2 * sizeof(void*)) { |
6541 | BaseValueIndex srcPtrLow(argvSrcBase, argvIndex, |
6542 | int32_t(argvSrcOffset) - 2 * sizeof(void*)); |
6543 | BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex, |
6544 | int32_t(argvDstOffset) - 2 * sizeof(void*)); |
6545 | masm.loadPtr(srcPtrLow, copyreg); |
6546 | masm.storePtr(copyreg, dstPtrLow); |
6547 | } |
6548 | |
6549 | masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop); |
6550 | } |
6551 | |
6552 | void CodeGenerator::emitRestoreStackPointerFromFP() { |
6553 | // This is used to restore the stack pointer after a call with a dynamic |
6554 | // number of arguments. |
6555 | |
6556 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6556); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 6556; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6557 | |
6558 | int32_t offset = -int32_t(frameSize()); |
6559 | masm.computeEffectiveAddress(Address(FramePointer, offset), |
6560 | masm.getStackPointer()); |
6561 | } |
6562 | |
6563 | void CodeGenerator::emitPushArguments(Register argcreg, Register scratch, |
6564 | Register copyreg, uint32_t extraFormals) { |
6565 | Label end; |
6566 | |
6567 | // Skip the copy of arguments if there are none. |
6568 | masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end); |
6569 | |
6570 | // clang-format off |
6571 | // |
6572 | // We are making a copy of the arguments which are above the JitFrameLayout |
6573 | // of the current Ion frame. |
6574 | // |
6575 | // [arg1] [arg0] <- src [this] [JitFrameLayout] [.. frameSize ..] [pad] [arg1] [arg0] <- dst |
6576 | // |
6577 | // clang-format on |
6578 | |
6579 | // Compute the source and destination offsets into the stack. |
6580 | // |
6581 | // The |extraFormals| parameter is used when copying rest-parameters and |
6582 | // allows to skip the initial parameters before the actual rest-parameters. |
6583 | Register argvSrcBase = FramePointer; |
6584 | size_t argvSrcOffset = |
6585 | JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value); |
6586 | size_t argvDstOffset = 0; |
6587 | |
6588 | Register argvIndex = scratch; |
6589 | masm.move32(argcreg, argvIndex); |
6590 | |
6591 | // Copy arguments. |
6592 | emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset, |
6593 | argvDstOffset); |
6594 | |
6595 | // Join with all arguments copied. |
6596 | masm.bind(&end); |
6597 | } |
6598 | |
6599 | void CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply) { |
6600 | // Holds the function nargs. |
6601 | Register argcreg = ToRegister(apply->getArgc()); |
6602 | Register copyreg = ToRegister(apply->getTempObject()); |
6603 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
6604 | uint32_t extraFormals = apply->numExtraFormals(); |
6605 | |
6606 | // Allocate space on the stack for arguments. |
6607 | emitAllocateSpaceForApply(argcreg, scratch); |
6608 | |
6609 | emitPushArguments(argcreg, scratch, copyreg, extraFormals); |
6610 | |
6611 | // Push |this|. |
6612 | masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex)); |
6613 | } |
6614 | |
6615 | void CodeGenerator::emitPushArguments(LApplyArgsObj* apply) { |
6616 | Register argsObj = ToRegister(apply->getArgsObj()); |
6617 | Register tmpArgc = ToRegister(apply->getTempObject()); |
6618 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
6619 | |
6620 | // argc and argsObj are mapped to the same calltemp register. |
6621 | MOZ_ASSERT(argsObj == ToRegister(apply->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(argsObj == ToRegister(apply->getArgc()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argsObj == ToRegister(apply->getArgc())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argsObj == ToRegister(apply->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6621); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argsObj == ToRegister(apply->getArgc())" ")"); do { *((volatile int*)__null) = 6621; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6622 | |
6623 | // Load argc into tmpArgc. |
6624 | masm.loadArgumentsObjectLength(argsObj, tmpArgc); |
6625 | |
6626 | // Allocate space on the stack for arguments. |
6627 | emitAllocateSpaceForApply(tmpArgc, scratch); |
6628 | |
6629 | // Load arguments data. |
6630 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
6631 | argsObj); |
6632 | size_t argsSrcOffset = ArgumentsData::offsetOfArgs(); |
6633 | |
6634 | // This is the end of the lifetime of argsObj. |
6635 | // After this call, the argsObj register holds the argument count instead. |
6636 | emitPushArrayAsArguments(tmpArgc, argsObj, scratch, argsSrcOffset); |
6637 | |
6638 | // Push |this|. |
6639 | masm.pushValue(ToValue(apply, LApplyArgsObj::ThisIndex)); |
6640 | } |
6641 | |
6642 | void CodeGenerator::emitPushArrayAsArguments(Register tmpArgc, |
6643 | Register srcBaseAndArgc, |
6644 | Register scratch, |
6645 | size_t argvSrcOffset) { |
6646 | // Preconditions: |
6647 | // 1. |tmpArgc| * sizeof(Value) bytes have been allocated at the top of |
6648 | // the stack to hold arguments. |
6649 | // 2. |srcBaseAndArgc| + |srcOffset| points to an array of |tmpArgc| values. |
6650 | // |
6651 | // Postconditions: |
6652 | // 1. The arguments at |srcBaseAndArgc| + |srcOffset| have been copied into |
6653 | // the allocated space. |
6654 | // 2. |srcBaseAndArgc| now contains the original value of |tmpArgc|. |
6655 | // |
6656 | // |scratch| is used as a temp register within this function and clobbered. |
6657 | |
6658 | Label noCopy, epilogue; |
6659 | |
6660 | // Skip the copy of arguments if there are none. |
6661 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
6662 | { |
6663 | // Copy the values. This code is skipped entirely if there are no values. |
6664 | size_t argvDstOffset = 0; |
6665 | |
6666 | Register argvSrcBase = srcBaseAndArgc; |
6667 | |
6668 | // Stash away |tmpArgc| and adjust argvDstOffset accordingly. |
6669 | masm.push(tmpArgc); |
6670 | Register argvIndex = tmpArgc; |
6671 | argvDstOffset += sizeof(void*); |
6672 | |
6673 | // Copy |
6674 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
6675 | argvDstOffset); |
6676 | |
6677 | // Restore. |
6678 | masm.pop(srcBaseAndArgc); // srcBaseAndArgc now contains argc. |
6679 | masm.jump(&epilogue); |
6680 | } |
6681 | masm.bind(&noCopy); |
6682 | { |
6683 | // Clear argc if we skipped the copy step. |
6684 | masm.movePtr(ImmWord(0), srcBaseAndArgc); |
6685 | } |
6686 | |
6687 | // Join with all arguments copied. |
6688 | // Note, "srcBase" has become "argc". |
6689 | masm.bind(&epilogue); |
6690 | } |
6691 | |
6692 | void CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply) { |
6693 | Register elements = ToRegister(apply->getElements()); |
6694 | Register tmpArgc = ToRegister(apply->getTempObject()); |
6695 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
6696 | |
6697 | // argc and elements are mapped to the same calltemp register. |
6698 | MOZ_ASSERT(elements == ToRegister(apply->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(elements == ToRegister(apply->getArgc()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(elements == ToRegister(apply->getArgc())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("elements == ToRegister(apply->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6698); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(apply->getArgc())" ")"); do { *((volatile int*)__null) = 6698; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6699 | |
6700 | // Invariants guarded in the caller: |
6701 | // - the array is not too long |
6702 | // - the array length equals its initialized length |
6703 | |
6704 | // The array length is our argc for the purposes of allocating space. |
6705 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
6706 | |
6707 | // Allocate space for the values. |
6708 | emitAllocateSpaceForApply(tmpArgc, scratch); |
6709 | |
6710 | // After this call "elements" has become "argc". |
6711 | size_t elementsOffset = 0; |
6712 | emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset); |
6713 | |
6714 | // Push |this|. |
6715 | masm.pushValue(ToValue(apply, LApplyArrayGeneric::ThisIndex)); |
6716 | } |
6717 | |
6718 | void CodeGenerator::emitPushArguments(LConstructArgsGeneric* construct) { |
6719 | // Holds the function nargs. |
6720 | Register argcreg = ToRegister(construct->getArgc()); |
6721 | Register copyreg = ToRegister(construct->getTempObject()); |
6722 | Register scratch = ToRegister(construct->getTempForArgCopy()); |
6723 | uint32_t extraFormals = construct->numExtraFormals(); |
6724 | |
6725 | // newTarget and scratch are mapped to the same calltemp register. |
6726 | MOZ_ASSERT(scratch == ToRegister(construct->getNewTarget()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch == ToRegister(construct->getNewTarget())) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(scratch == ToRegister(construct->getNewTarget())) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch == ToRegister(construct->getNewTarget())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6726); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())" ")"); do { *((volatile int*)__null) = 6726; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6727 | |
6728 | // Allocate space for the values. |
6729 | // After this call "newTarget" has become "scratch". |
6730 | emitAllocateSpaceForConstructAndPushNewTarget(argcreg, scratch); |
6731 | |
6732 | emitPushArguments(argcreg, scratch, copyreg, extraFormals); |
6733 | |
6734 | // Push |this|. |
6735 | masm.pushValue(ToValue(construct, LConstructArgsGeneric::ThisIndex)); |
6736 | } |
6737 | |
6738 | void CodeGenerator::emitPushArguments(LConstructArrayGeneric* construct) { |
6739 | Register elements = ToRegister(construct->getElements()); |
6740 | Register tmpArgc = ToRegister(construct->getTempObject()); |
6741 | Register scratch = ToRegister(construct->getTempForArgCopy()); |
6742 | |
6743 | // argc and elements are mapped to the same calltemp register. |
6744 | MOZ_ASSERT(elements == ToRegister(construct->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(elements == ToRegister(construct->getArgc()))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(elements == ToRegister(construct->getArgc())))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("elements == ToRegister(construct->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6744); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(construct->getArgc())" ")"); do { *((volatile int*)__null) = 6744; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6745 | |
6746 | // newTarget and scratch are mapped to the same calltemp register. |
6747 | MOZ_ASSERT(scratch == ToRegister(construct->getNewTarget()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch == ToRegister(construct->getNewTarget())) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(scratch == ToRegister(construct->getNewTarget())) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch == ToRegister(construct->getNewTarget())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6747); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())" ")"); do { *((volatile int*)__null) = 6747; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6748 | |
6749 | // Invariants guarded in the caller: |
6750 | // - the array is not too long |
6751 | // - the array length equals its initialized length |
6752 | |
6753 | // The array length is our argc for the purposes of allocating space. |
6754 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
6755 | |
6756 | // Allocate space for the values. |
6757 | // After this call "newTarget" has become "scratch". |
6758 | emitAllocateSpaceForConstructAndPushNewTarget(tmpArgc, scratch); |
6759 | |
6760 | // After this call "elements" has become "argc". |
6761 | size_t elementsOffset = 0; |
6762 | emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset); |
6763 | |
6764 | // Push |this|. |
6765 | masm.pushValue(ToValue(construct, LConstructArrayGeneric::ThisIndex)); |
6766 | } |
6767 | |
6768 | template <typename T> |
6769 | void CodeGenerator::emitApplyGeneric(T* apply) { |
6770 | // Holds the function object. |
6771 | Register calleereg = ToRegister(apply->getFunction()); |
6772 | |
6773 | // Temporary register for modifying the function object. |
6774 | Register objreg = ToRegister(apply->getTempObject()); |
6775 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
6776 | |
6777 | // Holds the function nargs, computed in the invoker or (for ApplyArray, |
6778 | // ConstructArray, or ApplyArgsObj) in the argument pusher. |
6779 | Register argcreg = ToRegister(apply->getArgc()); |
6780 | |
6781 | // Copy the arguments of the current function. |
6782 | // |
6783 | // In the case of ApplyArray, ConstructArray, or ApplyArgsObj, also compute |
6784 | // argc. The argc register and the elements/argsObj register are the same; |
6785 | // argc must not be referenced before the call to emitPushArguments() and |
6786 | // elements/argsObj must not be referenced after it returns. |
6787 | // |
6788 | // In the case of ConstructArray or ConstructArgs, also overwrite newTarget; |
6789 | // newTarget must not be referenced after this point. |
6790 | // |
6791 | // objreg is dead across this call. |
6792 | emitPushArguments(apply); |
6793 | |
6794 | masm.checkStackAlignment(); |
6795 | |
6796 | bool constructing = apply->mir()->isConstructing(); |
6797 | |
6798 | // If the function is native, the call is compiled through emitApplyNative. |
6799 | MOZ_ASSERT_IF(apply->hasSingleTarget(),do { if (apply->hasSingleTarget()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!apply->getSingleTarget ()->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!apply->getSingleTarget() ->isNativeWithoutJitEntry()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("!apply->getSingleTarget()->isNativeWithoutJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 6800; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
6800 | !apply->getSingleTarget()->isNativeWithoutJitEntry())do { if (apply->hasSingleTarget()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!apply->getSingleTarget ()->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!apply->getSingleTarget() ->isNativeWithoutJitEntry()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("!apply->getSingleTarget()->isNativeWithoutJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 6800; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
6801 | |
6802 | Label end, invoke; |
6803 | |
6804 | // Unless already known, guard that calleereg is actually a function object. |
6805 | if (!apply->hasSingleTarget()) { |
6806 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleereg, objreg, |
6807 | calleereg, &invoke); |
6808 | } |
6809 | |
6810 | // Guard that calleereg is an interpreted function with a JSScript. |
6811 | masm.branchIfFunctionHasNoJitEntry(calleereg, &invoke); |
6812 | |
6813 | // Guard that callee allows the [[Call]] or [[Construct]] operation required. |
6814 | if (constructing) { |
6815 | masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR, |
6816 | Assembler::Zero, &invoke); |
6817 | } else { |
6818 | masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor, |
6819 | calleereg, objreg, &invoke); |
6820 | } |
6821 | |
6822 | // Use the slow path if CreateThis was unable to create the |this| object. |
6823 | if (constructing) { |
6824 | Address thisAddr(masm.getStackPointer(), 0); |
6825 | masm.branchTestNull(Assembler::Equal, thisAddr, &invoke); |
6826 | } |
6827 | |
6828 | // Call with an Ion frame or a rectifier frame. |
6829 | { |
6830 | if (apply->mir()->maybeCrossRealm()) { |
6831 | masm.switchToObjectRealm(calleereg, objreg); |
6832 | } |
6833 | |
6834 | // Knowing that calleereg is a non-native function, load jitcode. |
6835 | masm.loadJitCodeRaw(calleereg, objreg); |
6836 | |
6837 | masm.PushCalleeToken(calleereg, constructing); |
6838 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcreg, scratch); |
6839 | |
6840 | Label underflow, rejoin; |
6841 | |
6842 | // Check whether the provided arguments satisfy target argc. |
6843 | if (!apply->hasSingleTarget()) { |
6844 | Register nformals = scratch; |
6845 | masm.loadFunctionArgCount(calleereg, nformals); |
6846 | masm.branch32(Assembler::Below, argcreg, nformals, &underflow); |
6847 | } else { |
6848 | masm.branch32(Assembler::Below, argcreg, |
6849 | Imm32(apply->getSingleTarget()->nargs()), &underflow); |
6850 | } |
6851 | |
6852 | // Skip the construction of the rectifier frame because we have no |
6853 | // underflow. |
6854 | masm.jump(&rejoin); |
6855 | |
6856 | // Argument fixup needed. Get ready to call the argumentsRectifier. |
6857 | { |
6858 | masm.bind(&underflow); |
6859 | |
6860 | // Hardcode the address of the argumentsRectifier code. |
6861 | TrampolinePtr argumentsRectifier = |
6862 | gen->jitRuntime()->getArgumentsRectifier(); |
6863 | masm.movePtr(argumentsRectifier, objreg); |
6864 | } |
6865 | |
6866 | masm.bind(&rejoin); |
6867 | |
6868 | // Finally call the function in objreg, as assigned by one of the paths |
6869 | // above. |
6870 | ensureOsiSpace(); |
6871 | uint32_t callOffset = masm.callJit(objreg); |
6872 | markSafepointAt(callOffset, apply); |
6873 | |
6874 | if (apply->mir()->maybeCrossRealm()) { |
6875 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
6876 | "ReturnReg available as scratch after scripted calls"); |
6877 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
6878 | } |
6879 | |
6880 | // Discard JitFrameLayout fields still left on the stack. |
6881 | masm.freeStack(sizeof(JitFrameLayout) - |
6882 | JitFrameLayout::bytesPoppedAfterCall()); |
6883 | masm.jump(&end); |
6884 | } |
6885 | |
6886 | // Handle uncompiled or native functions. |
6887 | { |
6888 | masm.bind(&invoke); |
6889 | emitCallInvokeFunction(apply); |
6890 | } |
6891 | |
6892 | masm.bind(&end); |
6893 | |
6894 | // If the return value of the constructing function is Primitive, replace the |
6895 | // return value with the Object from CreateThis. |
6896 | if (constructing) { |
6897 | Label notPrimitive; |
6898 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
6899 | ¬Primitive); |
6900 | masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand); |
6901 | |
6902 | #ifdef DEBUG1 |
6903 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
6904 | ¬Primitive); |
6905 | masm.assumeUnreachable("CreateThis creates an object"); |
6906 | #endif |
6907 | |
6908 | masm.bind(¬Primitive); |
6909 | } |
6910 | |
6911 | // Pop arguments and continue. |
6912 | emitRestoreStackPointerFromFP(); |
6913 | } |
6914 | |
6915 | template <typename T> |
6916 | void CodeGenerator::emitAlignStackForApplyNative(T* apply, Register argc) { |
6917 | static_assert(JitStackAlignment % ABIStackAlignment == 0, |
6918 | "aligning on JIT stack subsumes ABI alignment"); |
6919 | |
6920 | // Align the arguments on the JitStackAlignment. |
6921 | if (JitStackValueAlignment > 1) { |
6922 | MOZ_ASSERT(JitStackValueAlignment == 2,do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" " (" "Stack padding adds exactly one Value" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ") (" "Stack padding adds exactly one Value" ")"); do { *((volatile int*)__null) = 6923; __attribute__((nomerge)) ::abort(); } while (false); } } while (false) |
6923 | "Stack padding adds exactly one Value")do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" " (" "Stack padding adds exactly one Value" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ") (" "Stack padding adds exactly one Value" ")"); do { *((volatile int*)__null) = 6923; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
6924 | MOZ_ASSERT(frameSize() % JitStackValueAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackValueAlignment == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(frameSize() % JitStackValueAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("frameSize() % JitStackValueAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6925); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6925; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
6925 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackValueAlignment == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(frameSize() % JitStackValueAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("frameSize() % JitStackValueAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6925); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6925; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
6926 | |
6927 | Assembler::Condition cond; |
6928 | if constexpr (T::isConstructing()) { |
6929 | // If the number of arguments is even, then we do not need any padding. |
6930 | // |
6931 | // Also see emitAllocateSpaceForApply(). |
6932 | cond = Assembler::Zero; |
6933 | } else { |
6934 | // If the number of arguments is odd, then we do not need any padding. |
6935 | // |
6936 | // Also see emitAllocateSpaceForConstructAndPushNewTarget(). |
6937 | cond = Assembler::NonZero; |
6938 | } |
6939 | |
6940 | Label noPaddingNeeded; |
6941 | masm.branchTestPtr(cond, argc, Imm32(1), &noPaddingNeeded); |
6942 | masm.pushValue(MagicValue(JS_ARG_POISON)); |
6943 | masm.bind(&noPaddingNeeded); |
6944 | } |
6945 | } |
6946 | |
6947 | template <typename T> |
6948 | void CodeGenerator::emitPushNativeArguments(T* apply) { |
6949 | Register argc = ToRegister(apply->getArgc()); |
6950 | Register tmpArgc = ToRegister(apply->getTempObject()); |
6951 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
6952 | uint32_t extraFormals = apply->numExtraFormals(); |
6953 | |
6954 | // Align stack. |
6955 | emitAlignStackForApplyNative(apply, argc); |
6956 | |
6957 | // Push newTarget. |
6958 | if constexpr (T::isConstructing()) { |
6959 | masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget())); |
6960 | } |
6961 | |
6962 | // Push arguments. |
6963 | Label noCopy; |
6964 | masm.branchTestPtr(Assembler::Zero, argc, argc, &noCopy); |
6965 | { |
6966 | // Use scratch register to calculate stack space. |
6967 | masm.movePtr(argc, scratch); |
6968 | |
6969 | // Reserve space for copying the arguments. |
6970 | NativeObject::elementsSizeMustNotOverflow(); |
6971 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
6972 | masm.subFromStackPtr(scratch); |
6973 | |
6974 | // Compute the source and destination offsets into the stack. |
6975 | Register argvSrcBase = FramePointer; |
6976 | size_t argvSrcOffset = |
6977 | JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value); |
6978 | size_t argvDstOffset = 0; |
6979 | |
6980 | Register argvIndex = tmpArgc; |
6981 | masm.move32(argc, argvIndex); |
6982 | |
6983 | // Copy arguments. |
6984 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
6985 | argvDstOffset); |
6986 | } |
6987 | masm.bind(&noCopy); |
6988 | |
6989 | // Push |this|. |
6990 | if constexpr (T::isConstructing()) { |
6991 | masm.pushValue(MagicValue(JS_IS_CONSTRUCTING)); |
6992 | } else { |
6993 | masm.pushValue(ToValue(apply, T::ThisIndex)); |
6994 | } |
6995 | } |
6996 | |
6997 | template <typename T> |
6998 | void CodeGenerator::emitPushArrayAsNativeArguments(T* apply) { |
6999 | Register argc = ToRegister(apply->getArgc()); |
7000 | Register elements = ToRegister(apply->getElements()); |
7001 | Register tmpArgc = ToRegister(apply->getTempObject()); |
7002 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
7003 | |
7004 | // NB: argc and elements are mapped to the same register. |
7005 | MOZ_ASSERT(argc == elements)do { static_assert( mozilla::detail::AssertionConditionType< decltype(argc == elements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(argc == elements))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argc == elements" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7005); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == elements" ")"); do { *((volatile int*)__null) = 7005; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7006 | |
7007 | // Invariants guarded in the caller: |
7008 | // - the array is not too long |
7009 | // - the array length equals its initialized length |
7010 | |
7011 | // The array length is our argc. |
7012 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
7013 | |
7014 | // Align stack. |
7015 | emitAlignStackForApplyNative(apply, tmpArgc); |
7016 | |
7017 | // Push newTarget. |
7018 | if constexpr (T::isConstructing()) { |
7019 | masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget())); |
7020 | } |
7021 | |
7022 | // Skip the copy of arguments if there are none. |
7023 | Label noCopy; |
7024 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
7025 | { |
7026 | // |tmpArgc| is off-by-one, so adjust the offset accordingly. |
7027 | BaseObjectElementIndex srcPtr(elements, tmpArgc, |
7028 | -int32_t(sizeof(JS::Value))); |
7029 | |
7030 | Label loop; |
7031 | masm.bind(&loop); |
7032 | masm.pushValue(srcPtr, scratch); |
7033 | masm.decBranchPtr(Assembler::NonZero, tmpArgc, Imm32(1), &loop); |
7034 | } |
7035 | masm.bind(&noCopy); |
7036 | |
7037 | // Set argc in preparation for calling the native function. |
7038 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), argc); |
7039 | |
7040 | // Push |this|. |
7041 | if constexpr (T::isConstructing()) { |
7042 | masm.pushValue(MagicValue(JS_IS_CONSTRUCTING)); |
7043 | } else { |
7044 | masm.pushValue(ToValue(apply, T::ThisIndex)); |
7045 | } |
7046 | } |
7047 | |
7048 | void CodeGenerator::emitPushArguments(LApplyArgsNative* apply) { |
7049 | emitPushNativeArguments(apply); |
7050 | } |
7051 | |
7052 | void CodeGenerator::emitPushArguments(LApplyArrayNative* apply) { |
7053 | emitPushArrayAsNativeArguments(apply); |
7054 | } |
7055 | |
7056 | void CodeGenerator::emitPushArguments(LConstructArgsNative* construct) { |
7057 | emitPushNativeArguments(construct); |
7058 | } |
7059 | |
7060 | void CodeGenerator::emitPushArguments(LConstructArrayNative* construct) { |
7061 | emitPushArrayAsNativeArguments(construct); |
7062 | } |
7063 | |
7064 | void CodeGenerator::emitPushArguments(LApplyArgsObjNative* apply) { |
7065 | Register argc = ToRegister(apply->getArgc()); |
7066 | Register argsObj = ToRegister(apply->getArgsObj()); |
7067 | Register tmpArgc = ToRegister(apply->getTempObject()); |
7068 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
7069 | Register scratch2 = ToRegister(apply->getTempExtra()); |
7070 | |
7071 | // NB: argc and argsObj are mapped to the same register. |
7072 | MOZ_ASSERT(argc == argsObj)do { static_assert( mozilla::detail::AssertionConditionType< decltype(argc == argsObj)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(argc == argsObj))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argc == argsObj" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7072); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == argsObj" ")"); do { *((volatile int*)__null) = 7072; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7073 | |
7074 | // Load argc into tmpArgc. |
7075 | masm.loadArgumentsObjectLength(argsObj, tmpArgc); |
7076 | |
7077 | // Align stack. |
7078 | emitAlignStackForApplyNative(apply, tmpArgc); |
7079 | |
7080 | // Push arguments. |
7081 | Label noCopy, epilogue; |
7082 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
7083 | { |
7084 | // Use scratch register to calculate stack space. |
7085 | masm.movePtr(tmpArgc, scratch); |
7086 | |
7087 | // Reserve space for copying the arguments. |
7088 | NativeObject::elementsSizeMustNotOverflow(); |
7089 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
7090 | masm.subFromStackPtr(scratch); |
7091 | |
7092 | // Load arguments data. |
7093 | Register argvSrcBase = argsObj; |
7094 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
7095 | argvSrcBase); |
7096 | size_t argvSrcOffset = ArgumentsData::offsetOfArgs(); |
7097 | size_t argvDstOffset = 0; |
7098 | |
7099 | Register argvIndex = scratch2; |
7100 | masm.move32(tmpArgc, argvIndex); |
7101 | |
7102 | // Copy the values. |
7103 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
7104 | argvDstOffset); |
7105 | } |
7106 | masm.bind(&noCopy); |
7107 | |
7108 | // Set argc in preparation for calling the native function. |
7109 | masm.movePtr(tmpArgc, argc); |
7110 | |
7111 | // Push |this|. |
7112 | masm.pushValue(ToValue(apply, LApplyArgsObjNative::ThisIndex)); |
7113 | } |
7114 | |
7115 | template <typename T> |
7116 | void CodeGenerator::emitApplyNative(T* apply) { |
7117 | MOZ_ASSERT(T::isConstructing() == apply->mir()->isConstructing(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(T::isConstructing() == apply->mir()->isConstructing ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(T::isConstructing() == apply->mir()->isConstructing ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("T::isConstructing() == apply->mir()->isConstructing()" " (" "isConstructing condition must be consistent" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7118); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()" ") (" "isConstructing condition must be consistent" ")"); do { *((volatile int*)__null) = 7118; __attribute__((nomerge)) :: abort(); } while (false); } } while (false) |
7118 | "isConstructing condition must be consistent")do { static_assert( mozilla::detail::AssertionConditionType< decltype(T::isConstructing() == apply->mir()->isConstructing ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(T::isConstructing() == apply->mir()->isConstructing ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("T::isConstructing() == apply->mir()->isConstructing()" " (" "isConstructing condition must be consistent" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7118); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()" ") (" "isConstructing condition must be consistent" ")"); do { *((volatile int*)__null) = 7118; __attribute__((nomerge)) :: abort(); } while (false); } } while (false); |
7119 | |
7120 | WrappedFunction* target = apply->mir()->getSingleTarget(); |
7121 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 7121; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7122 | |
7123 | JSNative native = target->native(); |
7124 | if (apply->mir()->ignoresReturnValue() && target->hasJitInfo()) { |
7125 | const JSJitInfo* jitInfo = target->jitInfo(); |
7126 | if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) { |
7127 | native = jitInfo->ignoresReturnValueMethod; |
7128 | } |
7129 | } |
7130 | |
7131 | // Push arguments, including newTarget and |this|. |
7132 | emitPushArguments(apply); |
7133 | |
7134 | // Registers used for callWithABI() argument-passing. |
7135 | Register argContextReg = ToRegister(apply->getTempObject()); |
7136 | Register argUintNReg = ToRegister(apply->getArgc()); |
7137 | Register argVpReg = ToRegister(apply->getTempForArgCopy()); |
7138 | Register tempReg = ToRegister(apply->getTempExtra()); |
7139 | |
7140 | // No unused stack for variadic calls. |
7141 | uint32_t unusedStack = 0; |
7142 | |
7143 | // Pushed arguments don't change the pushed frames amount. |
7144 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7144); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 7144; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7145 | |
7146 | // Create the exit frame and call the native. |
7147 | emitCallNative(apply, native, argContextReg, argUintNReg, argVpReg, tempReg, |
7148 | unusedStack); |
7149 | |
7150 | // The exit frame is still on the stack. |
7151 | MOZ_ASSERT(masm.framePushed() == frameSize() + NativeExitFrameLayout::Size())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize() + NativeExitFrameLayout ::Size())>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(masm.framePushed() == frameSize() + NativeExitFrameLayout ::Size()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7151); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()" ")"); do { *((volatile int*)__null) = 7151; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7152 | |
7153 | // The next instruction is removing the exit frame, so there is no need for |
7154 | // leaveFakeExitFrame. |
7155 | |
7156 | // Pop arguments and continue. |
7157 | masm.setFramePushed(frameSize()); |
7158 | emitRestoreStackPointerFromFP(); |
7159 | } |
7160 | |
7161 | template <typename T> |
7162 | void CodeGenerator::emitApplyArgsGuard(T* apply) { |
7163 | LSnapshot* snapshot = apply->snapshot(); |
7164 | Register argcreg = ToRegister(apply->getArgc()); |
7165 | |
7166 | // Ensure that we have a reasonable number of arguments. |
7167 | bailoutCmp32(Assembler::Above, argcreg, Imm32(JIT_ARGS_LENGTH_MAX), snapshot); |
7168 | } |
7169 | |
7170 | template <typename T> |
7171 | void CodeGenerator::emitApplyArgsObjGuard(T* apply) { |
7172 | Register argsObj = ToRegister(apply->getArgsObj()); |
7173 | Register temp = ToRegister(apply->getTempObject()); |
7174 | |
7175 | Label bail; |
7176 | masm.loadArgumentsObjectLength(argsObj, temp, &bail); |
7177 | masm.branch32(Assembler::Above, temp, Imm32(JIT_ARGS_LENGTH_MAX), &bail); |
7178 | bailoutFrom(&bail, apply->snapshot()); |
7179 | } |
7180 | |
7181 | template <typename T> |
7182 | void CodeGenerator::emitApplyArrayGuard(T* apply) { |
7183 | LSnapshot* snapshot = apply->snapshot(); |
7184 | Register elements = ToRegister(apply->getElements()); |
7185 | Register tmp = ToRegister(apply->getTempObject()); |
7186 | |
7187 | Address length(elements, ObjectElements::offsetOfLength()); |
7188 | masm.load32(length, tmp); |
7189 | |
7190 | // Ensure that we have a reasonable number of arguments. |
7191 | bailoutCmp32(Assembler::Above, tmp, Imm32(JIT_ARGS_LENGTH_MAX), snapshot); |
7192 | |
7193 | // Ensure that the array does not contain an uninitialized tail. |
7194 | |
7195 | Address initializedLength(elements, |
7196 | ObjectElements::offsetOfInitializedLength()); |
7197 | masm.sub32(initializedLength, tmp); |
7198 | bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot); |
7199 | } |
7200 | |
7201 | void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) { |
7202 | emitApplyArgsGuard(apply); |
7203 | emitApplyGeneric(apply); |
7204 | } |
7205 | |
7206 | void CodeGenerator::visitApplyArgsObj(LApplyArgsObj* apply) { |
7207 | emitApplyArgsObjGuard(apply); |
7208 | emitApplyGeneric(apply); |
7209 | } |
7210 | |
7211 | void CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) { |
7212 | emitApplyArrayGuard(apply); |
7213 | emitApplyGeneric(apply); |
7214 | } |
7215 | |
7216 | void CodeGenerator::visitConstructArgsGeneric(LConstructArgsGeneric* lir) { |
7217 | emitApplyArgsGuard(lir); |
7218 | emitApplyGeneric(lir); |
7219 | } |
7220 | |
7221 | void CodeGenerator::visitConstructArrayGeneric(LConstructArrayGeneric* lir) { |
7222 | emitApplyArrayGuard(lir); |
7223 | emitApplyGeneric(lir); |
7224 | } |
7225 | |
7226 | void CodeGenerator::visitApplyArgsNative(LApplyArgsNative* lir) { |
7227 | emitApplyArgsGuard(lir); |
7228 | emitApplyNative(lir); |
7229 | } |
7230 | |
7231 | void CodeGenerator::visitApplyArgsObjNative(LApplyArgsObjNative* lir) { |
7232 | emitApplyArgsObjGuard(lir); |
7233 | emitApplyNative(lir); |
7234 | } |
7235 | |
7236 | void CodeGenerator::visitApplyArrayNative(LApplyArrayNative* lir) { |
7237 | emitApplyArrayGuard(lir); |
7238 | emitApplyNative(lir); |
7239 | } |
7240 | |
7241 | void CodeGenerator::visitConstructArgsNative(LConstructArgsNative* lir) { |
7242 | emitApplyArgsGuard(lir); |
7243 | emitApplyNative(lir); |
7244 | } |
7245 | |
7246 | void CodeGenerator::visitConstructArrayNative(LConstructArrayNative* lir) { |
7247 | emitApplyArrayGuard(lir); |
7248 | emitApplyNative(lir); |
7249 | } |
7250 | |
7251 | void CodeGenerator::visitBail(LBail* lir) { bailout(lir->snapshot()); } |
7252 | |
7253 | void CodeGenerator::visitUnreachable(LUnreachable* lir) { |
7254 | masm.assumeUnreachable("end-of-block assumed unreachable"); |
7255 | } |
7256 | |
7257 | void CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot* lir) { |
7258 | encode(lir->snapshot()); |
7259 | } |
7260 | |
7261 | void CodeGenerator::visitUnreachableResultV(LUnreachableResultV* lir) { |
7262 | masm.assumeUnreachable("must be unreachable"); |
7263 | } |
7264 | |
7265 | void CodeGenerator::visitUnreachableResultT(LUnreachableResultT* lir) { |
7266 | masm.assumeUnreachable("must be unreachable"); |
7267 | } |
7268 | |
7269 | // Out-of-line path to report over-recursed error and fail. |
7270 | class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator> { |
7271 | LInstruction* lir_; |
7272 | |
7273 | public: |
7274 | explicit CheckOverRecursedFailure(LInstruction* lir) : lir_(lir) {} |
7275 | |
7276 | void accept(CodeGenerator* codegen) override { |
7277 | codegen->visitCheckOverRecursedFailure(this); |
7278 | } |
7279 | |
7280 | LInstruction* lir() const { return lir_; } |
7281 | }; |
7282 | |
7283 | void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) { |
7284 | // If we don't push anything on the stack, skip the check. |
7285 | if (omitOverRecursedCheck()) { |
7286 | return; |
7287 | } |
7288 | |
7289 | // Ensure that this frame will not cross the stack limit. |
7290 | // This is a weak check, justified by Ion using the C stack: we must always |
7291 | // be some distance away from the actual limit, since if the limit is |
7292 | // crossed, an error must be thrown, which requires more frames. |
7293 | // |
7294 | // It must always be possible to trespass past the stack limit. |
7295 | // Ion may legally place frames very close to the limit. Calling additional |
7296 | // C functions may then violate the limit without any checking. |
7297 | // |
7298 | // Since Ion frames exist on the C stack, the stack limit may be |
7299 | // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota(). |
7300 | |
7301 | CheckOverRecursedFailure* ool = new (alloc()) CheckOverRecursedFailure(lir); |
7302 | addOutOfLineCode(ool, lir->mir()); |
7303 | |
7304 | // Conditional forward (unlikely) branch to failure. |
7305 | const void* limitAddr = gen->runtime->addressOfJitStackLimit(); |
7306 | masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), |
7307 | ool->entry()); |
7308 | masm.bind(ool->rejoin()); |
7309 | } |
7310 | |
7311 | void CodeGenerator::visitCheckOverRecursedFailure( |
7312 | CheckOverRecursedFailure* ool) { |
7313 | // The OOL path is hit if the recursion depth has been exceeded. |
7314 | // Throw an InternalError for over-recursion. |
7315 | |
7316 | // LFunctionEnvironment can appear before LCheckOverRecursed, so we have |
7317 | // to save all live registers to avoid crashes if CheckOverRecursed triggers |
7318 | // a GC. |
7319 | saveLive(ool->lir()); |
7320 | |
7321 | using Fn = bool (*)(JSContext*); |
7322 | callVM<Fn, CheckOverRecursed>(ool->lir()); |
7323 | |
7324 | restoreLive(ool->lir()); |
7325 | masm.jump(ool->rejoin()); |
7326 | } |
7327 | |
7328 | IonScriptCounts* CodeGenerator::maybeCreateScriptCounts() { |
7329 | // If scripts are being profiled, create a new IonScriptCounts for the |
7330 | // profiling data, which will be attached to the associated JSScript or |
7331 | // wasm module after code generation finishes. |
7332 | if (!gen->hasProfilingScripts()) { |
7333 | return nullptr; |
7334 | } |
7335 | |
7336 | // This test inhibits IonScriptCount creation for wasm code which is |
7337 | // currently incompatible with wasm codegen for two reasons: (1) wasm code |
7338 | // must be serializable and script count codegen bakes in absolute |
7339 | // addresses, (2) wasm code does not have a JSScript with which to associate |
7340 | // code coverage data. |
7341 | JSScript* script = gen->outerInfo().script(); |
7342 | if (!script) { |
7343 | return nullptr; |
7344 | } |
7345 | |
7346 | auto counts = MakeUnique<IonScriptCounts>(); |
7347 | if (!counts || !counts->init(graph.numBlocks())) { |
7348 | return nullptr; |
7349 | } |
7350 | |
7351 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
7352 | MBasicBlock* block = graph.getBlock(i)->mir(); |
7353 | |
7354 | uint32_t offset = 0; |
7355 | char* description = nullptr; |
7356 | if (MResumePoint* resume = block->entryResumePoint()) { |
7357 | // Find a PC offset in the outermost script to use. If this |
7358 | // block is from an inlined script, find a location in the |
7359 | // outer script to associate information about the inlining |
7360 | // with. |
7361 | while (resume->caller()) { |
7362 | resume = resume->caller(); |
7363 | } |
7364 | offset = script->pcToOffset(resume->pc()); |
7365 | |
7366 | if (block->entryResumePoint()->caller()) { |
7367 | // Get the filename and line number of the inner script. |
7368 | JSScript* innerScript = block->info().script(); |
7369 | description = js_pod_calloc<char>(200); |
7370 | if (description) { |
7371 | snprintf(description, 200, "%s:%u", innerScript->filename(), |
7372 | innerScript->lineno()); |
7373 | } |
7374 | } |
7375 | } |
7376 | |
7377 | if (!counts->block(i).init(block->id(), offset, description, |
7378 | block->numSuccessors())) { |
7379 | return nullptr; |
7380 | } |
7381 | |
7382 | for (size_t j = 0; j < block->numSuccessors(); j++) { |
7383 | counts->block(i).setSuccessor( |
7384 | j, skipTrivialBlocks(block->getSuccessor(j))->id()); |
7385 | } |
7386 | } |
7387 | |
7388 | scriptCounts_ = counts.release(); |
7389 | return scriptCounts_; |
7390 | } |
7391 | |
7392 | // Structure for managing the state tracked for a block by script counters. |
7393 | struct ScriptCountBlockState { |
7394 | IonBlockCounts& block; |
7395 | MacroAssembler& masm; |
7396 | |
7397 | Sprinter printer; |
7398 | |
7399 | public: |
7400 | ScriptCountBlockState(IonBlockCounts* block, MacroAssembler* masm) |
7401 | : block(*block), masm(*masm), printer(GetJitContext()->cx, false) {} |
7402 | |
7403 | bool init() { |
7404 | if (!printer.init()) { |
7405 | return false; |
7406 | } |
7407 | |
7408 | // Bump the hit count for the block at the start. This code is not |
7409 | // included in either the text for the block or the instruction byte |
7410 | // counts. |
7411 | masm.inc64(AbsoluteAddress(block.addressOfHitCount())); |
7412 | |
7413 | // Collect human readable assembly for the code generated in the block. |
7414 | masm.setPrinter(&printer); |
7415 | |
7416 | return true; |
7417 | } |
7418 | |
7419 | void visitInstruction(LInstruction* ins) { |
7420 | #ifdef JS_JITSPEW1 |
7421 | // Prefix stream of assembly instructions with their LIR instruction |
7422 | // name and any associated high level info. |
7423 | if (const char* extra = ins->getExtraName()) { |
7424 | printer.printf("[%s:%s]\n", ins->opName(), extra); |
7425 | } else { |
7426 | printer.printf("[%s]\n", ins->opName()); |
7427 | } |
7428 | #endif |
7429 | } |
7430 | |
7431 | ~ScriptCountBlockState() { |
7432 | masm.setPrinter(nullptr); |
7433 | |
7434 | if (JS::UniqueChars str = printer.release()) { |
7435 | block.setCode(str.get()); |
7436 | } |
7437 | } |
7438 | }; |
7439 | |
7440 | void CodeGenerator::branchIfInvalidated(Register temp, Label* invalidated) { |
7441 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp); |
7442 | masm.propagateOOM(ionScriptLabels_.append(label)); |
7443 | |
7444 | // If IonScript::invalidationCount_ != 0, the script has been invalidated. |
7445 | masm.branch32(Assembler::NotEqual, |
7446 | Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0), |
7447 | invalidated); |
7448 | } |
7449 | |
7450 | #ifdef DEBUG1 |
7451 | void CodeGenerator::emitAssertGCThingResult(Register input, |
7452 | const MDefinition* mir) { |
7453 | MIRType type = mir->type(); |
7454 | MOZ_ASSERT(type == MIRType::Object || type == MIRType::String ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 7455; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
7455 | type == MIRType::Symbol || type == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 7455; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7456 | |
7457 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
7458 | regs.take(input); |
7459 | |
7460 | Register temp = regs.takeAny(); |
7461 | masm.push(temp); |
7462 | |
7463 | // Don't check if the script has been invalidated. In that case invalid |
7464 | // types are expected (until we reach the OsiPoint and bailout). |
7465 | Label done; |
7466 | branchIfInvalidated(temp, &done); |
7467 | |
7468 | # ifndef JS_SIMULATOR |
7469 | // Check that we have a valid GC pointer. |
7470 | // Disable for wasm because we don't have a context on wasm compilation |
7471 | // threads and this needs a context. |
7472 | // Also disable for simulator builds because the C++ call is a lot slower |
7473 | // there than on actual hardware. |
7474 | if (JitOptions.fullDebugChecks && !IsCompilingWasm()) { |
7475 | saveVolatile(); |
7476 | masm.setupUnalignedABICall(temp); |
7477 | masm.loadJSContext(temp); |
7478 | masm.passABIArg(temp); |
7479 | masm.passABIArg(input); |
7480 | |
7481 | switch (type) { |
7482 | case MIRType::Object: { |
7483 | using Fn = void (*)(JSContext* cx, JSObject* obj); |
7484 | masm.callWithABI<Fn, AssertValidObjectPtr>(); |
7485 | break; |
7486 | } |
7487 | case MIRType::String: { |
7488 | using Fn = void (*)(JSContext* cx, JSString* str); |
7489 | masm.callWithABI<Fn, AssertValidStringPtr>(); |
7490 | break; |
7491 | } |
7492 | case MIRType::Symbol: { |
7493 | using Fn = void (*)(JSContext* cx, JS::Symbol* sym); |
7494 | masm.callWithABI<Fn, AssertValidSymbolPtr>(); |
7495 | break; |
7496 | } |
7497 | case MIRType::BigInt: { |
7498 | using Fn = void (*)(JSContext* cx, JS::BigInt* bi); |
7499 | masm.callWithABI<Fn, AssertValidBigIntPtr>(); |
7500 | break; |
7501 | } |
7502 | default: |
7503 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7503); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 7503; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
7504 | } |
7505 | |
7506 | restoreVolatile(); |
7507 | } |
7508 | # endif |
7509 | |
7510 | masm.bind(&done); |
7511 | masm.pop(temp); |
7512 | } |
7513 | |
7514 | void CodeGenerator::emitAssertResultV(const ValueOperand input, |
7515 | const MDefinition* mir) { |
7516 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
7517 | regs.take(input); |
7518 | |
7519 | Register temp1 = regs.takeAny(); |
7520 | Register temp2 = regs.takeAny(); |
7521 | masm.push(temp1); |
7522 | masm.push(temp2); |
7523 | |
7524 | // Don't check if the script has been invalidated. In that case invalid |
7525 | // types are expected (until we reach the OsiPoint and bailout). |
7526 | Label done; |
7527 | branchIfInvalidated(temp1, &done); |
7528 | |
7529 | // Check that we have a valid GC pointer. |
7530 | if (JitOptions.fullDebugChecks) { |
7531 | saveVolatile(); |
7532 | |
7533 | masm.pushValue(input); |
7534 | masm.moveStackPtrTo(temp1); |
7535 | |
7536 | using Fn = void (*)(JSContext* cx, Value* v); |
7537 | masm.setupUnalignedABICall(temp2); |
7538 | masm.loadJSContext(temp2); |
7539 | masm.passABIArg(temp2); |
7540 | masm.passABIArg(temp1); |
7541 | masm.callWithABI<Fn, AssertValidValue>(); |
7542 | masm.popValue(input); |
7543 | restoreVolatile(); |
7544 | } |
7545 | |
7546 | masm.bind(&done); |
7547 | masm.pop(temp2); |
7548 | masm.pop(temp1); |
7549 | } |
7550 | |
7551 | void CodeGenerator::emitGCThingResultChecks(LInstruction* lir, |
7552 | MDefinition* mir) { |
7553 | if (lir->numDefs() == 0) { |
7554 | return; |
7555 | } |
7556 | |
7557 | MOZ_ASSERT(lir->numDefs() == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->numDefs() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->numDefs() == 1))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("lir->numDefs() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7557); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { *((volatile int*)__null) = 7557; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7558 | if (lir->getDef(0)->isBogusTemp()) { |
7559 | return; |
7560 | } |
7561 | |
7562 | Register output = ToRegister(lir->getDef(0)); |
7563 | emitAssertGCThingResult(output, mir); |
7564 | } |
7565 | |
7566 | void CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir) { |
7567 | if (lir->numDefs() == 0) { |
7568 | return; |
7569 | } |
7570 | |
7571 | MOZ_ASSERT(lir->numDefs() == BOX_PIECES)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->numDefs() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->numDefs() == 1))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("lir->numDefs() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { *((volatile int*)__null) = 7571; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7572 | if (!lir->getDef(0)->output()->isRegister()) { |
7573 | return; |
7574 | } |
7575 | |
7576 | ValueOperand output = ToOutValue(lir); |
7577 | |
7578 | emitAssertResultV(output, mir); |
7579 | } |
7580 | |
7581 | void CodeGenerator::emitDebugResultChecks(LInstruction* ins) { |
7582 | // In debug builds, check that LIR instructions return valid values. |
7583 | |
7584 | MDefinition* mir = ins->mirRaw(); |
7585 | if (!mir) { |
7586 | return; |
7587 | } |
7588 | |
7589 | switch (mir->type()) { |
7590 | case MIRType::Object: |
7591 | case MIRType::String: |
7592 | case MIRType::Symbol: |
7593 | case MIRType::BigInt: |
7594 | emitGCThingResultChecks(ins, mir); |
7595 | break; |
7596 | case MIRType::Value: |
7597 | emitValueResultChecks(ins, mir); |
7598 | break; |
7599 | default: |
7600 | break; |
7601 | } |
7602 | } |
7603 | |
7604 | void CodeGenerator::emitDebugForceBailing(LInstruction* lir) { |
7605 | if (MOZ_LIKELY(!gen->options.ionBailAfterEnabled())(__builtin_expect(!!(!gen->options.ionBailAfterEnabled()), 1))) { |
7606 | return; |
7607 | } |
7608 | if (!lir->snapshot()) { |
7609 | return; |
7610 | } |
7611 | if (lir->isOsiPoint()) { |
7612 | return; |
7613 | } |
7614 | |
7615 | masm.comment("emitDebugForceBailing"); |
7616 | const void* bailAfterCounterAddr = |
7617 | gen->runtime->addressOfIonBailAfterCounter(); |
7618 | |
7619 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
7620 | |
7621 | Label done, notBail; |
7622 | masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterCounterAddr), |
7623 | Imm32(0), &done); |
7624 | { |
7625 | Register temp = regs.takeAny(); |
7626 | |
7627 | masm.push(temp); |
7628 | masm.load32(AbsoluteAddress(bailAfterCounterAddr), temp); |
7629 | masm.sub32(Imm32(1), temp); |
7630 | masm.store32(temp, AbsoluteAddress(bailAfterCounterAddr)); |
7631 | |
7632 | masm.branch32(Assembler::NotEqual, temp, Imm32(0), ¬Bail); |
7633 | { |
7634 | masm.pop(temp); |
7635 | bailout(lir->snapshot()); |
7636 | } |
7637 | masm.bind(¬Bail); |
7638 | masm.pop(temp); |
7639 | } |
7640 | masm.bind(&done); |
7641 | } |
7642 | #endif |
7643 | |
7644 | bool CodeGenerator::generateBody() { |
7645 | JitSpewCont(JitSpew_Codegen, "\n"); |
7646 | AutoCreatedBy acb(masm, "CodeGenerator::generateBody"); |
7647 | |
7648 | JitSpew(JitSpew_Codegen, "==== BEGIN CodeGenerator::generateBody ===="); |
7649 | IonScriptCounts* counts = maybeCreateScriptCounts(); |
7650 | |
7651 | const bool compilingWasm = gen->compilingWasm(); |
7652 | |
7653 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
7654 | current = graph.getBlock(i); |
7655 | |
7656 | // Don't emit any code for trivial blocks, containing just a goto. Such |
7657 | // blocks are created to split critical edges, and if we didn't end up |
7658 | // putting any instructions in them, we can skip them. |
7659 | if (current->isTrivial()) { |
7660 | continue; |
7661 | } |
7662 | |
7663 | #ifdef JS_JITSPEW1 |
7664 | const char* filename = nullptr; |
7665 | size_t lineNumber = 0; |
7666 | JS::LimitedColumnNumberOneOrigin columnNumber; |
7667 | if (current->mir()->info().script()) { |
7668 | filename = current->mir()->info().script()->filename(); |
7669 | if (current->mir()->pc()) { |
7670 | lineNumber = PCToLineNumber(current->mir()->info().script(), |
7671 | current->mir()->pc(), &columnNumber); |
7672 | } |
7673 | } |
7674 | JitSpew(JitSpew_Codegen, "--------------------------------"); |
7675 | JitSpew(JitSpew_Codegen, "# block%zu %s:%zu:%u%s:", i, |
7676 | filename ? filename : "?", lineNumber, |
7677 | columnNumber.oneOriginValue(), |
7678 | current->mir()->isLoopHeader() ? " (loop header)" : ""); |
7679 | #endif |
7680 | |
7681 | if (current->mir()->isLoopHeader() && compilingWasm) { |
7682 | masm.nopAlign(CodeAlignment); |
7683 | } |
7684 | |
7685 | masm.bind(current->label()); |
7686 | |
7687 | mozilla::Maybe<ScriptCountBlockState> blockCounts; |
7688 | if (counts) { |
7689 | blockCounts.emplace(&counts->block(i), &masm); |
7690 | if (!blockCounts->init()) { |
7691 | return false; |
7692 | } |
7693 | } |
7694 | |
7695 | for (LInstructionIterator iter = current->begin(); iter != current->end(); |
7696 | iter++) { |
7697 | if (!alloc().ensureBallast()) { |
7698 | return false; |
7699 | } |
7700 | |
7701 | perfSpewer_.recordInstruction(masm, *iter); |
7702 | #ifdef JS_JITSPEW1 |
7703 | JitSpewStart(JitSpew_Codegen, " # LIR=%s", |
7704 | iter->opName()); |
7705 | if (const char* extra = iter->getExtraName()) { |
7706 | JitSpewCont(JitSpew_Codegen, ":%s", extra); |
7707 | } |
7708 | JitSpewFin(JitSpew_Codegen); |
7709 | #endif |
7710 | |
7711 | if (counts) { |
7712 | blockCounts->visitInstruction(*iter); |
7713 | } |
7714 | |
7715 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
7716 | if (iter->safepoint() && !compilingWasm) { |
7717 | resetOsiPointRegs(iter->safepoint()); |
7718 | } |
7719 | #endif |
7720 | |
7721 | if (!compilingWasm) { |
7722 | if (MDefinition* mir = iter->mirRaw()) { |
7723 | if (!addNativeToBytecodeEntry(mir->trackedSite())) { |
7724 | return false; |
7725 | } |
7726 | } |
7727 | } |
7728 | |
7729 | setElement(*iter); // needed to encode correct snapshot location. |
7730 | |
7731 | #ifdef DEBUG1 |
7732 | emitDebugForceBailing(*iter); |
7733 | #endif |
7734 | |
7735 | switch (iter->op()) { |
7736 | #ifndef JS_CODEGEN_NONE |
7737 | # define LIROP(op) \ |
7738 | case LNode::Opcode::op: \ |
7739 | visit##op(iter->to##op()); \ |
7740 | break; |
7741 | LIR_OPCODE_LIST(LIROP)LIROP(Phi)LIROP(Box)LIROP(OsiPoint)LIROP(MoveGroup)LIROP(Integer )LIROP(Integer64)LIROP(Pointer)LIROP(Double)LIROP(Float32)LIROP (Value)LIROP(NurseryObject)LIROP(Parameter)LIROP(Callee)LIROP (IsConstructing)LIROP(Goto)LIROP(NewArray)LIROP(NewArrayDynamicLength )LIROP(NewIterator)LIROP(NewTypedArray)LIROP(NewTypedArrayDynamicLength )LIROP(NewTypedArrayFromArray)LIROP(NewTypedArrayFromArrayBuffer )LIROP(BindFunction)LIROP(NewBoundFunction)LIROP(NewObject)LIROP (NewPlainObject)LIROP(NewArrayObject)LIROP(NewNamedLambdaObject )LIROP(NewCallObject)LIROP(NewStringObject)LIROP(InitElemGetterSetter )LIROP(MutateProto)LIROP(InitPropGetterSetter)LIROP(CheckOverRecursed )LIROP(WasmTrap)LIROP(WasmTrapIfNull)LIROP(WasmRefIsSubtypeOfConcrete )LIROP(WasmRefIsSubtypeOfAbstract)LIROP(WasmRefIsSubtypeOfConcreteAndBranch )LIROP(WasmRefIsSubtypeOfAbstractAndBranch)LIROP(WasmNewStructObject )LIROP(WasmNewArrayObject)LIROP(WasmReinterpret)LIROP(WasmReinterpretFromI64 )LIROP(WasmReinterpretToI64)LIROP(Rotate)LIROP(RotateI64)LIROP (InterruptCheck)LIROP(WasmStackSwitchToMain)LIROP(WasmStackSwitchToSuspendable )LIROP(WasmStackContinueOnSuspendable)LIROP(WasmInterruptCheck )LIROP(TypeOfV)LIROP(TypeOfO)LIROP(TypeOfName)LIROP(TypeOfIsNonPrimitiveV )LIROP(TypeOfIsNonPrimitiveO)LIROP(TypeOfIsPrimitive)LIROP(ToAsyncIter )LIROP(ToPropertyKeyCache)LIROP(CreateThis)LIROP(CreateArgumentsObject )LIROP(CreateInlinedArgumentsObject)LIROP(GetInlinedArgument) LIROP(GetInlinedArgumentHole)LIROP(GetArgumentsObjectArg)LIROP (SetArgumentsObjectArg)LIROP(LoadArgumentsObjectArg)LIROP(LoadArgumentsObjectArgHole )LIROP(InArgumentsObjectArg)LIROP(ArgumentsObjectLength)LIROP (ArrayFromArgumentsObject)LIROP(GuardArgumentsObjectFlags)LIROP (BoundFunctionNumArgs)LIROP(GuardBoundFunctionIsConstructor)LIROP (ReturnFromCtor)LIROP(BoxNonStrictThis)LIROP(ImplicitThis)LIROP (StackArgT)LIROP(StackArgV)LIROP(CallGeneric)LIROP(CallKnown) LIROP(CallNative)LIROP(CallDOMNative)LIROP(CallClassHook)LIROP (Bail)LIROP(Unreachable)LIROP(EncodeSnapshot)LIROP(UnreachableResultV )LIROP(UnreachableResultT)LIROP(GetDOMProperty)LIROP(GetDOMMemberV )LIROP(GetDOMMemberT)LIROP(SetDOMProperty)LIROP(LoadDOMExpandoValue )LIROP(LoadDOMExpandoValueGuardGeneration)LIROP(LoadDOMExpandoValueIgnoreGeneration )LIROP(GuardDOMExpandoMissingOrGuardShape)LIROP(ApplyArgsGeneric )LIROP(ApplyArgsObj)LIROP(ApplyArrayGeneric)LIROP(ConstructArgsGeneric )LIROP(ConstructArrayGeneric)LIROP(ApplyArgsNative)LIROP(ApplyArgsObjNative )LIROP(ApplyArrayNative)LIROP(ConstructArgsNative)LIROP(ConstructArrayNative )LIROP(TestIAndBranch)LIROP(TestI64AndBranch)LIROP(TestDAndBranch )LIROP(TestFAndBranch)LIROP(TestBIAndBranch)LIROP(TestOAndBranch )LIROP(TestVAndBranch)LIROP(Compare)LIROP(CompareI64)LIROP(CompareI64AndBranch )LIROP(CompareAndBranch)LIROP(CompareD)LIROP(CompareF)LIROP(CompareDAndBranch )LIROP(CompareFAndBranch)LIROP(CompareS)LIROP(CompareSInline) LIROP(CompareSSingle)LIROP(CompareBigInt)LIROP(CompareBigIntInt32 )LIROP(CompareBigIntDouble)LIROP(CompareBigIntString)LIROP(BitAndAndBranch )LIROP(IsNullOrLikeUndefinedV)LIROP(IsNullOrLikeUndefinedT)LIROP (IsNull)LIROP(IsUndefined)LIROP(IsNullOrLikeUndefinedAndBranchV )LIROP(IsNullOrLikeUndefinedAndBranchT)LIROP(IsNullAndBranch) LIROP(IsUndefinedAndBranch)LIROP(SameValueDouble)LIROP(SameValue )LIROP(NotI)LIROP(NotI64)LIROP(NotD)LIROP(NotF)LIROP(NotBI)LIROP (NotO)LIROP(NotV)LIROP(BitNotI)LIROP(BitNotI64)LIROP(BitOpI)LIROP (BitOpI64)LIROP(ShiftI)LIROP(ShiftI64)LIROP(SignExtendInt32)LIROP (SignExtendInt64)LIROP(UrshD)LIROP(Return)LIROP(Throw)LIROP(ThrowWithStack )LIROP(MinMaxI)LIROP(MinMaxD)LIROP(MinMaxF)LIROP(MinMaxArrayI )LIROP(MinMaxArrayD)LIROP(NegI)LIROP(NegI64)LIROP(NegD)LIROP( NegF)LIROP(AbsI)LIROP(AbsD)LIROP(AbsF)LIROP(CopySignD)LIROP(CopySignF )LIROP(ClzI)LIROP(ClzI64)LIROP(CtzI)LIROP(CtzI64)LIROP(PopcntI )LIROP(PopcntI64)LIROP(SqrtD)LIROP(SqrtF)LIROP(Atan2D)LIROP(Hypot )LIROP(PowI)LIROP(PowII)LIROP(PowD)LIROP(PowOfTwoI)LIROP(SignI )LIROP(SignD)LIROP(SignDI)LIROP(MathFunctionD)LIROP(MathFunctionF )LIROP(AddI)LIROP(AddI64)LIROP(SubI)LIROP(SubI64)LIROP(MulI64 )LIROP(MathD)LIROP(MathF)LIROP(ModD)LIROP(ModPowTwoD)LIROP(WasmBuiltinModD )LIROP(BigIntAdd)LIROP(BigIntSub)LIROP(BigIntMul)LIROP(BigIntDiv )LIROP(BigIntMod)LIROP(BigIntPow)LIROP(BigIntBitAnd)LIROP(BigIntBitOr )LIROP(BigIntBitXor)LIROP(BigIntLsh)LIROP(BigIntRsh)LIROP(BigIntIncrement )LIROP(BigIntDecrement)LIROP(BigIntNegate)LIROP(BigIntBitNot) LIROP(Int32ToStringWithBase)LIROP(NumberParseInt)LIROP(DoubleParseInt )LIROP(Concat)LIROP(LinearizeString)LIROP(LinearizeForCharAccess )LIROP(LinearizeForCodePointAccess)LIROP(ToRelativeStringIndex )LIROP(CharCodeAt)LIROP(CharCodeAtOrNegative)LIROP(CodePointAt )LIROP(CodePointAtOrNegative)LIROP(NegativeToNaN)LIROP(NegativeToUndefined )LIROP(FromCharCode)LIROP(FromCharCodeEmptyIfNegative)LIROP(FromCharCodeUndefinedIfNegative )LIROP(FromCodePoint)LIROP(StringIncludes)LIROP(StringIncludesSIMD )LIROP(StringIndexOf)LIROP(StringIndexOfSIMD)LIROP(StringLastIndexOf )LIROP(StringStartsWith)LIROP(StringStartsWithInline)LIROP(StringEndsWith )LIROP(StringEndsWithInline)LIROP(StringToLowerCase)LIROP(CharCodeToLowerCase )LIROP(StringToUpperCase)LIROP(CharCodeToUpperCase)LIROP(StringTrimStartIndex )LIROP(StringTrimEndIndex)LIROP(StringSplit)LIROP(Substr)LIROP (Int32ToDouble)LIROP(Float32ToDouble)LIROP(DoubleToFloat32)LIROP (Int32ToFloat32)LIROP(DoubleToFloat16)LIROP(Float32ToFloat16) LIROP(Int32ToFloat16)LIROP(ValueToDouble)LIROP(ValueToFloat32 )LIROP(ValueToFloat16)LIROP(ValueToInt32)LIROP(ValueToBigInt) LIROP(DoubleToInt32)LIROP(Float32ToInt32)LIROP(TruncateDToInt32 )LIROP(WasmBuiltinTruncateDToInt32)LIROP(TruncateFToInt32)LIROP (WasmBuiltinTruncateFToInt32)LIROP(WasmTruncateToInt32)LIROP( WrapInt64ToInt32)LIROP(ExtendInt32ToInt64)LIROP(BooleanToString )LIROP(IntToString)LIROP(DoubleToString)LIROP(ValueToString)LIROP (PowHalfD)LIROP(NaNToZero)LIROP(OsrEntry)LIROP(OsrValue)LIROP (OsrEnvironmentChain)LIROP(OsrReturnValue)LIROP(OsrArgumentsObject )LIROP(RegExp)LIROP(RegExpMatcher)LIROP(RegExpSearcher)LIROP( RegExpSearcherLastLimit)LIROP(RegExpExecMatch)LIROP(RegExpExecTest )LIROP(RegExpHasCaptureGroups)LIROP(RegExpPrototypeOptimizable )LIROP(RegExpInstanceOptimizable)LIROP(GetFirstDollarIndex)LIROP (StringReplace)LIROP(BinaryValueCache)LIROP(BinaryBoolCache)LIROP (UnaryCache)LIROP(ModuleMetadata)LIROP(DynamicImport)LIROP(Lambda )LIROP(FunctionWithProto)LIROP(SetFunName)LIROP(KeepAliveObject )LIROP(DebugEnterGCUnsafeRegion)LIROP(DebugLeaveGCUnsafeRegion )LIROP(Slots)LIROP(Elements)LIROP(InitializedLength)LIROP(SetInitializedLength )LIROP(ArrayLength)LIROP(SetArrayLength)LIROP(FunctionLength) LIROP(FunctionName)LIROP(GetNextEntryForIterator)LIROP(ArrayBufferByteLength )LIROP(ArrayBufferViewLength)LIROP(ArrayBufferViewByteOffset) LIROP(ArrayBufferViewElements)LIROP(TypedArrayElementSize)LIROP (ResizableTypedArrayLength)LIROP(ResizableTypedArrayByteOffsetMaybeOutOfBounds )LIROP(ResizableDataViewByteLength)LIROP(GrowableSharedArrayBufferByteLength )LIROP(GuardResizableArrayBufferViewInBounds)LIROP(GuardResizableArrayBufferViewInBoundsOrDetached )LIROP(GuardHasAttachedArrayBuffer)LIROP(GuardNumberToIntPtrIndex )LIROP(BoundsCheck)LIROP(BoundsCheckRange)LIROP(BoundsCheckLower )LIROP(SpectreMaskIndex)LIROP(LoadElementV)LIROP(InArray)LIROP (GuardElementNotHole)LIROP(LoadElementHole)LIROP(StoreElementV )LIROP(StoreElementT)LIROP(StoreHoleValueElement)LIROP(StoreElementHoleV )LIROP(StoreElementHoleT)LIROP(ArrayPopShift)LIROP(ArrayPush) LIROP(ArraySlice)LIROP(ArgumentsSlice)LIROP(FrameArgumentsSlice )LIROP(InlineArgumentsSlice)LIROP(NormalizeSliceTerm)LIROP(ArrayJoin )LIROP(ObjectKeys)LIROP(ObjectKeysLength)LIROP(LoadUnboxedScalar )LIROP(LoadUnboxedBigInt)LIROP(LoadDataViewElement)LIROP(LoadTypedArrayElementHole )LIROP(LoadTypedArrayElementHoleBigInt)LIROP(StoreUnboxedScalar )LIROP(StoreUnboxedBigInt)LIROP(StoreDataViewElement)LIROP(StoreTypedArrayElementHole )LIROP(StoreTypedArrayElementHoleBigInt)LIROP(AtomicIsLockFree )LIROP(CompareExchangeTypedArrayElement)LIROP(AtomicExchangeTypedArrayElement )LIROP(AtomicTypedArrayElementBinop)LIROP(AtomicTypedArrayElementBinopForEffect )LIROP(AtomicLoad64)LIROP(AtomicStore64)LIROP(CompareExchangeTypedArrayElement64 )LIROP(AtomicExchangeTypedArrayElement64)LIROP(AtomicTypedArrayElementBinop64 )LIROP(AtomicTypedArrayElementBinopForEffect64)LIROP(EffectiveAddress )LIROP(ClampIToUint8)LIROP(ClampDToUint8)LIROP(ClampVToUint8) LIROP(LoadScriptedProxyHandler)LIROP(CheckScriptedProxyGetResult )LIROP(IdToStringOrSymbol)LIROP(LoadFixedSlotV)LIROP(LoadFixedSlotAndAtomize )LIROP(LoadFixedSlotT)LIROP(LoadFixedSlotAndUnbox)LIROP(LoadDynamicSlotAndUnbox )LIROP(LoadElementAndUnbox)LIROP(LoadFixedSlotUnboxAndAtomize )LIROP(LoadDynamicSlotUnboxAndAtomize)LIROP(AddAndStoreSlot)LIROP (AllocateAndStoreSlot)LIROP(AddSlotAndCallAddPropHook)LIROP(StoreFixedSlotV )LIROP(StoreFixedSlotT)LIROP(GetNameCache)LIROP(CallGetIntrinsicValue )LIROP(GetPropSuperCache)LIROP(GetPropertyCache)LIROP(BindNameCache )LIROP(CallBindVar)LIROP(LoadDynamicSlotV)LIROP(LoadDynamicSlotAndAtomize )LIROP(StoreDynamicSlotV)LIROP(StoreDynamicSlotT)LIROP(StringLength )LIROP(Floor)LIROP(FloorF)LIROP(Ceil)LIROP(CeilF)LIROP(Round) LIROP(RoundF)LIROP(Trunc)LIROP(TruncF)LIROP(NearbyInt)LIROP(NearbyIntF )LIROP(FunctionEnvironment)LIROP(HomeObject)LIROP(HomeObjectSuperBase )LIROP(NewLexicalEnvironmentObject)LIROP(NewClassBodyEnvironmentObject )LIROP(NewVarEnvironmentObject)LIROP(MegamorphicSetElement)LIROP (CallDeleteProperty)LIROP(CallDeleteElement)LIROP(ObjectToIterator )LIROP(ValueToIterator)LIROP(IteratorHasIndicesAndBranch)LIROP (LoadSlotByIteratorIndex)LIROP(StoreSlotByIteratorIndex)LIROP (SetPropertyCache)LIROP(GetIteratorCache)LIROP(OptimizeSpreadCallCache )LIROP(IteratorMore)LIROP(IsNoIterAndBranch)LIROP(IteratorEnd )LIROP(CloseIterCache)LIROP(OptimizeGetIteratorCache)LIROP(ArgumentsLength )LIROP(GetFrameArgument)LIROP(GetFrameArgumentHole)LIROP(Rest )LIROP(Int32ToIntPtr)LIROP(NonNegativeIntPtrToInt32)LIROP(IntPtrToDouble )LIROP(AdjustDataViewLength)LIROP(BooleanToInt64)LIROP(StringToInt64 )LIROP(ValueToInt64)LIROP(TruncateBigIntToInt64)LIROP(Int64ToBigInt )LIROP(PostWriteBarrierO)LIROP(PostWriteBarrierS)LIROP(PostWriteBarrierBI )LIROP(PostWriteBarrierV)LIROP(PostWriteElementBarrierO)LIROP (PostWriteElementBarrierS)LIROP(PostWriteElementBarrierBI)LIROP (PostWriteElementBarrierV)LIROP(AssertCanElidePostWriteBarrier )LIROP(GuardObjectIdentity)LIROP(GuardSpecificFunction)LIROP( GuardSpecificAtom)LIROP(GuardSpecificSymbol)LIROP(GuardSpecificInt32 )LIROP(GuardStringToIndex)LIROP(GuardStringToInt32)LIROP(GuardStringToDouble )LIROP(GuardShape)LIROP(GuardMultipleShapes)LIROP(GuardProto) LIROP(GuardNullProto)LIROP(GuardIsNativeObject)LIROP(GuardGlobalGeneration )LIROP(GuardFuse)LIROP(GuardIsProxy)LIROP(GuardIsNotProxy)LIROP (GuardIsNotDOMProxy)LIROP(ProxyGet)LIROP(ProxyGetByValue)LIROP (ProxyHasProp)LIROP(ProxySet)LIROP(ProxySetByValue)LIROP(CallSetArrayLength )LIROP(MegamorphicLoadSlot)LIROP(MegamorphicLoadSlotByValue)LIROP (MegamorphicLoadSlotPermissive)LIROP(MegamorphicLoadSlotByValuePermissive )LIROP(MegamorphicStoreSlot)LIROP(MegamorphicHasProp)LIROP(SmallObjectVariableKeyHasProp )LIROP(GuardIsNotArrayBufferMaybeShared)LIROP(GuardIsTypedArray )LIROP(GuardIsFixedLengthTypedArray)LIROP(GuardIsResizableTypedArray )LIROP(GuardHasProxyHandler)LIROP(GuardNoDenseElements)LIROP( InCache)LIROP(HasOwnCache)LIROP(CheckPrivateFieldCache)LIROP( NewPrivateName)LIROP(InstanceOfO)LIROP(InstanceOfV)LIROP(InstanceOfCache )LIROP(IsCallableO)LIROP(IsCallableV)LIROP(IsConstructor)LIROP (IsCrossRealmArrayConstructor)LIROP(IsArrayO)LIROP(IsArrayV)LIROP (IsTypedArray)LIROP(IsObject)LIROP(IsObjectAndBranch)LIROP(IsNullOrUndefined )LIROP(IsNullOrUndefinedAndBranch)LIROP(HasClass)LIROP(GuardToClass )LIROP(GuardToEitherClass)LIROP(GuardToFunction)LIROP(ObjectClassToString )LIROP(WasmSelect)LIROP(WasmSelectI64)LIROP(WasmCompareAndSelect )LIROP(WasmAddOffset)LIROP(WasmAddOffset64)LIROP(WasmBoundsCheck )LIROP(WasmBoundsCheck64)LIROP(WasmBoundsCheckRange32)LIROP(WasmExtendU32Index )LIROP(WasmWrapU32Index)LIROP(WasmClampTable64Index)LIROP(WasmAlignmentCheck )LIROP(WasmAlignmentCheck64)LIROP(WasmLoadInstance)LIROP(WasmLoadInstance64 )LIROP(WasmHeapReg)LIROP(WasmLoad)LIROP(WasmLoadI64)LIROP(WasmStore )LIROP(WasmStoreI64)LIROP(AsmJSLoadHeap)LIROP(AsmJSStoreHeap) LIROP(WasmCompareExchangeHeap)LIROP(WasmFence)LIROP(WasmAtomicExchangeHeap )LIROP(WasmAtomicBinopHeap)LIROP(WasmAtomicBinopHeapForEffect )LIROP(WasmLoadSlot)LIROP(WasmLoadElement)LIROP(WasmLoadSlotI64 )LIROP(WasmLoadElementI64)LIROP(WasmStoreSlot)LIROP(WasmStoreSlotI64 )LIROP(WasmStoreElement)LIROP(WasmStoreElementI64)LIROP(WasmStoreElementRef )LIROP(WasmLoadTableElement)LIROP(WasmDerivedPointer)LIROP(WasmDerivedIndexPointer )LIROP(WasmStoreRef)LIROP(WasmPostWriteBarrierImmediate)LIROP (WasmPostWriteBarrierIndex)LIROP(WasmParameter)LIROP(WasmParameterI64 )LIROP(WasmReturn)LIROP(WasmReturnI64)LIROP(WasmReturnVoid)LIROP (WasmStackArg)LIROP(WasmStackArgI64)LIROP(WasmNullConstant)LIROP (WasmCallIndirectAdjunctSafepoint)LIROP(WasmCall)LIROP(WasmCallLandingPrePad )LIROP(WasmRegisterResult)LIROP(WasmRegisterPairResult)LIROP( WasmStackResultArea)LIROP(WasmStackResult)LIROP(WasmStackResult64 )LIROP(AssertRangeI)LIROP(AssertRangeD)LIROP(AssertRangeF)LIROP (AssertRangeV)LIROP(AssertClass)LIROP(AssertShape)LIROP(GuardValue )LIROP(GuardNullOrUndefined)LIROP(GuardIsNotObject)LIROP(GuardFunctionFlags )LIROP(GuardFunctionIsNonBuiltinCtor)LIROP(GuardFunctionKind) LIROP(GuardFunctionScript)LIROP(IncrementWarmUpCounter)LIROP( LexicalCheck)LIROP(ThrowRuntimeLexicalError)LIROP(ThrowMsg)LIROP (GlobalDeclInstantiation)LIROP(MemoryBarrier)LIROP(Debugger)LIROP (NewTarget)LIROP(Random)LIROP(CheckReturn)LIROP(CheckIsObj)LIROP (CheckObjCoercible)LIROP(CheckClassHeritage)LIROP(CheckThis)LIROP (CheckThisReinit)LIROP(Generator)LIROP(AsyncResolve)LIROP(AsyncReject )LIROP(AsyncAwait)LIROP(CanSkipAwait)LIROP(MaybeExtractAwaitValue )LIROP(DebugCheckSelfHosted)LIROP(IsPackedArray)LIROP(GuardArrayIsPacked )LIROP(GetPrototypeOf)LIROP(ObjectWithProto)LIROP(ObjectStaticProto )LIROP(BuiltinObject)LIROP(SuperFunction)LIROP(InitHomeObject )LIROP(IsTypedArrayConstructor)LIROP(LoadValueTag)LIROP(GuardTagNotEqual )LIROP(LoadWrapperTarget)LIROP(GuardHasGetterSetter)LIROP(GuardIsExtensible )LIROP(GuardInt32IsNonNegative)LIROP(GuardInt32Range)LIROP(GuardIndexIsNotDenseElement )LIROP(GuardIndexIsValidUpdateOrAdd)LIROP(CallAddOrUpdateSparseElement )LIROP(CallGetSparseElement)LIROP(CallNativeGetElement)LIROP( CallNativeGetElementSuper)LIROP(CallObjectHasSparseElement)LIROP (BigIntAsIntN)LIROP(BigIntAsIntN64)LIROP(BigIntAsIntN32)LIROP (GuardNonGCThing)LIROP(ToHashableNonGCThing)LIROP(ToHashableString )LIROP(ToHashableValue)LIROP(HashNonGCThing)LIROP(HashString) LIROP(HashSymbol)LIROP(HashBigInt)LIROP(HashObject)LIROP(HashValue )LIROP(SetObjectHasNonBigInt)LIROP(SetObjectHasBigInt)LIROP(SetObjectHasValue )LIROP(SetObjectHasValueVMCall)LIROP(SetObjectSize)LIROP(MapObjectHasNonBigInt )LIROP(MapObjectHasBigInt)LIROP(MapObjectHasValue)LIROP(MapObjectHasValueVMCall )LIROP(MapObjectGetNonBigInt)LIROP(MapObjectGetBigInt)LIROP(MapObjectGetValue )LIROP(MapObjectGetValueVMCall)LIROP(MapObjectSize)LIROP(BigIntAsUintN )LIROP(BigIntAsUintN64)LIROP(BigIntAsUintN32)LIROP(IonToWasmCall )LIROP(IonToWasmCallV)LIROP(IonToWasmCallI64)LIROP(WasmAnyRefFromJSValue )LIROP(WasmAnyRefFromJSObject)LIROP(WasmAnyRefFromJSString)LIROP (WasmNewI31Ref)LIROP(WasmI31RefGet)LIROP(Simd128)LIROP(WasmTernarySimd128 )LIROP(WasmBinarySimd128)LIROP(WasmBinarySimd128WithConstant) LIROP(WasmVariableShiftSimd128)LIROP(WasmConstantShiftSimd128 )LIROP(WasmSignReplicationSimd128)LIROP(WasmShuffleSimd128)LIROP (WasmPermuteSimd128)LIROP(WasmReplaceLaneSimd128)LIROP(WasmReplaceInt64LaneSimd128 )LIROP(WasmScalarToSimd128)LIROP(WasmInt64ToSimd128)LIROP(WasmUnarySimd128 )LIROP(WasmReduceSimd128)LIROP(WasmReduceAndBranchSimd128)LIROP (WasmReduceSimd128ToInt64)LIROP(WasmLoadLaneSimd128)LIROP(WasmStoreLaneSimd128 )LIROP(Unbox)LIROP(UnboxFloatingPoint)LIROP(WasmUint32ToDouble )LIROP(WasmUint32ToFloat32)LIROP(DivI)LIROP(ModI)LIROP(DivPowTwoI )LIROP(ModPowTwoI)LIROP(TableSwitch)LIROP(TableSwitchV)LIROP( MulI)LIROP(DivOrModI64)LIROP(UDivOrModI64)LIROP(DivOrModConstantI )LIROP(UDivOrMod)LIROP(UDivOrModConstant)LIROP(WasmTruncateToInt64 )LIROP(Int64ToFloatingPoint) |
7742 | # undef LIROP |
7743 | #endif |
7744 | case LNode::Opcode::Invalid: |
7745 | default: |
7746 | MOZ_CRASH("Invalid LIR op")do { do { } while (false); MOZ_ReportCrash("" "Invalid LIR op" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7746); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid LIR op" ")"); do { *((volatile int*)__null) = 7746; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
7747 | } |
7748 | |
7749 | #ifdef DEBUG1 |
7750 | if (!counts) { |
7751 | emitDebugResultChecks(*iter); |
7752 | } |
7753 | #endif |
7754 | } |
7755 | if (masm.oom()) { |
7756 | return false; |
7757 | } |
7758 | } |
7759 | |
7760 | JitSpew(JitSpew_Codegen, "==== END CodeGenerator::generateBody ====\n"); |
7761 | return true; |
7762 | } |
7763 | |
7764 | // Out-of-line object allocation for LNewArray. |
7765 | class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator> { |
7766 | LNewArray* lir_; |
7767 | |
7768 | public: |
7769 | explicit OutOfLineNewArray(LNewArray* lir) : lir_(lir) {} |
7770 | |
7771 | void accept(CodeGenerator* codegen) override { |
7772 | codegen->visitOutOfLineNewArray(this); |
7773 | } |
7774 | |
7775 | LNewArray* lir() const { return lir_; } |
7776 | }; |
7777 | |
7778 | void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) { |
7779 | Register objReg = ToRegister(lir->output()); |
7780 | |
7781 | MOZ_ASSERT(!lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7781); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { *((volatile int*)__null) = 7781; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7782 | saveLive(lir); |
7783 | |
7784 | JSObject* templateObject = lir->mir()->templateObject(); |
7785 | |
7786 | if (templateObject) { |
7787 | pushArg(ImmGCPtr(templateObject->shape())); |
7788 | pushArg(Imm32(lir->mir()->length())); |
7789 | |
7790 | using Fn = ArrayObject* (*)(JSContext*, uint32_t, Handle<Shape*>); |
7791 | callVM<Fn, NewArrayWithShape>(lir); |
7792 | } else { |
7793 | pushArg(Imm32(GenericObject)); |
7794 | pushArg(Imm32(lir->mir()->length())); |
7795 | |
7796 | using Fn = ArrayObject* (*)(JSContext*, uint32_t, NewObjectKind); |
7797 | callVM<Fn, NewArrayOperation>(lir); |
7798 | } |
7799 | |
7800 | masm.storeCallPointerResult(objReg); |
7801 | |
7802 | MOZ_ASSERT(!lir->safepoint()->liveRegs().has(objReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->safepoint()->liveRegs().has(objReg))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(!lir->safepoint()->liveRegs().has(objReg)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->safepoint()->liveRegs().has(objReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7802); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)" ")"); do { *((volatile int*)__null) = 7802; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7803 | restoreLive(lir); |
7804 | } |
7805 | |
7806 | void CodeGenerator::visitAtan2D(LAtan2D* lir) { |
7807 | FloatRegister y = ToFloatRegister(lir->y()); |
7808 | FloatRegister x = ToFloatRegister(lir->x()); |
7809 | |
7810 | using Fn = double (*)(double x, double y); |
7811 | masm.setupAlignedABICall(); |
7812 | masm.passABIArg(y, ABIType::Float64); |
7813 | masm.passABIArg(x, ABIType::Float64); |
7814 | masm.callWithABI<Fn, ecmaAtan2>(ABIType::Float64); |
7815 | |
7816 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7816); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 7816; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7817 | } |
7818 | |
7819 | void CodeGenerator::visitHypot(LHypot* lir) { |
7820 | uint32_t numArgs = lir->numArgs(); |
7821 | masm.setupAlignedABICall(); |
7822 | |
7823 | for (uint32_t i = 0; i < numArgs; ++i) { |
7824 | masm.passABIArg(ToFloatRegister(lir->getOperand(i)), ABIType::Float64); |
7825 | } |
7826 | |
7827 | switch (numArgs) { |
7828 | case 2: { |
7829 | using Fn = double (*)(double x, double y); |
7830 | masm.callWithABI<Fn, ecmaHypot>(ABIType::Float64); |
7831 | break; |
7832 | } |
7833 | case 3: { |
7834 | using Fn = double (*)(double x, double y, double z); |
7835 | masm.callWithABI<Fn, hypot3>(ABIType::Float64); |
7836 | break; |
7837 | } |
7838 | case 4: { |
7839 | using Fn = double (*)(double x, double y, double z, double w); |
7840 | masm.callWithABI<Fn, hypot4>(ABIType::Float64); |
7841 | break; |
7842 | } |
7843 | default: |
7844 | MOZ_CRASH("Unexpected number of arguments to hypot function.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected number of arguments to hypot function." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7844); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected number of arguments to hypot function." ")"); do { *((volatile int*)__null) = 7844; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
7845 | } |
7846 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7846); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 7846; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7847 | } |
7848 | |
7849 | void CodeGenerator::visitNewArray(LNewArray* lir) { |
7850 | Register objReg = ToRegister(lir->output()); |
7851 | Register tempReg = ToRegister(lir->temp()); |
7852 | DebugOnly<uint32_t> length = lir->mir()->length(); |
7853 | |
7854 | MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7854); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT" ")"); do { *((volatile int*)__null) = 7854; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7855 | |
7856 | if (lir->mir()->isVMCall()) { |
7857 | visitNewArrayCallVM(lir); |
7858 | return; |
7859 | } |
7860 | |
7861 | OutOfLineNewArray* ool = new (alloc()) OutOfLineNewArray(lir); |
7862 | addOutOfLineCode(ool, lir->mir()); |
7863 | |
7864 | TemplateObject templateObject(lir->mir()->templateObject()); |
7865 | #ifdef DEBUG1 |
7866 | size_t numInlineElements = gc::GetGCKindSlots(templateObject.getAllocKind()) - |
7867 | ObjectElements::VALUES_PER_HEADER; |
7868 | MOZ_ASSERT(length <= numInlineElements,do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= numInlineElements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length <= numInlineElements ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "length <= numInlineElements" " (" "Inline allocation only supports inline elements" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7869); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements" ")") ; do { *((volatile int*)__null) = 7869; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
7869 | "Inline allocation only supports inline elements")do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= numInlineElements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length <= numInlineElements ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "length <= numInlineElements" " (" "Inline allocation only supports inline elements" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7869); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements" ")") ; do { *((volatile int*)__null) = 7869; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7870 | #endif |
7871 | masm.createGCObject(objReg, tempReg, templateObject, |
7872 | lir->mir()->initialHeap(), ool->entry()); |
7873 | |
7874 | masm.bind(ool->rejoin()); |
7875 | } |
7876 | |
7877 | void CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray* ool) { |
7878 | visitNewArrayCallVM(ool->lir()); |
7879 | masm.jump(ool->rejoin()); |
7880 | } |
7881 | |
7882 | void CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) { |
7883 | Register lengthReg = ToRegister(lir->length()); |
7884 | Register objReg = ToRegister(lir->output()); |
7885 | Register tempReg = ToRegister(lir->temp0()); |
7886 | |
7887 | JSObject* templateObject = lir->mir()->templateObject(); |
7888 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
7889 | |
7890 | using Fn = ArrayObject* (*)(JSContext*, Handle<ArrayObject*>, int32_t length); |
7891 | OutOfLineCode* ool = oolCallVM<Fn, ArrayConstructorOneArg>( |
7892 | lir, ArgList(ImmGCPtr(templateObject), lengthReg), |
7893 | StoreRegisterTo(objReg)); |
7894 | |
7895 | bool canInline = true; |
7896 | size_t inlineLength = 0; |
7897 | if (templateObject->as<ArrayObject>().hasFixedElements()) { |
7898 | size_t numSlots = |
7899 | gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); |
7900 | inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; |
7901 | } else { |
7902 | canInline = false; |
7903 | } |
7904 | |
7905 | if (canInline) { |
7906 | // Try to do the allocation inline if the template object is big enough |
7907 | // for the length in lengthReg. If the length is bigger we could still |
7908 | // use the template object and not allocate the elements, but it's more |
7909 | // efficient to do a single big allocation than (repeatedly) reallocating |
7910 | // the array later on when filling it. |
7911 | masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength), |
7912 | ool->entry()); |
7913 | |
7914 | TemplateObject templateObj(templateObject); |
7915 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, |
7916 | ool->entry()); |
7917 | |
7918 | size_t lengthOffset = NativeObject::offsetOfFixedElements() + |
7919 | ObjectElements::offsetOfLength(); |
7920 | masm.store32(lengthReg, Address(objReg, lengthOffset)); |
7921 | } else { |
7922 | masm.jump(ool->entry()); |
7923 | } |
7924 | |
7925 | masm.bind(ool->rejoin()); |
7926 | } |
7927 | |
7928 | void CodeGenerator::visitNewIterator(LNewIterator* lir) { |
7929 | Register objReg = ToRegister(lir->output()); |
7930 | Register tempReg = ToRegister(lir->temp0()); |
7931 | |
7932 | OutOfLineCode* ool; |
7933 | switch (lir->mir()->type()) { |
7934 | case MNewIterator::ArrayIterator: { |
7935 | using Fn = ArrayIteratorObject* (*)(JSContext*); |
7936 | ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(), |
7937 | StoreRegisterTo(objReg)); |
7938 | break; |
7939 | } |
7940 | case MNewIterator::StringIterator: { |
7941 | using Fn = StringIteratorObject* (*)(JSContext*); |
7942 | ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(), |
7943 | StoreRegisterTo(objReg)); |
7944 | break; |
7945 | } |
7946 | case MNewIterator::RegExpStringIterator: { |
7947 | using Fn = RegExpStringIteratorObject* (*)(JSContext*); |
7948 | ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(), |
7949 | StoreRegisterTo(objReg)); |
7950 | break; |
7951 | } |
7952 | default: |
7953 | MOZ_CRASH("unexpected iterator type")do { do { } while (false); MOZ_ReportCrash("" "unexpected iterator type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7953); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected iterator type" ")"); do { *((volatile int*)__null) = 7953; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
7954 | } |
7955 | |
7956 | TemplateObject templateObject(lir->mir()->templateObject()); |
7957 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
7958 | ool->entry()); |
7959 | |
7960 | masm.bind(ool->rejoin()); |
7961 | } |
7962 | |
7963 | void CodeGenerator::visitNewTypedArray(LNewTypedArray* lir) { |
7964 | Register objReg = ToRegister(lir->output()); |
7965 | Register tempReg = ToRegister(lir->temp0()); |
7966 | Register lengthReg = ToRegister(lir->temp1()); |
7967 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
7968 | |
7969 | JSObject* templateObject = lir->mir()->templateObject(); |
7970 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
7971 | |
7972 | auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>(); |
7973 | |
7974 | size_t n = ttemplate->length(); |
7975 | MOZ_ASSERT(n <= INT32_MAX,do { static_assert( mozilla::detail::AssertionConditionType< decltype(n <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)" " (" "Template objects are only created for int32 lengths" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")" ); do { *((volatile int*)__null) = 7976; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
7976 | "Template objects are only created for int32 lengths")do { static_assert( mozilla::detail::AssertionConditionType< decltype(n <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)" " (" "Template objects are only created for int32 lengths" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")" ); do { *((volatile int*)__null) = 7976; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
7977 | |
7978 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length); |
7979 | OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
7980 | lir, ArgList(ImmGCPtr(templateObject), Imm32(n)), |
7981 | StoreRegisterTo(objReg)); |
7982 | |
7983 | TemplateObject templateObj(templateObject); |
7984 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry()); |
7985 | |
7986 | masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(), |
7987 | ttemplate, MacroAssembler::TypedArrayLength::Fixed); |
7988 | |
7989 | masm.bind(ool->rejoin()); |
7990 | } |
7991 | |
7992 | void CodeGenerator::visitNewTypedArrayDynamicLength( |
7993 | LNewTypedArrayDynamicLength* lir) { |
7994 | Register lengthReg = ToRegister(lir->length()); |
7995 | Register objReg = ToRegister(lir->output()); |
7996 | Register tempReg = ToRegister(lir->temp0()); |
7997 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
7998 | |
7999 | JSObject* templateObject = lir->mir()->templateObject(); |
8000 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
8001 | |
8002 | auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>(); |
8003 | |
8004 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length); |
8005 | OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
8006 | lir, ArgList(ImmGCPtr(templateObject), lengthReg), |
8007 | StoreRegisterTo(objReg)); |
8008 | |
8009 | // Volatile |lengthReg| is saved across the ABI call in |initTypedArraySlots|. |
8010 | MOZ_ASSERT_IF(lengthReg.volatile_(), liveRegs.has(lengthReg))do { if (lengthReg.volatile_()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(liveRegs.has(lengthReg ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(liveRegs.has(lengthReg)))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("liveRegs.has(lengthReg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8010); AnnotateMozCrashReason("MOZ_ASSERT" "(" "liveRegs.has(lengthReg)" ")"); do { *((volatile int*)__null) = 8010; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
8011 | |
8012 | TemplateObject templateObj(templateObject); |
8013 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry()); |
8014 | |
8015 | masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(), |
8016 | ttemplate, |
8017 | MacroAssembler::TypedArrayLength::Dynamic); |
8018 | |
8019 | masm.bind(ool->rejoin()); |
8020 | } |
8021 | |
8022 | void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) { |
8023 | pushArg(ToRegister(lir->array())); |
8024 | pushArg(ImmGCPtr(lir->mir()->templateObject())); |
8025 | |
8026 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject); |
8027 | callVM<Fn, js::NewTypedArrayWithTemplateAndArray>(lir); |
8028 | } |
8029 | |
8030 | void CodeGenerator::visitNewTypedArrayFromArrayBuffer( |
8031 | LNewTypedArrayFromArrayBuffer* lir) { |
8032 | pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::LengthIndex)); |
8033 | pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::ByteOffsetIndex)); |
8034 | pushArg(ToRegister(lir->arrayBuffer())); |
8035 | pushArg(ImmGCPtr(lir->mir()->templateObject())); |
8036 | |
8037 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject, |
8038 | HandleValue, HandleValue); |
8039 | callVM<Fn, js::NewTypedArrayWithTemplateAndBuffer>(lir); |
8040 | } |
8041 | |
8042 | void CodeGenerator::visitBindFunction(LBindFunction* lir) { |
8043 | Register target = ToRegister(lir->target()); |
8044 | Register temp1 = ToRegister(lir->temp0()); |
8045 | Register temp2 = ToRegister(lir->temp1()); |
8046 | |
8047 | // Try to allocate a new BoundFunctionObject we can pass to the VM function. |
8048 | // If this fails, we set temp1 to nullptr so we do the allocation in C++. |
8049 | TemplateObject templateObject(lir->mir()->templateObject()); |
8050 | Label allocOk, allocFailed; |
8051 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
8052 | &allocFailed); |
8053 | masm.jump(&allocOk); |
8054 | |
8055 | masm.bind(&allocFailed); |
8056 | masm.movePtr(ImmWord(0), temp1); |
8057 | |
8058 | masm.bind(&allocOk); |
8059 | |
8060 | // Set temp2 to the address of the first argument on the stack. |
8061 | // Note that the Value slots used for arguments are currently aligned for a |
8062 | // JIT call, even though that's not strictly necessary for calling into C++. |
8063 | uint32_t argc = lir->mir()->numStackArgs(); |
8064 | if (JitStackValueAlignment > 1) { |
8065 | argc = AlignBytes(argc, JitStackValueAlignment); |
8066 | } |
8067 | uint32_t unusedStack = UnusedStackBytesForCall(argc); |
8068 | masm.computeEffectiveAddress(Address(masm.getStackPointer(), unusedStack), |
8069 | temp2); |
8070 | |
8071 | pushArg(temp1); |
8072 | pushArg(Imm32(lir->mir()->numStackArgs())); |
8073 | pushArg(temp2); |
8074 | pushArg(target); |
8075 | |
8076 | using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*, |
8077 | uint32_t, Handle<BoundFunctionObject*>); |
8078 | callVM<Fn, js::BoundFunctionObject::functionBindImpl>(lir); |
8079 | } |
8080 | |
8081 | void CodeGenerator::visitNewBoundFunction(LNewBoundFunction* lir) { |
8082 | Register output = ToRegister(lir->output()); |
8083 | Register temp = ToRegister(lir->temp0()); |
8084 | |
8085 | JSObject* templateObj = lir->mir()->templateObj(); |
8086 | |
8087 | using Fn = BoundFunctionObject* (*)(JSContext*, Handle<BoundFunctionObject*>); |
8088 | OutOfLineCode* ool = oolCallVM<Fn, BoundFunctionObject::createWithTemplate>( |
8089 | lir, ArgList(ImmGCPtr(templateObj)), StoreRegisterTo(output)); |
8090 | |
8091 | TemplateObject templateObject(templateObj); |
8092 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
8093 | ool->entry()); |
8094 | |
8095 | masm.bind(ool->rejoin()); |
8096 | } |
8097 | |
8098 | // Out-of-line object allocation for JSOp::NewObject. |
8099 | class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator> { |
8100 | LNewObject* lir_; |
8101 | |
8102 | public: |
8103 | explicit OutOfLineNewObject(LNewObject* lir) : lir_(lir) {} |
8104 | |
8105 | void accept(CodeGenerator* codegen) override { |
8106 | codegen->visitOutOfLineNewObject(this); |
8107 | } |
8108 | |
8109 | LNewObject* lir() const { return lir_; } |
8110 | }; |
8111 | |
8112 | void CodeGenerator::visitNewObjectVMCall(LNewObject* lir) { |
8113 | Register objReg = ToRegister(lir->output()); |
8114 | |
8115 | MOZ_ASSERT(!lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8115); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { *((volatile int*)__null) = 8115; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8116 | saveLive(lir); |
8117 | |
8118 | JSObject* templateObject = lir->mir()->templateObject(); |
8119 | |
8120 | // If we're making a new object with a class prototype (that is, an object |
8121 | // that derives its class from its prototype instead of being |
8122 | // PlainObject::class_'d) from self-hosted code, we need a different init |
8123 | // function. |
8124 | switch (lir->mir()->mode()) { |
8125 | case MNewObject::ObjectLiteral: { |
8126 | MOZ_ASSERT(!templateObject)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!templateObject)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!templateObject))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!templateObject" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8126); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateObject" ")"); do { *((volatile int*)__null) = 8126; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8127 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
8128 | pushArg(ImmGCPtr(lir->mir()->block()->info().script())); |
8129 | |
8130 | using Fn = JSObject* (*)(JSContext*, HandleScript, const jsbytecode* pc); |
8131 | callVM<Fn, NewObjectOperation>(lir); |
8132 | break; |
8133 | } |
8134 | case MNewObject::ObjectCreate: { |
8135 | pushArg(ImmGCPtr(templateObject)); |
8136 | |
8137 | using Fn = PlainObject* (*)(JSContext*, Handle<PlainObject*>); |
8138 | callVM<Fn, ObjectCreateWithTemplate>(lir); |
8139 | break; |
8140 | } |
8141 | } |
8142 | |
8143 | masm.storeCallPointerResult(objReg); |
8144 | |
8145 | MOZ_ASSERT(!lir->safepoint()->liveRegs().has(objReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->safepoint()->liveRegs().has(objReg))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(!lir->safepoint()->liveRegs().has(objReg)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->safepoint()->liveRegs().has(objReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8145); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)" ")"); do { *((volatile int*)__null) = 8145; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8146 | restoreLive(lir); |
8147 | } |
8148 | |
8149 | static bool ShouldInitFixedSlots(LNewPlainObject* lir, const Shape* shape, |
8150 | uint32_t nfixed) { |
8151 | // Look for StoreFixedSlot instructions following an object allocation |
8152 | // that write to this object before a GC is triggered or this object is |
8153 | // passed to a VM call. If all fixed slots will be initialized, the |
8154 | // allocation code doesn't need to set the slots to |undefined|. |
8155 | |
8156 | if (nfixed == 0) { |
8157 | return false; |
8158 | } |
8159 | |
8160 | // Keep track of the fixed slots that are initialized. initializedSlots is |
8161 | // a bit mask with a bit for each slot. |
8162 | MOZ_ASSERT(nfixed <= NativeObject::MAX_FIXED_SLOTS)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nfixed <= NativeObject::MAX_FIXED_SLOTS)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nfixed <= NativeObject::MAX_FIXED_SLOTS))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nfixed <= NativeObject::MAX_FIXED_SLOTS" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8162); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nfixed <= NativeObject::MAX_FIXED_SLOTS" ")"); do { *((volatile int*)__null) = 8162; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8163 | static_assert(NativeObject::MAX_FIXED_SLOTS <= 32, |
8164 | "Slot bits must fit in 32 bits"); |
8165 | uint32_t initializedSlots = 0; |
8166 | uint32_t numInitialized = 0; |
8167 | |
8168 | MInstruction* allocMir = lir->mir(); |
8169 | MBasicBlock* block = allocMir->block(); |
8170 | |
8171 | // Skip the allocation instruction. |
8172 | MInstructionIterator iter = block->begin(allocMir); |
8173 | MOZ_ASSERT(*iter == allocMir)do { static_assert( mozilla::detail::AssertionConditionType< decltype(*iter == allocMir)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*iter == allocMir))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*iter == allocMir" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "*iter == allocMir" ")"); do { *((volatile int*)__null) = 8173; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8174 | iter++; |
8175 | |
8176 | // Handle the leading shape guard, if present. |
8177 | for (; iter != block->end(); iter++) { |
8178 | if (iter->isConstant()) { |
8179 | // This instruction won't trigger a GC or read object slots. |
8180 | continue; |
8181 | } |
8182 | if (iter->isGuardShape()) { |
8183 | auto* guard = iter->toGuardShape(); |
8184 | if (guard->object() != allocMir || guard->shape() != shape) { |
8185 | return true; |
8186 | } |
8187 | allocMir = guard; |
8188 | iter++; |
8189 | } |
8190 | break; |
8191 | } |
8192 | |
8193 | for (; iter != block->end(); iter++) { |
8194 | if (iter->isConstant() || iter->isPostWriteBarrier()) { |
8195 | // These instructions won't trigger a GC or read object slots. |
8196 | continue; |
8197 | } |
8198 | |
8199 | if (iter->isStoreFixedSlot()) { |
8200 | MStoreFixedSlot* store = iter->toStoreFixedSlot(); |
8201 | if (store->object() != allocMir) { |
8202 | return true; |
8203 | } |
8204 | |
8205 | // We may not initialize this object slot on allocation, so the |
8206 | // pre-barrier could read uninitialized memory. Simply disable |
8207 | // the barrier for this store: the object was just initialized |
8208 | // so the barrier is not necessary. |
8209 | store->setNeedsBarrier(false); |
8210 | |
8211 | uint32_t slot = store->slot(); |
8212 | MOZ_ASSERT(slot < nfixed)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot < nfixed)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot < nfixed))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot < nfixed" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8212); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot < nfixed" ")"); do { *((volatile int*)__null) = 8212; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8213 | if ((initializedSlots & (1 << slot)) == 0) { |
8214 | numInitialized++; |
8215 | initializedSlots |= (1 << slot); |
8216 | |
8217 | if (numInitialized == nfixed) { |
8218 | // All fixed slots will be initialized. |
8219 | MOZ_ASSERT(mozilla::CountPopulation32(initializedSlots) == nfixed)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::CountPopulation32(initializedSlots) == nfixed )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mozilla::CountPopulation32(initializedSlots) == nfixed ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mozilla::CountPopulation32(initializedSlots) == nfixed", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8219); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::CountPopulation32(initializedSlots) == nfixed" ")"); do { *((volatile int*)__null) = 8219; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8220 | return false; |
8221 | } |
8222 | } |
8223 | continue; |
8224 | } |
8225 | |
8226 | // Unhandled instruction, assume it bails or reads object slots. |
8227 | return true; |
8228 | } |
8229 | |
8230 | MOZ_CRASH("Shouldn't get here")do { do { } while (false); MOZ_ReportCrash("" "Shouldn't get here" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8230); AnnotateMozCrashReason("MOZ_CRASH(" "Shouldn't get here" ")"); do { *((volatile int*)__null) = 8230; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
8231 | } |
8232 | |
8233 | void CodeGenerator::visitNewObject(LNewObject* lir) { |
8234 | Register objReg = ToRegister(lir->output()); |
8235 | Register tempReg = ToRegister(lir->temp()); |
8236 | |
8237 | if (lir->mir()->isVMCall()) { |
8238 | visitNewObjectVMCall(lir); |
8239 | return; |
8240 | } |
8241 | |
8242 | OutOfLineNewObject* ool = new (alloc()) OutOfLineNewObject(lir); |
8243 | addOutOfLineCode(ool, lir->mir()); |
8244 | |
8245 | TemplateObject templateObject(lir->mir()->templateObject()); |
8246 | |
8247 | masm.createGCObject(objReg, tempReg, templateObject, |
8248 | lir->mir()->initialHeap(), ool->entry()); |
8249 | |
8250 | masm.bind(ool->rejoin()); |
8251 | } |
8252 | |
8253 | void CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject* ool) { |
8254 | visitNewObjectVMCall(ool->lir()); |
8255 | masm.jump(ool->rejoin()); |
8256 | } |
8257 | |
8258 | void CodeGenerator::visitNewPlainObject(LNewPlainObject* lir) { |
8259 | Register objReg = ToRegister(lir->output()); |
8260 | Register temp0Reg = ToRegister(lir->temp0()); |
8261 | Register temp1Reg = ToRegister(lir->temp1()); |
8262 | Register shapeReg = ToRegister(lir->temp2()); |
8263 | |
8264 | auto* mir = lir->mir(); |
8265 | const Shape* shape = mir->shape(); |
8266 | gc::Heap initialHeap = mir->initialHeap(); |
8267 | gc::AllocKind allocKind = mir->allocKind(); |
8268 | |
8269 | using Fn = |
8270 | JSObject* (*)(JSContext*, Handle<SharedShape*>, gc::AllocKind, gc::Heap); |
8271 | OutOfLineCode* ool = oolCallVM<Fn, NewPlainObjectOptimizedFallback>( |
8272 | lir, |
8273 | ArgList(ImmGCPtr(shape), Imm32(int32_t(allocKind)), |
8274 | Imm32(int32_t(initialHeap))), |
8275 | StoreRegisterTo(objReg)); |
8276 | |
8277 | bool initContents = ShouldInitFixedSlots(lir, shape, mir->numFixedSlots()); |
8278 | |
8279 | masm.movePtr(ImmGCPtr(shape), shapeReg); |
8280 | masm.createPlainGCObject( |
8281 | objReg, shapeReg, temp0Reg, temp1Reg, mir->numFixedSlots(), |
8282 | mir->numDynamicSlots(), allocKind, initialHeap, ool->entry(), |
8283 | AllocSiteInput(gc::CatchAllAllocSite::Optimized), initContents); |
8284 | |
8285 | #ifdef DEBUG1 |
8286 | // ShouldInitFixedSlots expects that the leading GuardShape will never fail, |
8287 | // so ensure the newly created object has the correct shape. Should the guard |
8288 | // ever fail, we may end up with uninitialized fixed slots, which can confuse |
8289 | // the GC. |
8290 | Label ok; |
8291 | masm.branchTestObjShape(Assembler::Equal, objReg, shape, temp0Reg, objReg, |
8292 | &ok); |
8293 | masm.assumeUnreachable("Newly created object has the correct shape"); |
8294 | masm.bind(&ok); |
8295 | #endif |
8296 | |
8297 | masm.bind(ool->rejoin()); |
8298 | } |
8299 | |
8300 | void CodeGenerator::visitNewArrayObject(LNewArrayObject* lir) { |
8301 | Register objReg = ToRegister(lir->output()); |
8302 | Register temp0Reg = ToRegister(lir->temp0()); |
8303 | Register shapeReg = ToRegister(lir->temp1()); |
8304 | |
8305 | auto* mir = lir->mir(); |
8306 | uint32_t arrayLength = mir->length(); |
8307 | |
8308 | gc::AllocKind allocKind = GuessArrayGCKind(arrayLength); |
8309 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(allocKind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 8309; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8310 | allocKind = ForegroundToBackgroundAllocKind(allocKind); |
8311 | |
8312 | uint32_t slotCount = GetGCKindSlots(allocKind); |
8313 | MOZ_ASSERT(slotCount >= ObjectElements::VALUES_PER_HEADER)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotCount >= ObjectElements::VALUES_PER_HEADER)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(slotCount >= ObjectElements::VALUES_PER_HEADER))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("slotCount >= ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotCount >= ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 8313; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8314 | uint32_t arrayCapacity = slotCount - ObjectElements::VALUES_PER_HEADER; |
8315 | |
8316 | const Shape* shape = mir->shape(); |
8317 | |
8318 | NewObjectKind objectKind = |
8319 | mir->initialHeap() == gc::Heap::Tenured ? TenuredObject : GenericObject; |
8320 | |
8321 | using Fn = |
8322 | ArrayObject* (*)(JSContext*, uint32_t, gc::AllocKind, NewObjectKind); |
8323 | OutOfLineCode* ool = oolCallVM<Fn, NewArrayObjectOptimizedFallback>( |
8324 | lir, |
8325 | ArgList(Imm32(arrayLength), Imm32(int32_t(allocKind)), Imm32(objectKind)), |
8326 | StoreRegisterTo(objReg)); |
8327 | |
8328 | masm.movePtr(ImmPtr(shape), shapeReg); |
8329 | masm.createArrayWithFixedElements( |
8330 | objReg, shapeReg, temp0Reg, InvalidReg, arrayLength, arrayCapacity, 0, 0, |
8331 | allocKind, mir->initialHeap(), ool->entry(), |
8332 | AllocSiteInput(gc::CatchAllAllocSite::Optimized)); |
8333 | masm.bind(ool->rejoin()); |
8334 | } |
8335 | |
8336 | void CodeGenerator::visitNewNamedLambdaObject(LNewNamedLambdaObject* lir) { |
8337 | Register objReg = ToRegister(lir->output()); |
8338 | Register tempReg = ToRegister(lir->temp0()); |
8339 | const CompileInfo& info = lir->mir()->block()->info(); |
8340 | |
8341 | using Fn = js::NamedLambdaObject* (*)(JSContext*, HandleFunction); |
8342 | OutOfLineCode* ool = oolCallVM<Fn, NamedLambdaObject::createWithoutEnclosing>( |
8343 | lir, ArgList(info.funMaybeLazy()), StoreRegisterTo(objReg)); |
8344 | |
8345 | TemplateObject templateObject(lir->mir()->templateObj()); |
8346 | |
8347 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
8348 | ool->entry()); |
8349 | |
8350 | masm.bind(ool->rejoin()); |
8351 | } |
8352 | |
8353 | void CodeGenerator::visitNewCallObject(LNewCallObject* lir) { |
8354 | Register objReg = ToRegister(lir->output()); |
8355 | Register tempReg = ToRegister(lir->temp0()); |
8356 | |
8357 | CallObject* templateObj = lir->mir()->templateObject(); |
8358 | |
8359 | using Fn = CallObject* (*)(JSContext*, Handle<SharedShape*>); |
8360 | OutOfLineCode* ool = oolCallVM<Fn, CallObject::createWithShape>( |
8361 | lir, ArgList(ImmGCPtr(templateObj->sharedShape())), |
8362 | StoreRegisterTo(objReg)); |
8363 | |
8364 | // Inline call object creation, using the OOL path only for tricky cases. |
8365 | TemplateObject templateObject(templateObj); |
8366 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
8367 | ool->entry()); |
8368 | |
8369 | masm.bind(ool->rejoin()); |
8370 | } |
8371 | |
8372 | void CodeGenerator::visitNewStringObject(LNewStringObject* lir) { |
8373 | Register input = ToRegister(lir->input()); |
8374 | Register output = ToRegister(lir->output()); |
8375 | Register temp = ToRegister(lir->temp0()); |
8376 | |
8377 | StringObject* templateObj = lir->mir()->templateObj(); |
8378 | |
8379 | using Fn = JSObject* (*)(JSContext*, HandleString); |
8380 | OutOfLineCode* ool = oolCallVM<Fn, NewStringObject>(lir, ArgList(input), |
8381 | StoreRegisterTo(output)); |
8382 | |
8383 | TemplateObject templateObject(templateObj); |
8384 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
8385 | ool->entry()); |
8386 | |
8387 | masm.loadStringLength(input, temp); |
8388 | |
8389 | masm.storeValue(JSVAL_TYPE_STRING, input, |
8390 | Address(output, StringObject::offsetOfPrimitiveValue())); |
8391 | masm.storeValue(JSVAL_TYPE_INT32, temp, |
8392 | Address(output, StringObject::offsetOfLength())); |
8393 | |
8394 | masm.bind(ool->rejoin()); |
8395 | } |
8396 | |
8397 | void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) { |
8398 | Register obj = ToRegister(lir->object()); |
8399 | Register value = ToRegister(lir->value()); |
8400 | |
8401 | pushArg(value); |
8402 | pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex)); |
8403 | pushArg(obj); |
8404 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
8405 | |
8406 | using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue, |
8407 | HandleObject); |
8408 | callVM<Fn, InitElemGetterSetterOperation>(lir); |
8409 | } |
8410 | |
8411 | void CodeGenerator::visitMutateProto(LMutateProto* lir) { |
8412 | Register objReg = ToRegister(lir->object()); |
8413 | |
8414 | pushArg(ToValue(lir, LMutateProto::ValueIndex)); |
8415 | pushArg(objReg); |
8416 | |
8417 | using Fn = |
8418 | bool (*)(JSContext* cx, Handle<PlainObject*> obj, HandleValue value); |
8419 | callVM<Fn, MutatePrototype>(lir); |
8420 | } |
8421 | |
8422 | void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) { |
8423 | Register obj = ToRegister(lir->object()); |
8424 | Register value = ToRegister(lir->value()); |
8425 | |
8426 | pushArg(value); |
8427 | pushArg(ImmGCPtr(lir->mir()->name())); |
8428 | pushArg(obj); |
8429 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
8430 | |
8431 | using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, |
8432 | Handle<PropertyName*>, HandleObject); |
8433 | callVM<Fn, InitPropGetterSetterOperation>(lir); |
8434 | } |
8435 | |
8436 | void CodeGenerator::visitCreateThis(LCreateThis* lir) { |
8437 | const LAllocation* callee = lir->callee(); |
8438 | const LAllocation* newTarget = lir->newTarget(); |
8439 | |
8440 | if (newTarget->isConstant()) { |
8441 | pushArg(ImmGCPtr(&newTarget->toConstant()->toObject())); |
8442 | } else { |
8443 | pushArg(ToRegister(newTarget)); |
8444 | } |
8445 | |
8446 | if (callee->isConstant()) { |
8447 | pushArg(ImmGCPtr(&callee->toConstant()->toObject())); |
8448 | } else { |
8449 | pushArg(ToRegister(callee)); |
8450 | } |
8451 | |
8452 | using Fn = bool (*)(JSContext* cx, HandleObject callee, |
8453 | HandleObject newTarget, MutableHandleValue rval); |
8454 | callVM<Fn, jit::CreateThisFromIon>(lir); |
8455 | } |
8456 | |
8457 | void CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) { |
8458 | // This should be getting constructed in the first block only, and not any OSR |
8459 | // entry blocks. |
8460 | MOZ_ASSERT(lir->mir()->block()->id() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->block()->id() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->block()->id() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->block()->id() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->block()->id() == 0" ")"); do { *((volatile int*)__null) = 8460; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8461 | |
8462 | Register callObj = ToRegister(lir->callObject()); |
8463 | Register temp0 = ToRegister(lir->temp0()); |
8464 | Label done; |
8465 | |
8466 | if (ArgumentsObject* templateObj = lir->mir()->templateObject()) { |
8467 | Register objTemp = ToRegister(lir->temp1()); |
8468 | Register cxTemp = ToRegister(lir->temp2()); |
8469 | |
8470 | masm.Push(callObj); |
8471 | |
8472 | // Try to allocate an arguments object. This will leave the reserved |
8473 | // slots uninitialized, so it's important we don't GC until we |
8474 | // initialize these slots in ArgumentsObject::finishForIonPure. |
8475 | Label failure; |
8476 | TemplateObject templateObject(templateObj); |
8477 | masm.createGCObject(objTemp, temp0, templateObject, gc::Heap::Default, |
8478 | &failure, |
8479 | /* initContents = */ false); |
8480 | |
8481 | masm.moveStackPtrTo(temp0); |
8482 | masm.addPtr(Imm32(masm.framePushed()), temp0); |
8483 | |
8484 | using Fn = ArgumentsObject* (*)(JSContext* cx, jit::JitFrameLayout* frame, |
8485 | JSObject* scopeChain, ArgumentsObject* obj); |
8486 | masm.setupAlignedABICall(); |
8487 | masm.loadJSContext(cxTemp); |
8488 | masm.passABIArg(cxTemp); |
8489 | masm.passABIArg(temp0); |
8490 | masm.passABIArg(callObj); |
8491 | masm.passABIArg(objTemp); |
8492 | |
8493 | masm.callWithABI<Fn, ArgumentsObject::finishForIonPure>(); |
8494 | masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure); |
8495 | |
8496 | // Discard saved callObj on the stack. |
8497 | masm.addToStackPtr(Imm32(sizeof(uintptr_t))); |
8498 | masm.jump(&done); |
8499 | |
8500 | masm.bind(&failure); |
8501 | masm.Pop(callObj); |
8502 | } |
8503 | |
8504 | masm.moveStackPtrTo(temp0); |
8505 | masm.addPtr(Imm32(frameSize()), temp0); |
8506 | |
8507 | pushArg(callObj); |
8508 | pushArg(temp0); |
8509 | |
8510 | using Fn = ArgumentsObject* (*)(JSContext*, JitFrameLayout*, HandleObject); |
8511 | callVM<Fn, ArgumentsObject::createForIon>(lir); |
8512 | |
8513 | masm.bind(&done); |
8514 | } |
8515 | |
8516 | void CodeGenerator::visitCreateInlinedArgumentsObject( |
8517 | LCreateInlinedArgumentsObject* lir) { |
8518 | Register callObj = ToRegister(lir->getCallObject()); |
8519 | Register callee = ToRegister(lir->getCallee()); |
8520 | Register argsAddress = ToRegister(lir->temp1()); |
8521 | Register argsObj = ToRegister(lir->temp2()); |
8522 | |
8523 | // TODO: Do we have to worry about alignment here? |
8524 | |
8525 | // Create a contiguous array of values for ArgumentsObject::create |
8526 | // by pushing the arguments onto the stack in reverse order. |
8527 | uint32_t argc = lir->mir()->numActuals(); |
8528 | for (uint32_t i = 0; i < argc; i++) { |
8529 | uint32_t argNum = argc - i - 1; |
8530 | uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(argNum); |
8531 | ConstantOrRegister arg = |
8532 | toConstantOrRegister(lir, index, lir->mir()->getArg(argNum)->type()); |
8533 | masm.Push(arg); |
8534 | } |
8535 | masm.moveStackPtrTo(argsAddress); |
8536 | |
8537 | Label done; |
8538 | if (ArgumentsObject* templateObj = lir->mir()->templateObject()) { |
8539 | LiveRegisterSet liveRegs; |
8540 | liveRegs.add(callObj); |
8541 | liveRegs.add(callee); |
8542 | |
8543 | masm.PushRegsInMask(liveRegs); |
8544 | |
8545 | // We are free to clobber all registers, as LCreateInlinedArgumentsObject is |
8546 | // a call instruction. |
8547 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
8548 | allRegs.take(callObj); |
8549 | allRegs.take(callee); |
8550 | allRegs.take(argsObj); |
8551 | allRegs.take(argsAddress); |
8552 | |
8553 | Register temp3 = allRegs.takeAny(); |
8554 | Register temp4 = allRegs.takeAny(); |
8555 | |
8556 | // Try to allocate an arguments object. This will leave the reserved slots |
8557 | // uninitialized, so it's important we don't GC until we initialize these |
8558 | // slots in ArgumentsObject::finishForIonPure. |
8559 | Label failure; |
8560 | TemplateObject templateObject(templateObj); |
8561 | masm.createGCObject(argsObj, temp3, templateObject, gc::Heap::Default, |
8562 | &failure, |
8563 | /* initContents = */ false); |
8564 | |
8565 | Register numActuals = temp3; |
8566 | masm.move32(Imm32(argc), numActuals); |
8567 | |
8568 | using Fn = ArgumentsObject* (*)(JSContext*, JSObject*, JSFunction*, Value*, |
8569 | uint32_t, ArgumentsObject*); |
8570 | masm.setupAlignedABICall(); |
8571 | masm.loadJSContext(temp4); |
8572 | masm.passABIArg(temp4); |
8573 | masm.passABIArg(callObj); |
8574 | masm.passABIArg(callee); |
8575 | masm.passABIArg(argsAddress); |
8576 | masm.passABIArg(numActuals); |
8577 | masm.passABIArg(argsObj); |
8578 | |
8579 | masm.callWithABI<Fn, ArgumentsObject::finishInlineForIonPure>(); |
8580 | masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure); |
8581 | |
8582 | // Discard saved callObj, callee, and values array on the stack. |
8583 | masm.addToStackPtr( |
8584 | Imm32(MacroAssembler::PushRegsInMaskSizeInBytes(liveRegs) + |
8585 | argc * sizeof(Value))); |
8586 | masm.jump(&done); |
8587 | |
8588 | masm.bind(&failure); |
8589 | masm.PopRegsInMask(liveRegs); |
8590 | |
8591 | // Reload argsAddress because it may have been overridden. |
8592 | masm.moveStackPtrTo(argsAddress); |
8593 | } |
8594 | |
8595 | pushArg(Imm32(argc)); |
8596 | pushArg(callObj); |
8597 | pushArg(callee); |
8598 | pushArg(argsAddress); |
8599 | |
8600 | using Fn = ArgumentsObject* (*)(JSContext*, Value*, HandleFunction, |
8601 | HandleObject, uint32_t); |
8602 | callVM<Fn, ArgumentsObject::createForInlinedIon>(lir); |
8603 | |
8604 | // Discard the array of values. |
8605 | masm.freeStack(argc * sizeof(Value)); |
8606 | |
8607 | masm.bind(&done); |
8608 | } |
8609 | |
8610 | template <class GetInlinedArgument> |
8611 | void CodeGenerator::emitGetInlinedArgument(GetInlinedArgument* lir, |
8612 | Register index, |
8613 | ValueOperand output) { |
8614 | uint32_t numActuals = lir->mir()->numActuals(); |
8615 | MOZ_ASSERT(numActuals <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(numActuals <= ArgumentsObject::MaxInlinedArgs)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(numActuals <= ArgumentsObject::MaxInlinedArgs))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("numActuals <= ArgumentsObject::MaxInlinedArgs" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8615); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numActuals <= ArgumentsObject::MaxInlinedArgs" ")"); do { *((volatile int*)__null) = 8615; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
8616 | |
8617 | // The index has already been bounds-checked, so the code we |
8618 | // generate here should be unreachable. We can end up in this |
8619 | // situation in self-hosted code using GetArgument(), or in a |
8620 | // monomorphically inlined function if we've inlined some CacheIR |
8621 | // that was created for a different caller. |
8622 | if (numActuals == 0) { |
8623 | masm.assumeUnreachable("LGetInlinedArgument: invalid index"); |
8624 | return; |
8625 | } |
8626 | |
8627 | // Check the first n-1 possible indices. |
8628 | Label done; |
8629 | for (uint32_t i = 0; i < numActuals - 1; i++) { |
8630 | Label skip; |
8631 | ConstantOrRegister arg = toConstantOrRegister( |
8632 | lir, GetInlinedArgument::ArgIndex(i), lir->mir()->getArg(i)->type()); |
8633 | masm.branch32(Assembler::NotEqual, index, Imm32(i), &skip); |
8634 | masm.moveValue(arg, output); |
8635 | |
8636 | masm.jump(&done); |
8637 | masm.bind(&skip); |
8638 | } |
8639 | |
8640 | #ifdef DEBUG1 |
8641 | Label skip; |
8642 | masm.branch32(Assembler::Equal, index, Imm32(numActuals - 1), &skip); |
8643 | masm.assumeUnreachable("LGetInlinedArgument: invalid index"); |
8644 | masm.bind(&skip); |
8645 | #endif |
8646 | |
8647 | // The index has already been bounds-checked, so load the last argument. |
8648 | uint32_t lastIdx = numActuals - 1; |
8649 | ConstantOrRegister arg = |
8650 | toConstantOrRegister(lir, GetInlinedArgument::ArgIndex(lastIdx), |
8651 | lir->mir()->getArg(lastIdx)->type()); |
8652 | masm.moveValue(arg, output); |
8653 | masm.bind(&done); |
8654 | } |
8655 | |
8656 | void CodeGenerator::visitGetInlinedArgument(LGetInlinedArgument* lir) { |
8657 | Register index = ToRegister(lir->getIndex()); |
8658 | ValueOperand output = ToOutValue(lir); |
8659 | |
8660 | emitGetInlinedArgument(lir, index, output); |
8661 | } |
8662 | |
8663 | void CodeGenerator::visitGetInlinedArgumentHole(LGetInlinedArgumentHole* lir) { |
8664 | Register index = ToRegister(lir->getIndex()); |
8665 | ValueOperand output = ToOutValue(lir); |
8666 | |
8667 | uint32_t numActuals = lir->mir()->numActuals(); |
8668 | |
8669 | if (numActuals == 0) { |
8670 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
8671 | masm.moveValue(UndefinedValue(), output); |
8672 | return; |
8673 | } |
8674 | |
8675 | Label outOfBounds, done; |
8676 | masm.branch32(Assembler::AboveOrEqual, index, Imm32(numActuals), |
8677 | &outOfBounds); |
8678 | |
8679 | emitGetInlinedArgument(lir, index, output); |
8680 | masm.jump(&done); |
8681 | |
8682 | masm.bind(&outOfBounds); |
8683 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
8684 | masm.moveValue(UndefinedValue(), output); |
8685 | |
8686 | masm.bind(&done); |
8687 | } |
8688 | |
8689 | void CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir) { |
8690 | Register temp = ToRegister(lir->temp0()); |
8691 | Register argsObj = ToRegister(lir->argsObject()); |
8692 | ValueOperand out = ToOutValue(lir); |
8693 | |
8694 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
8695 | temp); |
8696 | Address argAddr(temp, ArgumentsData::offsetOfArgs() + |
8697 | lir->mir()->argno() * sizeof(Value)); |
8698 | masm.loadValue(argAddr, out); |
8699 | #ifdef DEBUG1 |
8700 | Label success; |
8701 | masm.branchTestMagic(Assembler::NotEqual, out, &success); |
8702 | masm.assumeUnreachable( |
8703 | "Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); |
8704 | masm.bind(&success); |
8705 | #endif |
8706 | } |
8707 | |
8708 | void CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir) { |
8709 | Register temp = ToRegister(lir->getTemp(0)); |
8710 | Register argsObj = ToRegister(lir->argsObject()); |
8711 | ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex); |
8712 | |
8713 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
8714 | temp); |
8715 | Address argAddr(temp, ArgumentsData::offsetOfArgs() + |
8716 | lir->mir()->argno() * sizeof(Value)); |
8717 | emitPreBarrier(argAddr); |
8718 | #ifdef DEBUG1 |
8719 | Label success; |
8720 | masm.branchTestMagic(Assembler::NotEqual, argAddr, &success); |
8721 | masm.assumeUnreachable( |
8722 | "Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); |
8723 | masm.bind(&success); |
8724 | #endif |
8725 | masm.storeValue(value, argAddr); |
8726 | } |
8727 | |
8728 | void CodeGenerator::visitLoadArgumentsObjectArg(LLoadArgumentsObjectArg* lir) { |
8729 | Register temp = ToRegister(lir->temp0()); |
8730 | Register argsObj = ToRegister(lir->argsObject()); |
8731 | Register index = ToRegister(lir->index()); |
8732 | ValueOperand out = ToOutValue(lir); |
8733 | |
8734 | Label bail; |
8735 | masm.loadArgumentsObjectElement(argsObj, index, out, temp, &bail); |
8736 | bailoutFrom(&bail, lir->snapshot()); |
8737 | } |
8738 | |
8739 | void CodeGenerator::visitLoadArgumentsObjectArgHole( |
8740 | LLoadArgumentsObjectArgHole* lir) { |
8741 | Register temp = ToRegister(lir->temp0()); |
8742 | Register argsObj = ToRegister(lir->argsObject()); |
8743 | Register index = ToRegister(lir->index()); |
8744 | ValueOperand out = ToOutValue(lir); |
8745 | |
8746 | Label bail; |
8747 | masm.loadArgumentsObjectElementHole(argsObj, index, out, temp, &bail); |
8748 | bailoutFrom(&bail, lir->snapshot()); |
8749 | } |
8750 | |
8751 | void CodeGenerator::visitInArgumentsObjectArg(LInArgumentsObjectArg* lir) { |
8752 | Register temp = ToRegister(lir->temp0()); |
8753 | Register argsObj = ToRegister(lir->argsObject()); |
8754 | Register index = ToRegister(lir->index()); |
8755 | Register out = ToRegister(lir->output()); |
8756 | |
8757 | Label bail; |
8758 | masm.loadArgumentsObjectElementExists(argsObj, index, out, temp, &bail); |
8759 | bailoutFrom(&bail, lir->snapshot()); |
8760 | } |
8761 | |
8762 | void CodeGenerator::visitArgumentsObjectLength(LArgumentsObjectLength* lir) { |
8763 | Register argsObj = ToRegister(lir->argsObject()); |
8764 | Register out = ToRegister(lir->output()); |
8765 | |
8766 | Label bail; |
8767 | masm.loadArgumentsObjectLength(argsObj, out, &bail); |
8768 | bailoutFrom(&bail, lir->snapshot()); |
8769 | } |
8770 | |
8771 | void CodeGenerator::visitArrayFromArgumentsObject( |
8772 | LArrayFromArgumentsObject* lir) { |
8773 | pushArg(ToRegister(lir->argsObject())); |
8774 | |
8775 | using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>); |
8776 | callVM<Fn, js::ArrayFromArgumentsObject>(lir); |
8777 | } |
8778 | |
8779 | void CodeGenerator::visitGuardArgumentsObjectFlags( |
8780 | LGuardArgumentsObjectFlags* lir) { |
8781 | Register argsObj = ToRegister(lir->argsObject()); |
8782 | Register temp = ToRegister(lir->temp0()); |
8783 | |
8784 | Label bail; |
8785 | masm.branchTestArgumentsObjectFlags(argsObj, temp, lir->mir()->flags(), |
8786 | Assembler::NonZero, &bail); |
8787 | bailoutFrom(&bail, lir->snapshot()); |
8788 | } |
8789 | |
8790 | void CodeGenerator::visitBoundFunctionNumArgs(LBoundFunctionNumArgs* lir) { |
8791 | Register obj = ToRegister(lir->object()); |
8792 | Register output = ToRegister(lir->output()); |
8793 | |
8794 | masm.unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()), |
8795 | output); |
8796 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), output); |
8797 | } |
8798 | |
8799 | void CodeGenerator::visitGuardBoundFunctionIsConstructor( |
8800 | LGuardBoundFunctionIsConstructor* lir) { |
8801 | Register obj = ToRegister(lir->object()); |
8802 | |
8803 | Label bail; |
8804 | Address flagsSlot(obj, BoundFunctionObject::offsetOfFlagsSlot()); |
8805 | masm.branchTest32(Assembler::Zero, flagsSlot, |
8806 | Imm32(BoundFunctionObject::IsConstructorFlag), &bail); |
8807 | bailoutFrom(&bail, lir->snapshot()); |
8808 | } |
8809 | |
8810 | void CodeGenerator::visitReturnFromCtor(LReturnFromCtor* lir) { |
8811 | ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex); |
8812 | Register obj = ToRegister(lir->object()); |
8813 | Register output = ToRegister(lir->output()); |
8814 | |
8815 | Label valueIsObject, end; |
8816 | |
8817 | masm.branchTestObject(Assembler::Equal, value, &valueIsObject); |
8818 | |
8819 | // Value is not an object. Return that other object. |
8820 | masm.movePtr(obj, output); |
8821 | masm.jump(&end); |
8822 | |
8823 | // Value is an object. Return unbox(Value). |
8824 | masm.bind(&valueIsObject); |
8825 | Register payload = masm.extractObject(value, output); |
8826 | if (payload != output) { |
8827 | masm.movePtr(payload, output); |
8828 | } |
8829 | |
8830 | masm.bind(&end); |
8831 | } |
8832 | |
8833 | class OutOfLineBoxNonStrictThis : public OutOfLineCodeBase<CodeGenerator> { |
8834 | LBoxNonStrictThis* ins_; |
8835 | |
8836 | public: |
8837 | explicit OutOfLineBoxNonStrictThis(LBoxNonStrictThis* ins) : ins_(ins) {} |
8838 | void accept(CodeGenerator* codegen) override { |
8839 | codegen->visitOutOfLineBoxNonStrictThis(this); |
8840 | } |
8841 | LBoxNonStrictThis* ins() const { return ins_; } |
8842 | }; |
8843 | |
8844 | void CodeGenerator::visitBoxNonStrictThis(LBoxNonStrictThis* lir) { |
8845 | ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex); |
8846 | Register output = ToRegister(lir->output()); |
8847 | |
8848 | auto* ool = new (alloc()) OutOfLineBoxNonStrictThis(lir); |
8849 | addOutOfLineCode(ool, lir->mir()); |
8850 | |
8851 | masm.fallibleUnboxObject(value, output, ool->entry()); |
8852 | masm.bind(ool->rejoin()); |
8853 | } |
8854 | |
8855 | void CodeGenerator::visitOutOfLineBoxNonStrictThis( |
8856 | OutOfLineBoxNonStrictThis* ool) { |
8857 | LBoxNonStrictThis* lir = ool->ins(); |
8858 | |
8859 | ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex); |
8860 | Register output = ToRegister(lir->output()); |
8861 | |
8862 | Label notNullOrUndefined; |
8863 | { |
8864 | Label isNullOrUndefined; |
8865 | ScratchTagScope tag(masm, value); |
8866 | masm.splitTagForTest(value, tag); |
8867 | masm.branchTestUndefined(Assembler::Equal, tag, &isNullOrUndefined); |
8868 | masm.branchTestNull(Assembler::NotEqual, tag, ¬NullOrUndefined); |
8869 | masm.bind(&isNullOrUndefined); |
8870 | masm.movePtr(ImmGCPtr(lir->mir()->globalThis()), output); |
8871 | masm.jump(ool->rejoin()); |
8872 | } |
8873 | |
8874 | masm.bind(¬NullOrUndefined); |
8875 | |
8876 | saveLive(lir); |
8877 | |
8878 | pushArg(value); |
8879 | using Fn = JSObject* (*)(JSContext*, HandleValue); |
8880 | callVM<Fn, BoxNonStrictThis>(lir); |
8881 | |
8882 | StoreRegisterTo(output).generate(this); |
8883 | restoreLiveIgnore(lir, StoreRegisterTo(output).clobbered()); |
8884 | |
8885 | masm.jump(ool->rejoin()); |
8886 | } |
8887 | |
8888 | void CodeGenerator::visitImplicitThis(LImplicitThis* lir) { |
8889 | pushArg(ImmGCPtr(lir->mir()->name())); |
8890 | pushArg(ToRegister(lir->env())); |
8891 | |
8892 | using Fn = bool (*)(JSContext*, HandleObject, Handle<PropertyName*>, |
8893 | MutableHandleValue); |
8894 | callVM<Fn, ImplicitThisOperation>(lir); |
8895 | } |
8896 | |
8897 | void CodeGenerator::visitArrayLength(LArrayLength* lir) { |
8898 | Register elements = ToRegister(lir->elements()); |
8899 | Register output = ToRegister(lir->output()); |
8900 | |
8901 | Address length(elements, ObjectElements::offsetOfLength()); |
8902 | masm.load32(length, output); |
8903 | |
8904 | // Bail out if the length doesn't fit in int32. |
8905 | bailoutTest32(Assembler::Signed, output, output, lir->snapshot()); |
8906 | } |
8907 | |
8908 | static void SetLengthFromIndex(MacroAssembler& masm, const LAllocation* index, |
8909 | const Address& length) { |
8910 | if (index->isConstant()) { |
8911 | masm.store32(Imm32(ToInt32(index) + 1), length); |
8912 | } else { |
8913 | Register newLength = ToRegister(index); |
8914 | masm.add32(Imm32(1), newLength); |
8915 | masm.store32(newLength, length); |
8916 | masm.sub32(Imm32(1), newLength); |
8917 | } |
8918 | } |
8919 | |
8920 | void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) { |
8921 | Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); |
8922 | SetLengthFromIndex(masm, lir->index(), length); |
8923 | } |
8924 | |
8925 | void CodeGenerator::visitFunctionLength(LFunctionLength* lir) { |
8926 | Register function = ToRegister(lir->function()); |
8927 | Register output = ToRegister(lir->output()); |
8928 | |
8929 | Label bail; |
8930 | |
8931 | // Get the JSFunction flags. |
8932 | masm.load32(Address(function, JSFunction::offsetOfFlagsAndArgCount()), |
8933 | output); |
8934 | |
8935 | // Functions with a SelfHostedLazyScript must be compiled with the slow-path |
8936 | // before the function length is known. If the length was previously resolved, |
8937 | // the length property may be shadowed. |
8938 | masm.branchTest32( |
8939 | Assembler::NonZero, output, |
8940 | Imm32(FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH), |
8941 | &bail); |
8942 | |
8943 | masm.loadFunctionLength(function, output, output, &bail); |
8944 | |
8945 | bailoutFrom(&bail, lir->snapshot()); |
8946 | } |
8947 | |
8948 | void CodeGenerator::visitFunctionName(LFunctionName* lir) { |
8949 | Register function = ToRegister(lir->function()); |
8950 | Register output = ToRegister(lir->output()); |
8951 | |
8952 | Label bail; |
8953 | |
8954 | const JSAtomState& names = gen->runtime->names(); |
8955 | masm.loadFunctionName(function, output, ImmGCPtr(names.empty_), &bail); |
8956 | |
8957 | bailoutFrom(&bail, lir->snapshot()); |
8958 | } |
8959 | |
8960 | template <class OrderedHashTable> |
8961 | static void RangeFront(MacroAssembler&, Register, Register, Register); |
8962 | |
8963 | template <> |
8964 | void RangeFront<ValueMap>(MacroAssembler& masm, Register range, Register i, |
8965 | Register front) { |
8966 | masm.loadPtr(Address(range, ValueMap::Range::offsetOfHashTable()), front); |
8967 | masm.loadPtr(Address(front, ValueMap::offsetOfImplData()), front); |
8968 | |
8969 | MOZ_ASSERT(ValueMap::offsetOfImplDataElement() == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(ValueMap::offsetOfImplDataElement() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ValueMap::offsetOfImplDataElement() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ValueMap::offsetOfImplDataElement() == 0" " (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8970); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ValueMap::offsetOfImplDataElement() == 0" ") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int *)__null) = 8970; __attribute__((nomerge)) ::abort(); } while (false); } } while (false) |
8970 | "offsetof(Data, element) is 0")do { static_assert( mozilla::detail::AssertionConditionType< decltype(ValueMap::offsetOfImplDataElement() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ValueMap::offsetOfImplDataElement() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ValueMap::offsetOfImplDataElement() == 0" " (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8970); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ValueMap::offsetOfImplDataElement() == 0" ") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int *)__null) = 8970; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
8971 | static_assert(ValueMap::sizeofImplData() == 24, "sizeof(Data) is 24"); |
8972 | masm.mulBy3(i, i); |
8973 | masm.lshiftPtr(Imm32(3), i); |
8974 | masm.addPtr(i, front); |
8975 | } |
8976 | |
8977 | template <> |
8978 | void RangeFront<ValueSet>(MacroAssembler& masm, Register range, Register i, |
8979 | Register front) { |
8980 | masm.loadPtr(Address(range, ValueSet::Range::offsetOfHashTable()), front); |
8981 | masm.loadPtr(Address(front, ValueSet::offsetOfImplData()), front); |
8982 | |
8983 | MOZ_ASSERT(ValueSet::offsetOfImplDataElement() == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(ValueSet::offsetOfImplDataElement() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ValueSet::offsetOfImplDataElement() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ValueSet::offsetOfImplDataElement() == 0" " (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ValueSet::offsetOfImplDataElement() == 0" ") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int *)__null) = 8984; __attribute__((nomerge)) ::abort(); } while (false); } } while (false) |
8984 | "offsetof(Data, element) is 0")do { static_assert( mozilla::detail::AssertionConditionType< decltype(ValueSet::offsetOfImplDataElement() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ValueSet::offsetOfImplDataElement() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ValueSet::offsetOfImplDataElement() == 0" " (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ValueSet::offsetOfImplDataElement() == 0" ") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int *)__null) = 8984; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
8985 | static_assert(ValueSet::sizeofImplData() == 16, "sizeof(Data) is 16"); |
8986 | masm.lshiftPtr(Imm32(4), i); |
8987 | masm.addPtr(i, front); |
8988 | } |
8989 | |
8990 | template <class OrderedHashTable> |
8991 | static void RangePopFront(MacroAssembler& masm, Register range, Register front, |
8992 | Register dataLength, Register temp) { |
8993 | Register i = temp; |
8994 | |
8995 | masm.add32(Imm32(1), |
8996 | Address(range, OrderedHashTable::Range::offsetOfCount())); |
8997 | |
8998 | masm.load32(Address(range, OrderedHashTable::Range::offsetOfI()), i); |
8999 | |
9000 | Label done, seek; |
9001 | masm.bind(&seek); |
9002 | masm.add32(Imm32(1), i); |
9003 | masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done); |
9004 | |
9005 | // We can add sizeof(Data) to |front| to select the next element, because |
9006 | // |front| and |range.ht.data[i]| point to the same location. |
9007 | MOZ_ASSERT(OrderedHashTable::offsetOfImplDataElement() == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(OrderedHashTable::offsetOfImplDataElement() == 0)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(OrderedHashTable::offsetOfImplDataElement() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("OrderedHashTable::offsetOfImplDataElement() == 0" " (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "OrderedHashTable::offsetOfImplDataElement() == 0" ") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int *)__null) = 9008; __attribute__((nomerge)) ::abort(); } while (false); } } while (false) |
9008 | "offsetof(Data, element) is 0")do { static_assert( mozilla::detail::AssertionConditionType< decltype(OrderedHashTable::offsetOfImplDataElement() == 0)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(OrderedHashTable::offsetOfImplDataElement() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("OrderedHashTable::offsetOfImplDataElement() == 0" " (" "offsetof(Data, element) is 0" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "OrderedHashTable::offsetOfImplDataElement() == 0" ") (" "offsetof(Data, element) is 0" ")"); do { *((volatile int *)__null) = 9008; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
9009 | masm.addPtr(Imm32(OrderedHashTable::sizeofImplData()), front); |
9010 | |
9011 | masm.branchTestMagic(Assembler::Equal, |
9012 | Address(front, OrderedHashTable::offsetOfEntryKey()), |
9013 | JS_HASH_KEY_EMPTY, &seek); |
9014 | |
9015 | masm.bind(&done); |
9016 | masm.store32(i, Address(range, OrderedHashTable::Range::offsetOfI())); |
9017 | } |
9018 | |
9019 | template <class OrderedHashTable> |
9020 | static inline void RangeDestruct(MacroAssembler& masm, Register iter, |
9021 | Register range, Register temp0, |
9022 | Register temp1) { |
9023 | Register next = temp0; |
9024 | Register prevp = temp1; |
9025 | |
9026 | masm.loadPtr(Address(range, OrderedHashTable::Range::offsetOfNext()), next); |
9027 | masm.loadPtr(Address(range, OrderedHashTable::Range::offsetOfPrevP()), prevp); |
9028 | masm.storePtr(next, Address(prevp, 0)); |
9029 | |
9030 | Label hasNoNext; |
9031 | masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext); |
9032 | |
9033 | masm.storePtr(prevp, Address(next, OrderedHashTable::Range::offsetOfPrevP())); |
9034 | |
9035 | masm.bind(&hasNoNext); |
9036 | |
9037 | Label nurseryAllocated; |
9038 | masm.branchPtrInNurseryChunk(Assembler::Equal, iter, temp0, |
9039 | &nurseryAllocated); |
9040 | |
9041 | masm.callFreeStub(range); |
9042 | |
9043 | masm.bind(&nurseryAllocated); |
9044 | } |
9045 | |
9046 | template <> |
9047 | void CodeGenerator::emitLoadIteratorValues<ValueMap>(Register result, |
9048 | Register temp, |
9049 | Register front) { |
9050 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
9051 | |
9052 | Address keyAddress(front, ValueMap::Entry::offsetOfKey()); |
9053 | Address valueAddress(front, ValueMap::Entry::offsetOfValue()); |
9054 | Address keyElemAddress(result, elementsOffset); |
9055 | Address valueElemAddress(result, elementsOffset + sizeof(Value)); |
9056 | masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value); |
9057 | masm.guardedCallPreBarrier(valueElemAddress, MIRType::Value); |
9058 | masm.storeValue(keyAddress, keyElemAddress, temp); |
9059 | masm.storeValue(valueAddress, valueElemAddress, temp); |
9060 | |
9061 | Label emitBarrier, skipBarrier; |
9062 | masm.branchValueIsNurseryCell(Assembler::Equal, keyAddress, temp, |
9063 | &emitBarrier); |
9064 | masm.branchValueIsNurseryCell(Assembler::NotEqual, valueAddress, temp, |
9065 | &skipBarrier); |
9066 | { |
9067 | masm.bind(&emitBarrier); |
9068 | saveVolatile(temp); |
9069 | emitPostWriteBarrier(result); |
9070 | restoreVolatile(temp); |
9071 | } |
9072 | masm.bind(&skipBarrier); |
9073 | } |
9074 | |
9075 | template <> |
9076 | void CodeGenerator::emitLoadIteratorValues<ValueSet>(Register result, |
9077 | Register temp, |
9078 | Register front) { |
9079 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
9080 | |
9081 | Address keyAddress(front, ValueSet::offsetOfEntryKey()); |
9082 | Address keyElemAddress(result, elementsOffset); |
9083 | masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value); |
9084 | masm.storeValue(keyAddress, keyElemAddress, temp); |
9085 | |
9086 | Label skipBarrier; |
9087 | masm.branchValueIsNurseryCell(Assembler::NotEqual, keyAddress, temp, |
9088 | &skipBarrier); |
9089 | { |
9090 | saveVolatile(temp); |
9091 | emitPostWriteBarrier(result); |
9092 | restoreVolatile(temp); |
9093 | } |
9094 | masm.bind(&skipBarrier); |
9095 | } |
9096 | |
9097 | template <class IteratorObject, class OrderedHashTable> |
9098 | void CodeGenerator::emitGetNextEntryForIterator(LGetNextEntryForIterator* lir) { |
9099 | Register iter = ToRegister(lir->iter()); |
9100 | Register result = ToRegister(lir->result()); |
9101 | Register temp = ToRegister(lir->temp0()); |
9102 | Register dataLength = ToRegister(lir->temp1()); |
9103 | Register range = ToRegister(lir->temp2()); |
9104 | Register output = ToRegister(lir->output()); |
9105 | |
9106 | #ifdef DEBUG1 |
9107 | // Self-hosted code is responsible for ensuring GetNextEntryForIterator is |
9108 | // only called with the correct iterator class. Assert here all self- |
9109 | // hosted callers of GetNextEntryForIterator perform this class check. |
9110 | // No Spectre mitigations are needed because this is DEBUG-only code. |
9111 | Label success; |
9112 | masm.branchTestObjClassNoSpectreMitigations( |
9113 | Assembler::Equal, iter, &IteratorObject::class_, temp, &success); |
9114 | masm.assumeUnreachable("Iterator object should have the correct class."); |
9115 | masm.bind(&success); |
9116 | #endif |
9117 | |
9118 | masm.loadPrivate(Address(iter, NativeObject::getFixedSlotOffset( |
9119 | IteratorObject::RangeSlot)), |
9120 | range); |
9121 | |
9122 | Label iterAlreadyDone, iterDone, done; |
9123 | masm.branchTestPtr(Assembler::Zero, range, range, &iterAlreadyDone); |
9124 | |
9125 | masm.load32(Address(range, OrderedHashTable::Range::offsetOfI()), temp); |
9126 | masm.loadPtr(Address(range, OrderedHashTable::Range::offsetOfHashTable()), |
9127 | dataLength); |
9128 | masm.load32(Address(dataLength, OrderedHashTable::offsetOfImplDataLength()), |
9129 | dataLength); |
9130 | masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone); |
9131 | { |
9132 | masm.Push(iter); |
9133 | |
9134 | Register front = iter; |
9135 | RangeFront<OrderedHashTable>(masm, range, temp, front); |
9136 | |
9137 | emitLoadIteratorValues<OrderedHashTable>(result, temp, front); |
9138 | |
9139 | RangePopFront<OrderedHashTable>(masm, range, front, dataLength, temp); |
9140 | |
9141 | masm.Pop(iter); |
9142 | masm.move32(Imm32(0), output); |
9143 | } |
9144 | masm.jump(&done); |
9145 | { |
9146 | masm.bind(&iterDone); |
9147 | |
9148 | RangeDestruct<OrderedHashTable>(masm, iter, range, temp, dataLength); |
9149 | |
9150 | masm.storeValue(PrivateValue(nullptr), |
9151 | Address(iter, NativeObject::getFixedSlotOffset( |
9152 | IteratorObject::RangeSlot))); |
9153 | |
9154 | masm.bind(&iterAlreadyDone); |
9155 | |
9156 | masm.move32(Imm32(1), output); |
9157 | } |
9158 | masm.bind(&done); |
9159 | } |
9160 | |
9161 | void CodeGenerator::visitGetNextEntryForIterator( |
9162 | LGetNextEntryForIterator* lir) { |
9163 | if (lir->mir()->mode() == MGetNextEntryForIterator::Map) { |
9164 | emitGetNextEntryForIterator<MapIteratorObject, ValueMap>(lir); |
9165 | } else { |
9166 | MOZ_ASSERT(lir->mir()->mode() == MGetNextEntryForIterator::Set)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->mode() == MGetNextEntryForIterator ::Set)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->mode() == MGetNextEntryForIterator ::Set))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->mode() == MGetNextEntryForIterator::Set", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9166); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MGetNextEntryForIterator::Set" ")"); do { *((volatile int*)__null) = 9166; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9167 | emitGetNextEntryForIterator<SetIteratorObject, ValueSet>(lir); |
9168 | } |
9169 | } |
9170 | |
9171 | // The point of these is to inform Ion of where these values already are; they |
9172 | // don't normally generate (much) code. |
9173 | void CodeGenerator::visitWasmRegisterPairResult(LWasmRegisterPairResult* lir) {} |
9174 | void CodeGenerator::visitWasmStackResult(LWasmStackResult* lir) {} |
9175 | void CodeGenerator::visitWasmStackResult64(LWasmStackResult64* lir) {} |
9176 | |
9177 | void CodeGenerator::visitWasmStackResultArea(LWasmStackResultArea* lir) { |
9178 | LAllocation* output = lir->getDef(0)->output(); |
9179 | MOZ_ASSERT(output->isStackArea())do { static_assert( mozilla::detail::AssertionConditionType< decltype(output->isStackArea())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output->isStackArea()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output->isStackArea()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9179); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output->isStackArea()" ")"); do { *((volatile int*)__null) = 9179; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9180 | bool tempInit = false; |
9181 | for (auto iter = output->toStackArea()->results(); iter; iter.next()) { |
9182 | // Zero out ref stack results. |
9183 | if (iter.isWasmAnyRef()) { |
9184 | Register temp = ToRegister(lir->temp0()); |
9185 | if (!tempInit) { |
9186 | masm.xorPtr(temp, temp); |
9187 | tempInit = true; |
9188 | } |
9189 | masm.storePtr(temp, ToAddress(iter.alloc())); |
9190 | } |
9191 | } |
9192 | } |
9193 | |
9194 | void CodeGenerator::visitWasmRegisterResult(LWasmRegisterResult* lir) { |
9195 | #ifdef JS_64BIT1 |
9196 | if (MWasmRegisterResult* mir = lir->mir()) { |
9197 | if (mir->type() == MIRType::Int32) { |
9198 | masm.widenInt32(ToRegister(lir->output())); |
9199 | } |
9200 | } |
9201 | #endif |
9202 | } |
9203 | |
9204 | void CodeGenerator::visitWasmCall(LWasmCall* lir) { |
9205 | const MWasmCallBase* callBase = lir->callBase(); |
9206 | bool isReturnCall = lir->isReturnCall(); |
9207 | |
9208 | // If this call is in Wasm try code block, initialise a wasm::TryNote for this |
9209 | // call. |
9210 | bool inTry = callBase->inTry(); |
9211 | if (inTry) { |
9212 | size_t tryNoteIndex = callBase->tryNoteIndex(); |
9213 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
9214 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
9215 | tryNote.setTryBodyBegin(masm.currentOffset()); |
9216 | } |
9217 | |
9218 | MOZ_ASSERT((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment ==do { static_assert( mozilla::detail::AssertionConditionType< decltype((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9219); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" ")"); do { *((volatile int*)__null) = 9219; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9219 | 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9219); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" ")"); do { *((volatile int*)__null) = 9219; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9220 | static_assert( |
9221 | WasmStackAlignment >= ABIStackAlignment && |
9222 | WasmStackAlignment % ABIStackAlignment == 0, |
9223 | "The wasm stack alignment should subsume the ABI-required alignment"); |
9224 | |
9225 | #ifdef DEBUG1 |
9226 | Label ok; |
9227 | masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok); |
9228 | masm.breakpoint(); |
9229 | masm.bind(&ok); |
9230 | #endif |
9231 | |
9232 | // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the |
9233 | // instance and pinned regs. The only case where where we don't have to |
9234 | // reload the instance and pinned regs is when the callee preserves them. |
9235 | bool reloadRegs = true; |
9236 | bool switchRealm = true; |
9237 | |
9238 | const wasm::CallSiteDesc& desc = callBase->desc(); |
9239 | const wasm::CalleeDesc& callee = callBase->callee(); |
9240 | CodeOffset retOffset; |
9241 | CodeOffset secondRetOffset; |
9242 | switch (callee.which()) { |
9243 | case wasm::CalleeDesc::Func: |
9244 | #ifdef ENABLE_WASM_TAIL_CALLS1 |
9245 | if (isReturnCall) { |
9246 | ReturnCallAdjustmentInfo retCallInfo( |
9247 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
9248 | masm.wasmReturnCall(desc, callee.funcIndex(), retCallInfo); |
9249 | // The rest of the method is unnecessary for a return call. |
9250 | return; |
9251 | } |
9252 | #endif |
9253 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9253); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9253; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9254 | retOffset = masm.call(desc, callee.funcIndex()); |
9255 | reloadRegs = false; |
9256 | switchRealm = false; |
9257 | break; |
9258 | case wasm::CalleeDesc::Import: |
9259 | #ifdef ENABLE_WASM_TAIL_CALLS1 |
9260 | if (isReturnCall) { |
9261 | ReturnCallAdjustmentInfo retCallInfo( |
9262 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
9263 | masm.wasmReturnCallImport(desc, callee, retCallInfo); |
9264 | // The rest of the method is unnecessary for a return call. |
9265 | return; |
9266 | } |
9267 | #endif |
9268 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9268); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9268; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9269 | retOffset = masm.wasmCallImport(desc, callee); |
9270 | break; |
9271 | case wasm::CalleeDesc::AsmJSTable: |
9272 | retOffset = masm.asmCallIndirect(desc, callee); |
9273 | break; |
9274 | case wasm::CalleeDesc::WasmTable: { |
9275 | Label* boundsCheckFailed = nullptr; |
9276 | if (lir->needsBoundsCheck()) { |
9277 | OutOfLineAbortingWasmTrap* ool = |
9278 | new (alloc()) OutOfLineAbortingWasmTrap( |
9279 | wasm::BytecodeOffset(desc.lineOrBytecode()), |
9280 | wasm::Trap::OutOfBounds); |
9281 | if (lir->isCatchable()) { |
9282 | addOutOfLineCode(ool, lir->mirCatchable()); |
9283 | } else if (isReturnCall) { |
9284 | #ifdef ENABLE_WASM_TAIL_CALLS1 |
9285 | addOutOfLineCode(ool, lir->mirReturnCall()); |
9286 | #else |
9287 | MOZ_CRASH("Return calls are disabled.")do { do { } while (false); MOZ_ReportCrash("" "Return calls are disabled." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9287); AnnotateMozCrashReason("MOZ_CRASH(" "Return calls are disabled." ")"); do { *((volatile int*)__null) = 9287; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
9288 | #endif |
9289 | } else { |
9290 | addOutOfLineCode(ool, lir->mirUncatchable()); |
9291 | } |
9292 | boundsCheckFailed = ool->entry(); |
9293 | } |
9294 | Label* nullCheckFailed = nullptr; |
9295 | #ifndef WASM_HAS_HEAPREG1 |
9296 | { |
9297 | OutOfLineAbortingWasmTrap* ool = |
9298 | new (alloc()) OutOfLineAbortingWasmTrap( |
9299 | wasm::BytecodeOffset(desc.lineOrBytecode()), |
9300 | wasm::Trap::IndirectCallToNull); |
9301 | if (lir->isCatchable()) { |
9302 | addOutOfLineCode(ool, lir->mirCatchable()); |
9303 | } else if (isReturnCall) { |
9304 | # ifdef ENABLE_WASM_TAIL_CALLS1 |
9305 | addOutOfLineCode(ool, lir->mirReturnCall()); |
9306 | # else |
9307 | MOZ_CRASH("Return calls are disabled.")do { do { } while (false); MOZ_ReportCrash("" "Return calls are disabled." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9307); AnnotateMozCrashReason("MOZ_CRASH(" "Return calls are disabled." ")"); do { *((volatile int*)__null) = 9307; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
9308 | # endif |
9309 | } else { |
9310 | addOutOfLineCode(ool, lir->mirUncatchable()); |
9311 | } |
9312 | nullCheckFailed = ool->entry(); |
9313 | } |
9314 | #endif |
9315 | #ifdef ENABLE_WASM_TAIL_CALLS1 |
9316 | if (isReturnCall) { |
9317 | ReturnCallAdjustmentInfo retCallInfo( |
9318 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
9319 | masm.wasmReturnCallIndirect(desc, callee, boundsCheckFailed, |
9320 | nullCheckFailed, mozilla::Nothing(), |
9321 | retCallInfo); |
9322 | // The rest of the method is unnecessary for a return call. |
9323 | return; |
9324 | } |
9325 | #endif |
9326 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9326); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9326; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9327 | masm.wasmCallIndirect(desc, callee, boundsCheckFailed, nullCheckFailed, |
9328 | lir->tableSize(), &retOffset, &secondRetOffset); |
9329 | // Register reloading and realm switching are handled dynamically inside |
9330 | // wasmCallIndirect. There are two return offsets, one for each call |
9331 | // instruction (fast path and slow path). |
9332 | reloadRegs = false; |
9333 | switchRealm = false; |
9334 | break; |
9335 | } |
9336 | case wasm::CalleeDesc::Builtin: |
9337 | retOffset = masm.call(desc, callee.builtin()); |
9338 | reloadRegs = false; |
9339 | switchRealm = false; |
9340 | break; |
9341 | case wasm::CalleeDesc::BuiltinInstanceMethod: |
9342 | retOffset = masm.wasmCallBuiltinInstanceMethod( |
9343 | desc, callBase->instanceArg(), callee.builtin(), |
9344 | callBase->builtinMethodFailureMode()); |
9345 | switchRealm = false; |
9346 | break; |
9347 | case wasm::CalleeDesc::FuncRef: |
9348 | #ifdef ENABLE_WASM_TAIL_CALLS1 |
9349 | if (isReturnCall) { |
9350 | ReturnCallAdjustmentInfo retCallInfo( |
9351 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
9352 | masm.wasmReturnCallRef(desc, callee, retCallInfo); |
9353 | // The rest of the method is unnecessary for a return call. |
9354 | return; |
9355 | } |
9356 | #endif |
9357 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9357); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9357; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9358 | // Register reloading and realm switching are handled dynamically inside |
9359 | // wasmCallRef. There are two return offsets, one for each call |
9360 | // instruction (fast path and slow path). |
9361 | masm.wasmCallRef(desc, callee, &retOffset, &secondRetOffset); |
9362 | reloadRegs = false; |
9363 | switchRealm = false; |
9364 | break; |
9365 | } |
9366 | |
9367 | // Note the assembler offset for the associated LSafePoint. |
9368 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9368); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9368; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9369 | markSafepointAt(retOffset.offset(), lir); |
9370 | |
9371 | // Now that all the outbound in-memory args are on the stack, note the |
9372 | // required lower boundary point of the associated StackMap. |
9373 | uint32_t framePushedAtStackMapBase = |
9374 | masm.framePushed() - |
9375 | wasm::AlignStackArgAreaSize(callBase->stackArgAreaSizeUnaligned()); |
9376 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAtStackMapBase); |
9377 | MOZ_ASSERT(lir->safepoint()->wasmSafepointKind() ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->safepoint()->wasmSafepointKind() == WasmSafepointKind ::LirCall)>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(lir->safepoint()->wasmSafepointKind () == WasmSafepointKind::LirCall))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9378); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" ")"); do { *((volatile int*)__null) = 9378; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9378 | WasmSafepointKind::LirCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->safepoint()->wasmSafepointKind() == WasmSafepointKind ::LirCall)>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(lir->safepoint()->wasmSafepointKind () == WasmSafepointKind::LirCall))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9378); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" ")"); do { *((volatile int*)__null) = 9378; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9379 | |
9380 | // Note the assembler offset and framePushed for use by the adjunct |
9381 | // LSafePoint, see visitor for LWasmCallIndirectAdjunctSafepoint below. |
9382 | if (callee.which() == wasm::CalleeDesc::WasmTable) { |
9383 | lir->adjunctSafepoint()->recordSafepointInfo(secondRetOffset, |
9384 | framePushedAtStackMapBase); |
9385 | } |
9386 | |
9387 | if (reloadRegs) { |
9388 | masm.loadPtr( |
9389 | Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall), |
9390 | InstanceReg); |
9391 | masm.loadWasmPinnedRegsFromInstance(); |
9392 | if (switchRealm) { |
9393 | masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1); |
9394 | } |
9395 | } else { |
9396 | MOZ_ASSERT(!switchRealm)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!switchRealm)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!switchRealm))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!switchRealm", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9396); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!switchRealm" ")"); do { *((volatile int*)__null) = 9396; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9397 | } |
9398 | |
9399 | #ifdef ENABLE_WASM_TAIL_CALLS1 |
9400 | switch (callee.which()) { |
9401 | case wasm::CalleeDesc::Func: |
9402 | case wasm::CalleeDesc::Import: |
9403 | case wasm::CalleeDesc::WasmTable: |
9404 | case wasm::CalleeDesc::FuncRef: |
9405 | // Stack allocation could change during Wasm (return) calls, |
9406 | // recover pre-call state. |
9407 | masm.freeStackTo(masm.framePushed()); |
9408 | break; |
9409 | default: |
9410 | break; |
9411 | } |
9412 | #endif // ENABLE_WASM_TAIL_CALLS |
9413 | |
9414 | if (inTry) { |
9415 | // Set the end of the try note range |
9416 | size_t tryNoteIndex = callBase->tryNoteIndex(); |
9417 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
9418 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
9419 | |
9420 | // Don't set the end of the try note if we've OOM'ed, as the above |
9421 | // instructions may not have been emitted, which will trigger an assert |
9422 | // about zero-length try-notes. This is okay as this compilation will be |
9423 | // thrown away. |
9424 | if (!masm.oom()) { |
9425 | tryNote.setTryBodyEnd(masm.currentOffset()); |
9426 | } |
9427 | |
9428 | // This instruction or the adjunct safepoint must be the last instruction |
9429 | // in the block. No other instructions may be inserted. |
9430 | LBlock* block = lir->block(); |
9431 | MOZ_RELEASE_ASSERT(*block->rbegin() == lir ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9433); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9433; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9432 | (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9433); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9433; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9433 | *(++block->rbegin()) == lir))do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9433); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9433; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9434 | |
9435 | // Jump to the fallthrough block |
9436 | jumpToBlock(lir->mirCatchable()->getSuccessor( |
9437 | MWasmCallCatchable::FallthroughBranchIndex)); |
9438 | } |
9439 | } |
9440 | |
9441 | #ifdef ENABLE_WASM_JSPI1 |
9442 | void CodeGenerator::callWasmUpdateSuspenderState( |
9443 | wasm::UpdateSuspenderStateAction kind, Register suspender, Register temp) { |
9444 | masm.Push(InstanceReg); |
9445 | int32_t framePushedAfterInstance = masm.framePushed(); |
9446 | |
9447 | masm.move32(Imm32(uint32_t(kind)), temp); |
9448 | |
9449 | masm.setupWasmABICall(); |
9450 | masm.passABIArg(InstanceReg); |
9451 | masm.passABIArg(suspender); |
9452 | masm.passABIArg(temp); |
9453 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
9454 | masm.callWithABI(wasm::BytecodeOffset(0), |
9455 | wasm::SymbolicAddress::UpdateSuspenderState, |
9456 | mozilla::Some(instanceOffset)); |
9457 | |
9458 | masm.Pop(InstanceReg); |
9459 | } |
9460 | |
9461 | void CodeGenerator::prepareWasmStackSwitchTrampolineCall(Register suspender, |
9462 | Register data) { |
9463 | // Reserve stack space for the wasm call. |
9464 | unsigned argDecrement; |
9465 | { |
9466 | WasmABIArgGenerator abi; |
9467 | ABIArg arg; |
9468 | arg = abi.next(MIRType::Pointer); |
9469 | arg = abi.next(MIRType::Pointer); |
9470 | argDecrement = StackDecrementForCall(WasmStackAlignment, 0, |
9471 | abi.stackBytesConsumedSoFar()); |
9472 | } |
9473 | masm.reserveStack(argDecrement); |
9474 | |
9475 | // Pass the suspender and data params through the wasm function ABI registers. |
9476 | WasmABIArgGenerator abi; |
9477 | ABIArg arg; |
9478 | arg = abi.next(MIRType::Pointer); |
9479 | if (arg.kind() == ABIArg::GPR) { |
9480 | masm.movePtr(suspender, arg.gpr()); |
9481 | } else { |
9482 | MOZ_ASSERT(arg.kind() == ABIArg::Stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(arg.kind() == ABIArg::Stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(arg.kind() == ABIArg::Stack) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arg.kind() == ABIArg::Stack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9482); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack" ")"); do { *((volatile int*)__null) = 9482; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9483 | masm.storePtr(suspender, |
9484 | Address(masm.getStackPointer(), arg.offsetFromArgBase())); |
9485 | } |
9486 | arg = abi.next(MIRType::Pointer); |
9487 | if (arg.kind() == ABIArg::GPR) { |
9488 | masm.movePtr(data, arg.gpr()); |
9489 | } else { |
9490 | MOZ_ASSERT(arg.kind() == ABIArg::Stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(arg.kind() == ABIArg::Stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(arg.kind() == ABIArg::Stack) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arg.kind() == ABIArg::Stack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9490); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack" ")"); do { *((volatile int*)__null) = 9490; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9491 | masm.storePtr(data, |
9492 | Address(masm.getStackPointer(), arg.offsetFromArgBase())); |
9493 | } |
9494 | |
9495 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
9496 | WasmCallerInstanceOffsetBeforeCall)); |
9497 | } |
9498 | #endif // ENABLE_WASM_JSPI |
9499 | |
9500 | void CodeGenerator::visitWasmStackSwitchToSuspendable( |
9501 | LWasmStackSwitchToSuspendable* lir) { |
9502 | #ifdef ENABLE_WASM_JSPI1 |
9503 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
9504 | const Register FnReg = lir->fn()->toRegister().gpr(); |
9505 | const Register DataReg = lir->data()->toRegister().gpr(); |
9506 | const Register SuspenderDataReg = ABINonArgReg3; |
9507 | |
9508 | # ifdef JS_CODEGEN_ARM64 |
9509 | vixl::UseScratchRegisterScope temps(&masm); |
9510 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
9511 | # elif defined(JS_CODEGEN_X86) |
9512 | const Register ScratchReg1 = ABINonArgReg3; |
9513 | # elif defined(JS_CODEGEN_X641) |
9514 | const Register ScratchReg1 = ScratchReg; |
9515 | # elif defined(JS_CODEGEN_ARM) |
9516 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
9517 | # else |
9518 | # error "NYI: scratch register" |
9519 | # endif |
9520 | |
9521 | masm.Push(SuspenderReg); |
9522 | masm.Push(FnReg); |
9523 | masm.Push(DataReg); |
9524 | |
9525 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Enter, |
9526 | SuspenderReg, ScratchReg1); |
9527 | masm.Pop(DataReg); |
9528 | masm.Pop(FnReg); |
9529 | masm.Pop(SuspenderReg); |
9530 | |
9531 | masm.Push(SuspenderReg); |
9532 | int32_t framePushedAtSuspender = masm.framePushed(); |
9533 | masm.Push(InstanceReg); |
9534 | |
9535 | wasm::CallSiteDesc desc(wasm::CallSiteDesc::Kind::StackSwitch); |
9536 | CodeLabel returnCallsite; |
9537 | |
9538 | // Aligning stack before trampoline call. |
9539 | uint32_t reserve = ComputeByteAlignment( |
9540 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
9541 | masm.reserveStack(reserve); |
9542 | |
9543 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
9544 | wasm::SuspenderObjectDataSlot)), |
9545 | SuspenderDataReg); |
9546 | |
9547 | // Switch stacks to suspendable, keep original FP to maintain |
9548 | // frames chain between main and suspendable stack segments. |
9549 | masm.storeStackPtr( |
9550 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
9551 | masm.storePtr( |
9552 | FramePointer, |
9553 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP())); |
9554 | |
9555 | masm.loadStackPtr(Address( |
9556 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
9557 | |
9558 | masm.assertStackAlignment(WasmStackAlignment); |
9559 | |
9560 | // The FramePointer is not changed for SwitchToSuspendable. |
9561 | uint32_t framePushed = masm.framePushed(); |
9562 | |
9563 | // On different stack, reset framePushed. FramePointer is not valid here. |
9564 | masm.setFramePushed(0); |
9565 | |
9566 | prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg); |
9567 | |
9568 | // Get wasm instance pointer for callee. |
9569 | size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot( |
9570 | FunctionExtended::WASM_INSTANCE_SLOT); |
9571 | masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg); |
9572 | |
9573 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
9574 | WasmCalleeInstanceOffsetBeforeCall)); |
9575 | masm.loadWasmPinnedRegsFromInstance(); |
9576 | |
9577 | masm.assertStackAlignment(WasmStackAlignment); |
9578 | |
9579 | const Register ReturnAddressReg = ScratchReg1; |
9580 | |
9581 | // DataReg is not needed anymore, using it as a scratch register. |
9582 | const Register ScratchReg2 = DataReg; |
9583 | |
9584 | // Save future of suspendable stack exit frame pointer. |
9585 | masm.computeEffectiveAddress( |
9586 | Address(masm.getStackPointer(), -int32_t(sizeof(wasm::Frame))), |
9587 | ScratchReg2); |
9588 | masm.storePtr( |
9589 | ScratchReg2, |
9590 | Address(SuspenderDataReg, |
9591 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP())); |
9592 | |
9593 | masm.mov(&returnCallsite, ReturnAddressReg); |
9594 | |
9595 | // Call wasm function fast. |
9596 | # ifdef JS_USE_LINK_REGISTER |
9597 | masm.mov(ReturnAddressReg, lr); |
9598 | # else |
9599 | masm.Push(ReturnAddressReg); |
9600 | # endif |
9601 | // Get funcUncheckedCallEntry() from the function's |
9602 | // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot. |
9603 | size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot( |
9604 | FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT); |
9605 | masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2); |
9606 | masm.jump(ScratchReg2); |
9607 | |
9608 | // About to use valid FramePointer -- restore framePushed. |
9609 | masm.setFramePushed(framePushed); |
9610 | |
9611 | // For IsPlausibleStackMapKey check for the following callsite. |
9612 | masm.wasmTrapInstruction(); |
9613 | |
9614 | // Callsite for return from main stack. |
9615 | masm.bind(&returnCallsite); |
9616 | masm.append(desc, *returnCallsite.target()); |
9617 | masm.addCodeLabel(returnCallsite); |
9618 | |
9619 | masm.assertStackAlignment(WasmStackAlignment); |
9620 | |
9621 | markSafepointAt(returnCallsite.target()->offset(), lir); |
9622 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
9623 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
9624 | // Rooting SuspenderReg. |
9625 | masm.propagateOOM( |
9626 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
9627 | |
9628 | masm.freeStackTo(framePushed); |
9629 | |
9630 | masm.freeStack(reserve); |
9631 | masm.Pop(InstanceReg); |
9632 | masm.Pop(SuspenderReg); |
9633 | |
9634 | masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2); |
9635 | |
9636 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave, |
9637 | SuspenderReg, ScratchReg1); |
9638 | #else |
9639 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9639); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 9639; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
9640 | #endif // ENABLE_WASM_JSPI |
9641 | } |
9642 | |
9643 | void CodeGenerator::visitWasmStackSwitchToMain(LWasmStackSwitchToMain* lir) { |
9644 | #ifdef ENABLE_WASM_JSPI1 |
9645 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
9646 | const Register FnReg = lir->fn()->toRegister().gpr(); |
9647 | const Register DataReg = lir->data()->toRegister().gpr(); |
9648 | const Register SuspenderDataReg = ABINonArgReg3; |
9649 | |
9650 | # ifdef JS_CODEGEN_ARM64 |
9651 | vixl::UseScratchRegisterScope temps(&masm); |
9652 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
9653 | # elif defined(JS_CODEGEN_X86) |
9654 | const Register ScratchReg1 = ABINonArgReg3; |
9655 | # elif defined(JS_CODEGEN_X641) |
9656 | const Register ScratchReg1 = ScratchReg; |
9657 | # elif defined(JS_CODEGEN_ARM) |
9658 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
9659 | # else |
9660 | # error "NYI: scratch register" |
9661 | # endif |
9662 | |
9663 | masm.Push(SuspenderReg); |
9664 | masm.Push(FnReg); |
9665 | masm.Push(DataReg); |
9666 | |
9667 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Suspend, |
9668 | SuspenderReg, ScratchReg1); |
9669 | |
9670 | masm.Pop(DataReg); |
9671 | masm.Pop(FnReg); |
9672 | masm.Pop(SuspenderReg); |
9673 | |
9674 | masm.Push(SuspenderReg); |
9675 | int32_t framePushedAtSuspender = masm.framePushed(); |
9676 | masm.Push(InstanceReg); |
9677 | |
9678 | wasm::CallSiteDesc desc(wasm::CallSiteDesc::Kind::StackSwitch); |
9679 | CodeLabel returnCallsite; |
9680 | |
9681 | // Aligning stack before trampoline call. |
9682 | uint32_t reserve = ComputeByteAlignment( |
9683 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
9684 | masm.reserveStack(reserve); |
9685 | |
9686 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
9687 | wasm::SuspenderObjectDataSlot)), |
9688 | SuspenderDataReg); |
9689 | |
9690 | // Switch stacks to main. |
9691 | masm.storeStackPtr(Address( |
9692 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
9693 | masm.storePtr(FramePointer, |
9694 | Address(SuspenderDataReg, |
9695 | wasm::SuspenderObjectData::offsetOfSuspendableFP())); |
9696 | |
9697 | masm.loadStackPtr( |
9698 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
9699 | masm.loadPtr( |
9700 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()), |
9701 | FramePointer); |
9702 | |
9703 | // Set main_ra field to returnCallsite. |
9704 | # ifdef JS_CODEGEN_X86 |
9705 | // SuspenderDataReg is also ScratchReg1, use DataReg as a scratch register. |
9706 | MOZ_ASSERT(ScratchReg1 == SuspenderDataReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ScratchReg1 == SuspenderDataReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ScratchReg1 == SuspenderDataReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ScratchReg1 == SuspenderDataReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9706); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 == SuspenderDataReg" ")"); do { *((volatile int*)__null) = 9706; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9707 | masm.push(DataReg); |
9708 | masm.mov(&returnCallsite, DataReg); |
9709 | masm.storePtr( |
9710 | DataReg, |
9711 | Address(SuspenderDataReg, |
9712 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress())); |
9713 | masm.pop(DataReg); |
9714 | # else |
9715 | MOZ_ASSERT(ScratchReg1 != SuspenderDataReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ScratchReg1 != SuspenderDataReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ScratchReg1 != SuspenderDataReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ScratchReg1 != SuspenderDataReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9715); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 != SuspenderDataReg" ")"); do { *((volatile int*)__null) = 9715; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9716 | masm.mov(&returnCallsite, ScratchReg1); |
9717 | masm.storePtr( |
9718 | ScratchReg1, |
9719 | Address(SuspenderDataReg, |
9720 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress())); |
9721 | # endif |
9722 | |
9723 | masm.assertStackAlignment(WasmStackAlignment); |
9724 | |
9725 | // The FramePointer is pointing to the same |
9726 | // place as before switch happened. |
9727 | uint32_t framePushed = masm.framePushed(); |
9728 | |
9729 | // On different stack, reset framePushed. FramePointer is not valid here. |
9730 | masm.setFramePushed(0); |
9731 | |
9732 | prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg); |
9733 | |
9734 | // Get wasm instance pointer for callee. |
9735 | size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot( |
9736 | FunctionExtended::WASM_INSTANCE_SLOT); |
9737 | masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg); |
9738 | |
9739 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
9740 | WasmCalleeInstanceOffsetBeforeCall)); |
9741 | masm.loadWasmPinnedRegsFromInstance(); |
9742 | |
9743 | masm.assertStackAlignment(WasmStackAlignment); |
9744 | |
9745 | const Register ReturnAddressReg = ScratchReg1; |
9746 | // DataReg is not needed anymore, using it as a scratch register. |
9747 | const Register ScratchReg2 = DataReg; |
9748 | |
9749 | // Load InstanceReg from suspendable stack exit frame. |
9750 | masm.loadPtr(Address(SuspenderDataReg, |
9751 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
9752 | ScratchReg2); |
9753 | masm.loadPtr( |
9754 | Address(ScratchReg2, wasm::FrameWithInstances::callerInstanceOffset()), |
9755 | ScratchReg2); |
9756 | masm.storePtr(ScratchReg2, Address(masm.getStackPointer(), |
9757 | WasmCallerInstanceOffsetBeforeCall)); |
9758 | |
9759 | // Load RA from suspendable stack exit frame. |
9760 | masm.loadPtr(Address(SuspenderDataReg, |
9761 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
9762 | ScratchReg1); |
9763 | masm.loadPtr(Address(ScratchReg1, wasm::Frame::returnAddressOffset()), |
9764 | ReturnAddressReg); |
9765 | |
9766 | // Call wasm function fast. |
9767 | # ifdef JS_USE_LINK_REGISTER |
9768 | masm.mov(ReturnAddressReg, lr); |
9769 | # else |
9770 | masm.Push(ReturnAddressReg); |
9771 | # endif |
9772 | // Get funcUncheckedCallEntry() from the function's |
9773 | // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot. |
9774 | size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot( |
9775 | FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT); |
9776 | masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2); |
9777 | masm.jump(ScratchReg2); |
9778 | |
9779 | // About to use valid FramePointer -- restore framePushed. |
9780 | masm.setFramePushed(framePushed); |
9781 | |
9782 | // For IsPlausibleStackMapKey check for the following callsite. |
9783 | masm.wasmTrapInstruction(); |
9784 | |
9785 | // Callsite for return from suspendable stack. |
9786 | masm.bind(&returnCallsite); |
9787 | masm.append(desc, *returnCallsite.target()); |
9788 | masm.addCodeLabel(returnCallsite); |
9789 | |
9790 | masm.assertStackAlignment(WasmStackAlignment); |
9791 | |
9792 | markSafepointAt(returnCallsite.target()->offset(), lir); |
9793 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
9794 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
9795 | // Rooting SuspenderReg. |
9796 | masm.propagateOOM( |
9797 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
9798 | |
9799 | masm.freeStackTo(framePushed); |
9800 | |
9801 | masm.freeStack(reserve); |
9802 | masm.Pop(InstanceReg); |
9803 | masm.Pop(SuspenderReg); |
9804 | |
9805 | masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2); |
9806 | |
9807 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Resume, |
9808 | SuspenderReg, ScratchReg1); |
9809 | #else |
9810 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9810); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 9810; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
9811 | #endif // ENABLE_WASM_JSPI |
9812 | } |
9813 | |
9814 | void CodeGenerator::visitWasmStackContinueOnSuspendable( |
9815 | LWasmStackContinueOnSuspendable* lir) { |
9816 | #ifdef ENABLE_WASM_JSPI1 |
9817 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
9818 | const Register SuspenderDataReg = ABINonArgReg3; |
9819 | |
9820 | # ifdef JS_CODEGEN_ARM64 |
9821 | vixl::UseScratchRegisterScope temps(&masm); |
9822 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
9823 | # elif defined(JS_CODEGEN_X86) |
9824 | const Register ScratchReg1 = ABINonArgReg2; |
9825 | # elif defined(JS_CODEGEN_X641) |
9826 | const Register ScratchReg1 = ScratchReg; |
9827 | # elif defined(JS_CODEGEN_ARM) |
9828 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
9829 | # else |
9830 | # error "NYI: scratch register" |
9831 | # endif |
9832 | const Register ScratchReg2 = ABINonArgReg1; |
9833 | |
9834 | masm.Push(SuspenderReg); |
9835 | int32_t framePushedAtSuspender = masm.framePushed(); |
9836 | masm.Push(InstanceReg); |
9837 | |
9838 | wasm::CallSiteDesc desc(wasm::CallSiteDesc::Kind::StackSwitch); |
9839 | CodeLabel returnCallsite; |
9840 | |
9841 | // Aligning stack before trampoline call. |
9842 | uint32_t reserve = ComputeByteAlignment( |
9843 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
9844 | masm.reserveStack(reserve); |
9845 | |
9846 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
9847 | wasm::SuspenderObjectDataSlot)), |
9848 | SuspenderDataReg); |
9849 | masm.storeStackPtr( |
9850 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
9851 | masm.storePtr( |
9852 | FramePointer, |
9853 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP())); |
9854 | |
9855 | // Adjust exit frame FP. |
9856 | masm.loadPtr(Address(SuspenderDataReg, |
9857 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
9858 | ScratchReg1); |
9859 | masm.storePtr(FramePointer, |
9860 | Address(ScratchReg1, wasm::Frame::callerFPOffset())); |
9861 | |
9862 | // Adjust exit frame RA. |
9863 | masm.mov(&returnCallsite, ScratchReg2); |
9864 | |
9865 | masm.storePtr(ScratchReg2, |
9866 | Address(ScratchReg1, wasm::Frame::returnAddressOffset())); |
9867 | // Adjust exit frame caller instance slot. |
9868 | masm.storePtr( |
9869 | InstanceReg, |
9870 | Address(ScratchReg1, wasm::FrameWithInstances::callerInstanceOffset())); |
9871 | |
9872 | // Switch stacks to suspendable. |
9873 | masm.loadStackPtr(Address( |
9874 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
9875 | masm.loadPtr(Address(SuspenderDataReg, |
9876 | wasm::SuspenderObjectData::offsetOfSuspendableFP()), |
9877 | FramePointer); |
9878 | |
9879 | masm.assertStackAlignment(WasmStackAlignment); |
9880 | |
9881 | // The FramePointer is pointing to the same |
9882 | // place as before switch happened. |
9883 | uint32_t framePushed = masm.framePushed(); |
9884 | |
9885 | // On different stack, reset framePushed. FramePointer is not valid here. |
9886 | masm.setFramePushed(0); |
9887 | |
9888 | // Restore shadow stack area and instance slots. |
9889 | WasmABIArgGenerator abi; |
9890 | unsigned reserveBeforeCall = abi.stackBytesConsumedSoFar(); |
9891 | MOZ_ASSERT(masm.framePushed() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("masm.framePushed() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { *((volatile int*)__null) = 9891; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9892 | unsigned argDecrement = |
9893 | StackDecrementForCall(WasmStackAlignment, 0, reserveBeforeCall); |
9894 | masm.reserveStack(argDecrement); |
9895 | |
9896 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
9897 | WasmCallerInstanceOffsetBeforeCall)); |
9898 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
9899 | WasmCalleeInstanceOffsetBeforeCall)); |
9900 | |
9901 | masm.assertStackAlignment(WasmStackAlignment); |
9902 | |
9903 | const Register ReturnAddressReg = ScratchReg1; |
9904 | |
9905 | // Pretend we just returned from the function. |
9906 | masm.loadPtr( |
9907 | Address(SuspenderDataReg, |
9908 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()), |
9909 | ReturnAddressReg); |
9910 | masm.jump(ReturnAddressReg); |
9911 | |
9912 | // About to use valid FramePointer -- restore framePushed. |
9913 | masm.setFramePushed(framePushed); |
9914 | |
9915 | // For IsPlausibleStackMapKey check for the following callsite. |
9916 | masm.wasmTrapInstruction(); |
9917 | |
9918 | // Callsite for return from suspendable stack. |
9919 | masm.bind(&returnCallsite); |
9920 | masm.append(desc, *returnCallsite.target()); |
9921 | masm.addCodeLabel(returnCallsite); |
9922 | |
9923 | masm.assertStackAlignment(WasmStackAlignment); |
9924 | |
9925 | markSafepointAt(returnCallsite.target()->offset(), lir); |
9926 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
9927 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
9928 | // Rooting SuspenderReg. |
9929 | masm.propagateOOM( |
9930 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
9931 | |
9932 | masm.freeStackTo(framePushed); |
9933 | |
9934 | masm.freeStack(reserve); |
9935 | masm.Pop(InstanceReg); |
9936 | masm.Pop(SuspenderReg); |
9937 | |
9938 | // Using SuspenderDataReg and ABINonArgReg2 as temps. |
9939 | masm.switchToWasmInstanceRealm(SuspenderDataReg, ABINonArgReg2); |
9940 | |
9941 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave, |
9942 | SuspenderReg, ScratchReg1); |
9943 | #else |
9944 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9944); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 9944; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
9945 | #endif // ENABLE_WASM_JSPI |
9946 | } |
9947 | |
9948 | void CodeGenerator::visitWasmCallLandingPrePad(LWasmCallLandingPrePad* lir) { |
9949 | LBlock* block = lir->block(); |
9950 | MWasmCallLandingPrePad* mir = lir->mir(); |
9951 | MBasicBlock* mirBlock = mir->block(); |
9952 | MBasicBlock* callMirBlock = mir->callBlock(); |
9953 | |
9954 | // This block must be the pre-pad successor of the call block. No blocks may |
9955 | // be inserted between us, such as for critical edge splitting. |
9956 | MOZ_RELEASE_ASSERT(mirBlock == callMirBlock->getSuccessor(do { static_assert( mozilla::detail::AssertionConditionType< decltype(mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable ::PrePadBranchIndex))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mirBlock == callMirBlock-> getSuccessor( MWasmCallCatchable::PrePadBranchIndex)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9957); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" ")"); do { *((volatile int*)__null) = 9957; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9957 | MWasmCallCatchable::PrePadBranchIndex))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable ::PrePadBranchIndex))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mirBlock == callMirBlock-> getSuccessor( MWasmCallCatchable::PrePadBranchIndex)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9957); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" ")"); do { *((volatile int*)__null) = 9957; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9958 | |
9959 | // This instruction or a move group must be the first instruction in the |
9960 | // block. No other instructions may be inserted. |
9961 | MOZ_RELEASE_ASSERT(*block->begin() == lir || (block->begin()->isMoveGroup() &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir))>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9962); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" ")"); do { *((volatile int*)__null) = 9962; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
9962 | *(++block->begin()) == lir))do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir))>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9962); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" ")"); do { *((volatile int*)__null) = 9962; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
9963 | |
9964 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
9965 | wasm::TryNote& tryNote = tryNotes[mir->tryNoteIndex()]; |
9966 | // Set the entry point for the call try note to be the beginning of this |
9967 | // block. The above assertions (and assertions in visitWasmCall) guarantee |
9968 | // that we are not skipping over instructions that should be executed. |
9969 | tryNote.setLandingPad(block->label()->offset(), masm.framePushed()); |
9970 | } |
9971 | |
9972 | void CodeGenerator::visitWasmCallIndirectAdjunctSafepoint( |
9973 | LWasmCallIndirectAdjunctSafepoint* lir) { |
9974 | markSafepointAt(lir->safepointLocation().offset(), lir); |
9975 | lir->safepoint()->setFramePushedAtStackMapBase( |
9976 | lir->framePushedAtStackMapBase()); |
9977 | } |
9978 | |
9979 | template <typename InstructionWithMaybeTrapSite> |
9980 | void EmitSignalNullCheckTrapSite(MacroAssembler& masm, |
9981 | InstructionWithMaybeTrapSite* ins, |
9982 | FaultingCodeOffset fco, |
9983 | wasm::TrapMachineInsn tmi) { |
9984 | if (!ins->maybeTrap()) { |
9985 | return; |
9986 | } |
9987 | wasm::BytecodeOffset trapOffset(ins->maybeTrap()->offset); |
9988 | masm.append(wasm::Trap::NullPointerDereference, |
9989 | wasm::TrapSite(tmi, fco, trapOffset)); |
9990 | } |
9991 | |
9992 | template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex> |
9993 | void CodeGenerator::emitWasmValueLoad(InstructionWithMaybeTrapSite* ins, |
9994 | MIRType type, MWideningOp wideningOp, |
9995 | AddressOrBaseIndex addr, |
9996 | AnyRegister dst) { |
9997 | FaultingCodeOffset fco; |
9998 | switch (type) { |
9999 | case MIRType::Int32: |
10000 | switch (wideningOp) { |
10001 | case MWideningOp::None: |
10002 | fco = masm.load32(addr, dst.gpr()); |
10003 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10004 | wasm::TrapMachineInsn::Load32); |
10005 | break; |
10006 | case MWideningOp::FromU16: |
10007 | fco = masm.load16ZeroExtend(addr, dst.gpr()); |
10008 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10009 | wasm::TrapMachineInsn::Load16); |
10010 | break; |
10011 | case MWideningOp::FromS16: |
10012 | fco = masm.load16SignExtend(addr, dst.gpr()); |
10013 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10014 | wasm::TrapMachineInsn::Load16); |
10015 | break; |
10016 | case MWideningOp::FromU8: |
10017 | fco = masm.load8ZeroExtend(addr, dst.gpr()); |
10018 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10019 | wasm::TrapMachineInsn::Load8); |
10020 | break; |
10021 | case MWideningOp::FromS8: |
10022 | fco = masm.load8SignExtend(addr, dst.gpr()); |
10023 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10024 | wasm::TrapMachineInsn::Load8); |
10025 | break; |
10026 | default: |
10027 | MOZ_CRASH("unexpected widening op in ::visitWasmLoadElement")do { do { } while (false); MOZ_ReportCrash("" "unexpected widening op in ::visitWasmLoadElement" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10027); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected widening op in ::visitWasmLoadElement" ")"); do { *((volatile int*)__null) = 10027; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10028 | } |
10029 | break; |
10030 | case MIRType::Float32: |
10031 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10031); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10031; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10032 | fco = masm.loadFloat32(addr, dst.fpu()); |
10033 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10034 | wasm::TrapMachineInsn::Load32); |
10035 | break; |
10036 | case MIRType::Double: |
10037 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10037); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10037; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10038 | fco = masm.loadDouble(addr, dst.fpu()); |
10039 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10040 | wasm::TrapMachineInsn::Load64); |
10041 | break; |
10042 | case MIRType::Pointer: |
10043 | case MIRType::WasmAnyRef: |
10044 | case MIRType::WasmArrayData: |
10045 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10045); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10045; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10046 | fco = masm.loadPtr(addr, dst.gpr()); |
10047 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10048 | wasm::TrapMachineInsnForLoadWord()); |
10049 | break; |
10050 | default: |
10051 | MOZ_CRASH("unexpected type in ::emitWasmValueLoad")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueLoad" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10051); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueLoad" ")"); do { *((volatile int*)__null) = 10051; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10052 | } |
10053 | } |
10054 | |
10055 | template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex> |
10056 | void CodeGenerator::emitWasmValueStore(InstructionWithMaybeTrapSite* ins, |
10057 | MIRType type, MNarrowingOp narrowingOp, |
10058 | AnyRegister src, |
10059 | AddressOrBaseIndex addr) { |
10060 | FaultingCodeOffset fco; |
10061 | switch (type) { |
10062 | case MIRType::Int32: |
10063 | switch (narrowingOp) { |
10064 | case MNarrowingOp::None: |
10065 | fco = masm.store32(src.gpr(), addr); |
10066 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10067 | wasm::TrapMachineInsn::Store32); |
10068 | break; |
10069 | case MNarrowingOp::To16: |
10070 | fco = masm.store16(src.gpr(), addr); |
10071 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10072 | wasm::TrapMachineInsn::Store16); |
10073 | break; |
10074 | case MNarrowingOp::To8: |
10075 | fco = masm.store8(src.gpr(), addr); |
10076 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10077 | wasm::TrapMachineInsn::Store8); |
10078 | break; |
10079 | default: |
10080 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10080); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 10080; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
10081 | } |
10082 | break; |
10083 | case MIRType::Float32: |
10084 | fco = masm.storeFloat32(src.fpu(), addr); |
10085 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10086 | wasm::TrapMachineInsn::Store32); |
10087 | break; |
10088 | case MIRType::Double: |
10089 | fco = masm.storeDouble(src.fpu(), addr); |
10090 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10091 | wasm::TrapMachineInsn::Store64); |
10092 | break; |
10093 | case MIRType::Pointer: |
10094 | // This could be correct, but it would be a new usage, so check carefully. |
10095 | MOZ_CRASH("Unexpected type in ::emitWasmValueStore.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type in ::emitWasmValueStore." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10095); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected type in ::emitWasmValueStore." ")"); do { *((volatile int*)__null) = 10095; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10096 | case MIRType::WasmAnyRef: |
10097 | MOZ_CRASH("Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef.")do { do { } while (false); MOZ_ReportCrash("" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10097); AnnotateMozCrashReason("MOZ_CRASH(" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef." ")"); do { *((volatile int*)__null) = 10097; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10098 | default: |
10099 | MOZ_CRASH("unexpected type in ::emitWasmValueStore")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueStore" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10099); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueStore" ")"); do { *((volatile int*)__null) = 10099; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10100 | } |
10101 | } |
10102 | |
10103 | void CodeGenerator::visitWasmLoadSlot(LWasmLoadSlot* ins) { |
10104 | MIRType type = ins->type(); |
10105 | MWideningOp wideningOp = ins->wideningOp(); |
10106 | Register container = ToRegister(ins->containerRef()); |
10107 | Address addr(container, ins->offset()); |
10108 | AnyRegister dst = ToAnyRegister(ins->output()); |
10109 | |
10110 | #ifdef ENABLE_WASM_SIMD1 |
10111 | if (type == MIRType::Simd128) { |
10112 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10112); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10112; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10113 | FaultingCodeOffset fco = masm.loadUnalignedSimd128(addr, dst.fpu()); |
10114 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128); |
10115 | return; |
10116 | } |
10117 | #endif |
10118 | emitWasmValueLoad(ins, type, wideningOp, addr, dst); |
10119 | } |
10120 | |
10121 | void CodeGenerator::visitWasmLoadElement(LWasmLoadElement* ins) { |
10122 | MIRType type = ins->type(); |
10123 | MWideningOp wideningOp = ins->wideningOp(); |
10124 | Scale scale = ins->scale(); |
10125 | Register base = ToRegister(ins->base()); |
10126 | Register index = ToRegister(ins->index()); |
10127 | AnyRegister dst = ToAnyRegister(ins->output()); |
10128 | |
10129 | #ifdef ENABLE_WASM_SIMD1 |
10130 | if (type == MIRType::Simd128) { |
10131 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10131); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10131; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10132 | FaultingCodeOffset fco; |
10133 | Register temp = ToRegister(ins->temp0()); |
10134 | masm.movePtr(index, temp); |
10135 | masm.lshiftPtr(Imm32(4), temp); |
10136 | fco = masm.loadUnalignedSimd128(BaseIndex(base, temp, Scale::TimesOne), |
10137 | dst.fpu()); |
10138 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128); |
10139 | return; |
10140 | } |
10141 | #endif |
10142 | emitWasmValueLoad(ins, type, wideningOp, BaseIndex(base, index, scale), dst); |
10143 | } |
10144 | |
10145 | void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) { |
10146 | MIRType type = ins->type(); |
10147 | MNarrowingOp narrowingOp = ins->narrowingOp(); |
10148 | Register container = ToRegister(ins->containerRef()); |
10149 | Address addr(container, ins->offset()); |
10150 | AnyRegister src = ToAnyRegister(ins->value()); |
10151 | if (type != MIRType::Int32) { |
10152 | MOZ_RELEASE_ASSERT(narrowingOp == MNarrowingOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(narrowingOp == MNarrowingOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(narrowingOp == MNarrowingOp:: None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("narrowingOp == MNarrowingOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10152); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None" ")"); do { *((volatile int*)__null) = 10152; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10153 | } |
10154 | |
10155 | #ifdef ENABLE_WASM_SIMD1 |
10156 | if (type == MIRType::Simd128) { |
10157 | FaultingCodeOffset fco = masm.storeUnalignedSimd128(src.fpu(), addr); |
10158 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10159 | wasm::TrapMachineInsn::Store128); |
10160 | return; |
10161 | } |
10162 | #endif |
10163 | emitWasmValueStore(ins, type, narrowingOp, src, addr); |
10164 | } |
10165 | |
10166 | void CodeGenerator::visitWasmStoreElement(LWasmStoreElement* ins) { |
10167 | MIRType type = ins->type(); |
10168 | MNarrowingOp narrowingOp = ins->narrowingOp(); |
10169 | Scale scale = ins->scale(); |
10170 | Register base = ToRegister(ins->base()); |
10171 | Register index = ToRegister(ins->index()); |
10172 | AnyRegister src = ToAnyRegister(ins->value()); |
10173 | if (type != MIRType::Int32) { |
10174 | MOZ_RELEASE_ASSERT(narrowingOp == MNarrowingOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(narrowingOp == MNarrowingOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(narrowingOp == MNarrowingOp:: None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("narrowingOp == MNarrowingOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10174); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None" ")"); do { *((volatile int*)__null) = 10174; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10175 | } |
10176 | |
10177 | #ifdef ENABLE_WASM_SIMD1 |
10178 | if (type == MIRType::Simd128) { |
10179 | Register temp = ToRegister(ins->temp0()); |
10180 | masm.movePtr(index, temp); |
10181 | masm.lshiftPtr(Imm32(4), temp); |
10182 | FaultingCodeOffset fco = masm.storeUnalignedSimd128( |
10183 | src.fpu(), BaseIndex(base, temp, Scale::TimesOne)); |
10184 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10185 | wasm::TrapMachineInsn::Store128); |
10186 | return; |
10187 | } |
10188 | #endif |
10189 | emitWasmValueStore(ins, type, narrowingOp, src, |
10190 | BaseIndex(base, index, scale)); |
10191 | } |
10192 | |
10193 | void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) { |
10194 | Register elements = ToRegister(ins->elements()); |
10195 | Register index = ToRegister(ins->index()); |
10196 | Register output = ToRegister(ins->output()); |
10197 | masm.loadPtr(BaseIndex(elements, index, ScalePointer), output); |
10198 | } |
10199 | |
10200 | void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) { |
10201 | masm.movePtr(ToRegister(ins->base()), ToRegister(ins->output())); |
10202 | masm.addPtr(Imm32(int32_t(ins->offset())), ToRegister(ins->output())); |
10203 | } |
10204 | |
10205 | void CodeGenerator::visitWasmDerivedIndexPointer( |
10206 | LWasmDerivedIndexPointer* ins) { |
10207 | Register base = ToRegister(ins->base()); |
10208 | Register index = ToRegister(ins->index()); |
10209 | Register output = ToRegister(ins->output()); |
10210 | masm.computeEffectiveAddress(BaseIndex(base, index, ins->scale()), output); |
10211 | } |
10212 | |
10213 | void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) { |
10214 | Register instance = ToRegister(ins->instance()); |
10215 | Register valueBase = ToRegister(ins->valueBase()); |
10216 | size_t offset = ins->offset(); |
10217 | Register value = ToRegister(ins->value()); |
10218 | Register temp = ToRegister(ins->temp0()); |
10219 | |
10220 | if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) { |
10221 | Label skipPreBarrier; |
10222 | wasm::EmitWasmPreBarrierGuard( |
10223 | masm, instance, temp, Address(valueBase, offset), &skipPreBarrier, |
10224 | ins->maybeTrap() ? &ins->maybeTrap()->offset : nullptr); |
10225 | wasm::EmitWasmPreBarrierCallImmediate(masm, instance, temp, valueBase, |
10226 | offset); |
10227 | masm.bind(&skipPreBarrier); |
10228 | } |
10229 | |
10230 | FaultingCodeOffset fco = masm.storePtr(value, Address(valueBase, offset)); |
10231 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10232 | wasm::TrapMachineInsnForStoreWord()); |
10233 | // The postbarrier is handled separately. |
10234 | } |
10235 | |
10236 | void CodeGenerator::visitWasmStoreElementRef(LWasmStoreElementRef* ins) { |
10237 | Register instance = ToRegister(ins->instance()); |
10238 | Register base = ToRegister(ins->base()); |
10239 | Register index = ToRegister(ins->index()); |
10240 | Register value = ToRegister(ins->value()); |
10241 | Register temp0 = ToTempRegisterOrInvalid(ins->temp0()); |
10242 | Register temp1 = ToTempRegisterOrInvalid(ins->temp1()); |
10243 | |
10244 | BaseIndex addr(base, index, ScalePointer); |
10245 | |
10246 | if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) { |
10247 | Label skipPreBarrier; |
10248 | wasm::EmitWasmPreBarrierGuard( |
10249 | masm, instance, temp0, addr, &skipPreBarrier, |
10250 | ins->maybeTrap() ? &ins->maybeTrap()->offset : nullptr); |
10251 | wasm::EmitWasmPreBarrierCallIndex(masm, instance, temp0, temp1, addr); |
10252 | masm.bind(&skipPreBarrier); |
10253 | } |
10254 | |
10255 | FaultingCodeOffset fco = masm.storePtr(value, addr); |
10256 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
10257 | wasm::TrapMachineInsnForStoreWord()); |
10258 | // The postbarrier is handled separately. |
10259 | } |
10260 | |
10261 | // Out-of-line path to update the store buffer for wasm references. |
10262 | class OutOfLineWasmCallPostWriteBarrierImmediate |
10263 | : public OutOfLineCodeBase<CodeGenerator> { |
10264 | LInstruction* lir_; |
10265 | Register valueBase_; |
10266 | Register temp_; |
10267 | uint32_t valueOffset_; |
10268 | |
10269 | public: |
10270 | OutOfLineWasmCallPostWriteBarrierImmediate(LInstruction* lir, |
10271 | Register valueBase, Register temp, |
10272 | uint32_t valueOffset) |
10273 | : lir_(lir), |
10274 | valueBase_(valueBase), |
10275 | temp_(temp), |
10276 | valueOffset_(valueOffset) {} |
10277 | |
10278 | void accept(CodeGenerator* codegen) override { |
10279 | codegen->visitOutOfLineWasmCallPostWriteBarrierImmediate(this); |
10280 | } |
10281 | |
10282 | LInstruction* lir() const { return lir_; } |
10283 | Register valueBase() const { return valueBase_; } |
10284 | Register temp() const { return temp_; } |
10285 | uint32_t valueOffset() const { return valueOffset_; } |
10286 | }; |
10287 | |
10288 | void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierImmediate( |
10289 | OutOfLineWasmCallPostWriteBarrierImmediate* ool) { |
10290 | saveLiveVolatile(ool->lir()); |
10291 | masm.Push(InstanceReg); |
10292 | int32_t framePushedAfterInstance = masm.framePushed(); |
10293 | |
10294 | // Fold the value offset into the value base |
10295 | Register valueAddr = ool->valueBase(); |
10296 | Register temp = ool->temp(); |
10297 | masm.computeEffectiveAddress(Address(valueAddr, ool->valueOffset()), temp); |
10298 | |
10299 | // Call Instance::postBarrier |
10300 | masm.setupWasmABICall(); |
10301 | masm.passABIArg(InstanceReg); |
10302 | masm.passABIArg(temp); |
10303 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
10304 | masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier, |
10305 | mozilla::Some(instanceOffset), ABIType::General); |
10306 | |
10307 | masm.Pop(InstanceReg); |
10308 | restoreLiveVolatile(ool->lir()); |
10309 | |
10310 | masm.jump(ool->rejoin()); |
10311 | } |
10312 | |
10313 | void CodeGenerator::visitWasmPostWriteBarrierImmediate( |
10314 | LWasmPostWriteBarrierImmediate* lir) { |
10315 | Register object = ToRegister(lir->object()); |
10316 | Register value = ToRegister(lir->value()); |
10317 | Register valueBase = ToRegister(lir->valueBase()); |
10318 | Register temp = ToRegister(lir->temp0()); |
10319 | MOZ_ASSERT(ToRegister(lir->instance()) == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->instance()) == InstanceReg)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->instance()) == InstanceReg))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->instance()) == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10319); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg" ")"); do { *((volatile int*)__null) = 10319; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10320 | auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierImmediate( |
10321 | lir, valueBase, temp, lir->valueOffset()); |
10322 | addOutOfLineCode(ool, lir->mir()); |
10323 | |
10324 | wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, |
10325 | ool->rejoin()); |
10326 | masm.jump(ool->entry()); |
10327 | masm.bind(ool->rejoin()); |
10328 | } |
10329 | |
10330 | // Out-of-line path to update the store buffer for wasm references. |
10331 | class OutOfLineWasmCallPostWriteBarrierIndex |
10332 | : public OutOfLineCodeBase<CodeGenerator> { |
10333 | LInstruction* lir_; |
10334 | Register valueBase_; |
10335 | Register index_; |
10336 | Register temp_; |
10337 | uint32_t elemSize_; |
10338 | |
10339 | public: |
10340 | OutOfLineWasmCallPostWriteBarrierIndex(LInstruction* lir, Register valueBase, |
10341 | Register index, Register temp, |
10342 | uint32_t elemSize) |
10343 | : lir_(lir), |
10344 | valueBase_(valueBase), |
10345 | index_(index), |
10346 | temp_(temp), |
10347 | elemSize_(elemSize) { |
10348 | MOZ_ASSERT(elemSize == 1 || elemSize == 2 || elemSize == 4 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" ")"); do { *((volatile int*)__null) = 10349; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
10349 | elemSize == 8 || elemSize == 16)do { static_assert( mozilla::detail::AssertionConditionType< decltype(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" ")"); do { *((volatile int*)__null) = 10349; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10350 | } |
10351 | |
10352 | void accept(CodeGenerator* codegen) override { |
10353 | codegen->visitOutOfLineWasmCallPostWriteBarrierIndex(this); |
10354 | } |
10355 | |
10356 | LInstruction* lir() const { return lir_; } |
10357 | Register valueBase() const { return valueBase_; } |
10358 | Register index() const { return index_; } |
10359 | Register temp() const { return temp_; } |
10360 | uint32_t elemSize() const { return elemSize_; } |
10361 | }; |
10362 | |
10363 | void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierIndex( |
10364 | OutOfLineWasmCallPostWriteBarrierIndex* ool) { |
10365 | saveLiveVolatile(ool->lir()); |
10366 | masm.Push(InstanceReg); |
10367 | int32_t framePushedAfterInstance = masm.framePushed(); |
10368 | |
10369 | // Fold the value offset into the value base |
10370 | Register temp = ool->temp(); |
10371 | if (ool->elemSize() == 16) { |
10372 | masm.movePtr(ool->index(), temp); |
10373 | masm.lshiftPtr(Imm32(4), temp); |
10374 | masm.addPtr(ool->valueBase(), temp); |
10375 | } else { |
10376 | masm.computeEffectiveAddress(BaseIndex(ool->valueBase(), ool->index(), |
10377 | ScaleFromElemWidth(ool->elemSize())), |
10378 | temp); |
10379 | } |
10380 | |
10381 | // Call Instance::postBarrier |
10382 | masm.setupWasmABICall(); |
10383 | masm.passABIArg(InstanceReg); |
10384 | masm.passABIArg(temp); |
10385 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
10386 | masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier, |
10387 | mozilla::Some(instanceOffset), ABIType::General); |
10388 | |
10389 | masm.Pop(InstanceReg); |
10390 | restoreLiveVolatile(ool->lir()); |
10391 | |
10392 | masm.jump(ool->rejoin()); |
10393 | } |
10394 | |
10395 | void CodeGenerator::visitWasmPostWriteBarrierIndex( |
10396 | LWasmPostWriteBarrierIndex* lir) { |
10397 | Register object = ToRegister(lir->object()); |
10398 | Register value = ToRegister(lir->value()); |
10399 | Register valueBase = ToRegister(lir->valueBase()); |
10400 | Register index = ToRegister(lir->index()); |
10401 | Register temp = ToRegister(lir->temp0()); |
10402 | MOZ_ASSERT(ToRegister(lir->instance()) == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->instance()) == InstanceReg)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->instance()) == InstanceReg))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->instance()) == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10402); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg" ")"); do { *((volatile int*)__null) = 10402; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10403 | auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierIndex( |
10404 | lir, valueBase, index, temp, lir->elemSize()); |
10405 | addOutOfLineCode(ool, lir->mir()); |
10406 | |
10407 | wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, |
10408 | ool->rejoin()); |
10409 | masm.jump(ool->entry()); |
10410 | masm.bind(ool->rejoin()); |
10411 | } |
10412 | |
10413 | void CodeGenerator::visitWasmLoadSlotI64(LWasmLoadSlotI64* ins) { |
10414 | Register container = ToRegister(ins->containerRef()); |
10415 | Address addr(container, ins->offset()); |
10416 | Register64 output = ToOutRegister64(ins); |
10417 | // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one |
10418 | // transaction will always trap before the other, so it seems safest to |
10419 | // register both of them as potentially trapping. |
10420 | #ifdef JS_64BIT1 |
10421 | FaultingCodeOffset fco = masm.load64(addr, output); |
10422 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64); |
10423 | #else |
10424 | FaultingCodeOffsetPair fcop = masm.load64(addr, output); |
10425 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
10426 | wasm::TrapMachineInsn::Load32); |
10427 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
10428 | wasm::TrapMachineInsn::Load32); |
10429 | #endif |
10430 | } |
10431 | |
10432 | void CodeGenerator::visitWasmLoadElementI64(LWasmLoadElementI64* ins) { |
10433 | Register base = ToRegister(ins->base()); |
10434 | Register index = ToRegister(ins->index()); |
10435 | BaseIndex addr(base, index, Scale::TimesEight); |
10436 | Register64 output = ToOutRegister64(ins); |
10437 | // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one |
10438 | // transaction will always trap before the other, so it seems safest to |
10439 | // register both of them as potentially trapping. |
10440 | #ifdef JS_64BIT1 |
10441 | FaultingCodeOffset fco = masm.load64(addr, output); |
10442 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64); |
10443 | #else |
10444 | FaultingCodeOffsetPair fcop = masm.load64(addr, output); |
10445 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
10446 | wasm::TrapMachineInsn::Load32); |
10447 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
10448 | wasm::TrapMachineInsn::Load32); |
10449 | #endif |
10450 | } |
10451 | |
10452 | void CodeGenerator::visitWasmStoreSlotI64(LWasmStoreSlotI64* ins) { |
10453 | Register container = ToRegister(ins->containerRef()); |
10454 | Address addr(container, ins->offset()); |
10455 | Register64 value = ToRegister64(ins->value()); |
10456 | // Either 1 or 2 words. As above we register both transactions in the |
10457 | // 2-word case. |
10458 | #ifdef JS_64BIT1 |
10459 | FaultingCodeOffset fco = masm.store64(value, addr); |
10460 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64); |
10461 | #else |
10462 | FaultingCodeOffsetPair fcop = masm.store64(value, addr); |
10463 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
10464 | wasm::TrapMachineInsn::Store32); |
10465 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
10466 | wasm::TrapMachineInsn::Store32); |
10467 | #endif |
10468 | } |
10469 | |
10470 | void CodeGenerator::visitWasmStoreElementI64(LWasmStoreElementI64* ins) { |
10471 | Register base = ToRegister(ins->base()); |
10472 | Register index = ToRegister(ins->index()); |
10473 | BaseIndex addr(base, index, Scale::TimesEight); |
10474 | Register64 value = ToRegister64(ins->value()); |
10475 | // Either 1 or 2 words. As above we register both transactions in the |
10476 | // 2-word case. |
10477 | #ifdef JS_64BIT1 |
10478 | FaultingCodeOffset fco = masm.store64(value, addr); |
10479 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64); |
10480 | #else |
10481 | FaultingCodeOffsetPair fcop = masm.store64(value, addr); |
10482 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
10483 | wasm::TrapMachineInsn::Store32); |
10484 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
10485 | wasm::TrapMachineInsn::Store32); |
10486 | #endif |
10487 | } |
10488 | |
10489 | void CodeGenerator::visitWasmClampTable64Index(LWasmClampTable64Index* lir) { |
10490 | #ifdef ENABLE_WASM_MEMORY641 |
10491 | Register64 index = ToRegister64(lir->index()); |
10492 | Register out = ToRegister(lir->output()); |
10493 | masm.wasmClampTable64Index(index, out); |
10494 | #else |
10495 | MOZ_CRASH("table64 indexes should not be valid without memory64")do { do { } while (false); MOZ_ReportCrash("" "table64 indexes should not be valid without memory64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10495); AnnotateMozCrashReason("MOZ_CRASH(" "table64 indexes should not be valid without memory64" ")"); do { *((volatile int*)__null) = 10495; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10496 | #endif |
10497 | } |
10498 | |
10499 | void CodeGenerator::visitArrayBufferByteLength(LArrayBufferByteLength* lir) { |
10500 | Register obj = ToRegister(lir->object()); |
10501 | Register out = ToRegister(lir->output()); |
10502 | masm.loadArrayBufferByteLengthIntPtr(obj, out); |
10503 | } |
10504 | |
10505 | void CodeGenerator::visitArrayBufferViewLength(LArrayBufferViewLength* lir) { |
10506 | Register obj = ToRegister(lir->object()); |
10507 | Register out = ToRegister(lir->output()); |
10508 | masm.loadArrayBufferViewLengthIntPtr(obj, out); |
10509 | } |
10510 | |
10511 | void CodeGenerator::visitArrayBufferViewByteOffset( |
10512 | LArrayBufferViewByteOffset* lir) { |
10513 | Register obj = ToRegister(lir->object()); |
10514 | Register out = ToRegister(lir->output()); |
10515 | masm.loadArrayBufferViewByteOffsetIntPtr(obj, out); |
10516 | } |
10517 | |
10518 | void CodeGenerator::visitArrayBufferViewElements( |
10519 | LArrayBufferViewElements* lir) { |
10520 | Register obj = ToRegister(lir->object()); |
10521 | Register out = ToRegister(lir->output()); |
10522 | masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), out); |
10523 | } |
10524 | |
10525 | void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) { |
10526 | Register obj = ToRegister(lir->object()); |
10527 | Register out = ToRegister(lir->output()); |
10528 | |
10529 | masm.typedArrayElementSize(obj, out); |
10530 | } |
10531 | |
10532 | void CodeGenerator::visitResizableTypedArrayByteOffsetMaybeOutOfBounds( |
10533 | LResizableTypedArrayByteOffsetMaybeOutOfBounds* lir) { |
10534 | Register obj = ToRegister(lir->object()); |
10535 | Register out = ToRegister(lir->output()); |
10536 | Register temp = ToRegister(lir->temp0()); |
10537 | |
10538 | masm.loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr(obj, out, temp); |
10539 | } |
10540 | |
10541 | void CodeGenerator::visitResizableTypedArrayLength( |
10542 | LResizableTypedArrayLength* lir) { |
10543 | Register obj = ToRegister(lir->object()); |
10544 | Register out = ToRegister(lir->output()); |
10545 | Register temp = ToRegister(lir->temp0()); |
10546 | |
10547 | masm.loadResizableTypedArrayLengthIntPtr(lir->synchronization(), obj, out, |
10548 | temp); |
10549 | } |
10550 | |
10551 | void CodeGenerator::visitResizableDataViewByteLength( |
10552 | LResizableDataViewByteLength* lir) { |
10553 | Register obj = ToRegister(lir->object()); |
10554 | Register out = ToRegister(lir->output()); |
10555 | Register temp = ToRegister(lir->temp0()); |
10556 | |
10557 | masm.loadResizableDataViewByteLengthIntPtr(lir->synchronization(), obj, out, |
10558 | temp); |
10559 | } |
10560 | |
10561 | void CodeGenerator::visitGrowableSharedArrayBufferByteLength( |
10562 | LGrowableSharedArrayBufferByteLength* lir) { |
10563 | Register obj = ToRegister(lir->object()); |
10564 | Register out = ToRegister(lir->output()); |
10565 | |
10566 | // Explicit |byteLength| accesses are seq-consistent atomic loads. |
10567 | auto sync = Synchronization::Load(); |
10568 | |
10569 | masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out); |
10570 | } |
10571 | |
10572 | void CodeGenerator::visitGuardResizableArrayBufferViewInBounds( |
10573 | LGuardResizableArrayBufferViewInBounds* lir) { |
10574 | Register obj = ToRegister(lir->object()); |
10575 | Register temp = ToRegister(lir->temp0()); |
10576 | |
10577 | Label bail; |
10578 | masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail); |
10579 | bailoutFrom(&bail, lir->snapshot()); |
10580 | } |
10581 | |
10582 | void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached( |
10583 | LGuardResizableArrayBufferViewInBoundsOrDetached* lir) { |
10584 | Register obj = ToRegister(lir->object()); |
10585 | Register temp = ToRegister(lir->temp0()); |
10586 | |
10587 | Label done, bail; |
10588 | masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done); |
10589 | masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail); |
10590 | masm.bind(&done); |
10591 | bailoutFrom(&bail, lir->snapshot()); |
10592 | } |
10593 | |
10594 | void CodeGenerator::visitGuardHasAttachedArrayBuffer( |
10595 | LGuardHasAttachedArrayBuffer* lir) { |
10596 | Register obj = ToRegister(lir->object()); |
10597 | Register temp = ToRegister(lir->temp0()); |
10598 | |
10599 | Label bail; |
10600 | masm.branchIfHasDetachedArrayBuffer(obj, temp, &bail); |
10601 | bailoutFrom(&bail, lir->snapshot()); |
10602 | } |
10603 | |
10604 | class OutOfLineGuardNumberToIntPtrIndex |
10605 | : public OutOfLineCodeBase<CodeGenerator> { |
10606 | LGuardNumberToIntPtrIndex* lir_; |
10607 | |
10608 | public: |
10609 | explicit OutOfLineGuardNumberToIntPtrIndex(LGuardNumberToIntPtrIndex* lir) |
10610 | : lir_(lir) {} |
10611 | |
10612 | void accept(CodeGenerator* codegen) override { |
10613 | codegen->visitOutOfLineGuardNumberToIntPtrIndex(this); |
10614 | } |
10615 | LGuardNumberToIntPtrIndex* lir() const { return lir_; } |
10616 | }; |
10617 | |
10618 | void CodeGenerator::visitGuardNumberToIntPtrIndex( |
10619 | LGuardNumberToIntPtrIndex* lir) { |
10620 | FloatRegister input = ToFloatRegister(lir->input()); |
10621 | Register output = ToRegister(lir->output()); |
10622 | |
10623 | if (!lir->mir()->supportOOB()) { |
10624 | Label bail; |
10625 | masm.convertDoubleToPtr(input, output, &bail, false); |
10626 | bailoutFrom(&bail, lir->snapshot()); |
10627 | return; |
10628 | } |
10629 | |
10630 | auto* ool = new (alloc()) OutOfLineGuardNumberToIntPtrIndex(lir); |
10631 | addOutOfLineCode(ool, lir->mir()); |
10632 | |
10633 | masm.convertDoubleToPtr(input, output, ool->entry(), false); |
10634 | masm.bind(ool->rejoin()); |
10635 | } |
10636 | |
10637 | void CodeGenerator::visitOutOfLineGuardNumberToIntPtrIndex( |
10638 | OutOfLineGuardNumberToIntPtrIndex* ool) { |
10639 | // Substitute the invalid index with an arbitrary out-of-bounds index. |
10640 | masm.movePtr(ImmWord(-1), ToRegister(ool->lir()->output())); |
10641 | masm.jump(ool->rejoin()); |
10642 | } |
10643 | |
10644 | void CodeGenerator::visitStringLength(LStringLength* lir) { |
10645 | Register input = ToRegister(lir->string()); |
10646 | Register output = ToRegister(lir->output()); |
10647 | |
10648 | masm.loadStringLength(input, output); |
10649 | } |
10650 | |
10651 | void CodeGenerator::visitMinMaxI(LMinMaxI* ins) { |
10652 | Register first = ToRegister(ins->first()); |
10653 | Register output = ToRegister(ins->output()); |
10654 | |
10655 | MOZ_ASSERT(first == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(first == output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(first == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("first == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "first == output" ")"); do { *((volatile int*)__null) = 10655; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10656 | |
10657 | Assembler::Condition cond = |
10658 | ins->mir()->isMax() ? Assembler::GreaterThan : Assembler::LessThan; |
10659 | |
10660 | if (ins->second()->isConstant()) { |
10661 | Label done; |
10662 | masm.branch32(cond, first, Imm32(ToInt32(ins->second())), &done); |
10663 | masm.move32(Imm32(ToInt32(ins->second())), output); |
10664 | masm.bind(&done); |
10665 | } else { |
10666 | Register second = ToRegister(ins->second()); |
10667 | masm.cmp32Move32(cond, second, first, second, output); |
10668 | } |
10669 | } |
10670 | |
10671 | void CodeGenerator::visitMinMaxArrayI(LMinMaxArrayI* ins) { |
10672 | Register array = ToRegister(ins->array()); |
10673 | Register output = ToRegister(ins->output()); |
10674 | Register temp1 = ToRegister(ins->temp1()); |
10675 | Register temp2 = ToRegister(ins->temp2()); |
10676 | Register temp3 = ToRegister(ins->temp3()); |
10677 | bool isMax = ins->isMax(); |
10678 | |
10679 | Label bail; |
10680 | masm.minMaxArrayInt32(array, output, temp1, temp2, temp3, isMax, &bail); |
10681 | bailoutFrom(&bail, ins->snapshot()); |
10682 | } |
10683 | |
10684 | void CodeGenerator::visitMinMaxArrayD(LMinMaxArrayD* ins) { |
10685 | Register array = ToRegister(ins->array()); |
10686 | FloatRegister output = ToFloatRegister(ins->output()); |
10687 | Register temp1 = ToRegister(ins->temp1()); |
10688 | Register temp2 = ToRegister(ins->temp2()); |
10689 | FloatRegister floatTemp = ToFloatRegister(ins->floatTemp()); |
10690 | bool isMax = ins->isMax(); |
10691 | |
10692 | Label bail; |
10693 | masm.minMaxArrayNumber(array, output, floatTemp, temp1, temp2, isMax, &bail); |
10694 | bailoutFrom(&bail, ins->snapshot()); |
10695 | } |
10696 | |
10697 | // For Abs*, lowering will have tied input to output on platforms where that is |
10698 | // sensible, and otherwise left them untied. |
10699 | |
10700 | void CodeGenerator::visitAbsI(LAbsI* ins) { |
10701 | Register input = ToRegister(ins->input()); |
10702 | Register output = ToRegister(ins->output()); |
10703 | |
10704 | if (ins->mir()->fallible()) { |
10705 | Label positive; |
10706 | if (input != output) { |
10707 | masm.move32(input, output); |
10708 | } |
10709 | masm.branchTest32(Assembler::NotSigned, output, output, &positive); |
10710 | Label bail; |
10711 | masm.branchNeg32(Assembler::Overflow, output, &bail); |
10712 | bailoutFrom(&bail, ins->snapshot()); |
10713 | masm.bind(&positive); |
10714 | } else { |
10715 | masm.abs32(input, output); |
10716 | } |
10717 | } |
10718 | |
10719 | void CodeGenerator::visitAbsD(LAbsD* ins) { |
10720 | masm.absDouble(ToFloatRegister(ins->input()), ToFloatRegister(ins->output())); |
10721 | } |
10722 | |
10723 | void CodeGenerator::visitAbsF(LAbsF* ins) { |
10724 | masm.absFloat32(ToFloatRegister(ins->input()), |
10725 | ToFloatRegister(ins->output())); |
10726 | } |
10727 | |
10728 | void CodeGenerator::visitPowII(LPowII* ins) { |
10729 | Register value = ToRegister(ins->value()); |
10730 | Register power = ToRegister(ins->power()); |
10731 | Register output = ToRegister(ins->output()); |
10732 | Register temp0 = ToRegister(ins->temp0()); |
10733 | Register temp1 = ToRegister(ins->temp1()); |
10734 | |
10735 | Label bailout; |
10736 | masm.pow32(value, power, output, temp0, temp1, &bailout); |
10737 | bailoutFrom(&bailout, ins->snapshot()); |
10738 | } |
10739 | |
10740 | void CodeGenerator::visitPowI(LPowI* ins) { |
10741 | FloatRegister value = ToFloatRegister(ins->value()); |
10742 | Register power = ToRegister(ins->power()); |
10743 | |
10744 | using Fn = double (*)(double x, int32_t y); |
10745 | masm.setupAlignedABICall(); |
10746 | masm.passABIArg(value, ABIType::Float64); |
10747 | masm.passABIArg(power); |
10748 | |
10749 | masm.callWithABI<Fn, js::powi>(ABIType::Float64); |
10750 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10750); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 10750; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10751 | } |
10752 | |
10753 | void CodeGenerator::visitPowD(LPowD* ins) { |
10754 | FloatRegister value = ToFloatRegister(ins->value()); |
10755 | FloatRegister power = ToFloatRegister(ins->power()); |
10756 | |
10757 | using Fn = double (*)(double x, double y); |
10758 | masm.setupAlignedABICall(); |
10759 | masm.passABIArg(value, ABIType::Float64); |
10760 | masm.passABIArg(power, ABIType::Float64); |
10761 | masm.callWithABI<Fn, ecmaPow>(ABIType::Float64); |
10762 | |
10763 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10763); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 10763; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10764 | } |
10765 | |
10766 | void CodeGenerator::visitPowOfTwoI(LPowOfTwoI* ins) { |
10767 | Register power = ToRegister(ins->power()); |
10768 | Register output = ToRegister(ins->output()); |
10769 | |
10770 | uint32_t base = ins->base(); |
10771 | MOZ_ASSERT(mozilla::IsPowerOfTwo(base))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::IsPowerOfTwo(base))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mozilla::IsPowerOfTwo(base)) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mozilla::IsPowerOfTwo(base)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10771); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(base)" ")"); do { *((volatile int*)__null) = 10771; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10772 | |
10773 | uint32_t n = mozilla::FloorLog2(base); |
10774 | MOZ_ASSERT(n != 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(n != 0)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(n != 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("n != 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10774); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n != 0" ")" ); do { *((volatile int*)__null) = 10774; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
10775 | |
10776 | // Hacker's Delight, 2nd edition, theorem D2. |
10777 | auto ceilingDiv = [](uint32_t x, uint32_t y) { return (x + y - 1) / y; }; |
10778 | |
10779 | // Take bailout if |power| is greater-or-equals |log_y(2^31)| or is negative. |
10780 | // |2^(n*y) < 2^31| must hold, hence |n*y < 31| resp. |y < 31/n|. |
10781 | // |
10782 | // Note: it's important for this condition to match the code in CacheIR.cpp |
10783 | // (CanAttachInt32Pow) to prevent failure loops. |
10784 | bailoutCmp32(Assembler::AboveOrEqual, power, Imm32(ceilingDiv(31, n)), |
10785 | ins->snapshot()); |
10786 | |
10787 | // Compute (2^n)^y as 2^(n*y) using repeated shifts. We could directly scale |
10788 | // |power| and perform a single shift, but due to the lack of necessary |
10789 | // MacroAssembler functionality, like multiplying a register with an |
10790 | // immediate, we restrict the number of generated shift instructions when |
10791 | // lowering this operation. |
10792 | masm.move32(Imm32(1), output); |
10793 | do { |
10794 | masm.lshift32(power, output); |
10795 | n--; |
10796 | } while (n > 0); |
10797 | } |
10798 | |
10799 | void CodeGenerator::visitSqrtD(LSqrtD* ins) { |
10800 | FloatRegister input = ToFloatRegister(ins->input()); |
10801 | FloatRegister output = ToFloatRegister(ins->output()); |
10802 | masm.sqrtDouble(input, output); |
10803 | } |
10804 | |
10805 | void CodeGenerator::visitSqrtF(LSqrtF* ins) { |
10806 | FloatRegister input = ToFloatRegister(ins->input()); |
10807 | FloatRegister output = ToFloatRegister(ins->output()); |
10808 | masm.sqrtFloat32(input, output); |
10809 | } |
10810 | |
10811 | void CodeGenerator::visitSignI(LSignI* ins) { |
10812 | Register input = ToRegister(ins->input()); |
10813 | Register output = ToRegister(ins->output()); |
10814 | masm.signInt32(input, output); |
10815 | } |
10816 | |
10817 | void CodeGenerator::visitSignD(LSignD* ins) { |
10818 | FloatRegister input = ToFloatRegister(ins->input()); |
10819 | FloatRegister output = ToFloatRegister(ins->output()); |
10820 | masm.signDouble(input, output); |
10821 | } |
10822 | |
10823 | void CodeGenerator::visitSignDI(LSignDI* ins) { |
10824 | FloatRegister input = ToFloatRegister(ins->input()); |
10825 | FloatRegister temp = ToFloatRegister(ins->temp0()); |
10826 | Register output = ToRegister(ins->output()); |
10827 | |
10828 | Label bail; |
10829 | masm.signDoubleToInt32(input, output, temp, &bail); |
10830 | bailoutFrom(&bail, ins->snapshot()); |
10831 | } |
10832 | |
10833 | void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) { |
10834 | FloatRegister input = ToFloatRegister(ins->input()); |
10835 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10835); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 10835; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10836 | |
10837 | UnaryMathFunction fun = ins->mir()->function(); |
10838 | UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(fun); |
10839 | |
10840 | masm.setupAlignedABICall(); |
10841 | |
10842 | masm.passABIArg(input, ABIType::Float64); |
10843 | masm.callWithABI(DynamicFunction<UnaryMathFunctionType>(funPtr), |
10844 | ABIType::Float64); |
10845 | } |
10846 | |
10847 | void CodeGenerator::visitMathFunctionF(LMathFunctionF* ins) { |
10848 | FloatRegister input = ToFloatRegister(ins->input()); |
10849 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnFloat32Reg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnFloat32Reg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnFloat32Reg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnFloat32Reg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10849); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnFloat32Reg" ")"); do { *((volatile int*)__null) = 10849; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10850 | |
10851 | masm.setupAlignedABICall(); |
10852 | masm.passABIArg(input, ABIType::Float32); |
10853 | |
10854 | using Fn = float (*)(float x); |
10855 | Fn funptr = nullptr; |
10856 | CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check; |
10857 | switch (ins->mir()->function()) { |
10858 | case UnaryMathFunction::Floor: |
10859 | funptr = floorf; |
10860 | check = CheckUnsafeCallWithABI::DontCheckOther; |
10861 | break; |
10862 | case UnaryMathFunction::Round: |
10863 | funptr = math_roundf_impl; |
10864 | break; |
10865 | case UnaryMathFunction::Trunc: |
10866 | funptr = math_truncf_impl; |
10867 | break; |
10868 | case UnaryMathFunction::Ceil: |
10869 | funptr = ceilf; |
10870 | check = CheckUnsafeCallWithABI::DontCheckOther; |
10871 | break; |
10872 | default: |
10873 | MOZ_CRASH("Unknown or unsupported float32 math function")do { do { } while (false); MOZ_ReportCrash("" "Unknown or unsupported float32 math function" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10873); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown or unsupported float32 math function" ")"); do { *((volatile int*)__null) = 10873; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
10874 | } |
10875 | |
10876 | masm.callWithABI(DynamicFunction<Fn>(funptr), ABIType::Float32, check); |
10877 | } |
10878 | |
10879 | void CodeGenerator::visitModD(LModD* ins) { |
10880 | MOZ_ASSERT(!gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!gen->compilingWasm()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10880); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 10880; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10881 | |
10882 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
10883 | FloatRegister rhs = ToFloatRegister(ins->rhs()); |
10884 | |
10885 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10885); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 10885; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10886 | |
10887 | using Fn = double (*)(double a, double b); |
10888 | masm.setupAlignedABICall(); |
10889 | masm.passABIArg(lhs, ABIType::Float64); |
10890 | masm.passABIArg(rhs, ABIType::Float64); |
10891 | masm.callWithABI<Fn, NumberMod>(ABIType::Float64); |
10892 | } |
10893 | |
10894 | void CodeGenerator::visitModPowTwoD(LModPowTwoD* ins) { |
10895 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
10896 | uint32_t divisor = ins->divisor(); |
10897 | MOZ_ASSERT(mozilla::IsPowerOfTwo(divisor))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::IsPowerOfTwo(divisor))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mozilla::IsPowerOfTwo(divisor )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mozilla::IsPowerOfTwo(divisor)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10897); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(divisor)" ")"); do { *((volatile int*)__null) = 10897; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10898 | |
10899 | FloatRegister output = ToFloatRegister(ins->output()); |
10900 | |
10901 | // Compute |n % d| using |copysign(n - (d * trunc(n / d)), n)|. |
10902 | // |
10903 | // This doesn't work if |d| isn't a power of two, because we may lose too much |
10904 | // precision. For example |Number.MAX_VALUE % 3 == 2|, but |
10905 | // |3 * trunc(Number.MAX_VALUE / 3) == Infinity|. |
10906 | |
10907 | Label done; |
10908 | { |
10909 | ScratchDoubleScope scratch(masm); |
10910 | |
10911 | // Subnormals can lead to performance degradation, which can make calling |
10912 | // |fmod| faster than this inline implementation. Work around this issue by |
10913 | // directly returning the input for any value in the interval ]-1, +1[. |
10914 | Label notSubnormal; |
10915 | masm.loadConstantDouble(1.0, scratch); |
10916 | masm.loadConstantDouble(-1.0, output); |
10917 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, lhs, scratch, |
10918 | ¬Subnormal); |
10919 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, lhs, output, |
10920 | ¬Subnormal); |
10921 | |
10922 | masm.moveDouble(lhs, output); |
10923 | masm.jump(&done); |
10924 | |
10925 | masm.bind(¬Subnormal); |
10926 | |
10927 | if (divisor == 1) { |
10928 | // The pattern |n % 1 == 0| is used to detect integer numbers. We can skip |
10929 | // the multiplication by one in this case. |
10930 | masm.moveDouble(lhs, output); |
10931 | masm.nearbyIntDouble(RoundingMode::TowardsZero, output, scratch); |
10932 | masm.subDouble(scratch, output); |
10933 | } else { |
10934 | masm.loadConstantDouble(1.0 / double(divisor), scratch); |
10935 | masm.loadConstantDouble(double(divisor), output); |
10936 | |
10937 | masm.mulDouble(lhs, scratch); |
10938 | masm.nearbyIntDouble(RoundingMode::TowardsZero, scratch, scratch); |
10939 | masm.mulDouble(output, scratch); |
10940 | |
10941 | masm.moveDouble(lhs, output); |
10942 | masm.subDouble(scratch, output); |
10943 | } |
10944 | } |
10945 | |
10946 | masm.copySignDouble(output, lhs, output); |
10947 | masm.bind(&done); |
10948 | } |
10949 | |
10950 | void CodeGenerator::visitWasmBuiltinModD(LWasmBuiltinModD* ins) { |
10951 | masm.Push(InstanceReg); |
10952 | int32_t framePushedAfterInstance = masm.framePushed(); |
10953 | |
10954 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
10955 | FloatRegister rhs = ToFloatRegister(ins->rhs()); |
10956 | |
10957 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10957); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 10957; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
10958 | |
10959 | masm.setupWasmABICall(); |
10960 | masm.passABIArg(lhs, ABIType::Float64); |
10961 | masm.passABIArg(rhs, ABIType::Float64); |
10962 | |
10963 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
10964 | masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD, |
10965 | mozilla::Some(instanceOffset), ABIType::Float64); |
10966 | |
10967 | masm.Pop(InstanceReg); |
10968 | } |
10969 | |
10970 | void CodeGenerator::visitBigIntAdd(LBigIntAdd* ins) { |
10971 | Register lhs = ToRegister(ins->lhs()); |
10972 | Register rhs = ToRegister(ins->rhs()); |
10973 | Register temp1 = ToRegister(ins->temp1()); |
10974 | Register temp2 = ToRegister(ins->temp2()); |
10975 | Register output = ToRegister(ins->output()); |
10976 | |
10977 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
10978 | auto* ool = oolCallVM<Fn, BigInt::add>(ins, ArgList(lhs, rhs), |
10979 | StoreRegisterTo(output)); |
10980 | |
10981 | // 0n + x == x |
10982 | Label lhsNonZero; |
10983 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
10984 | masm.movePtr(rhs, output); |
10985 | masm.jump(ool->rejoin()); |
10986 | masm.bind(&lhsNonZero); |
10987 | |
10988 | // x + 0n == x |
10989 | Label rhsNonZero; |
10990 | masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero); |
10991 | masm.movePtr(lhs, output); |
10992 | masm.jump(ool->rejoin()); |
10993 | masm.bind(&rhsNonZero); |
10994 | |
10995 | // Call into the VM when either operand can't be loaded into a pointer-sized |
10996 | // register. |
10997 | masm.loadBigIntNonZero(lhs, temp1, ool->entry()); |
10998 | masm.loadBigIntNonZero(rhs, temp2, ool->entry()); |
10999 | |
11000 | masm.branchAddPtr(Assembler::Overflow, temp2, temp1, ool->entry()); |
11001 | |
11002 | // Create and return the result. |
11003 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11004 | masm.initializeBigInt(output, temp1); |
11005 | |
11006 | masm.bind(ool->rejoin()); |
11007 | } |
11008 | |
11009 | void CodeGenerator::visitBigIntSub(LBigIntSub* ins) { |
11010 | Register lhs = ToRegister(ins->lhs()); |
11011 | Register rhs = ToRegister(ins->rhs()); |
11012 | Register temp1 = ToRegister(ins->temp1()); |
11013 | Register temp2 = ToRegister(ins->temp2()); |
11014 | Register output = ToRegister(ins->output()); |
11015 | |
11016 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11017 | auto* ool = oolCallVM<Fn, BigInt::sub>(ins, ArgList(lhs, rhs), |
11018 | StoreRegisterTo(output)); |
11019 | |
11020 | // x - 0n == x |
11021 | Label rhsNonZero; |
11022 | masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero); |
11023 | masm.movePtr(lhs, output); |
11024 | masm.jump(ool->rejoin()); |
11025 | masm.bind(&rhsNonZero); |
11026 | |
11027 | // Call into the VM when either operand can't be loaded into a pointer-sized |
11028 | // register. |
11029 | masm.loadBigInt(lhs, temp1, ool->entry()); |
11030 | masm.loadBigIntNonZero(rhs, temp2, ool->entry()); |
11031 | |
11032 | masm.branchSubPtr(Assembler::Overflow, temp2, temp1, ool->entry()); |
11033 | |
11034 | // Create and return the result. |
11035 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11036 | masm.initializeBigInt(output, temp1); |
11037 | |
11038 | masm.bind(ool->rejoin()); |
11039 | } |
11040 | |
11041 | void CodeGenerator::visitBigIntMul(LBigIntMul* ins) { |
11042 | Register lhs = ToRegister(ins->lhs()); |
11043 | Register rhs = ToRegister(ins->rhs()); |
11044 | Register temp1 = ToRegister(ins->temp1()); |
11045 | Register temp2 = ToRegister(ins->temp2()); |
11046 | Register output = ToRegister(ins->output()); |
11047 | |
11048 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11049 | auto* ool = oolCallVM<Fn, BigInt::mul>(ins, ArgList(lhs, rhs), |
11050 | StoreRegisterTo(output)); |
11051 | |
11052 | // 0n * x == 0n |
11053 | Label lhsNonZero; |
11054 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11055 | masm.movePtr(lhs, output); |
11056 | masm.jump(ool->rejoin()); |
11057 | masm.bind(&lhsNonZero); |
11058 | |
11059 | // x * 0n == 0n |
11060 | Label rhsNonZero; |
11061 | masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero); |
11062 | masm.movePtr(rhs, output); |
11063 | masm.jump(ool->rejoin()); |
11064 | masm.bind(&rhsNonZero); |
11065 | |
11066 | // Call into the VM when either operand can't be loaded into a pointer-sized |
11067 | // register. |
11068 | masm.loadBigIntNonZero(lhs, temp1, ool->entry()); |
11069 | masm.loadBigIntNonZero(rhs, temp2, ool->entry()); |
11070 | |
11071 | masm.branchMulPtr(Assembler::Overflow, temp2, temp1, ool->entry()); |
11072 | |
11073 | // Create and return the result. |
11074 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11075 | masm.initializeBigInt(output, temp1); |
11076 | |
11077 | masm.bind(ool->rejoin()); |
11078 | } |
11079 | |
11080 | void CodeGenerator::visitBigIntDiv(LBigIntDiv* ins) { |
11081 | Register lhs = ToRegister(ins->lhs()); |
11082 | Register rhs = ToRegister(ins->rhs()); |
11083 | Register temp1 = ToRegister(ins->temp1()); |
11084 | Register temp2 = ToRegister(ins->temp2()); |
11085 | Register output = ToRegister(ins->output()); |
11086 | |
11087 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11088 | auto* ool = oolCallVM<Fn, BigInt::div>(ins, ArgList(lhs, rhs), |
11089 | StoreRegisterTo(output)); |
11090 | |
11091 | // x / 0 throws an error. |
11092 | if (ins->mir()->canBeDivideByZero()) { |
11093 | masm.branchIfBigIntIsZero(rhs, ool->entry()); |
11094 | } |
11095 | |
11096 | // 0n / x == 0n |
11097 | Label lhsNonZero; |
11098 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11099 | masm.movePtr(lhs, output); |
11100 | masm.jump(ool->rejoin()); |
11101 | masm.bind(&lhsNonZero); |
11102 | |
11103 | // Call into the VM when either operand can't be loaded into a pointer-sized |
11104 | // register. |
11105 | masm.loadBigIntNonZero(lhs, temp1, ool->entry()); |
11106 | masm.loadBigIntNonZero(rhs, temp2, ool->entry()); |
11107 | |
11108 | // |BigInt::div()| returns |lhs| for |lhs / 1n|, which means there's no |
11109 | // allocation which might trigger a minor GC to free up nursery space. This |
11110 | // requires us to apply the same optimization here, otherwise we'd end up with |
11111 | // always entering the OOL call, because the nursery is never evicted. |
11112 | Label notOne; |
11113 | masm.branchPtr(Assembler::NotEqual, temp2, ImmWord(1), ¬One); |
11114 | masm.movePtr(lhs, output); |
11115 | masm.jump(ool->rejoin()); |
11116 | masm.bind(¬One); |
11117 | |
11118 | static constexpr auto DigitMin = std::numeric_limits< |
11119 | mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min(); |
11120 | |
11121 | // Handle an integer overflow from INT{32,64}_MIN / -1. |
11122 | Label notOverflow; |
11123 | masm.branchPtr(Assembler::NotEqual, temp1, ImmWord(DigitMin), ¬Overflow); |
11124 | masm.branchPtr(Assembler::Equal, temp2, ImmWord(-1), ool->entry()); |
11125 | masm.bind(¬Overflow); |
11126 | |
11127 | emitBigIntDiv(ins, temp1, temp2, output, ool->entry()); |
11128 | |
11129 | masm.bind(ool->rejoin()); |
11130 | } |
11131 | |
11132 | void CodeGenerator::visitBigIntMod(LBigIntMod* ins) { |
11133 | Register lhs = ToRegister(ins->lhs()); |
11134 | Register rhs = ToRegister(ins->rhs()); |
11135 | Register temp1 = ToRegister(ins->temp1()); |
11136 | Register temp2 = ToRegister(ins->temp2()); |
11137 | Register output = ToRegister(ins->output()); |
11138 | |
11139 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11140 | auto* ool = oolCallVM<Fn, BigInt::mod>(ins, ArgList(lhs, rhs), |
11141 | StoreRegisterTo(output)); |
11142 | |
11143 | // x % 0 throws an error. |
11144 | if (ins->mir()->canBeDivideByZero()) { |
11145 | masm.branchIfBigIntIsZero(rhs, ool->entry()); |
11146 | } |
11147 | |
11148 | // 0n % x == 0n |
11149 | Label lhsNonZero; |
11150 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11151 | masm.movePtr(lhs, output); |
11152 | masm.jump(ool->rejoin()); |
11153 | masm.bind(&lhsNonZero); |
11154 | |
11155 | // Call into the VM when either operand can't be loaded into a pointer-sized |
11156 | // register. |
11157 | masm.loadBigIntAbsolute(lhs, temp1, ool->entry()); |
11158 | masm.loadBigIntAbsolute(rhs, temp2, ool->entry()); |
11159 | |
11160 | // Similar to the case for BigInt division, we must apply the same allocation |
11161 | // optimizations as performed in |BigInt::mod()|. |
11162 | Label notBelow; |
11163 | masm.branchPtr(Assembler::AboveOrEqual, temp1, temp2, ¬Below); |
11164 | masm.movePtr(lhs, output); |
11165 | masm.jump(ool->rejoin()); |
11166 | masm.bind(¬Below); |
11167 | |
11168 | // Convert both digits to signed pointer-sized values. |
11169 | masm.bigIntDigitToSignedPtr(lhs, temp1, ool->entry()); |
11170 | masm.bigIntDigitToSignedPtr(rhs, temp2, ool->entry()); |
11171 | |
11172 | static constexpr auto DigitMin = std::numeric_limits< |
11173 | mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min(); |
11174 | |
11175 | // Handle an integer overflow from INT{32,64}_MIN / -1. |
11176 | Label notOverflow; |
11177 | masm.branchPtr(Assembler::NotEqual, temp1, ImmWord(DigitMin), ¬Overflow); |
11178 | masm.branchPtr(Assembler::NotEqual, temp2, ImmWord(-1), ¬Overflow); |
11179 | masm.movePtr(ImmWord(0), temp1); |
11180 | masm.bind(¬Overflow); |
11181 | |
11182 | emitBigIntMod(ins, temp1, temp2, output, ool->entry()); |
11183 | |
11184 | masm.bind(ool->rejoin()); |
11185 | } |
11186 | |
11187 | void CodeGenerator::visitBigIntPow(LBigIntPow* ins) { |
11188 | Register lhs = ToRegister(ins->lhs()); |
11189 | Register rhs = ToRegister(ins->rhs()); |
11190 | Register temp1 = ToRegister(ins->temp1()); |
11191 | Register temp2 = ToRegister(ins->temp2()); |
11192 | Register output = ToRegister(ins->output()); |
11193 | |
11194 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11195 | auto* ool = oolCallVM<Fn, BigInt::pow>(ins, ArgList(lhs, rhs), |
11196 | StoreRegisterTo(output)); |
11197 | |
11198 | // x ** -y throws an error. |
11199 | if (ins->mir()->canBeNegativeExponent()) { |
11200 | masm.branchIfBigIntIsNegative(rhs, ool->entry()); |
11201 | } |
11202 | |
11203 | Register dest = temp1; |
11204 | Register base = temp2; |
11205 | Register exponent = output; |
11206 | |
11207 | Label done; |
11208 | masm.movePtr(ImmWord(1), dest); // p = 1 |
11209 | |
11210 | // 1n ** y == 1n |
11211 | // -1n ** y == 1n when y is even |
11212 | // -1n ** y == -1n when y is odd |
11213 | Label lhsNotOne; |
11214 | masm.branch32(Assembler::Above, Address(lhs, BigInt::offsetOfLength()), |
11215 | Imm32(1), &lhsNotOne); |
11216 | masm.loadFirstBigIntDigitOrZero(lhs, base); |
11217 | masm.branchPtr(Assembler::NotEqual, base, Imm32(1), &lhsNotOne); |
11218 | { |
11219 | masm.loadFirstBigIntDigitOrZero(rhs, exponent); |
11220 | |
11221 | Label lhsNonNegative; |
11222 | masm.branchIfBigIntIsNonNegative(lhs, &lhsNonNegative); |
11223 | masm.branchTestPtr(Assembler::Zero, exponent, Imm32(1), &done); |
11224 | masm.bind(&lhsNonNegative); |
11225 | masm.movePtr(lhs, output); |
11226 | masm.jump(ool->rejoin()); |
11227 | } |
11228 | masm.bind(&lhsNotOne); |
11229 | |
11230 | // x ** 0n == 1n |
11231 | masm.branchIfBigIntIsZero(rhs, &done); |
11232 | |
11233 | // 0n ** y == 0n with y != 0n |
11234 | Label lhsNonZero; |
11235 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11236 | { |
11237 | masm.movePtr(lhs, output); |
11238 | masm.jump(ool->rejoin()); |
11239 | } |
11240 | masm.bind(&lhsNonZero); |
11241 | |
11242 | // Call into the VM when the exponent can't be loaded into a pointer-sized |
11243 | // register. |
11244 | masm.loadBigIntAbsolute(rhs, exponent, ool->entry()); |
11245 | |
11246 | // x ** y with x > 1 and y >= DigitBits can't be pointer-sized. |
11247 | masm.branchPtr(Assembler::AboveOrEqual, exponent, Imm32(BigInt::DigitBits), |
11248 | ool->entry()); |
11249 | |
11250 | // x ** 1n == x |
11251 | Label rhsNotOne; |
11252 | masm.branch32(Assembler::NotEqual, exponent, Imm32(1), &rhsNotOne); |
11253 | { |
11254 | masm.movePtr(lhs, output); |
11255 | masm.jump(ool->rejoin()); |
11256 | } |
11257 | masm.bind(&rhsNotOne); |
11258 | |
11259 | // Call into the VM when the base operand can't be loaded into a pointer-sized |
11260 | // register. |
11261 | masm.loadBigIntNonZero(lhs, base, ool->entry()); |
11262 | |
11263 | // MacroAssembler::pow32() adjusted to work on pointer-sized registers. |
11264 | { |
11265 | // m = base |
11266 | // n = exponent |
11267 | |
11268 | Label start, loop; |
11269 | masm.jump(&start); |
11270 | masm.bind(&loop); |
11271 | |
11272 | // m *= m |
11273 | masm.branchMulPtr(Assembler::Overflow, base, base, ool->entry()); |
11274 | |
11275 | masm.bind(&start); |
11276 | |
11277 | // if ((n & 1) != 0) p *= m |
11278 | Label even; |
11279 | masm.branchTest32(Assembler::Zero, exponent, Imm32(1), &even); |
11280 | masm.branchMulPtr(Assembler::Overflow, base, dest, ool->entry()); |
11281 | masm.bind(&even); |
11282 | |
11283 | // n >>= 1 |
11284 | // if (n == 0) return p |
11285 | masm.branchRshift32(Assembler::NonZero, Imm32(1), exponent, &loop); |
11286 | } |
11287 | |
11288 | MOZ_ASSERT(temp1 == dest)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 == dest)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 == dest))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == dest", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11288); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp1 == dest" ")"); do { *((volatile int*)__null) = 11288; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11289 | |
11290 | // Create and return the result. |
11291 | masm.bind(&done); |
11292 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11293 | masm.initializeBigInt(output, temp1); |
11294 | |
11295 | masm.bind(ool->rejoin()); |
11296 | } |
11297 | |
11298 | void CodeGenerator::visitBigIntBitAnd(LBigIntBitAnd* ins) { |
11299 | Register lhs = ToRegister(ins->lhs()); |
11300 | Register rhs = ToRegister(ins->rhs()); |
11301 | Register temp1 = ToRegister(ins->temp1()); |
11302 | Register temp2 = ToRegister(ins->temp2()); |
11303 | Register output = ToRegister(ins->output()); |
11304 | |
11305 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11306 | auto* ool = oolCallVM<Fn, BigInt::bitAnd>(ins, ArgList(lhs, rhs), |
11307 | StoreRegisterTo(output)); |
11308 | |
11309 | // 0n & x == 0n |
11310 | Label lhsNonZero; |
11311 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11312 | masm.movePtr(lhs, output); |
11313 | masm.jump(ool->rejoin()); |
11314 | masm.bind(&lhsNonZero); |
11315 | |
11316 | // x & 0n == 0n |
11317 | Label rhsNonZero; |
11318 | masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero); |
11319 | masm.movePtr(rhs, output); |
11320 | masm.jump(ool->rejoin()); |
11321 | masm.bind(&rhsNonZero); |
11322 | |
11323 | // Call into the VM when either operand can't be loaded into a pointer-sized |
11324 | // register. |
11325 | masm.loadBigIntNonZero(lhs, temp1, ool->entry()); |
11326 | masm.loadBigIntNonZero(rhs, temp2, ool->entry()); |
11327 | |
11328 | masm.andPtr(temp2, temp1); |
11329 | |
11330 | // Create and return the result. |
11331 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11332 | masm.initializeBigInt(output, temp1); |
11333 | |
11334 | masm.bind(ool->rejoin()); |
11335 | } |
11336 | |
11337 | void CodeGenerator::visitBigIntBitOr(LBigIntBitOr* ins) { |
11338 | Register lhs = ToRegister(ins->lhs()); |
11339 | Register rhs = ToRegister(ins->rhs()); |
11340 | Register temp1 = ToRegister(ins->temp1()); |
11341 | Register temp2 = ToRegister(ins->temp2()); |
11342 | Register output = ToRegister(ins->output()); |
11343 | |
11344 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11345 | auto* ool = oolCallVM<Fn, BigInt::bitOr>(ins, ArgList(lhs, rhs), |
11346 | StoreRegisterTo(output)); |
11347 | |
11348 | // 0n | x == x |
11349 | Label lhsNonZero; |
11350 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11351 | masm.movePtr(rhs, output); |
11352 | masm.jump(ool->rejoin()); |
11353 | masm.bind(&lhsNonZero); |
11354 | |
11355 | // x | 0n == x |
11356 | Label rhsNonZero; |
11357 | masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero); |
11358 | masm.movePtr(lhs, output); |
11359 | masm.jump(ool->rejoin()); |
11360 | masm.bind(&rhsNonZero); |
11361 | |
11362 | // Call into the VM when either operand can't be loaded into a pointer-sized |
11363 | // register. |
11364 | masm.loadBigIntNonZero(lhs, temp1, ool->entry()); |
11365 | masm.loadBigIntNonZero(rhs, temp2, ool->entry()); |
11366 | |
11367 | masm.orPtr(temp2, temp1); |
11368 | |
11369 | // Create and return the result. |
11370 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11371 | masm.initializeBigInt(output, temp1); |
11372 | |
11373 | masm.bind(ool->rejoin()); |
11374 | } |
11375 | |
11376 | void CodeGenerator::visitBigIntBitXor(LBigIntBitXor* ins) { |
11377 | Register lhs = ToRegister(ins->lhs()); |
11378 | Register rhs = ToRegister(ins->rhs()); |
11379 | Register temp1 = ToRegister(ins->temp1()); |
11380 | Register temp2 = ToRegister(ins->temp2()); |
11381 | Register output = ToRegister(ins->output()); |
11382 | |
11383 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11384 | auto* ool = oolCallVM<Fn, BigInt::bitXor>(ins, ArgList(lhs, rhs), |
11385 | StoreRegisterTo(output)); |
11386 | |
11387 | // 0n ^ x == x |
11388 | Label lhsNonZero; |
11389 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11390 | masm.movePtr(rhs, output); |
11391 | masm.jump(ool->rejoin()); |
11392 | masm.bind(&lhsNonZero); |
11393 | |
11394 | // x ^ 0n == x |
11395 | Label rhsNonZero; |
11396 | masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero); |
11397 | masm.movePtr(lhs, output); |
11398 | masm.jump(ool->rejoin()); |
11399 | masm.bind(&rhsNonZero); |
11400 | |
11401 | // Call into the VM when either operand can't be loaded into a pointer-sized |
11402 | // register. |
11403 | masm.loadBigIntNonZero(lhs, temp1, ool->entry()); |
11404 | masm.loadBigIntNonZero(rhs, temp2, ool->entry()); |
11405 | |
11406 | masm.xorPtr(temp2, temp1); |
11407 | |
11408 | // Create and return the result. |
11409 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11410 | masm.initializeBigInt(output, temp1); |
11411 | |
11412 | masm.bind(ool->rejoin()); |
11413 | } |
11414 | |
11415 | void CodeGenerator::visitBigIntLsh(LBigIntLsh* ins) { |
11416 | Register lhs = ToRegister(ins->lhs()); |
11417 | Register rhs = ToRegister(ins->rhs()); |
11418 | Register temp1 = ToRegister(ins->temp1()); |
11419 | Register temp2 = ToRegister(ins->temp2()); |
11420 | Register temp3 = ToRegister(ins->temp3()); |
11421 | Register output = ToRegister(ins->output()); |
11422 | |
11423 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11424 | auto* ool = oolCallVM<Fn, BigInt::lsh>(ins, ArgList(lhs, rhs), |
11425 | StoreRegisterTo(output)); |
11426 | |
11427 | // 0n << x == 0n |
11428 | Label lhsNonZero; |
11429 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11430 | masm.movePtr(lhs, output); |
11431 | masm.jump(ool->rejoin()); |
11432 | masm.bind(&lhsNonZero); |
11433 | |
11434 | // x << 0n == x |
11435 | Label rhsNonZero; |
11436 | masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero); |
11437 | masm.movePtr(lhs, output); |
11438 | masm.jump(ool->rejoin()); |
11439 | masm.bind(&rhsNonZero); |
11440 | |
11441 | // Inline |BigInt::lsh| for the case when |lhs| contains a single digit. |
11442 | |
11443 | Label rhsTooLarge; |
11444 | masm.loadBigIntAbsolute(rhs, temp2, &rhsTooLarge); |
11445 | |
11446 | // Call into the VM when the left-hand side operand can't be loaded into a |
11447 | // pointer-sized register. |
11448 | masm.loadBigIntAbsolute(lhs, temp1, ool->entry()); |
11449 | |
11450 | // Handle shifts exceeding |BigInt::DigitBits| first. |
11451 | Label shift, create; |
11452 | masm.branchPtr(Assembler::Below, temp2, Imm32(BigInt::DigitBits), &shift); |
11453 | { |
11454 | masm.bind(&rhsTooLarge); |
11455 | |
11456 | // x << DigitBits with x != 0n always exceeds pointer-sized storage. |
11457 | masm.branchIfBigIntIsNonNegative(rhs, ool->entry()); |
11458 | |
11459 | // x << -DigitBits == x >> DigitBits, which is either 0n or -1n. |
11460 | masm.move32(Imm32(0), temp1); |
11461 | masm.branchIfBigIntIsNonNegative(lhs, &create); |
11462 | masm.move32(Imm32(1), temp1); |
11463 | masm.jump(&create); |
11464 | } |
11465 | masm.bind(&shift); |
11466 | |
11467 | Label nonNegative; |
11468 | masm.branchIfBigIntIsNonNegative(rhs, &nonNegative); |
11469 | { |
11470 | masm.movePtr(temp1, temp3); |
11471 | |
11472 | // |x << -y| is computed as |x >> y|. |
11473 | masm.rshiftPtr(temp2, temp1); |
11474 | |
11475 | // For negative numbers, round down if any bit was shifted out. |
11476 | masm.branchIfBigIntIsNonNegative(lhs, &create); |
11477 | |
11478 | // Compute |mask = (static_cast<Digit>(1) << shift) - 1|. |
11479 | masm.movePtr(ImmWord(-1), output); |
11480 | masm.lshiftPtr(temp2, output); |
11481 | masm.notPtr(output); |
11482 | |
11483 | // Add plus one when |(lhs.digit(0) & mask) != 0|. |
11484 | masm.branchTestPtr(Assembler::Zero, output, temp3, &create); |
11485 | masm.addPtr(ImmWord(1), temp1); |
11486 | masm.jump(&create); |
11487 | } |
11488 | masm.bind(&nonNegative); |
11489 | { |
11490 | masm.movePtr(temp2, temp3); |
11491 | |
11492 | // Compute |grow = lhs.digit(0) >> (DigitBits - shift)|. |
11493 | masm.negPtr(temp2); |
11494 | masm.addPtr(Imm32(BigInt::DigitBits), temp2); |
11495 | masm.movePtr(temp1, output); |
11496 | masm.rshiftPtr(temp2, output); |
11497 | |
11498 | // Call into the VM when any bit will be shifted out. |
11499 | masm.branchTestPtr(Assembler::NonZero, output, output, ool->entry()); |
11500 | |
11501 | masm.movePtr(temp3, temp2); |
11502 | masm.lshiftPtr(temp2, temp1); |
11503 | } |
11504 | masm.bind(&create); |
11505 | |
11506 | // Create and return the result. |
11507 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11508 | masm.initializeBigIntAbsolute(output, temp1); |
11509 | |
11510 | // Set the sign bit when the left-hand side is negative. |
11511 | masm.branchIfBigIntIsNonNegative(lhs, ool->rejoin()); |
11512 | masm.or32(Imm32(BigInt::signBitMask()), |
11513 | Address(output, BigInt::offsetOfFlags())); |
11514 | |
11515 | masm.bind(ool->rejoin()); |
11516 | } |
11517 | |
11518 | void CodeGenerator::visitBigIntRsh(LBigIntRsh* ins) { |
11519 | Register lhs = ToRegister(ins->lhs()); |
11520 | Register rhs = ToRegister(ins->rhs()); |
11521 | Register temp1 = ToRegister(ins->temp1()); |
11522 | Register temp2 = ToRegister(ins->temp2()); |
11523 | Register temp3 = ToRegister(ins->temp3()); |
11524 | Register output = ToRegister(ins->output()); |
11525 | |
11526 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
11527 | auto* ool = oolCallVM<Fn, BigInt::rsh>(ins, ArgList(lhs, rhs), |
11528 | StoreRegisterTo(output)); |
11529 | |
11530 | // 0n >> x == 0n |
11531 | Label lhsNonZero; |
11532 | masm.branchIfBigIntIsNonZero(lhs, &lhsNonZero); |
11533 | masm.movePtr(lhs, output); |
11534 | masm.jump(ool->rejoin()); |
11535 | masm.bind(&lhsNonZero); |
11536 | |
11537 | // x >> 0n == x |
11538 | Label rhsNonZero; |
11539 | masm.branchIfBigIntIsNonZero(rhs, &rhsNonZero); |
11540 | masm.movePtr(lhs, output); |
11541 | masm.jump(ool->rejoin()); |
11542 | masm.bind(&rhsNonZero); |
11543 | |
11544 | // Inline |BigInt::rsh| for the case when |lhs| contains a single digit. |
11545 | |
11546 | Label rhsTooLarge; |
11547 | masm.loadBigIntAbsolute(rhs, temp2, &rhsTooLarge); |
11548 | |
11549 | // Call into the VM when the left-hand side operand can't be loaded into a |
11550 | // pointer-sized register. |
11551 | masm.loadBigIntAbsolute(lhs, temp1, ool->entry()); |
11552 | |
11553 | // Handle shifts exceeding |BigInt::DigitBits| first. |
11554 | Label shift, create; |
11555 | masm.branchPtr(Assembler::Below, temp2, Imm32(BigInt::DigitBits), &shift); |
11556 | { |
11557 | masm.bind(&rhsTooLarge); |
11558 | |
11559 | // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage. |
11560 | masm.branchIfBigIntIsNegative(rhs, ool->entry()); |
11561 | |
11562 | // x >> DigitBits is either 0n or -1n. |
11563 | masm.move32(Imm32(0), temp1); |
11564 | masm.branchIfBigIntIsNonNegative(lhs, &create); |
11565 | masm.move32(Imm32(1), temp1); |
11566 | masm.jump(&create); |
11567 | } |
11568 | masm.bind(&shift); |
11569 | |
11570 | Label nonNegative; |
11571 | masm.branchIfBigIntIsNonNegative(rhs, &nonNegative); |
11572 | { |
11573 | masm.movePtr(temp2, temp3); |
11574 | |
11575 | // Compute |grow = lhs.digit(0) >> (DigitBits - shift)|. |
11576 | masm.negPtr(temp2); |
11577 | masm.addPtr(Imm32(BigInt::DigitBits), temp2); |
11578 | masm.movePtr(temp1, output); |
11579 | masm.rshiftPtr(temp2, output); |
11580 | |
11581 | // Call into the VM when any bit will be shifted out. |
11582 | masm.branchTestPtr(Assembler::NonZero, output, output, ool->entry()); |
11583 | |
11584 | // |x >> -y| is computed as |x << y|. |
11585 | masm.movePtr(temp3, temp2); |
11586 | masm.lshiftPtr(temp2, temp1); |
11587 | masm.jump(&create); |
11588 | } |
11589 | masm.bind(&nonNegative); |
11590 | { |
11591 | masm.movePtr(temp1, temp3); |
11592 | |
11593 | masm.rshiftPtr(temp2, temp1); |
11594 | |
11595 | // For negative numbers, round down if any bit was shifted out. |
11596 | masm.branchIfBigIntIsNonNegative(lhs, &create); |
11597 | |
11598 | // Compute |mask = (static_cast<Digit>(1) << shift) - 1|. |
11599 | masm.movePtr(ImmWord(-1), output); |
11600 | masm.lshiftPtr(temp2, output); |
11601 | masm.notPtr(output); |
11602 | |
11603 | // Add plus one when |(lhs.digit(0) & mask) != 0|. |
11604 | masm.branchTestPtr(Assembler::Zero, output, temp3, &create); |
11605 | masm.addPtr(ImmWord(1), temp1); |
11606 | } |
11607 | masm.bind(&create); |
11608 | |
11609 | // Create and return the result. |
11610 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11611 | masm.initializeBigIntAbsolute(output, temp1); |
11612 | |
11613 | // Set the sign bit when the left-hand side is negative. |
11614 | masm.branchIfBigIntIsNonNegative(lhs, ool->rejoin()); |
11615 | masm.or32(Imm32(BigInt::signBitMask()), |
11616 | Address(output, BigInt::offsetOfFlags())); |
11617 | |
11618 | masm.bind(ool->rejoin()); |
11619 | } |
11620 | |
11621 | void CodeGenerator::visitBigIntIncrement(LBigIntIncrement* ins) { |
11622 | Register input = ToRegister(ins->input()); |
11623 | Register temp1 = ToRegister(ins->temp1()); |
11624 | Register temp2 = ToRegister(ins->temp2()); |
11625 | Register output = ToRegister(ins->output()); |
11626 | |
11627 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
11628 | auto* ool = |
11629 | oolCallVM<Fn, BigInt::inc>(ins, ArgList(input), StoreRegisterTo(output)); |
11630 | |
11631 | // Call into the VM when the input can't be loaded into a pointer-sized |
11632 | // register. |
11633 | masm.loadBigInt(input, temp1, ool->entry()); |
11634 | masm.movePtr(ImmWord(1), temp2); |
11635 | |
11636 | masm.branchAddPtr(Assembler::Overflow, temp2, temp1, ool->entry()); |
11637 | |
11638 | // Create and return the result. |
11639 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11640 | masm.initializeBigInt(output, temp1); |
11641 | |
11642 | masm.bind(ool->rejoin()); |
11643 | } |
11644 | |
11645 | void CodeGenerator::visitBigIntDecrement(LBigIntDecrement* ins) { |
11646 | Register input = ToRegister(ins->input()); |
11647 | Register temp1 = ToRegister(ins->temp1()); |
11648 | Register temp2 = ToRegister(ins->temp2()); |
11649 | Register output = ToRegister(ins->output()); |
11650 | |
11651 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
11652 | auto* ool = |
11653 | oolCallVM<Fn, BigInt::dec>(ins, ArgList(input), StoreRegisterTo(output)); |
11654 | |
11655 | // Call into the VM when the input can't be loaded into a pointer-sized |
11656 | // register. |
11657 | masm.loadBigInt(input, temp1, ool->entry()); |
11658 | masm.movePtr(ImmWord(1), temp2); |
11659 | |
11660 | masm.branchSubPtr(Assembler::Overflow, temp2, temp1, ool->entry()); |
11661 | |
11662 | // Create and return the result. |
11663 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11664 | masm.initializeBigInt(output, temp1); |
11665 | |
11666 | masm.bind(ool->rejoin()); |
11667 | } |
11668 | |
11669 | void CodeGenerator::visitBigIntNegate(LBigIntNegate* ins) { |
11670 | Register input = ToRegister(ins->input()); |
11671 | Register temp = ToRegister(ins->temp()); |
11672 | Register output = ToRegister(ins->output()); |
11673 | |
11674 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
11675 | auto* ool = |
11676 | oolCallVM<Fn, BigInt::neg>(ins, ArgList(input), StoreRegisterTo(output)); |
11677 | |
11678 | // -0n == 0n |
11679 | Label lhsNonZero; |
11680 | masm.branchIfBigIntIsNonZero(input, &lhsNonZero); |
11681 | masm.movePtr(input, output); |
11682 | masm.jump(ool->rejoin()); |
11683 | masm.bind(&lhsNonZero); |
11684 | |
11685 | // Call into the VM when the input uses heap digits. |
11686 | masm.copyBigIntWithInlineDigits(input, output, temp, initialBigIntHeap(), |
11687 | ool->entry()); |
11688 | |
11689 | // Flip the sign bit. |
11690 | masm.xor32(Imm32(BigInt::signBitMask()), |
11691 | Address(output, BigInt::offsetOfFlags())); |
11692 | |
11693 | masm.bind(ool->rejoin()); |
11694 | } |
11695 | |
11696 | void CodeGenerator::visitBigIntBitNot(LBigIntBitNot* ins) { |
11697 | Register input = ToRegister(ins->input()); |
11698 | Register temp1 = ToRegister(ins->temp1()); |
11699 | Register temp2 = ToRegister(ins->temp2()); |
11700 | Register output = ToRegister(ins->output()); |
11701 | |
11702 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
11703 | auto* ool = oolCallVM<Fn, BigInt::bitNot>(ins, ArgList(input), |
11704 | StoreRegisterTo(output)); |
11705 | |
11706 | masm.loadBigIntAbsolute(input, temp1, ool->entry()); |
11707 | |
11708 | // This follows the C++ implementation because it let's us support the full |
11709 | // range [-2^64, 2^64 - 1] on 64-bit resp. [-2^32, 2^32 - 1] on 32-bit. |
11710 | Label nonNegative, done; |
11711 | masm.branchIfBigIntIsNonNegative(input, &nonNegative); |
11712 | { |
11713 | // ~(-x) == ~(~(x-1)) == x-1 |
11714 | masm.subPtr(Imm32(1), temp1); |
11715 | masm.jump(&done); |
11716 | } |
11717 | masm.bind(&nonNegative); |
11718 | { |
11719 | // ~x == -x-1 == -(x+1) |
11720 | masm.movePtr(ImmWord(1), temp2); |
11721 | masm.branchAddPtr(Assembler::CarrySet, temp2, temp1, ool->entry()); |
11722 | } |
11723 | masm.bind(&done); |
11724 | |
11725 | // Create and return the result. |
11726 | masm.newGCBigInt(output, temp2, initialBigIntHeap(), ool->entry()); |
11727 | masm.initializeBigIntAbsolute(output, temp1); |
11728 | |
11729 | // Set the sign bit when the input is positive. |
11730 | masm.branchIfBigIntIsNegative(input, ool->rejoin()); |
11731 | masm.or32(Imm32(BigInt::signBitMask()), |
11732 | Address(output, BigInt::offsetOfFlags())); |
11733 | |
11734 | masm.bind(ool->rejoin()); |
11735 | } |
11736 | |
11737 | void CodeGenerator::visitInt32ToStringWithBase(LInt32ToStringWithBase* lir) { |
11738 | Register input = ToRegister(lir->input()); |
11739 | RegisterOrInt32 base = ToRegisterOrInt32(lir->base()); |
11740 | Register output = ToRegister(lir->output()); |
11741 | Register temp0 = ToRegister(lir->temp0()); |
11742 | Register temp1 = ToRegister(lir->temp1()); |
11743 | |
11744 | bool lowerCase = lir->mir()->lowerCase(); |
11745 | |
11746 | using Fn = JSString* (*)(JSContext*, int32_t, int32_t, bool); |
11747 | if (base.is<Register>()) { |
11748 | auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase>( |
11749 | lir, ArgList(input, base.as<Register>(), Imm32(lowerCase)), |
11750 | StoreRegisterTo(output)); |
11751 | |
11752 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
11753 | masm.loadInt32ToStringWithBase(input, base.as<Register>(), output, temp0, |
11754 | temp1, gen->runtime->staticStrings(), |
11755 | liveRegs, lowerCase, ool->entry()); |
11756 | masm.bind(ool->rejoin()); |
11757 | } else { |
11758 | auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase>( |
11759 | lir, ArgList(input, Imm32(base.as<int32_t>()), Imm32(lowerCase)), |
11760 | StoreRegisterTo(output)); |
11761 | |
11762 | masm.loadInt32ToStringWithBase(input, base.as<int32_t>(), output, temp0, |
11763 | temp1, gen->runtime->staticStrings(), |
11764 | lowerCase, ool->entry()); |
11765 | masm.bind(ool->rejoin()); |
11766 | } |
11767 | } |
11768 | |
11769 | void CodeGenerator::visitNumberParseInt(LNumberParseInt* lir) { |
11770 | Register string = ToRegister(lir->string()); |
11771 | Register radix = ToRegister(lir->radix()); |
11772 | ValueOperand output = ToOutValue(lir); |
11773 | Register temp = ToRegister(lir->temp0()); |
11774 | |
11775 | #ifdef DEBUG1 |
11776 | Label ok; |
11777 | masm.branch32(Assembler::Equal, radix, Imm32(0), &ok); |
11778 | masm.branch32(Assembler::Equal, radix, Imm32(10), &ok); |
11779 | masm.assumeUnreachable("radix must be 0 or 10 for indexed value fast path"); |
11780 | masm.bind(&ok); |
11781 | #endif |
11782 | |
11783 | // Use indexed value as fast path if possible. |
11784 | Label vmCall, done; |
11785 | masm.loadStringIndexValue(string, temp, &vmCall); |
11786 | masm.tagValue(JSVAL_TYPE_INT32, temp, output); |
11787 | masm.jump(&done); |
11788 | { |
11789 | masm.bind(&vmCall); |
11790 | |
11791 | pushArg(radix); |
11792 | pushArg(string); |
11793 | |
11794 | using Fn = bool (*)(JSContext*, HandleString, int32_t, MutableHandleValue); |
11795 | callVM<Fn, js::NumberParseInt>(lir); |
11796 | } |
11797 | masm.bind(&done); |
11798 | } |
11799 | |
11800 | void CodeGenerator::visitDoubleParseInt(LDoubleParseInt* lir) { |
11801 | FloatRegister number = ToFloatRegister(lir->number()); |
11802 | Register output = ToRegister(lir->output()); |
11803 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
11804 | |
11805 | Label bail; |
11806 | masm.branchDouble(Assembler::DoubleUnordered, number, number, &bail); |
11807 | masm.branchTruncateDoubleToInt32(number, output, &bail); |
11808 | |
11809 | Label ok; |
11810 | masm.branch32(Assembler::NotEqual, output, Imm32(0), &ok); |
11811 | { |
11812 | // Accept both +0 and -0 and return 0. |
11813 | masm.loadConstantDouble(0.0, temp); |
11814 | masm.branchDouble(Assembler::DoubleEqual, number, temp, &ok); |
11815 | |
11816 | // Fail if a non-zero input is in the exclusive range (-1, 1.0e-6). |
11817 | masm.loadConstantDouble(DOUBLE_DECIMAL_IN_SHORTEST_LOW, temp); |
11818 | masm.branchDouble(Assembler::DoubleLessThan, number, temp, &bail); |
11819 | } |
11820 | masm.bind(&ok); |
11821 | |
11822 | bailoutFrom(&bail, lir->snapshot()); |
11823 | } |
11824 | |
11825 | void CodeGenerator::visitFloor(LFloor* lir) { |
11826 | FloatRegister input = ToFloatRegister(lir->input()); |
11827 | Register output = ToRegister(lir->output()); |
11828 | |
11829 | Label bail; |
11830 | masm.floorDoubleToInt32(input, output, &bail); |
11831 | bailoutFrom(&bail, lir->snapshot()); |
11832 | } |
11833 | |
11834 | void CodeGenerator::visitFloorF(LFloorF* lir) { |
11835 | FloatRegister input = ToFloatRegister(lir->input()); |
11836 | Register output = ToRegister(lir->output()); |
11837 | |
11838 | Label bail; |
11839 | masm.floorFloat32ToInt32(input, output, &bail); |
11840 | bailoutFrom(&bail, lir->snapshot()); |
11841 | } |
11842 | |
11843 | void CodeGenerator::visitCeil(LCeil* lir) { |
11844 | FloatRegister input = ToFloatRegister(lir->input()); |
11845 | Register output = ToRegister(lir->output()); |
11846 | |
11847 | Label bail; |
11848 | masm.ceilDoubleToInt32(input, output, &bail); |
11849 | bailoutFrom(&bail, lir->snapshot()); |
11850 | } |
11851 | |
11852 | void CodeGenerator::visitCeilF(LCeilF* lir) { |
11853 | FloatRegister input = ToFloatRegister(lir->input()); |
11854 | Register output = ToRegister(lir->output()); |
11855 | |
11856 | Label bail; |
11857 | masm.ceilFloat32ToInt32(input, output, &bail); |
11858 | bailoutFrom(&bail, lir->snapshot()); |
11859 | } |
11860 | |
11861 | void CodeGenerator::visitRound(LRound* lir) { |
11862 | FloatRegister input = ToFloatRegister(lir->input()); |
11863 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
11864 | Register output = ToRegister(lir->output()); |
11865 | |
11866 | Label bail; |
11867 | masm.roundDoubleToInt32(input, output, temp, &bail); |
11868 | bailoutFrom(&bail, lir->snapshot()); |
11869 | } |
11870 | |
11871 | void CodeGenerator::visitRoundF(LRoundF* lir) { |
11872 | FloatRegister input = ToFloatRegister(lir->input()); |
11873 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
11874 | Register output = ToRegister(lir->output()); |
11875 | |
11876 | Label bail; |
11877 | masm.roundFloat32ToInt32(input, output, temp, &bail); |
11878 | bailoutFrom(&bail, lir->snapshot()); |
11879 | } |
11880 | |
11881 | void CodeGenerator::visitTrunc(LTrunc* lir) { |
11882 | FloatRegister input = ToFloatRegister(lir->input()); |
11883 | Register output = ToRegister(lir->output()); |
11884 | |
11885 | Label bail; |
11886 | masm.truncDoubleToInt32(input, output, &bail); |
11887 | bailoutFrom(&bail, lir->snapshot()); |
11888 | } |
11889 | |
11890 | void CodeGenerator::visitTruncF(LTruncF* lir) { |
11891 | FloatRegister input = ToFloatRegister(lir->input()); |
11892 | Register output = ToRegister(lir->output()); |
11893 | |
11894 | Label bail; |
11895 | masm.truncFloat32ToInt32(input, output, &bail); |
11896 | bailoutFrom(&bail, lir->snapshot()); |
11897 | } |
11898 | |
11899 | void CodeGenerator::visitCompareS(LCompareS* lir) { |
11900 | JSOp op = lir->mir()->jsop(); |
11901 | Register left = ToRegister(lir->left()); |
11902 | Register right = ToRegister(lir->right()); |
11903 | Register output = ToRegister(lir->output()); |
11904 | |
11905 | OutOfLineCode* ool = nullptr; |
11906 | |
11907 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
11908 | if (op == JSOp::Eq || op == JSOp::StrictEq) { |
11909 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
11910 | lir, ArgList(left, right), StoreRegisterTo(output)); |
11911 | } else if (op == JSOp::Ne || op == JSOp::StrictNe) { |
11912 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
11913 | lir, ArgList(left, right), StoreRegisterTo(output)); |
11914 | } else if (op == JSOp::Lt) { |
11915 | ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>( |
11916 | lir, ArgList(left, right), StoreRegisterTo(output)); |
11917 | } else if (op == JSOp::Le) { |
11918 | // Push the operands in reverse order for JSOp::Le: |
11919 | // - |left <= right| is implemented as |right >= left|. |
11920 | ool = |
11921 | oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>( |
11922 | lir, ArgList(right, left), StoreRegisterTo(output)); |
11923 | } else if (op == JSOp::Gt) { |
11924 | // Push the operands in reverse order for JSOp::Gt: |
11925 | // - |left > right| is implemented as |right < left|. |
11926 | ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>( |
11927 | lir, ArgList(right, left), StoreRegisterTo(output)); |
11928 | } else { |
11929 | MOZ_ASSERT(op == JSOp::Ge)do { static_assert( mozilla::detail::AssertionConditionType< decltype(op == JSOp::Ge)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(op == JSOp::Ge))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("op == JSOp::Ge" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11929); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ge" ")"); do { *((volatile int*)__null) = 11929; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11930 | ool = |
11931 | oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>( |
11932 | lir, ArgList(left, right), StoreRegisterTo(output)); |
11933 | } |
11934 | |
11935 | masm.compareStrings(op, left, right, output, ool->entry()); |
11936 | |
11937 | masm.bind(ool->rejoin()); |
11938 | } |
11939 | |
11940 | void CodeGenerator::visitCompareSInline(LCompareSInline* lir) { |
11941 | JSOp op = lir->mir()->jsop(); |
11942 | MOZ_ASSERT(IsEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsEqualityOp(op)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11942); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsEqualityOp(op)" ")"); do { *((volatile int*)__null) = 11942; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11943 | |
11944 | Register input = ToRegister(lir->input()); |
11945 | Register output = ToRegister(lir->output()); |
11946 | |
11947 | const JSLinearString* str = lir->constant(); |
11948 | MOZ_ASSERT(str->length() > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(str->length() > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(str->length() > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("str->length() > 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11948); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() > 0" ")"); do { *((volatile int*)__null) = 11948; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11949 | |
11950 | OutOfLineCode* ool = nullptr; |
11951 | |
11952 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
11953 | if (op == JSOp::Eq || op == JSOp::StrictEq) { |
11954 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
11955 | lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output)); |
11956 | } else { |
11957 | MOZ_ASSERT(op == JSOp::Ne || op == JSOp::StrictNe)do { static_assert( mozilla::detail::AssertionConditionType< decltype(op == JSOp::Ne || op == JSOp::StrictNe)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(op == JSOp::Ne || op == JSOp::StrictNe))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("op == JSOp::Ne || op == JSOp::StrictNe" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11957); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ne || op == JSOp::StrictNe" ")"); do { *((volatile int*)__null) = 11957; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
11958 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
11959 | lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output)); |
11960 | } |
11961 | |
11962 | Label compareChars; |
11963 | { |
11964 | Label notPointerEqual; |
11965 | |
11966 | // If operands point to the same instance, the strings are trivially equal. |
11967 | masm.branchPtr(Assembler::NotEqual, input, ImmGCPtr(str), ¬PointerEqual); |
11968 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
11969 | masm.jump(ool->rejoin()); |
11970 | |
11971 | masm.bind(¬PointerEqual); |
11972 | |
11973 | Label setNotEqualResult; |
11974 | |
11975 | if (str->isAtom()) { |
11976 | // Atoms cannot be equal to each other if they point to different strings. |
11977 | Imm32 atomBit(JSString::ATOM_BIT); |
11978 | masm.branchTest32(Assembler::NonZero, |
11979 | Address(input, JSString::offsetOfFlags()), atomBit, |
11980 | &setNotEqualResult); |
11981 | } |
11982 | |
11983 | if (str->hasTwoByteChars()) { |
11984 | // Pure two-byte strings can't be equal to Latin-1 strings. |
11985 | JS::AutoCheckCannotGC nogc; |
11986 | if (!mozilla::IsUtf16Latin1(str->twoByteRange(nogc))) { |
11987 | masm.branchLatin1String(input, &setNotEqualResult); |
11988 | } |
11989 | } |
11990 | |
11991 | // Strings of different length can never be equal. |
11992 | masm.branch32(Assembler::NotEqual, |
11993 | Address(input, JSString::offsetOfLength()), |
11994 | Imm32(str->length()), &setNotEqualResult); |
11995 | |
11996 | if (str->isAtom()) { |
11997 | Label forwardedPtrEqual; |
11998 | masm.tryFastAtomize(input, output, output, &compareChars); |
11999 | |
12000 | // We now have two atoms. Just check pointer equality. |
12001 | masm.branchPtr(Assembler::Equal, output, ImmGCPtr(str), |
12002 | &forwardedPtrEqual); |
12003 | |
12004 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
12005 | masm.jump(ool->rejoin()); |
12006 | |
12007 | masm.bind(&forwardedPtrEqual); |
12008 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
12009 | masm.jump(ool->rejoin()); |
12010 | } else { |
12011 | masm.jump(&compareChars); |
12012 | } |
12013 | |
12014 | masm.bind(&setNotEqualResult); |
12015 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
12016 | masm.jump(ool->rejoin()); |
12017 | } |
12018 | |
12019 | masm.bind(&compareChars); |
12020 | |
12021 | // Load the input string's characters. |
12022 | Register stringChars = output; |
12023 | masm.loadStringCharsForCompare(input, str, stringChars, ool->entry()); |
12024 | |
12025 | // Start comparing character by character. |
12026 | masm.compareStringChars(op, stringChars, str, output); |
12027 | |
12028 | masm.bind(ool->rejoin()); |
12029 | } |
12030 | |
12031 | void CodeGenerator::visitCompareSSingle(LCompareSSingle* lir) { |
12032 | JSOp op = lir->jsop(); |
12033 | MOZ_ASSERT(IsRelationalOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsRelationalOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsRelationalOp(op)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsRelationalOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12033); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsRelationalOp(op)" ")"); do { *((volatile int*)__null) = 12033; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12034 | |
12035 | Register input = ToRegister(lir->input()); |
12036 | Register output = ToRegister(lir->output()); |
12037 | Register temp = ToRegister(lir->temp0()); |
12038 | |
12039 | const JSLinearString* str = lir->constant(); |
12040 | MOZ_ASSERT(str->length() == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(str->length() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(str->length() == 1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("str->length() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12040); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() == 1" ")"); do { *((volatile int*)__null) = 12040; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12041 | |
12042 | char16_t ch = str->latin1OrTwoByteChar(0); |
12043 | |
12044 | masm.movePtr(input, temp); |
12045 | |
12046 | // Check if the string is empty. |
12047 | Label compareLength; |
12048 | masm.branch32(Assembler::Equal, Address(temp, JSString::offsetOfLength()), |
12049 | Imm32(0), &compareLength); |
12050 | |
12051 | // The first character is in the left-most rope child. |
12052 | Label notRope; |
12053 | masm.branchIfNotRope(temp, ¬Rope); |
12054 | { |
12055 | // Unwind ropes at the start if possible. |
12056 | Label unwindRope; |
12057 | masm.bind(&unwindRope); |
12058 | masm.loadRopeLeftChild(temp, output); |
12059 | masm.movePtr(output, temp); |
12060 | |
12061 | #ifdef DEBUG1 |
12062 | Label notEmpty; |
12063 | masm.branch32(Assembler::NotEqual, |
12064 | Address(temp, JSString::offsetOfLength()), Imm32(0), |
12065 | ¬Empty); |
12066 | masm.assumeUnreachable("rope children are non-empty"); |
12067 | masm.bind(¬Empty); |
12068 | #endif |
12069 | |
12070 | // Otherwise keep unwinding ropes. |
12071 | masm.branchIfRope(temp, &unwindRope); |
12072 | } |
12073 | masm.bind(¬Rope); |
12074 | |
12075 | // Load the first character into |output|. |
12076 | auto loadFirstChar = [&](auto encoding) { |
12077 | masm.loadStringChars(temp, output, encoding); |
12078 | masm.loadChar(Address(output, 0), output, encoding); |
12079 | }; |
12080 | |
12081 | Label done; |
12082 | if (ch <= JSString::MAX_LATIN1_CHAR) { |
12083 | // Handle both encodings when the search character is Latin-1. |
12084 | Label twoByte, compare; |
12085 | masm.branchTwoByteString(temp, &twoByte); |
12086 | |
12087 | loadFirstChar(CharEncoding::Latin1); |
12088 | masm.jump(&compare); |
12089 | |
12090 | masm.bind(&twoByte); |
12091 | loadFirstChar(CharEncoding::TwoByte); |
12092 | |
12093 | masm.bind(&compare); |
12094 | } else { |
12095 | // The search character is a two-byte character, so it can't be equal to any |
12096 | // character of a Latin-1 string. |
12097 | masm.move32(Imm32(int32_t(op == JSOp::Lt || op == JSOp::Le)), output); |
12098 | masm.branchLatin1String(temp, &done); |
12099 | |
12100 | loadFirstChar(CharEncoding::TwoByte); |
12101 | } |
12102 | |
12103 | // Compare the string length when the search character is equal to the |
12104 | // input's first character. |
12105 | masm.branch32(Assembler::Equal, output, Imm32(ch), &compareLength); |
12106 | |
12107 | // Otherwise compute the result and jump to the end. |
12108 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), output, Imm32(ch), |
12109 | output); |
12110 | masm.jump(&done); |
12111 | |
12112 | // Compare the string length to compute the overall result. |
12113 | masm.bind(&compareLength); |
12114 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), |
12115 | Address(temp, JSString::offsetOfLength()), Imm32(1), output); |
12116 | |
12117 | masm.bind(&done); |
12118 | } |
12119 | |
12120 | void CodeGenerator::visitCompareBigInt(LCompareBigInt* lir) { |
12121 | JSOp op = lir->mir()->jsop(); |
12122 | Register left = ToRegister(lir->left()); |
12123 | Register right = ToRegister(lir->right()); |
12124 | Register temp0 = ToRegister(lir->temp0()); |
12125 | Register temp1 = ToRegister(lir->temp1()); |
12126 | Register temp2 = ToRegister(lir->temp2()); |
12127 | Register output = ToRegister(lir->output()); |
12128 | |
12129 | Label notSame; |
12130 | Label compareSign; |
12131 | Label compareLength; |
12132 | Label compareDigit; |
12133 | |
12134 | Label* notSameSign; |
12135 | Label* notSameLength; |
12136 | Label* notSameDigit; |
12137 | if (IsEqualityOp(op)) { |
12138 | notSameSign = ¬Same; |
12139 | notSameLength = ¬Same; |
12140 | notSameDigit = ¬Same; |
12141 | } else { |
12142 | notSameSign = &compareSign; |
12143 | notSameLength = &compareLength; |
12144 | notSameDigit = &compareDigit; |
12145 | } |
12146 | |
12147 | masm.equalBigInts(left, right, temp0, temp1, temp2, output, notSameSign, |
12148 | notSameLength, notSameDigit); |
12149 | |
12150 | Label done; |
12151 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le || |
12152 | op == JSOp::Ge), |
12153 | output); |
12154 | masm.jump(&done); |
12155 | |
12156 | if (IsEqualityOp(op)) { |
12157 | masm.bind(¬Same); |
12158 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
12159 | } else { |
12160 | Label invertWhenNegative; |
12161 | |
12162 | // There are two cases when sign(left) != sign(right): |
12163 | // 1. sign(left) = positive and sign(right) = negative, |
12164 | // 2. or the dual case with reversed signs. |
12165 | // |
12166 | // For case 1, |left| <cmp> |right| is true for cmp=Gt or cmp=Ge and false |
12167 | // for cmp=Lt or cmp=Le. Initialize the result for case 1 and handle case 2 |
12168 | // with |invertWhenNegative|. |
12169 | masm.bind(&compareSign); |
12170 | masm.move32(Imm32(op == JSOp::Gt || op == JSOp::Ge), output); |
12171 | masm.jump(&invertWhenNegative); |
12172 | |
12173 | // For sign(left) = sign(right) and len(digits(left)) != len(digits(right)), |
12174 | // we have to consider the two cases: |
12175 | // 1. len(digits(left)) < len(digits(right)) |
12176 | // 2. len(digits(left)) > len(digits(right)) |
12177 | // |
12178 | // For |left| <cmp> |right| with cmp=Lt: |
12179 | // Assume both BigInts are positive, then |left < right| is true for case 1 |
12180 | // and false for case 2. When both are negative, the result is reversed. |
12181 | // |
12182 | // The other comparison operators can be handled similarly. |
12183 | // |
12184 | // |temp0| holds the digits length of the right-hand side operand. |
12185 | masm.bind(&compareLength); |
12186 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), |
12187 | Address(left, BigInt::offsetOfLength()), temp0, output); |
12188 | masm.jump(&invertWhenNegative); |
12189 | |
12190 | // Similar to the case above, compare the current digit to determine the |
12191 | // overall comparison result. |
12192 | // |
12193 | // |temp1| points to the current digit of the left-hand side operand. |
12194 | // |output| holds the current digit of the right-hand side operand. |
12195 | masm.bind(&compareDigit); |
12196 | masm.cmpPtrSet(JSOpToCondition(op, /* isSigned = */ false), |
12197 | Address(temp1, 0), output, output); |
12198 | |
12199 | Label nonNegative; |
12200 | masm.bind(&invertWhenNegative); |
12201 | masm.branchIfBigIntIsNonNegative(left, &nonNegative); |
12202 | masm.xor32(Imm32(1), output); |
12203 | masm.bind(&nonNegative); |
12204 | } |
12205 | |
12206 | masm.bind(&done); |
12207 | } |
12208 | |
12209 | void CodeGenerator::visitCompareBigIntInt32(LCompareBigIntInt32* lir) { |
12210 | JSOp op = lir->mir()->jsop(); |
12211 | Register left = ToRegister(lir->left()); |
12212 | Register right = ToRegister(lir->right()); |
12213 | Register temp0 = ToRegister(lir->temp0()); |
12214 | Register temp1 = ToRegister(lir->temp1()); |
12215 | Register output = ToRegister(lir->output()); |
12216 | |
12217 | Label ifTrue, ifFalse; |
12218 | masm.compareBigIntAndInt32(op, left, right, temp0, temp1, &ifTrue, &ifFalse); |
12219 | |
12220 | Label done; |
12221 | masm.bind(&ifFalse); |
12222 | masm.move32(Imm32(0), output); |
12223 | masm.jump(&done); |
12224 | masm.bind(&ifTrue); |
12225 | masm.move32(Imm32(1), output); |
12226 | masm.bind(&done); |
12227 | } |
12228 | |
12229 | void CodeGenerator::visitCompareBigIntDouble(LCompareBigIntDouble* lir) { |
12230 | JSOp op = lir->mir()->jsop(); |
12231 | Register left = ToRegister(lir->left()); |
12232 | FloatRegister right = ToFloatRegister(lir->right()); |
12233 | Register output = ToRegister(lir->output()); |
12234 | |
12235 | masm.setupAlignedABICall(); |
12236 | |
12237 | // Push the operands in reverse order for JSOp::Le and JSOp::Gt: |
12238 | // - |left <= right| is implemented as |right >= left|. |
12239 | // - |left > right| is implemented as |right < left|. |
12240 | if (op == JSOp::Le || op == JSOp::Gt) { |
12241 | masm.passABIArg(right, ABIType::Float64); |
12242 | masm.passABIArg(left); |
12243 | } else { |
12244 | masm.passABIArg(left); |
12245 | masm.passABIArg(right, ABIType::Float64); |
12246 | } |
12247 | |
12248 | using FnBigIntNumber = bool (*)(BigInt*, double); |
12249 | using FnNumberBigInt = bool (*)(double, BigInt*); |
12250 | switch (op) { |
12251 | case JSOp::Eq: { |
12252 | masm.callWithABI<FnBigIntNumber, |
12253 | jit::BigIntNumberEqual<EqualityKind::Equal>>(); |
12254 | break; |
12255 | } |
12256 | case JSOp::Ne: { |
12257 | masm.callWithABI<FnBigIntNumber, |
12258 | jit::BigIntNumberEqual<EqualityKind::NotEqual>>(); |
12259 | break; |
12260 | } |
12261 | case JSOp::Lt: { |
12262 | masm.callWithABI<FnBigIntNumber, |
12263 | jit::BigIntNumberCompare<ComparisonKind::LessThan>>(); |
12264 | break; |
12265 | } |
12266 | case JSOp::Gt: { |
12267 | masm.callWithABI<FnNumberBigInt, |
12268 | jit::NumberBigIntCompare<ComparisonKind::LessThan>>(); |
12269 | break; |
12270 | } |
12271 | case JSOp::Le: { |
12272 | masm.callWithABI< |
12273 | FnNumberBigInt, |
12274 | jit::NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>>(); |
12275 | break; |
12276 | } |
12277 | case JSOp::Ge: { |
12278 | masm.callWithABI< |
12279 | FnBigIntNumber, |
12280 | jit::BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>>(); |
12281 | break; |
12282 | } |
12283 | default: |
12284 | MOZ_CRASH("unhandled op")do { do { } while (false); MOZ_ReportCrash("" "unhandled op", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12284); AnnotateMozCrashReason("MOZ_CRASH(" "unhandled op" ")" ); do { *((volatile int*)__null) = 12284; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
12285 | } |
12286 | |
12287 | masm.storeCallBoolResult(output); |
12288 | } |
12289 | |
12290 | void CodeGenerator::visitCompareBigIntString(LCompareBigIntString* lir) { |
12291 | JSOp op = lir->mir()->jsop(); |
12292 | Register left = ToRegister(lir->left()); |
12293 | Register right = ToRegister(lir->right()); |
12294 | |
12295 | // Push the operands in reverse order for JSOp::Le and JSOp::Gt: |
12296 | // - |left <= right| is implemented as |right >= left|. |
12297 | // - |left > right| is implemented as |right < left|. |
12298 | if (op == JSOp::Le || op == JSOp::Gt) { |
12299 | pushArg(left); |
12300 | pushArg(right); |
12301 | } else { |
12302 | pushArg(right); |
12303 | pushArg(left); |
12304 | } |
12305 | |
12306 | using FnBigIntString = |
12307 | bool (*)(JSContext*, HandleBigInt, HandleString, bool*); |
12308 | using FnStringBigInt = |
12309 | bool (*)(JSContext*, HandleString, HandleBigInt, bool*); |
12310 | |
12311 | switch (op) { |
12312 | case JSOp::Eq: { |
12313 | constexpr auto Equal = EqualityKind::Equal; |
12314 | callVM<FnBigIntString, BigIntStringEqual<Equal>>(lir); |
12315 | break; |
12316 | } |
12317 | case JSOp::Ne: { |
12318 | constexpr auto NotEqual = EqualityKind::NotEqual; |
12319 | callVM<FnBigIntString, BigIntStringEqual<NotEqual>>(lir); |
12320 | break; |
12321 | } |
12322 | case JSOp::Lt: { |
12323 | constexpr auto LessThan = ComparisonKind::LessThan; |
12324 | callVM<FnBigIntString, BigIntStringCompare<LessThan>>(lir); |
12325 | break; |
12326 | } |
12327 | case JSOp::Gt: { |
12328 | constexpr auto LessThan = ComparisonKind::LessThan; |
12329 | callVM<FnStringBigInt, StringBigIntCompare<LessThan>>(lir); |
12330 | break; |
12331 | } |
12332 | case JSOp::Le: { |
12333 | constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual; |
12334 | callVM<FnStringBigInt, StringBigIntCompare<GreaterThanOrEqual>>(lir); |
12335 | break; |
12336 | } |
12337 | case JSOp::Ge: { |
12338 | constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual; |
12339 | callVM<FnBigIntString, BigIntStringCompare<GreaterThanOrEqual>>(lir); |
12340 | break; |
12341 | } |
12342 | default: |
12343 | MOZ_CRASH("Unexpected compare op")do { do { } while (false); MOZ_ReportCrash("" "Unexpected compare op" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12343); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected compare op" ")"); do { *((volatile int*)__null) = 12343; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
12344 | } |
12345 | } |
12346 | |
12347 | void CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir) { |
12348 | MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12349; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
12349 | lir->mir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12349); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12349; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12350 | |
12351 | JSOp op = lir->mir()->jsop(); |
12352 | MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12352); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12352; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12353 | |
12354 | const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedV::ValueIndex); |
12355 | Register output = ToRegister(lir->output()); |
12356 | |
12357 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
12358 | if (!intact) { |
12359 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
12360 | addOutOfLineCode(ool, lir->mir()); |
12361 | |
12362 | Label* nullOrLikeUndefined = ool->label1(); |
12363 | Label* notNullOrLikeUndefined = ool->label2(); |
12364 | |
12365 | { |
12366 | ScratchTagScope tag(masm, value); |
12367 | masm.splitTagForTest(value, tag); |
12368 | |
12369 | masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined); |
12370 | masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined); |
12371 | |
12372 | // Check whether it's a truthy object or a falsy object that emulates |
12373 | // undefined. |
12374 | masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined); |
12375 | } |
12376 | |
12377 | Register objreg = |
12378 | masm.extractObject(value, ToTempUnboxRegister(lir->temp0())); |
12379 | branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, |
12380 | notNullOrLikeUndefined, output, ool); |
12381 | // fall through |
12382 | |
12383 | Label done; |
12384 | |
12385 | // It's not null or undefined, and if it's an object it doesn't |
12386 | // emulate undefined, so it's not like undefined. |
12387 | masm.move32(Imm32(op == JSOp::Ne), output); |
12388 | masm.jump(&done); |
12389 | |
12390 | masm.bind(nullOrLikeUndefined); |
12391 | masm.move32(Imm32(op == JSOp::Eq), output); |
12392 | |
12393 | // Both branches meet here. |
12394 | masm.bind(&done); |
12395 | } else { |
12396 | Label nullOrUndefined, notNullOrLikeUndefined; |
12397 | #if defined(DEBUG1) || defined(FUZZING) |
12398 | Register objreg = Register::Invalid(); |
12399 | #endif |
12400 | { |
12401 | ScratchTagScope tag(masm, value); |
12402 | masm.splitTagForTest(value, tag); |
12403 | |
12404 | masm.branchTestNull(Assembler::Equal, tag, &nullOrUndefined); |
12405 | masm.branchTestUndefined(Assembler::Equal, tag, &nullOrUndefined); |
12406 | |
12407 | #if defined(DEBUG1) || defined(FUZZING) |
12408 | // Check whether it's a truthy object or a falsy object that emulates |
12409 | // undefined. |
12410 | masm.branchTestObject(Assembler::NotEqual, tag, ¬NullOrLikeUndefined); |
12411 | objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp0())); |
12412 | #endif |
12413 | } |
12414 | |
12415 | #if defined(DEBUG1) || defined(FUZZING) |
12416 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
12417 | masm.bind(¬NullOrLikeUndefined); |
12418 | #endif |
12419 | |
12420 | Label done; |
12421 | |
12422 | // It's not null or undefined, and if it's an object it doesn't |
12423 | // emulate undefined. |
12424 | masm.move32(Imm32(op == JSOp::Ne), output); |
12425 | masm.jump(&done); |
12426 | |
12427 | masm.bind(&nullOrUndefined); |
12428 | masm.move32(Imm32(op == JSOp::Eq), output); |
12429 | |
12430 | // Both branches meet here. |
12431 | masm.bind(&done); |
12432 | } |
12433 | } |
12434 | |
12435 | void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV( |
12436 | LIsNullOrLikeUndefinedAndBranchV* lir) { |
12437 | MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12438); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12438; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
12438 | lir->cmpMir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12438); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12438; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12439 | |
12440 | JSOp op = lir->cmpMir()->jsop(); |
12441 | MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12441); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12441; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12442 | |
12443 | const ValueOperand value = |
12444 | ToValue(lir, LIsNullOrLikeUndefinedAndBranchV::Value); |
12445 | |
12446 | MBasicBlock* ifTrue = lir->ifTrue(); |
12447 | MBasicBlock* ifFalse = lir->ifFalse(); |
12448 | |
12449 | if (op == JSOp::Ne) { |
12450 | // Swap branches. |
12451 | std::swap(ifTrue, ifFalse); |
12452 | } |
12453 | |
12454 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
12455 | |
12456 | Label* ifTrueLabel = getJumpLabelForBranch(ifTrue); |
12457 | Label* ifFalseLabel = getJumpLabelForBranch(ifFalse); |
12458 | |
12459 | { |
12460 | ScratchTagScope tag(masm, value); |
12461 | masm.splitTagForTest(value, tag); |
12462 | |
12463 | masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel); |
12464 | masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel); |
12465 | |
12466 | masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel); |
12467 | } |
12468 | |
12469 | bool extractObject = !intact; |
Value stored to 'extractObject' during its initialization is never read | |
12470 | #if defined(DEBUG1) || defined(FUZZING) |
12471 | // always extract objreg if we're in debug and |
12472 | // assertObjectDoesNotEmulateUndefined; |
12473 | extractObject = true; |
12474 | #endif |
12475 | |
12476 | Register objreg = Register::Invalid(); |
12477 | Register scratch = ToRegister(lir->temp()); |
12478 | if (extractObject) { |
12479 | objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox())); |
12480 | } |
12481 | if (!intact) { |
12482 | // Objects that emulate undefined are loosely equal to null/undefined. |
12483 | OutOfLineTestObject* ool = new (alloc()) OutOfLineTestObject(); |
12484 | addOutOfLineCode(ool, lir->cmpMir()); |
12485 | testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch, |
12486 | ool); |
12487 | } else { |
12488 | assertObjectDoesNotEmulateUndefined(objreg, scratch, lir->cmpMir()); |
12489 | // Bug 1874905. This would be nice to optimize out at the MIR level. |
12490 | masm.jump(ifFalseLabel); |
12491 | } |
12492 | } |
12493 | |
12494 | void CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir) { |
12495 | MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12496; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
12496 | lir->mir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12496; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12497 | MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->lhs()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->lhs()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->lhs()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12497); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->lhs()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 12497; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12498 | |
12499 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
12500 | JSOp op = lir->mir()->jsop(); |
12501 | Register output = ToRegister(lir->output()); |
12502 | Register objreg = ToRegister(lir->input()); |
12503 | if (!intact) { |
12504 | MOZ_ASSERT(IsLooseEqualityOp(op),do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12505; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
12505 | "Strict equality should have been folded")do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12505; __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
12506 | |
12507 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
12508 | addOutOfLineCode(ool, lir->mir()); |
12509 | |
12510 | Label* emulatesUndefined = ool->label1(); |
12511 | Label* doesntEmulateUndefined = ool->label2(); |
12512 | |
12513 | branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, |
12514 | doesntEmulateUndefined, output, ool); |
12515 | |
12516 | Label done; |
12517 | |
12518 | masm.move32(Imm32(op == JSOp::Ne), output); |
12519 | masm.jump(&done); |
12520 | |
12521 | masm.bind(emulatesUndefined); |
12522 | masm.move32(Imm32(op == JSOp::Eq), output); |
12523 | masm.bind(&done); |
12524 | } else { |
12525 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
12526 | masm.move32(Imm32(op == JSOp::Ne), output); |
12527 | } |
12528 | } |
12529 | |
12530 | void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT( |
12531 | LIsNullOrLikeUndefinedAndBranchT* lir) { |
12532 | MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12533; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
12533 | lir->cmpMir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12533; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12534 | MOZ_ASSERT(lir->cmpMir()->lhs()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->lhs()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->lhs()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->lhs()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12534); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->lhs()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 12534; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12535 | |
12536 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
12537 | |
12538 | JSOp op = lir->cmpMir()->jsop(); |
12539 | MOZ_ASSERT(IsLooseEqualityOp(op), "Strict equality should have been folded")do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12539); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12539; __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
12540 | |
12541 | MBasicBlock* ifTrue = lir->ifTrue(); |
12542 | MBasicBlock* ifFalse = lir->ifFalse(); |
12543 | |
12544 | if (op == JSOp::Ne) { |
12545 | // Swap branches. |
12546 | std::swap(ifTrue, ifFalse); |
12547 | } |
12548 | |
12549 | Register input = ToRegister(lir->getOperand(0)); |
12550 | Register scratch = ToRegister(lir->temp()); |
12551 | Label* ifTrueLabel = getJumpLabelForBranch(ifTrue); |
12552 | Label* ifFalseLabel = getJumpLabelForBranch(ifFalse); |
12553 | |
12554 | if (intact) { |
12555 | // Bug 1874905. Ideally branches like this would be optimized out. |
12556 | assertObjectDoesNotEmulateUndefined(input, scratch, lir->mir()); |
12557 | masm.jump(ifFalseLabel); |
12558 | } else { |
12559 | auto* ool = new (alloc()) OutOfLineTestObject(); |
12560 | addOutOfLineCode(ool, lir->cmpMir()); |
12561 | |
12562 | // Objects that emulate undefined are loosely equal to null/undefined. |
12563 | testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool); |
12564 | } |
12565 | } |
12566 | |
12567 | void CodeGenerator::visitIsNull(LIsNull* lir) { |
12568 | MCompare::CompareType compareType = lir->mir()->compareType(); |
12569 | MOZ_ASSERT(compareType == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Null)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(compareType == MCompare::Compare_Null))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12569); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12569; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12570 | |
12571 | JSOp op = lir->mir()->jsop(); |
12572 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12572; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12573 | |
12574 | const ValueOperand value = ToValue(lir, LIsNull::ValueIndex); |
12575 | Register output = ToRegister(lir->output()); |
12576 | |
12577 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
12578 | masm.testNullSet(cond, value, output); |
12579 | } |
12580 | |
12581 | void CodeGenerator::visitIsUndefined(LIsUndefined* lir) { |
12582 | MCompare::CompareType compareType = lir->mir()->compareType(); |
12583 | MOZ_ASSERT(compareType == MCompare::Compare_Undefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Undefined)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(compareType == MCompare::Compare_Undefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Undefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12583); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined" ")"); do { *((volatile int*)__null) = 12583; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12584 | |
12585 | JSOp op = lir->mir()->jsop(); |
12586 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12586; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12587 | |
12588 | const ValueOperand value = ToValue(lir, LIsUndefined::ValueIndex); |
12589 | Register output = ToRegister(lir->output()); |
12590 | |
12591 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
12592 | masm.testUndefinedSet(cond, value, output); |
12593 | } |
12594 | |
12595 | void CodeGenerator::visitIsNullAndBranch(LIsNullAndBranch* lir) { |
12596 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
12597 | MOZ_ASSERT(compareType == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Null)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(compareType == MCompare::Compare_Null))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12597); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12597; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12598 | |
12599 | JSOp op = lir->cmpMir()->jsop(); |
12600 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12600); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12600; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12601 | |
12602 | const ValueOperand value = ToValue(lir, LIsNullAndBranch::Value); |
12603 | |
12604 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
12605 | testNullEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse()); |
12606 | } |
12607 | |
12608 | void CodeGenerator::visitIsUndefinedAndBranch(LIsUndefinedAndBranch* lir) { |
12609 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
12610 | MOZ_ASSERT(compareType == MCompare::Compare_Undefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Undefined)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(compareType == MCompare::Compare_Undefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Undefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12610); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined" ")"); do { *((volatile int*)__null) = 12610; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12611 | |
12612 | JSOp op = lir->cmpMir()->jsop(); |
12613 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12613); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12613; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12614 | |
12615 | const ValueOperand value = ToValue(lir, LIsUndefinedAndBranch::Value); |
12616 | |
12617 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
12618 | testUndefinedEmitBranch(cond, value, lir->ifTrue(), lir->ifFalse()); |
12619 | } |
12620 | |
12621 | void CodeGenerator::visitSameValueDouble(LSameValueDouble* lir) { |
12622 | FloatRegister left = ToFloatRegister(lir->left()); |
12623 | FloatRegister right = ToFloatRegister(lir->right()); |
12624 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
12625 | Register output = ToRegister(lir->output()); |
12626 | |
12627 | masm.sameValueDouble(left, right, temp, output); |
12628 | } |
12629 | |
12630 | void CodeGenerator::visitSameValue(LSameValue* lir) { |
12631 | ValueOperand lhs = ToValue(lir, LSameValue::LhsIndex); |
12632 | ValueOperand rhs = ToValue(lir, LSameValue::RhsIndex); |
12633 | Register output = ToRegister(lir->output()); |
12634 | |
12635 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*); |
12636 | OutOfLineCode* ool = |
12637 | oolCallVM<Fn, SameValue>(lir, ArgList(lhs, rhs), StoreRegisterTo(output)); |
12638 | |
12639 | // First check to see if the values have identical bits. |
12640 | // This is correct for SameValue because SameValue(NaN,NaN) is true, |
12641 | // and SameValue(0,-0) is false. |
12642 | masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(), |
12643 | ool->entry()); |
12644 | masm.move32(Imm32(1), output); |
12645 | |
12646 | // If this fails, call SameValue. |
12647 | masm.bind(ool->rejoin()); |
12648 | } |
12649 | |
12650 | void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs, |
12651 | Register output) { |
12652 | using Fn = |
12653 | JSString* (*)(JSContext*, HandleString, HandleString, js::gc::Heap); |
12654 | OutOfLineCode* ool = oolCallVM<Fn, ConcatStrings<CanGC>>( |
12655 | lir, ArgList(lhs, rhs, static_cast<Imm32>(int32_t(gc::Heap::Default))), |
12656 | StoreRegisterTo(output)); |
12657 | |
12658 | const JitZone* jitZone = gen->realm->zone()->jitZone(); |
12659 | JitCode* stringConcatStub = |
12660 | jitZone->stringConcatStubNoBarrier(&zoneStubsToReadBarrier_); |
12661 | masm.call(stringConcatStub); |
12662 | masm.branchTestPtr(Assembler::Zero, output, output, ool->entry()); |
12663 | |
12664 | masm.bind(ool->rejoin()); |
12665 | } |
12666 | |
12667 | void CodeGenerator::visitConcat(LConcat* lir) { |
12668 | Register lhs = ToRegister(lir->lhs()); |
12669 | Register rhs = ToRegister(lir->rhs()); |
12670 | |
12671 | Register output = ToRegister(lir->output()); |
12672 | |
12673 | MOZ_ASSERT(lhs == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lhs == CallTempReg0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lhs == CallTempReg0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lhs == CallTempReg0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12673); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs == CallTempReg0" ")"); do { *((volatile int*)__null) = 12673; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12674 | MOZ_ASSERT(rhs == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(rhs == CallTempReg1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(rhs == CallTempReg1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("rhs == CallTempReg1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs == CallTempReg1" ")"); do { *((volatile int*)__null) = 12674; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12675 | MOZ_ASSERT(ToRegister(lir->temp0()) == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp0()) == CallTempReg0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp0()) == CallTempReg0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp0()) == CallTempReg0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12675); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp0()) == CallTempReg0" ")"); do { *((volatile int*)__null) = 12675; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12676 | MOZ_ASSERT(ToRegister(lir->temp1()) == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp1()) == CallTempReg1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp1()) == CallTempReg1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp1()) == CallTempReg1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12676); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp1()) == CallTempReg1" ")"); do { *((volatile int*)__null) = 12676; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12677 | MOZ_ASSERT(ToRegister(lir->temp2()) == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp2()) == CallTempReg2)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp2()) == CallTempReg2))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp2()) == CallTempReg2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp2()) == CallTempReg2" ")"); do { *((volatile int*)__null) = 12677; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12678 | MOZ_ASSERT(ToRegister(lir->temp3()) == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp3()) == CallTempReg3)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp3()) == CallTempReg3))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp3()) == CallTempReg3" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12678); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp3()) == CallTempReg3" ")"); do { *((volatile int*)__null) = 12678; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12679 | MOZ_ASSERT(ToRegister(lir->temp4()) == CallTempReg4)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp4()) == CallTempReg4)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp4()) == CallTempReg4))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp4()) == CallTempReg4" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12679); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp4()) == CallTempReg4" ")"); do { *((volatile int*)__null) = 12679; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12680 | MOZ_ASSERT(output == CallTempReg5)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output == CallTempReg5)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output == CallTempReg5))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("output == CallTempReg5" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12680); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == CallTempReg5" ")"); do { *((volatile int*)__null) = 12680; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12681 | |
12682 | emitConcat(lir, lhs, rhs, output); |
12683 | } |
12684 | |
12685 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
12686 | Register len, Register byteOpScratch, |
12687 | CharEncoding fromEncoding, CharEncoding toEncoding, |
12688 | size_t maximumLength = SIZE_MAX(18446744073709551615UL)) { |
12689 | // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0 |
12690 | // (checked below in debug builds), and when done |to| must point to the |
12691 | // next available char. |
12692 | |
12693 | #ifdef DEBUG1 |
12694 | Label ok; |
12695 | masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); |
12696 | masm.assumeUnreachable("Length should be greater than 0."); |
12697 | masm.bind(&ok); |
12698 | |
12699 | if (maximumLength != SIZE_MAX(18446744073709551615UL)) { |
12700 | MOZ_ASSERT(maximumLength <= INT32_MAX, "maximum length fits into int32")do { static_assert( mozilla::detail::AssertionConditionType< decltype(maximumLength <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(maximumLength <= (2147483647 )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("maximumLength <= (2147483647)" " (" "maximum length fits into int32" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12700); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maximumLength <= (2147483647)" ") (" "maximum length fits into int32" ")"); do { *((volatile int*)__null) = 12700; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
12701 | |
12702 | Label ok; |
12703 | masm.branchPtr(Assembler::BelowOrEqual, len, Imm32(maximumLength), &ok); |
12704 | masm.assumeUnreachable("Length should not exceed maximum length."); |
12705 | masm.bind(&ok); |
12706 | } |
12707 | #endif |
12708 | |
12709 | MOZ_ASSERT_IF(toEncoding == CharEncoding::Latin1,do { if (toEncoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(fromEncoding == CharEncoding::Latin1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fromEncoding == CharEncoding ::Latin1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("fromEncoding == CharEncoding::Latin1", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1" ")"); do { *((volatile int*)__null) = 12710; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
12710 | fromEncoding == CharEncoding::Latin1)do { if (toEncoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(fromEncoding == CharEncoding::Latin1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fromEncoding == CharEncoding ::Latin1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("fromEncoding == CharEncoding::Latin1", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1" ")"); do { *((volatile int*)__null) = 12710; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
12711 | |
12712 | size_t fromWidth = |
12713 | fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); |
12714 | size_t toWidth = |
12715 | toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); |
12716 | |
12717 | // Try to copy multiple characters at once when both encoding are equal. |
12718 | if (fromEncoding == toEncoding) { |
12719 | constexpr size_t ptrWidth = sizeof(uintptr_t); |
12720 | |
12721 | // Copy |width| bytes and then adjust |from| and |to|. |
12722 | auto copyCharacters = [&](size_t width) { |
12723 | static_assert(ptrWidth <= 8, "switch handles only up to eight bytes"); |
12724 | |
12725 | switch (width) { |
12726 | case 1: |
12727 | masm.load8ZeroExtend(Address(from, 0), byteOpScratch); |
12728 | masm.store8(byteOpScratch, Address(to, 0)); |
12729 | break; |
12730 | case 2: |
12731 | masm.load16ZeroExtend(Address(from, 0), byteOpScratch); |
12732 | masm.store16(byteOpScratch, Address(to, 0)); |
12733 | break; |
12734 | case 4: |
12735 | masm.load32(Address(from, 0), byteOpScratch); |
12736 | masm.store32(byteOpScratch, Address(to, 0)); |
12737 | break; |
12738 | case 8: |
12739 | MOZ_ASSERT(width == ptrWidth)do { static_assert( mozilla::detail::AssertionConditionType< decltype(width == ptrWidth)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(width == ptrWidth))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("width == ptrWidth" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12739); AnnotateMozCrashReason("MOZ_ASSERT" "(" "width == ptrWidth" ")"); do { *((volatile int*)__null) = 12739; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12740 | masm.loadPtr(Address(from, 0), byteOpScratch); |
12741 | masm.storePtr(byteOpScratch, Address(to, 0)); |
12742 | break; |
12743 | } |
12744 | |
12745 | masm.addPtr(Imm32(width), from); |
12746 | masm.addPtr(Imm32(width), to); |
12747 | }; |
12748 | |
12749 | // First align |len| to pointer width. |
12750 | Label done; |
12751 | for (size_t width = fromWidth; width < ptrWidth; width *= 2) { |
12752 | // Number of characters which fit into |width| bytes. |
12753 | size_t charsPerWidth = width / fromWidth; |
12754 | |
12755 | if (charsPerWidth < maximumLength) { |
12756 | Label next; |
12757 | masm.branchTest32(Assembler::Zero, len, Imm32(charsPerWidth), &next); |
12758 | |
12759 | copyCharacters(width); |
12760 | |
12761 | masm.branchSub32(Assembler::Zero, Imm32(charsPerWidth), len, &done); |
12762 | masm.bind(&next); |
12763 | } else if (charsPerWidth == maximumLength) { |
12764 | copyCharacters(width); |
12765 | masm.sub32(Imm32(charsPerWidth), len); |
12766 | } |
12767 | } |
12768 | |
12769 | size_t maxInlineLength; |
12770 | if (fromEncoding == CharEncoding::Latin1) { |
12771 | maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; |
12772 | } else { |
12773 | maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
12774 | } |
12775 | |
12776 | // Number of characters which fit into a single register. |
12777 | size_t charsPerPtr = ptrWidth / fromWidth; |
12778 | |
12779 | // Unroll small loops. |
12780 | constexpr size_t unrollLoopLimit = 3; |
12781 | size_t loopCount = std::min(maxInlineLength, maximumLength) / charsPerPtr; |
12782 | |
12783 | #ifdef JS_64BIT1 |
12784 | static constexpr size_t latin1MaxInlineByteLength = |
12785 | JSFatInlineString::MAX_LENGTH_LATIN1 * sizeof(char); |
12786 | static constexpr size_t twoByteMaxInlineByteLength = |
12787 | JSFatInlineString::MAX_LENGTH_TWO_BYTE * sizeof(char16_t); |
12788 | |
12789 | // |unrollLoopLimit| should be large enough to allow loop unrolling on |
12790 | // 64-bit targets. |
12791 | static_assert(latin1MaxInlineByteLength / ptrWidth == unrollLoopLimit, |
12792 | "Latin-1 loops are unrolled on 64-bit"); |
12793 | static_assert(twoByteMaxInlineByteLength / ptrWidth == unrollLoopLimit, |
12794 | "Two-byte loops are unrolled on 64-bit"); |
12795 | #endif |
12796 | |
12797 | if (loopCount <= unrollLoopLimit) { |
12798 | Label labels[unrollLoopLimit]; |
12799 | |
12800 | // Check up front how many characters can be copied. |
12801 | for (size_t i = 1; i < loopCount; i++) { |
12802 | masm.branch32(Assembler::Below, len, Imm32((i + 1) * charsPerPtr), |
12803 | &labels[i]); |
12804 | } |
12805 | |
12806 | // Generate the unrolled loop body. |
12807 | for (size_t i = loopCount; i > 0; i--) { |
12808 | copyCharacters(ptrWidth); |
12809 | masm.sub32(Imm32(charsPerPtr), len); |
12810 | |
12811 | // Jump target for the previous length check. |
12812 | if (i != 1) { |
12813 | masm.bind(&labels[i - 1]); |
12814 | } |
12815 | } |
12816 | } else { |
12817 | Label start; |
12818 | masm.bind(&start); |
12819 | copyCharacters(ptrWidth); |
12820 | masm.branchSub32(Assembler::NonZero, Imm32(charsPerPtr), len, &start); |
12821 | } |
12822 | |
12823 | masm.bind(&done); |
12824 | } else { |
12825 | Label start; |
12826 | masm.bind(&start); |
12827 | masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding); |
12828 | masm.storeChar(byteOpScratch, Address(to, 0), toEncoding); |
12829 | masm.addPtr(Imm32(fromWidth), from); |
12830 | masm.addPtr(Imm32(toWidth), to); |
12831 | masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start); |
12832 | } |
12833 | } |
12834 | |
12835 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
12836 | Register len, Register byteOpScratch, |
12837 | CharEncoding encoding, size_t maximumLength) { |
12838 | CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding, |
12839 | maximumLength); |
12840 | } |
12841 | |
12842 | static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, |
12843 | Register destChars, Register temp1, |
12844 | Register temp2) { |
12845 | // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may |
12846 | // have to inflate. |
12847 | |
12848 | Label isLatin1, done; |
12849 | masm.loadStringLength(input, temp1); |
12850 | masm.branchLatin1String(input, &isLatin1); |
12851 | { |
12852 | masm.loadStringChars(input, temp2, CharEncoding::TwoByte); |
12853 | masm.movePtr(temp2, input); |
12854 | CopyStringChars(masm, destChars, input, temp1, temp2, |
12855 | CharEncoding::TwoByte); |
12856 | masm.jump(&done); |
12857 | } |
12858 | masm.bind(&isLatin1); |
12859 | { |
12860 | masm.loadStringChars(input, temp2, CharEncoding::Latin1); |
12861 | masm.movePtr(temp2, input); |
12862 | CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1, |
12863 | CharEncoding::TwoByte); |
12864 | } |
12865 | masm.bind(&done); |
12866 | } |
12867 | |
12868 | static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output, |
12869 | Register length, Register temp, |
12870 | gc::Heap initialStringHeap, |
12871 | Label* failure, |
12872 | CharEncoding encoding) { |
12873 | #ifdef DEBUG1 |
12874 | size_t maxInlineLength; |
12875 | if (encoding == CharEncoding::Latin1) { |
12876 | maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; |
12877 | } else { |
12878 | maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
12879 | } |
12880 | |
12881 | Label ok; |
12882 | masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok); |
12883 | masm.assumeUnreachable("string length too large to be allocated as inline"); |
12884 | masm.bind(&ok); |
12885 | #endif |
12886 | |
12887 | size_t maxThinInlineLength; |
12888 | if (encoding == CharEncoding::Latin1) { |
12889 | maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1; |
12890 | } else { |
12891 | maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
12892 | } |
12893 | |
12894 | Label isFat, allocDone; |
12895 | masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat); |
12896 | { |
12897 | uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS; |
12898 | if (encoding == CharEncoding::Latin1) { |
12899 | flags |= JSString::LATIN1_CHARS_BIT; |
12900 | } |
12901 | masm.newGCString(output, temp, initialStringHeap, failure); |
12902 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
12903 | masm.jump(&allocDone); |
12904 | } |
12905 | masm.bind(&isFat); |
12906 | { |
12907 | uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS; |
12908 | if (encoding == CharEncoding::Latin1) { |
12909 | flags |= JSString::LATIN1_CHARS_BIT; |
12910 | } |
12911 | masm.newGCFatInlineString(output, temp, initialStringHeap, failure); |
12912 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
12913 | } |
12914 | masm.bind(&allocDone); |
12915 | |
12916 | // Store length. |
12917 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
12918 | } |
12919 | |
12920 | static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, |
12921 | Register output, Register temp1, Register temp2, |
12922 | Register temp3, gc::Heap initialStringHeap, |
12923 | Label* failure, CharEncoding encoding) { |
12924 | JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)", |
12925 | (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
12926 | |
12927 | // State: result length in temp2. |
12928 | |
12929 | // Ensure both strings are linear. |
12930 | masm.branchIfRope(lhs, failure); |
12931 | masm.branchIfRope(rhs, failure); |
12932 | |
12933 | // Allocate a JSThinInlineString or JSFatInlineString. |
12934 | AllocateThinOrFatInlineString(masm, output, temp2, temp1, initialStringHeap, |
12935 | failure, encoding); |
12936 | |
12937 | // Load chars pointer in temp2. |
12938 | masm.loadInlineStringCharsForStore(output, temp2); |
12939 | |
12940 | auto copyChars = [&](Register src) { |
12941 | if (encoding == CharEncoding::TwoByte) { |
12942 | CopyStringCharsMaybeInflate(masm, src, temp2, temp1, temp3); |
12943 | } else { |
12944 | masm.loadStringLength(src, temp3); |
12945 | masm.loadStringChars(src, temp1, CharEncoding::Latin1); |
12946 | masm.movePtr(temp1, src); |
12947 | CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1); |
12948 | } |
12949 | }; |
12950 | |
12951 | // Copy lhs chars. Note that this advances temp2 to point to the next |
12952 | // char. This also clobbers the lhs register. |
12953 | copyChars(lhs); |
12954 | |
12955 | // Copy rhs chars. Clobbers the rhs register. |
12956 | copyChars(rhs); |
12957 | } |
12958 | |
12959 | void CodeGenerator::visitSubstr(LSubstr* lir) { |
12960 | Register string = ToRegister(lir->string()); |
12961 | Register begin = ToRegister(lir->begin()); |
12962 | Register length = ToRegister(lir->length()); |
12963 | Register output = ToRegister(lir->output()); |
12964 | Register temp0 = ToRegister(lir->temp0()); |
12965 | Register temp2 = ToRegister(lir->temp2()); |
12966 | |
12967 | // On x86 there are not enough registers. In that case reuse the string |
12968 | // register as temporary. |
12969 | Register temp1 = |
12970 | lir->temp1()->isBogusTemp() ? string : ToRegister(lir->temp1()); |
12971 | |
12972 | size_t maximumLength = SIZE_MAX(18446744073709551615UL); |
12973 | |
12974 | Range* range = lir->mir()->length()->range(); |
12975 | if (range && range->hasInt32UpperBound()) { |
12976 | MOZ_ASSERT(range->upper() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(range->upper() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(range->upper() >= 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("range->upper() >= 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "range->upper() >= 0" ")"); do { *((volatile int*)__null) = 12976; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
12977 | maximumLength = size_t(range->upper()); |
12978 | } |
12979 | |
12980 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <= |
12981 | JSThinInlineString::MAX_LENGTH_LATIN1); |
12982 | |
12983 | static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE <= |
12984 | JSFatInlineString::MAX_LENGTH_LATIN1); |
12985 | |
12986 | bool tryFatInlineOrDependent = |
12987 | maximumLength > JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
12988 | bool tryDependent = maximumLength > JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
12989 | |
12990 | #ifdef DEBUG1 |
12991 | if (maximumLength != SIZE_MAX(18446744073709551615UL)) { |
12992 | Label ok; |
12993 | masm.branch32(Assembler::BelowOrEqual, length, Imm32(maximumLength), &ok); |
12994 | masm.assumeUnreachable("length should not exceed maximum length"); |
12995 | masm.bind(&ok); |
12996 | } |
12997 | #endif |
12998 | |
12999 | Label nonZero, nonInput; |
13000 | |
13001 | // For every edge case use the C++ variant. |
13002 | // Note: we also use this upon allocation failure in newGCString and |
13003 | // newGCFatInlineString. To squeeze out even more performance those failures |
13004 | // can be handled by allocate in ool code and returning to jit code to fill |
13005 | // in all data. |
13006 | using Fn = JSString* (*)(JSContext* cx, HandleString str, int32_t begin, |
13007 | int32_t len); |
13008 | OutOfLineCode* ool = oolCallVM<Fn, SubstringKernel>( |
13009 | lir, ArgList(string, begin, length), StoreRegisterTo(output)); |
13010 | Label* slowPath = ool->entry(); |
13011 | Label* done = ool->rejoin(); |
13012 | |
13013 | // Zero length, return emptystring. |
13014 | masm.branchTest32(Assembler::NonZero, length, length, &nonZero); |
13015 | const JSAtomState& names = gen->runtime->names(); |
13016 | masm.movePtr(ImmGCPtr(names.empty_), output); |
13017 | masm.jump(done); |
13018 | |
13019 | // Substring from 0..|str.length|, return str. |
13020 | masm.bind(&nonZero); |
13021 | masm.branch32(Assembler::NotEqual, |
13022 | Address(string, JSString::offsetOfLength()), length, &nonInput); |
13023 | #ifdef DEBUG1 |
13024 | { |
13025 | Label ok; |
13026 | masm.branchTest32(Assembler::Zero, begin, begin, &ok); |
13027 | masm.assumeUnreachable("length == str.length implies begin == 0"); |
13028 | masm.bind(&ok); |
13029 | } |
13030 | #endif |
13031 | masm.movePtr(string, output); |
13032 | masm.jump(done); |
13033 | |
13034 | // Use slow path for ropes. |
13035 | masm.bind(&nonInput); |
13036 | masm.branchIfRope(string, slowPath); |
13037 | |
13038 | // Optimize one and two character strings. |
13039 | Label nonStatic; |
13040 | masm.branch32(Assembler::Above, length, Imm32(2), &nonStatic); |
13041 | { |
13042 | Label loadLengthOne, loadLengthTwo; |
13043 | |
13044 | auto loadChars = [&](CharEncoding encoding, bool fallthru) { |
13045 | size_t size = encoding == CharEncoding::Latin1 ? sizeof(JS::Latin1Char) |
13046 | : sizeof(char16_t); |
13047 | |
13048 | masm.loadStringChars(string, temp0, encoding); |
13049 | masm.loadChar(temp0, begin, temp2, encoding); |
13050 | masm.branch32(Assembler::Equal, length, Imm32(1), &loadLengthOne); |
13051 | masm.loadChar(temp0, begin, temp0, encoding, int32_t(size)); |
13052 | if (!fallthru) { |
13053 | masm.jump(&loadLengthTwo); |
13054 | } |
13055 | }; |
13056 | |
13057 | Label isLatin1; |
13058 | masm.branchLatin1String(string, &isLatin1); |
13059 | loadChars(CharEncoding::TwoByte, /* fallthru = */ false); |
13060 | |
13061 | masm.bind(&isLatin1); |
13062 | loadChars(CharEncoding::Latin1, /* fallthru = */ true); |
13063 | |
13064 | // Try to load a length-two static string. |
13065 | masm.bind(&loadLengthTwo); |
13066 | masm.lookupStaticString(temp2, temp0, output, gen->runtime->staticStrings(), |
13067 | &nonStatic); |
13068 | masm.jump(done); |
13069 | |
13070 | // Try to load a length-one static string. |
13071 | masm.bind(&loadLengthOne); |
13072 | masm.lookupStaticString(temp2, output, gen->runtime->staticStrings(), |
13073 | &nonStatic); |
13074 | masm.jump(done); |
13075 | } |
13076 | masm.bind(&nonStatic); |
13077 | |
13078 | // Allocate either a JSThinInlineString or JSFatInlineString, or jump to |
13079 | // notInline if we need a dependent string. |
13080 | Label notInline; |
13081 | { |
13082 | static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 < |
13083 | JSFatInlineString::MAX_LENGTH_LATIN1); |
13084 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE < |
13085 | JSFatInlineString::MAX_LENGTH_TWO_BYTE); |
13086 | |
13087 | // Use temp2 to store the JS(Thin|Fat)InlineString flags. This avoids having |
13088 | // duplicate newGCString/newGCFatInlineString codegen for Latin1 vs TwoByte |
13089 | // strings. |
13090 | |
13091 | Label allocFat, allocDone; |
13092 | if (tryFatInlineOrDependent) { |
13093 | Label isLatin1, allocThin; |
13094 | masm.branchLatin1String(string, &isLatin1); |
13095 | { |
13096 | if (tryDependent) { |
13097 | masm.branch32(Assembler::Above, length, |
13098 | Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), |
13099 | ¬Inline); |
13100 | } |
13101 | masm.move32(Imm32(0), temp2); |
13102 | masm.branch32(Assembler::Above, length, |
13103 | Imm32(JSThinInlineString::MAX_LENGTH_TWO_BYTE), |
13104 | &allocFat); |
13105 | masm.jump(&allocThin); |
13106 | } |
13107 | |
13108 | masm.bind(&isLatin1); |
13109 | { |
13110 | if (tryDependent) { |
13111 | masm.branch32(Assembler::Above, length, |
13112 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), |
13113 | ¬Inline); |
13114 | } |
13115 | masm.move32(Imm32(JSString::LATIN1_CHARS_BIT), temp2); |
13116 | masm.branch32(Assembler::Above, length, |
13117 | Imm32(JSThinInlineString::MAX_LENGTH_LATIN1), &allocFat); |
13118 | } |
13119 | |
13120 | masm.bind(&allocThin); |
13121 | } else { |
13122 | masm.load32(Address(string, JSString::offsetOfFlags()), temp2); |
13123 | masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp2); |
13124 | } |
13125 | |
13126 | { |
13127 | masm.newGCString(output, temp0, initialStringHeap(), slowPath); |
13128 | masm.or32(Imm32(JSString::INIT_THIN_INLINE_FLAGS), temp2); |
13129 | } |
13130 | |
13131 | if (tryFatInlineOrDependent) { |
13132 | masm.jump(&allocDone); |
13133 | |
13134 | masm.bind(&allocFat); |
13135 | { |
13136 | masm.newGCFatInlineString(output, temp0, initialStringHeap(), slowPath); |
13137 | masm.or32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), temp2); |
13138 | } |
13139 | |
13140 | masm.bind(&allocDone); |
13141 | } |
13142 | |
13143 | masm.store32(temp2, Address(output, JSString::offsetOfFlags())); |
13144 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
13145 | |
13146 | auto initializeInlineString = [&](CharEncoding encoding) { |
13147 | masm.loadStringChars(string, temp0, encoding); |
13148 | masm.addToCharPtr(temp0, begin, encoding); |
13149 | if (temp1 == string) { |
13150 | masm.push(string); |
13151 | } |
13152 | masm.loadInlineStringCharsForStore(output, temp1); |
13153 | CopyStringChars(masm, temp1, temp0, length, temp2, encoding, |
13154 | maximumLength); |
13155 | masm.loadStringLength(output, length); |
13156 | if (temp1 == string) { |
13157 | masm.pop(string); |
13158 | } |
13159 | }; |
13160 | |
13161 | Label isInlineLatin1; |
13162 | masm.branchTest32(Assembler::NonZero, temp2, |
13163 | Imm32(JSString::LATIN1_CHARS_BIT), &isInlineLatin1); |
13164 | initializeInlineString(CharEncoding::TwoByte); |
13165 | masm.jump(done); |
13166 | |
13167 | masm.bind(&isInlineLatin1); |
13168 | initializeInlineString(CharEncoding::Latin1); |
13169 | } |
13170 | |
13171 | // Handle other cases with a DependentString. |
13172 | if (tryDependent) { |
13173 | masm.jump(done); |
13174 | |
13175 | masm.bind(¬Inline); |
13176 | masm.newGCString(output, temp0, gen->initialStringHeap(), slowPath); |
13177 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
13178 | |
13179 | // Note: no post barrier is needed because the dependent string is either |
13180 | // allocated in the nursery or both strings are tenured (if nursery strings |
13181 | // are disabled for this zone). |
13182 | EmitInitDependentStringBase(masm, output, string, temp0, temp2, |
13183 | /* needsPostBarrier = */ false); |
13184 | |
13185 | auto initializeDependentString = [&](CharEncoding encoding) { |
13186 | uint32_t flags = JSString::INIT_DEPENDENT_FLAGS; |
13187 | if (encoding == CharEncoding::Latin1) { |
13188 | flags |= JSString::LATIN1_CHARS_BIT; |
13189 | } |
13190 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
13191 | masm.loadNonInlineStringChars(string, temp0, encoding); |
13192 | masm.addToCharPtr(temp0, begin, encoding); |
13193 | masm.storeNonInlineStringChars(temp0, output); |
13194 | }; |
13195 | |
13196 | Label isLatin1; |
13197 | masm.branchLatin1String(string, &isLatin1); |
13198 | initializeDependentString(CharEncoding::TwoByte); |
13199 | masm.jump(done); |
13200 | |
13201 | masm.bind(&isLatin1); |
13202 | initializeDependentString(CharEncoding::Latin1); |
13203 | } |
13204 | |
13205 | masm.bind(done); |
13206 | } |
13207 | |
13208 | JitCode* JitZone::generateStringConcatStub(JSContext* cx) { |
13209 | JitSpew(JitSpew_Codegen, "# Emitting StringConcat stub"); |
13210 | |
13211 | TempAllocator temp(&cx->tempLifoAlloc()); |
13212 | JitContext jcx(cx); |
13213 | StackMacroAssembler masm(cx, temp); |
13214 | AutoCreatedBy acb(masm, "JitZone::generateStringConcatStub"); |
13215 | |
13216 | Register lhs = CallTempReg0; |
13217 | Register rhs = CallTempReg1; |
13218 | Register temp1 = CallTempReg2; |
13219 | Register temp2 = CallTempReg3; |
13220 | Register temp3 = CallTempReg4; |
13221 | Register output = CallTempReg5; |
13222 | |
13223 | Label failure; |
13224 | #ifdef JS_USE_LINK_REGISTER |
13225 | masm.pushReturnAddress(); |
13226 | #endif |
13227 | masm.Push(FramePointer); |
13228 | masm.moveStackPtrTo(FramePointer); |
13229 | |
13230 | // If lhs is empty, return rhs. |
13231 | Label leftEmpty; |
13232 | masm.loadStringLength(lhs, temp1); |
13233 | masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty); |
13234 | |
13235 | // If rhs is empty, return lhs. |
13236 | Label rightEmpty; |
13237 | masm.loadStringLength(rhs, temp2); |
13238 | masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty); |
13239 | |
13240 | masm.add32(temp1, temp2); |
13241 | |
13242 | // Check if we can use a JSInlineString. The result is a Latin1 string if |
13243 | // lhs and rhs are both Latin1, so we AND the flags. |
13244 | Label isInlineTwoByte, isInlineLatin1; |
13245 | masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1); |
13246 | masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1); |
13247 | |
13248 | Label isLatin1, notInline; |
13249 | masm.branchTest32(Assembler::NonZero, temp1, |
13250 | Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1); |
13251 | { |
13252 | masm.branch32(Assembler::BelowOrEqual, temp2, |
13253 | Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), |
13254 | &isInlineTwoByte); |
13255 | masm.jump(¬Inline); |
13256 | } |
13257 | masm.bind(&isLatin1); |
13258 | { |
13259 | masm.branch32(Assembler::BelowOrEqual, temp2, |
13260 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1); |
13261 | } |
13262 | masm.bind(¬Inline); |
13263 | |
13264 | // Keep AND'ed flags in temp1. |
13265 | |
13266 | // Ensure result length <= JSString::MAX_LENGTH. |
13267 | masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure); |
13268 | |
13269 | // Allocate a new rope, guaranteed to be in the nursery if initialStringHeap |
13270 | // == gc::Heap::Default. (As a result, no post barriers are needed below.) |
13271 | masm.newGCString(output, temp3, initialStringHeap, &failure); |
13272 | |
13273 | // Store rope length and flags. temp1 still holds the result of AND'ing the |
13274 | // lhs and rhs flags, so we just have to clear the other flags to get our rope |
13275 | // flags (Latin1 if both lhs and rhs are Latin1). |
13276 | static_assert(JSString::INIT_ROPE_FLAGS == 0, |
13277 | "Rope type flags must have no bits set"); |
13278 | masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1); |
13279 | masm.store32(temp1, Address(output, JSString::offsetOfFlags())); |
13280 | masm.store32(temp2, Address(output, JSString::offsetOfLength())); |
13281 | |
13282 | // Store left and right nodes. |
13283 | masm.storeRopeChildren(lhs, rhs, output); |
13284 | masm.pop(FramePointer); |
13285 | masm.ret(); |
13286 | |
13287 | masm.bind(&leftEmpty); |
13288 | masm.mov(rhs, output); |
13289 | masm.pop(FramePointer); |
13290 | masm.ret(); |
13291 | |
13292 | masm.bind(&rightEmpty); |
13293 | masm.mov(lhs, output); |
13294 | masm.pop(FramePointer); |
13295 | masm.ret(); |
13296 | |
13297 | masm.bind(&isInlineTwoByte); |
13298 | ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, |
13299 | initialStringHeap, &failure, CharEncoding::TwoByte); |
13300 | masm.pop(FramePointer); |
13301 | masm.ret(); |
13302 | |
13303 | masm.bind(&isInlineLatin1); |
13304 | ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, |
13305 | initialStringHeap, &failure, CharEncoding::Latin1); |
13306 | masm.pop(FramePointer); |
13307 | masm.ret(); |
13308 | |
13309 | masm.bind(&failure); |
13310 | masm.movePtr(ImmPtr(nullptr), output); |
13311 | masm.pop(FramePointer); |
13312 | masm.ret(); |
13313 | |
13314 | Linker linker(masm); |
13315 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
13316 | |
13317 | CollectPerfSpewerJitCodeProfile(code, "StringConcatStub"); |
13318 | #ifdef MOZ_VTUNE1 |
13319 | vtune::MarkStub(code, "StringConcatStub"); |
13320 | #endif |
13321 | |
13322 | return code; |
13323 | } |
13324 | |
13325 | void JitRuntime::generateFreeStub(MacroAssembler& masm) { |
13326 | AutoCreatedBy acb(masm, "JitRuntime::generateFreeStub"); |
13327 | |
13328 | const Register regSlots = CallTempReg0; |
13329 | |
13330 | freeStubOffset_ = startTrampolineCode(masm); |
13331 | |
13332 | #ifdef JS_USE_LINK_REGISTER |
13333 | masm.pushReturnAddress(); |
13334 | #endif |
13335 | AllocatableRegisterSet regs(RegisterSet::Volatile()); |
13336 | regs.takeUnchecked(regSlots); |
13337 | LiveRegisterSet save(regs.asLiveSet()); |
13338 | masm.PushRegsInMask(save); |
13339 | |
13340 | const Register regTemp = regs.takeAnyGeneral(); |
13341 | MOZ_ASSERT(regTemp != regSlots)do { static_assert( mozilla::detail::AssertionConditionType< decltype(regTemp != regSlots)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(regTemp != regSlots))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("regTemp != regSlots" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13341); AnnotateMozCrashReason("MOZ_ASSERT" "(" "regTemp != regSlots" ")"); do { *((volatile int*)__null) = 13341; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13342 | |
13343 | using Fn = void (*)(void* p); |
13344 | masm.setupUnalignedABICall(regTemp); |
13345 | masm.passABIArg(regSlots); |
13346 | masm.callWithABI<Fn, js_free>(ABIType::General, |
13347 | CheckUnsafeCallWithABI::DontCheckOther); |
13348 | |
13349 | masm.PopRegsInMask(save); |
13350 | |
13351 | masm.ret(); |
13352 | } |
13353 | |
13354 | void JitRuntime::generateLazyLinkStub(MacroAssembler& masm) { |
13355 | AutoCreatedBy acb(masm, "JitRuntime::generateLazyLinkStub"); |
13356 | |
13357 | lazyLinkStubOffset_ = startTrampolineCode(masm); |
13358 | |
13359 | #ifdef JS_USE_LINK_REGISTER |
13360 | masm.pushReturnAddress(); |
13361 | #endif |
13362 | masm.Push(FramePointer); |
13363 | masm.moveStackPtrTo(FramePointer); |
13364 | |
13365 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
13366 | Register temp0 = regs.takeAny(); |
13367 | Register temp1 = regs.takeAny(); |
13368 | Register temp2 = regs.takeAny(); |
13369 | |
13370 | masm.loadJSContext(temp0); |
13371 | masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink); |
13372 | masm.moveStackPtrTo(temp1); |
13373 | |
13374 | using Fn = uint8_t* (*)(JSContext* cx, LazyLinkExitFrameLayout* frame); |
13375 | masm.setupUnalignedABICall(temp2); |
13376 | masm.passABIArg(temp0); |
13377 | masm.passABIArg(temp1); |
13378 | masm.callWithABI<Fn, LazyLinkTopActivation>( |
13379 | ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
13380 | |
13381 | // Discard exit frame and restore frame pointer. |
13382 | masm.leaveExitFrame(0); |
13383 | masm.pop(FramePointer); |
13384 | |
13385 | #ifdef JS_USE_LINK_REGISTER |
13386 | // Restore the return address such that the emitPrologue function of the |
13387 | // CodeGenerator can push it back on the stack with pushReturnAddress. |
13388 | masm.popReturnAddress(); |
13389 | #endif |
13390 | masm.jump(ReturnReg); |
13391 | } |
13392 | |
13393 | void JitRuntime::generateInterpreterStub(MacroAssembler& masm) { |
13394 | AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterStub"); |
13395 | |
13396 | interpreterStubOffset_ = startTrampolineCode(masm); |
13397 | |
13398 | #ifdef JS_USE_LINK_REGISTER |
13399 | masm.pushReturnAddress(); |
13400 | #endif |
13401 | masm.Push(FramePointer); |
13402 | masm.moveStackPtrTo(FramePointer); |
13403 | |
13404 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
13405 | Register temp0 = regs.takeAny(); |
13406 | Register temp1 = regs.takeAny(); |
13407 | Register temp2 = regs.takeAny(); |
13408 | |
13409 | masm.loadJSContext(temp0); |
13410 | masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub); |
13411 | masm.moveStackPtrTo(temp1); |
13412 | |
13413 | using Fn = bool (*)(JSContext* cx, InterpreterStubExitFrameLayout* frame); |
13414 | masm.setupUnalignedABICall(temp2); |
13415 | masm.passABIArg(temp0); |
13416 | masm.passABIArg(temp1); |
13417 | masm.callWithABI<Fn, InvokeFromInterpreterStub>( |
13418 | ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
13419 | |
13420 | masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); |
13421 | |
13422 | // Discard exit frame and restore frame pointer. |
13423 | masm.leaveExitFrame(0); |
13424 | masm.pop(FramePointer); |
13425 | |
13426 | // InvokeFromInterpreterStub stores the return value in argv[0], where the |
13427 | // caller stored |this|. Subtract |sizeof(void*)| for the frame pointer we |
13428 | // just popped. |
13429 | masm.loadValue(Address(masm.getStackPointer(), |
13430 | JitFrameLayout::offsetOfThis() - sizeof(void*)), |
13431 | JSReturnOperand); |
13432 | masm.ret(); |
13433 | } |
13434 | |
13435 | void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) { |
13436 | AutoCreatedBy acb(masm, "JitRuntime::generateDoubleToInt32ValueStub"); |
13437 | doubleToInt32ValueStubOffset_ = startTrampolineCode(masm); |
13438 | |
13439 | Label done; |
13440 | masm.branchTestDouble(Assembler::NotEqual, R0, &done); |
13441 | |
13442 | masm.unboxDouble(R0, FloatReg0); |
13443 | masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done, |
13444 | /* negativeZeroCheck = */ false); |
13445 | masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0); |
13446 | |
13447 | masm.bind(&done); |
13448 | masm.abiret(); |
13449 | } |
13450 | |
13451 | void CodeGenerator::visitLinearizeString(LLinearizeString* lir) { |
13452 | Register str = ToRegister(lir->str()); |
13453 | Register output = ToRegister(lir->output()); |
13454 | |
13455 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
13456 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
13457 | lir, ArgList(str), StoreRegisterTo(output)); |
13458 | |
13459 | masm.branchIfRope(str, ool->entry()); |
13460 | |
13461 | masm.movePtr(str, output); |
13462 | masm.bind(ool->rejoin()); |
13463 | } |
13464 | |
13465 | void CodeGenerator::visitLinearizeForCharAccess(LLinearizeForCharAccess* lir) { |
13466 | Register str = ToRegister(lir->str()); |
13467 | Register index = ToRegister(lir->index()); |
13468 | Register output = ToRegister(lir->output()); |
13469 | |
13470 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
13471 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
13472 | lir, ArgList(str), StoreRegisterTo(output)); |
13473 | |
13474 | masm.branchIfNotCanLoadStringChar(str, index, output, ool->entry()); |
13475 | |
13476 | masm.movePtr(str, output); |
13477 | masm.bind(ool->rejoin()); |
13478 | } |
13479 | |
13480 | void CodeGenerator::visitLinearizeForCodePointAccess( |
13481 | LLinearizeForCodePointAccess* lir) { |
13482 | Register str = ToRegister(lir->str()); |
13483 | Register index = ToRegister(lir->index()); |
13484 | Register output = ToRegister(lir->output()); |
13485 | Register temp = ToRegister(lir->temp0()); |
13486 | |
13487 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
13488 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
13489 | lir, ArgList(str), StoreRegisterTo(output)); |
13490 | |
13491 | masm.branchIfNotCanLoadStringCodePoint(str, index, output, temp, |
13492 | ool->entry()); |
13493 | |
13494 | masm.movePtr(str, output); |
13495 | masm.bind(ool->rejoin()); |
13496 | } |
13497 | |
13498 | void CodeGenerator::visitToRelativeStringIndex(LToRelativeStringIndex* lir) { |
13499 | Register index = ToRegister(lir->index()); |
13500 | Register length = ToRegister(lir->length()); |
13501 | Register output = ToRegister(lir->output()); |
13502 | |
13503 | masm.move32(Imm32(0), output); |
13504 | masm.cmp32Move32(Assembler::LessThan, index, Imm32(0), length, output); |
13505 | masm.add32(index, output); |
13506 | } |
13507 | |
13508 | void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) { |
13509 | Register str = ToRegister(lir->str()); |
13510 | Register output = ToRegister(lir->output()); |
13511 | Register temp0 = ToRegister(lir->temp0()); |
13512 | Register temp1 = ToRegister(lir->temp1()); |
13513 | |
13514 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
13515 | |
13516 | if (lir->index()->isBogus()) { |
13517 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)), |
13518 | StoreRegisterTo(output)); |
13519 | masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry()); |
13520 | masm.bind(ool->rejoin()); |
13521 | } else { |
13522 | Register index = ToRegister(lir->index()); |
13523 | |
13524 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index), |
13525 | StoreRegisterTo(output)); |
13526 | masm.loadStringChar(str, index, output, temp0, temp1, ool->entry()); |
13527 | masm.bind(ool->rejoin()); |
13528 | } |
13529 | } |
13530 | |
13531 | void CodeGenerator::visitCharCodeAtOrNegative(LCharCodeAtOrNegative* lir) { |
13532 | Register str = ToRegister(lir->str()); |
13533 | Register output = ToRegister(lir->output()); |
13534 | Register temp0 = ToRegister(lir->temp0()); |
13535 | Register temp1 = ToRegister(lir->temp1()); |
13536 | |
13537 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
13538 | |
13539 | // Return -1 for out-of-bounds access. |
13540 | masm.move32(Imm32(-1), output); |
13541 | |
13542 | if (lir->index()->isBogus()) { |
13543 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)), |
13544 | StoreRegisterTo(output)); |
13545 | |
13546 | masm.branch32(Assembler::Equal, Address(str, JSString::offsetOfLength()), |
13547 | Imm32(0), ool->rejoin()); |
13548 | masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry()); |
13549 | masm.bind(ool->rejoin()); |
13550 | } else { |
13551 | Register index = ToRegister(lir->index()); |
13552 | |
13553 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index), |
13554 | StoreRegisterTo(output)); |
13555 | |
13556 | masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), |
13557 | temp0, ool->rejoin()); |
13558 | masm.loadStringChar(str, index, output, temp0, temp1, ool->entry()); |
13559 | masm.bind(ool->rejoin()); |
13560 | } |
13561 | } |
13562 | |
13563 | void CodeGenerator::visitCodePointAt(LCodePointAt* lir) { |
13564 | Register str = ToRegister(lir->str()); |
13565 | Register index = ToRegister(lir->index()); |
13566 | Register output = ToRegister(lir->output()); |
13567 | Register temp0 = ToRegister(lir->temp0()); |
13568 | Register temp1 = ToRegister(lir->temp1()); |
13569 | |
13570 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
13571 | auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index), |
13572 | StoreRegisterTo(output)); |
13573 | |
13574 | masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry()); |
13575 | masm.bind(ool->rejoin()); |
13576 | } |
13577 | |
13578 | void CodeGenerator::visitCodePointAtOrNegative(LCodePointAtOrNegative* lir) { |
13579 | Register str = ToRegister(lir->str()); |
13580 | Register index = ToRegister(lir->index()); |
13581 | Register output = ToRegister(lir->output()); |
13582 | Register temp0 = ToRegister(lir->temp0()); |
13583 | Register temp1 = ToRegister(lir->temp1()); |
13584 | |
13585 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
13586 | auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index), |
13587 | StoreRegisterTo(output)); |
13588 | |
13589 | // Return -1 for out-of-bounds access. |
13590 | masm.move32(Imm32(-1), output); |
13591 | |
13592 | masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), |
13593 | temp0, ool->rejoin()); |
13594 | masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry()); |
13595 | masm.bind(ool->rejoin()); |
13596 | } |
13597 | |
13598 | void CodeGenerator::visitNegativeToNaN(LNegativeToNaN* lir) { |
13599 | Register input = ToRegister(lir->input()); |
13600 | ValueOperand output = ToOutValue(lir); |
13601 | |
13602 | masm.tagValue(JSVAL_TYPE_INT32, input, output); |
13603 | |
13604 | Label done; |
13605 | masm.branchTest32(Assembler::NotSigned, input, input, &done); |
13606 | masm.moveValue(JS::NaNValue(), output); |
13607 | masm.bind(&done); |
13608 | } |
13609 | |
13610 | void CodeGenerator::visitNegativeToUndefined(LNegativeToUndefined* lir) { |
13611 | Register input = ToRegister(lir->input()); |
13612 | ValueOperand output = ToOutValue(lir); |
13613 | |
13614 | masm.tagValue(JSVAL_TYPE_INT32, input, output); |
13615 | |
13616 | Label done; |
13617 | masm.branchTest32(Assembler::NotSigned, input, input, &done); |
13618 | masm.moveValue(JS::UndefinedValue(), output); |
13619 | masm.bind(&done); |
13620 | } |
13621 | |
13622 | void CodeGenerator::visitFromCharCode(LFromCharCode* lir) { |
13623 | Register code = ToRegister(lir->code()); |
13624 | Register output = ToRegister(lir->output()); |
13625 | |
13626 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
13627 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
13628 | StoreRegisterTo(output)); |
13629 | |
13630 | // OOL path if code >= UNIT_STATIC_LIMIT. |
13631 | masm.lookupStaticString(code, output, gen->runtime->staticStrings(), |
13632 | ool->entry()); |
13633 | |
13634 | masm.bind(ool->rejoin()); |
13635 | } |
13636 | |
13637 | void CodeGenerator::visitFromCharCodeEmptyIfNegative( |
13638 | LFromCharCodeEmptyIfNegative* lir) { |
13639 | Register code = ToRegister(lir->code()); |
13640 | Register output = ToRegister(lir->output()); |
13641 | |
13642 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
13643 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
13644 | StoreRegisterTo(output)); |
13645 | |
13646 | // Return the empty string for negative inputs. |
13647 | const JSAtomState& names = gen->runtime->names(); |
13648 | masm.movePtr(ImmGCPtr(names.empty_), output); |
13649 | masm.branchTest32(Assembler::Signed, code, code, ool->rejoin()); |
13650 | |
13651 | // OOL path if code >= UNIT_STATIC_LIMIT. |
13652 | masm.lookupStaticString(code, output, gen->runtime->staticStrings(), |
13653 | ool->entry()); |
13654 | |
13655 | masm.bind(ool->rejoin()); |
13656 | } |
13657 | |
13658 | void CodeGenerator::visitFromCharCodeUndefinedIfNegative( |
13659 | LFromCharCodeUndefinedIfNegative* lir) { |
13660 | Register code = ToRegister(lir->code()); |
13661 | ValueOperand output = ToOutValue(lir); |
13662 | Register temp = output.scratchReg(); |
13663 | |
13664 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
13665 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
13666 | StoreRegisterTo(temp)); |
13667 | |
13668 | // Return |undefined| for negative inputs. |
13669 | Label done; |
13670 | masm.moveValue(UndefinedValue(), output); |
13671 | masm.branchTest32(Assembler::Signed, code, code, &done); |
13672 | |
13673 | // OOL path if code >= UNIT_STATIC_LIMIT. |
13674 | masm.lookupStaticString(code, temp, gen->runtime->staticStrings(), |
13675 | ool->entry()); |
13676 | |
13677 | masm.bind(ool->rejoin()); |
13678 | masm.tagValue(JSVAL_TYPE_STRING, temp, output); |
13679 | |
13680 | masm.bind(&done); |
13681 | } |
13682 | |
13683 | void CodeGenerator::visitFromCodePoint(LFromCodePoint* lir) { |
13684 | Register codePoint = ToRegister(lir->codePoint()); |
13685 | Register output = ToRegister(lir->output()); |
13686 | Register temp0 = ToRegister(lir->temp0()); |
13687 | Register temp1 = ToRegister(lir->temp1()); |
13688 | LSnapshot* snapshot = lir->snapshot(); |
13689 | |
13690 | // The OOL path is only taken when we can't allocate the inline string. |
13691 | using Fn = JSLinearString* (*)(JSContext*, char32_t); |
13692 | auto* ool = oolCallVM<Fn, js::StringFromCodePoint>(lir, ArgList(codePoint), |
13693 | StoreRegisterTo(output)); |
13694 | |
13695 | Label isTwoByte; |
13696 | Label* done = ool->rejoin(); |
13697 | |
13698 | static_assert( |
13699 | StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR, |
13700 | "Latin-1 strings can be loaded from static strings"); |
13701 | |
13702 | { |
13703 | masm.lookupStaticString(codePoint, output, gen->runtime->staticStrings(), |
13704 | &isTwoByte); |
13705 | masm.jump(done); |
13706 | } |
13707 | masm.bind(&isTwoByte); |
13708 | { |
13709 | // Use a bailout if the input is not a valid code point, because |
13710 | // MFromCodePoint is movable and it'd be observable when a moved |
13711 | // fromCodePoint throws an exception before its actual call site. |
13712 | bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax), |
13713 | snapshot); |
13714 | |
13715 | // Allocate a JSThinInlineString. |
13716 | { |
13717 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE >= 2, |
13718 | "JSThinInlineString can hold a supplementary code point"); |
13719 | |
13720 | uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS; |
13721 | masm.newGCString(output, temp0, gen->initialStringHeap(), ool->entry()); |
13722 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
13723 | } |
13724 | |
13725 | Label isSupplementary; |
13726 | masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(unicode::NonBMPMin), |
13727 | &isSupplementary); |
13728 | { |
13729 | // Store length. |
13730 | masm.store32(Imm32(1), Address(output, JSString::offsetOfLength())); |
13731 | |
13732 | // Load chars pointer in temp0. |
13733 | masm.loadInlineStringCharsForStore(output, temp0); |
13734 | |
13735 | masm.store16(codePoint, Address(temp0, 0)); |
13736 | |
13737 | masm.jump(done); |
13738 | } |
13739 | masm.bind(&isSupplementary); |
13740 | { |
13741 | // Store length. |
13742 | masm.store32(Imm32(2), Address(output, JSString::offsetOfLength())); |
13743 | |
13744 | // Load chars pointer in temp0. |
13745 | masm.loadInlineStringCharsForStore(output, temp0); |
13746 | |
13747 | // Inlined unicode::LeadSurrogate(uint32_t). |
13748 | masm.move32(codePoint, temp1); |
13749 | masm.rshift32(Imm32(10), temp1); |
13750 | masm.add32(Imm32(unicode::LeadSurrogateMin - (unicode::NonBMPMin >> 10)), |
13751 | temp1); |
13752 | |
13753 | masm.store16(temp1, Address(temp0, 0)); |
13754 | |
13755 | // Inlined unicode::TrailSurrogate(uint32_t). |
13756 | masm.move32(codePoint, temp1); |
13757 | masm.and32(Imm32(0x3FF), temp1); |
13758 | masm.or32(Imm32(unicode::TrailSurrogateMin), temp1); |
13759 | |
13760 | masm.store16(temp1, Address(temp0, sizeof(char16_t))); |
13761 | } |
13762 | } |
13763 | |
13764 | masm.bind(done); |
13765 | } |
13766 | |
13767 | void CodeGenerator::visitStringIncludes(LStringIncludes* lir) { |
13768 | pushArg(ToRegister(lir->searchString())); |
13769 | pushArg(ToRegister(lir->string())); |
13770 | |
13771 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
13772 | callVM<Fn, js::StringIncludes>(lir); |
13773 | } |
13774 | |
13775 | template <typename LIns> |
13776 | static void CallStringMatch(MacroAssembler& masm, LIns* lir, OutOfLineCode* ool, |
13777 | LiveRegisterSet volatileRegs) { |
13778 | Register string = ToRegister(lir->string()); |
13779 | Register output = ToRegister(lir->output()); |
13780 | Register tempLength = ToRegister(lir->temp0()); |
13781 | Register tempChars = ToRegister(lir->temp1()); |
13782 | Register maybeTempPat = ToTempRegisterOrInvalid(lir->temp2()); |
13783 | |
13784 | const JSLinearString* searchString = lir->searchString(); |
13785 | size_t length = searchString->length(); |
13786 | MOZ_ASSERT(length == 1 || length == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length == 1 || length == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length == 1 || length == 2)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length == 1 || length == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13786); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length == 1 || length == 2" ")"); do { *((volatile int*)__null) = 13786; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
13787 | |
13788 | // The additional temp register is only needed when searching for two |
13789 | // pattern characters. |
13790 | MOZ_ASSERT_IF(length == 2, maybeTempPat != InvalidReg)do { if (length == 2) { do { static_assert( mozilla::detail:: AssertionConditionType<decltype(maybeTempPat != InvalidReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(maybeTempPat != InvalidReg))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("maybeTempPat != InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13790); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeTempPat != InvalidReg" ")"); do { *((volatile int*)__null) = 13790; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
13791 | |
13792 | if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) { |
13793 | masm.move32(Imm32(0), output); |
13794 | } else { |
13795 | masm.move32(Imm32(-1), output); |
13796 | } |
13797 | |
13798 | masm.loadStringLength(string, tempLength); |
13799 | |
13800 | // Can't be a substring when the string is smaller than the search string. |
13801 | Label done; |
13802 | masm.branch32(Assembler::Below, tempLength, Imm32(length), ool->rejoin()); |
13803 | |
13804 | bool searchStringIsPureTwoByte = false; |
13805 | if (searchString->hasTwoByteChars()) { |
13806 | JS::AutoCheckCannotGC nogc; |
13807 | searchStringIsPureTwoByte = |
13808 | !mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc)); |
13809 | } |
13810 | |
13811 | // Pure two-byte strings can't occur in a Latin-1 string. |
13812 | if (searchStringIsPureTwoByte) { |
13813 | masm.branchLatin1String(string, ool->rejoin()); |
13814 | } |
13815 | |
13816 | // Slow path when we need to linearize the string. |
13817 | masm.branchIfRope(string, ool->entry()); |
13818 | |
13819 | Label restoreVolatile; |
13820 | |
13821 | auto callMatcher = [&](CharEncoding encoding) { |
13822 | masm.loadStringChars(string, tempChars, encoding); |
13823 | |
13824 | LiveGeneralRegisterSet liveRegs; |
13825 | if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) { |
13826 | // Save |tempChars| to compute the result index. |
13827 | liveRegs.add(tempChars); |
13828 | |
13829 | #ifdef DEBUG1 |
13830 | // Save |tempLength| in debug-mode for assertions. |
13831 | liveRegs.add(tempLength); |
13832 | #endif |
13833 | |
13834 | // Exclude non-volatile registers. |
13835 | liveRegs.set() = GeneralRegisterSet::Intersect( |
13836 | liveRegs.set(), GeneralRegisterSet::Volatile()); |
13837 | |
13838 | masm.PushRegsInMask(liveRegs); |
13839 | } |
13840 | |
13841 | if (length == 1) { |
13842 | char16_t pat = searchString->latin1OrTwoByteChar(0); |
13843 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13844); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 13844; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
13844 | pat <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13844); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 13844; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
13845 | |
13846 | masm.move32(Imm32(pat), output); |
13847 | |
13848 | masm.setupAlignedABICall(); |
13849 | masm.passABIArg(tempChars); |
13850 | masm.passABIArg(output); |
13851 | masm.passABIArg(tempLength); |
13852 | if (encoding == CharEncoding::Latin1) { |
13853 | using Fn = const char* (*)(const char*, char, size_t); |
13854 | masm.callWithABI<Fn, mozilla::SIMD::memchr8>( |
13855 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
13856 | } else { |
13857 | using Fn = const char16_t* (*)(const char16_t*, char16_t, size_t); |
13858 | masm.callWithABI<Fn, mozilla::SIMD::memchr16>( |
13859 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
13860 | } |
13861 | } else { |
13862 | char16_t pat0 = searchString->latin1OrTwoByteChar(0); |
13863 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat0 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat0 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat0 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 13864; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
13864 | pat0 <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat0 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat0 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat0 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 13864; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
13865 | |
13866 | char16_t pat1 = searchString->latin1OrTwoByteChar(1); |
13867 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat1 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat1 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat1 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13868); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 13868; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
13868 | pat1 <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat1 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat1 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat1 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13868); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 13868; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
13869 | |
13870 | masm.move32(Imm32(pat0), output); |
13871 | masm.move32(Imm32(pat1), maybeTempPat); |
13872 | |
13873 | masm.setupAlignedABICall(); |
13874 | masm.passABIArg(tempChars); |
13875 | masm.passABIArg(output); |
13876 | masm.passABIArg(maybeTempPat); |
13877 | masm.passABIArg(tempLength); |
13878 | if (encoding == CharEncoding::Latin1) { |
13879 | using Fn = const char* (*)(const char*, char, char, size_t); |
13880 | masm.callWithABI<Fn, mozilla::SIMD::memchr2x8>( |
13881 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
13882 | } else { |
13883 | using Fn = |
13884 | const char16_t* (*)(const char16_t*, char16_t, char16_t, size_t); |
13885 | masm.callWithABI<Fn, mozilla::SIMD::memchr2x16>( |
13886 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
13887 | } |
13888 | } |
13889 | |
13890 | masm.storeCallPointerResult(output); |
13891 | |
13892 | // Convert to string index for `indexOf`. |
13893 | if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) { |
13894 | // Restore |tempChars|. (And in debug mode |tempLength|.) |
13895 | masm.PopRegsInMask(liveRegs); |
13896 | |
13897 | Label found; |
13898 | masm.branchPtr(Assembler::NotEqual, output, ImmPtr(nullptr), &found); |
13899 | { |
13900 | masm.move32(Imm32(-1), output); |
13901 | masm.jump(&restoreVolatile); |
13902 | } |
13903 | masm.bind(&found); |
13904 | |
13905 | #ifdef DEBUG1 |
13906 | // Check lower bound. |
13907 | Label lower; |
13908 | masm.branchPtr(Assembler::AboveOrEqual, output, tempChars, &lower); |
13909 | masm.assumeUnreachable("result pointer below string chars"); |
13910 | masm.bind(&lower); |
13911 | |
13912 | // Compute the end position of the characters. |
13913 | auto scale = encoding == CharEncoding::Latin1 ? TimesOne : TimesTwo; |
13914 | masm.computeEffectiveAddress(BaseIndex(tempChars, tempLength, scale), |
13915 | tempLength); |
13916 | |
13917 | // Check upper bound. |
13918 | Label upper; |
13919 | masm.branchPtr(Assembler::Below, output, tempLength, &upper); |
13920 | masm.assumeUnreachable("result pointer above string chars"); |
13921 | masm.bind(&upper); |
13922 | #endif |
13923 | |
13924 | masm.subPtr(tempChars, output); |
13925 | |
13926 | if (encoding == CharEncoding::TwoByte) { |
13927 | masm.rshiftPtr(Imm32(1), output); |
13928 | } |
13929 | } |
13930 | }; |
13931 | |
13932 | volatileRegs.takeUnchecked(output); |
13933 | volatileRegs.takeUnchecked(tempLength); |
13934 | volatileRegs.takeUnchecked(tempChars); |
13935 | if (maybeTempPat != InvalidReg) { |
13936 | volatileRegs.takeUnchecked(maybeTempPat); |
13937 | } |
13938 | masm.PushRegsInMask(volatileRegs); |
13939 | |
13940 | // Handle the case when the input is a Latin-1 string. |
13941 | if (!searchStringIsPureTwoByte) { |
13942 | Label twoByte; |
13943 | masm.branchTwoByteString(string, &twoByte); |
13944 | { |
13945 | callMatcher(CharEncoding::Latin1); |
13946 | masm.jump(&restoreVolatile); |
13947 | } |
13948 | masm.bind(&twoByte); |
13949 | } |
13950 | |
13951 | // Handle the case when the input is a two-byte string. |
13952 | callMatcher(CharEncoding::TwoByte); |
13953 | |
13954 | masm.bind(&restoreVolatile); |
13955 | masm.PopRegsInMask(volatileRegs); |
13956 | |
13957 | // Convert to bool for `includes`. |
13958 | if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) { |
13959 | masm.cmpPtrSet(Assembler::NotEqual, output, ImmPtr(nullptr), output); |
13960 | } |
13961 | |
13962 | masm.bind(ool->rejoin()); |
13963 | } |
13964 | |
13965 | void CodeGenerator::visitStringIncludesSIMD(LStringIncludesSIMD* lir) { |
13966 | Register string = ToRegister(lir->string()); |
13967 | Register output = ToRegister(lir->output()); |
13968 | const JSLinearString* searchString = lir->searchString(); |
13969 | |
13970 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
13971 | auto* ool = oolCallVM<Fn, js::StringIncludes>( |
13972 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
13973 | |
13974 | CallStringMatch(masm, lir, ool, liveVolatileRegs(lir)); |
13975 | } |
13976 | |
13977 | void CodeGenerator::visitStringIndexOf(LStringIndexOf* lir) { |
13978 | pushArg(ToRegister(lir->searchString())); |
13979 | pushArg(ToRegister(lir->string())); |
13980 | |
13981 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
13982 | callVM<Fn, js::StringIndexOf>(lir); |
13983 | } |
13984 | |
13985 | void CodeGenerator::visitStringIndexOfSIMD(LStringIndexOfSIMD* lir) { |
13986 | Register string = ToRegister(lir->string()); |
13987 | Register output = ToRegister(lir->output()); |
13988 | const JSLinearString* searchString = lir->searchString(); |
13989 | |
13990 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
13991 | auto* ool = oolCallVM<Fn, js::StringIndexOf>( |
13992 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
13993 | |
13994 | CallStringMatch(masm, lir, ool, liveVolatileRegs(lir)); |
13995 | } |
13996 | |
13997 | void CodeGenerator::visitStringLastIndexOf(LStringLastIndexOf* lir) { |
13998 | pushArg(ToRegister(lir->searchString())); |
13999 | pushArg(ToRegister(lir->string())); |
14000 | |
14001 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
14002 | callVM<Fn, js::StringLastIndexOf>(lir); |
14003 | } |
14004 | |
14005 | void CodeGenerator::visitStringStartsWith(LStringStartsWith* lir) { |
14006 | pushArg(ToRegister(lir->searchString())); |
14007 | pushArg(ToRegister(lir->string())); |
14008 | |
14009 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14010 | callVM<Fn, js::StringStartsWith>(lir); |
14011 | } |
14012 | |
14013 | void CodeGenerator::visitStringStartsWithInline(LStringStartsWithInline* lir) { |
14014 | Register string = ToRegister(lir->string()); |
14015 | Register output = ToRegister(lir->output()); |
14016 | Register temp = ToRegister(lir->temp0()); |
14017 | |
14018 | const JSLinearString* searchString = lir->searchString(); |
14019 | |
14020 | size_t length = searchString->length(); |
14021 | MOZ_ASSERT(length > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14021); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0" ")"); do { *((volatile int*)__null) = 14021; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14022 | |
14023 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14024 | auto* ool = oolCallVM<Fn, js::StringStartsWith>( |
14025 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
14026 | |
14027 | masm.move32(Imm32(0), output); |
14028 | |
14029 | // Can't be a prefix when the string is smaller than the search string. |
14030 | masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()), |
14031 | Imm32(length), ool->rejoin()); |
14032 | |
14033 | // Unwind ropes at the start if possible. |
14034 | Label compare; |
14035 | masm.movePtr(string, temp); |
14036 | masm.branchIfNotRope(temp, &compare); |
14037 | |
14038 | Label unwindRope; |
14039 | masm.bind(&unwindRope); |
14040 | masm.loadRopeLeftChild(temp, output); |
14041 | masm.movePtr(output, temp); |
14042 | |
14043 | // If the left child is smaller than the search string, jump into the VM to |
14044 | // linearize the string. |
14045 | masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()), |
14046 | Imm32(length), ool->entry()); |
14047 | |
14048 | // Otherwise keep unwinding ropes. |
14049 | masm.branchIfRope(temp, &unwindRope); |
14050 | |
14051 | masm.bind(&compare); |
14052 | |
14053 | // If operands point to the same instance, it's trivially a prefix. |
14054 | Label notPointerEqual; |
14055 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString), |
14056 | ¬PointerEqual); |
14057 | masm.move32(Imm32(1), output); |
14058 | masm.jump(ool->rejoin()); |
14059 | masm.bind(¬PointerEqual); |
14060 | |
14061 | if (searchString->hasTwoByteChars()) { |
14062 | // Pure two-byte strings can't be a prefix of Latin-1 strings. |
14063 | JS::AutoCheckCannotGC nogc; |
14064 | if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) { |
14065 | Label compareChars; |
14066 | masm.branchTwoByteString(temp, &compareChars); |
14067 | masm.move32(Imm32(0), output); |
14068 | masm.jump(ool->rejoin()); |
14069 | masm.bind(&compareChars); |
14070 | } |
14071 | } |
14072 | |
14073 | // Load the input string's characters. |
14074 | Register stringChars = output; |
14075 | masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry()); |
14076 | |
14077 | // Start comparing character by character. |
14078 | masm.compareStringChars(JSOp::Eq, stringChars, searchString, output); |
14079 | |
14080 | masm.bind(ool->rejoin()); |
14081 | } |
14082 | |
14083 | void CodeGenerator::visitStringEndsWith(LStringEndsWith* lir) { |
14084 | pushArg(ToRegister(lir->searchString())); |
14085 | pushArg(ToRegister(lir->string())); |
14086 | |
14087 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14088 | callVM<Fn, js::StringEndsWith>(lir); |
14089 | } |
14090 | |
14091 | void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) { |
14092 | Register string = ToRegister(lir->string()); |
14093 | Register output = ToRegister(lir->output()); |
14094 | Register temp = ToRegister(lir->temp0()); |
14095 | |
14096 | const JSLinearString* searchString = lir->searchString(); |
14097 | |
14098 | size_t length = searchString->length(); |
14099 | MOZ_ASSERT(length > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14099); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0" ")"); do { *((volatile int*)__null) = 14099; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14100 | |
14101 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
14102 | auto* ool = oolCallVM<Fn, js::StringEndsWith>( |
14103 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
14104 | |
14105 | masm.move32(Imm32(0), output); |
14106 | |
14107 | // Can't be a suffix when the string is smaller than the search string. |
14108 | masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()), |
14109 | Imm32(length), ool->rejoin()); |
14110 | |
14111 | // Unwind ropes at the end if possible. |
14112 | Label compare; |
14113 | masm.movePtr(string, temp); |
14114 | masm.branchIfNotRope(temp, &compare); |
14115 | |
14116 | Label unwindRope; |
14117 | masm.bind(&unwindRope); |
14118 | masm.loadRopeRightChild(temp, output); |
14119 | masm.movePtr(output, temp); |
14120 | |
14121 | // If the right child is smaller than the search string, jump into the VM to |
14122 | // linearize the string. |
14123 | masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()), |
14124 | Imm32(length), ool->entry()); |
14125 | |
14126 | // Otherwise keep unwinding ropes. |
14127 | masm.branchIfRope(temp, &unwindRope); |
14128 | |
14129 | masm.bind(&compare); |
14130 | |
14131 | // If operands point to the same instance, it's trivially a suffix. |
14132 | Label notPointerEqual; |
14133 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString), |
14134 | ¬PointerEqual); |
14135 | masm.move32(Imm32(1), output); |
14136 | masm.jump(ool->rejoin()); |
14137 | masm.bind(¬PointerEqual); |
14138 | |
14139 | CharEncoding encoding = searchString->hasLatin1Chars() |
14140 | ? CharEncoding::Latin1 |
14141 | : CharEncoding::TwoByte; |
14142 | if (encoding == CharEncoding::TwoByte) { |
14143 | // Pure two-byte strings can't be a suffix of Latin-1 strings. |
14144 | JS::AutoCheckCannotGC nogc; |
14145 | if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) { |
14146 | Label compareChars; |
14147 | masm.branchTwoByteString(temp, &compareChars); |
14148 | masm.move32(Imm32(0), output); |
14149 | masm.jump(ool->rejoin()); |
14150 | masm.bind(&compareChars); |
14151 | } |
14152 | } |
14153 | |
14154 | // Load the input string's characters. |
14155 | Register stringChars = output; |
14156 | masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry()); |
14157 | |
14158 | // Move string-char pointer to the suffix string. |
14159 | masm.loadStringLength(temp, temp); |
14160 | masm.sub32(Imm32(length), temp); |
14161 | masm.addToCharPtr(stringChars, temp, encoding); |
14162 | |
14163 | // Start comparing character by character. |
14164 | masm.compareStringChars(JSOp::Eq, stringChars, searchString, output); |
14165 | |
14166 | masm.bind(ool->rejoin()); |
14167 | } |
14168 | |
14169 | void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) { |
14170 | Register string = ToRegister(lir->string()); |
14171 | Register output = ToRegister(lir->output()); |
14172 | Register temp0 = ToRegister(lir->temp0()); |
14173 | Register temp1 = ToRegister(lir->temp1()); |
14174 | Register temp2 = ToRegister(lir->temp2()); |
14175 | |
14176 | // On x86 there are not enough registers. In that case reuse the string |
14177 | // register as a temporary. |
14178 | Register temp3 = |
14179 | lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3()); |
14180 | Register temp4 = ToRegister(lir->temp4()); |
14181 | |
14182 | using Fn = JSString* (*)(JSContext*, HandleString); |
14183 | OutOfLineCode* ool = oolCallVM<Fn, js::StringToLowerCase>( |
14184 | lir, ArgList(string), StoreRegisterTo(output)); |
14185 | |
14186 | // Take the slow path if the string isn't a linear Latin-1 string. |
14187 | Imm32 linearLatin1Bits(JSString::LINEAR_BIT | JSString::LATIN1_CHARS_BIT); |
14188 | Register flags = temp0; |
14189 | masm.load32(Address(string, JSString::offsetOfFlags()), flags); |
14190 | masm.and32(linearLatin1Bits, flags); |
14191 | masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry()); |
14192 | |
14193 | Register length = temp0; |
14194 | masm.loadStringLength(string, length); |
14195 | |
14196 | // Return the input if it's the empty string. |
14197 | Label notEmptyString; |
14198 | masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬EmptyString); |
14199 | { |
14200 | masm.movePtr(string, output); |
14201 | masm.jump(ool->rejoin()); |
14202 | } |
14203 | masm.bind(¬EmptyString); |
14204 | |
14205 | Register inputChars = temp1; |
14206 | masm.loadStringChars(string, inputChars, CharEncoding::Latin1); |
14207 | |
14208 | Register toLowerCaseTable = temp2; |
14209 | masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable); |
14210 | |
14211 | // Single element strings can be directly retrieved from static strings cache. |
14212 | Label notSingleElementString; |
14213 | masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleElementString); |
14214 | { |
14215 | Register current = temp4; |
14216 | |
14217 | masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); |
14218 | masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), |
14219 | current); |
14220 | masm.lookupStaticString(current, output, gen->runtime->staticStrings()); |
14221 | |
14222 | masm.jump(ool->rejoin()); |
14223 | } |
14224 | masm.bind(¬SingleElementString); |
14225 | |
14226 | // Use the OOL-path when the string is too long. This prevents scanning long |
14227 | // strings which have upper case characters only near the end a second time in |
14228 | // the VM. |
14229 | constexpr int32_t MaxInlineLength = 64; |
14230 | masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry()); |
14231 | |
14232 | { |
14233 | // Check if there are any characters which need to be converted. |
14234 | // |
14235 | // This extra loop gives a small performance improvement for strings which |
14236 | // are already lower cased and lets us avoid calling into the runtime for |
14237 | // non-inline, all lower case strings. But more importantly it avoids |
14238 | // repeated inline allocation failures: |
14239 | // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the |
14240 | // |js::StringToLowerCase| runtime function when the result string can't be |
14241 | // allocated inline. And |js::StringToLowerCase| directly returns the input |
14242 | // string when no characters need to be converted. That means it won't |
14243 | // trigger GC to clear up the free nursery space, so the next toLowerCase() |
14244 | // call will again fail to inline allocate the result string. |
14245 | Label hasUpper; |
14246 | { |
14247 | Register checkInputChars = output; |
14248 | masm.movePtr(inputChars, checkInputChars); |
14249 | |
14250 | Register current = temp4; |
14251 | |
14252 | Label start; |
14253 | masm.bind(&start); |
14254 | masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1); |
14255 | masm.branch8(Assembler::NotEqual, |
14256 | BaseIndex(toLowerCaseTable, current, TimesOne), current, |
14257 | &hasUpper); |
14258 | masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars); |
14259 | masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); |
14260 | |
14261 | // Input is already in lower case. |
14262 | masm.movePtr(string, output); |
14263 | masm.jump(ool->rejoin()); |
14264 | } |
14265 | masm.bind(&hasUpper); |
14266 | |
14267 | // |length| was clobbered above, reload. |
14268 | masm.loadStringLength(string, length); |
14269 | |
14270 | // Call into the runtime when we can't create an inline string. |
14271 | masm.branch32(Assembler::Above, length, |
14272 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry()); |
14273 | |
14274 | AllocateThinOrFatInlineString(masm, output, length, temp4, |
14275 | initialStringHeap(), ool->entry(), |
14276 | CharEncoding::Latin1); |
14277 | |
14278 | if (temp3 == string) { |
14279 | masm.push(string); |
14280 | } |
14281 | |
14282 | Register outputChars = temp3; |
14283 | masm.loadInlineStringCharsForStore(output, outputChars); |
14284 | |
14285 | { |
14286 | Register current = temp4; |
14287 | |
14288 | Label start; |
14289 | masm.bind(&start); |
14290 | masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); |
14291 | masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), |
14292 | current); |
14293 | masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1); |
14294 | masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars); |
14295 | masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars); |
14296 | masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); |
14297 | } |
14298 | |
14299 | if (temp3 == string) { |
14300 | masm.pop(string); |
14301 | } |
14302 | } |
14303 | |
14304 | masm.bind(ool->rejoin()); |
14305 | } |
14306 | |
14307 | void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) { |
14308 | pushArg(ToRegister(lir->string())); |
14309 | |
14310 | using Fn = JSString* (*)(JSContext*, HandleString); |
14311 | callVM<Fn, js::StringToUpperCase>(lir); |
14312 | } |
14313 | |
14314 | void CodeGenerator::visitCharCodeToLowerCase(LCharCodeToLowerCase* lir) { |
14315 | Register code = ToRegister(lir->code()); |
14316 | Register output = ToRegister(lir->output()); |
14317 | Register temp = ToRegister(lir->temp0()); |
14318 | |
14319 | using Fn = JSString* (*)(JSContext*, int32_t); |
14320 | auto* ool = oolCallVM<Fn, jit::CharCodeToLowerCase>(lir, ArgList(code), |
14321 | StoreRegisterTo(output)); |
14322 | |
14323 | constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1; |
14324 | |
14325 | // OOL path if code >= NonLatin1Min. |
14326 | masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry()); |
14327 | |
14328 | // Convert to lower case. |
14329 | masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), temp); |
14330 | masm.load8ZeroExtend(BaseIndex(temp, code, TimesOne), temp); |
14331 | |
14332 | // Load static string for lower case character. |
14333 | masm.lookupStaticString(temp, output, gen->runtime->staticStrings()); |
14334 | |
14335 | masm.bind(ool->rejoin()); |
14336 | } |
14337 | |
14338 | void CodeGenerator::visitCharCodeToUpperCase(LCharCodeToUpperCase* lir) { |
14339 | Register code = ToRegister(lir->code()); |
14340 | Register output = ToRegister(lir->output()); |
14341 | Register temp = ToRegister(lir->temp0()); |
14342 | |
14343 | using Fn = JSString* (*)(JSContext*, int32_t); |
14344 | auto* ool = oolCallVM<Fn, jit::CharCodeToUpperCase>(lir, ArgList(code), |
14345 | StoreRegisterTo(output)); |
14346 | |
14347 | constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1; |
14348 | |
14349 | // OOL path if code >= NonLatin1Min. |
14350 | masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry()); |
14351 | |
14352 | // Most one element Latin-1 strings can be directly retrieved from the |
14353 | // static strings cache, except the following three characters: |
14354 | // |
14355 | // 1. ToUpper(U+00B5) = 0+039C |
14356 | // 2. ToUpper(U+00FF) = 0+0178 |
14357 | // 3. ToUpper(U+00DF) = 0+0053 0+0053 |
14358 | masm.branch32(Assembler::Equal, code, Imm32(unicode::MICRO_SIGN), |
14359 | ool->entry()); |
14360 | masm.branch32(Assembler::Equal, code, |
14361 | Imm32(unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS), |
14362 | ool->entry()); |
14363 | masm.branch32(Assembler::Equal, code, |
14364 | Imm32(unicode::LATIN_SMALL_LETTER_SHARP_S), ool->entry()); |
14365 | |
14366 | // Inline unicode::ToUpperCase (without the special case for ASCII characters) |
14367 | |
14368 | constexpr size_t shift = unicode::CharInfoShift; |
14369 | |
14370 | // code >> shift |
14371 | masm.move32(code, temp); |
14372 | masm.rshift32(Imm32(shift), temp); |
14373 | |
14374 | // index = index1[code >> shift]; |
14375 | masm.movePtr(ImmPtr(unicode::index1), output); |
14376 | masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp); |
14377 | |
14378 | // (code & ((1 << shift) - 1) |
14379 | masm.move32(code, output); |
14380 | masm.and32(Imm32((1 << shift) - 1), output); |
14381 | |
14382 | // (index << shift) + (code & ((1 << shift) - 1)) |
14383 | masm.lshift32(Imm32(shift), temp); |
14384 | masm.add32(output, temp); |
14385 | |
14386 | // index = index2[(index << shift) + (code & ((1 << shift) - 1))] |
14387 | masm.movePtr(ImmPtr(unicode::index2), output); |
14388 | masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp); |
14389 | |
14390 | // Compute |index * 6| through |(index * 3) * TimesTwo|. |
14391 | static_assert(sizeof(unicode::CharacterInfo) == 6); |
14392 | masm.mulBy3(temp, temp); |
14393 | |
14394 | // upperCase = js_charinfo[index].upperCase |
14395 | masm.movePtr(ImmPtr(unicode::js_charinfo), output); |
14396 | masm.load16ZeroExtend(BaseIndex(output, temp, TimesTwo, |
14397 | offsetof(unicode::CharacterInfo, upperCase)__builtin_offsetof(unicode::CharacterInfo, upperCase)), |
14398 | temp); |
14399 | |
14400 | // uint16_t(ch) + upperCase |
14401 | masm.add32(code, temp); |
14402 | |
14403 | // Clear any high bits added when performing the unsigned 16-bit addition |
14404 | // through a signed 32-bit addition. |
14405 | masm.move8ZeroExtend(temp, temp); |
14406 | |
14407 | // Load static string for upper case character. |
14408 | masm.lookupStaticString(temp, output, gen->runtime->staticStrings()); |
14409 | |
14410 | masm.bind(ool->rejoin()); |
14411 | } |
14412 | |
14413 | void CodeGenerator::visitStringTrimStartIndex(LStringTrimStartIndex* lir) { |
14414 | Register string = ToRegister(lir->string()); |
14415 | Register output = ToRegister(lir->output()); |
14416 | |
14417 | auto volatileRegs = liveVolatileRegs(lir); |
14418 | volatileRegs.takeUnchecked(output); |
14419 | |
14420 | masm.PushRegsInMask(volatileRegs); |
14421 | |
14422 | using Fn = int32_t (*)(const JSString*); |
14423 | masm.setupAlignedABICall(); |
14424 | masm.passABIArg(string); |
14425 | masm.callWithABI<Fn, jit::StringTrimStartIndex>(); |
14426 | masm.storeCallInt32Result(output); |
14427 | |
14428 | masm.PopRegsInMask(volatileRegs); |
14429 | } |
14430 | |
14431 | void CodeGenerator::visitStringTrimEndIndex(LStringTrimEndIndex* lir) { |
14432 | Register string = ToRegister(lir->string()); |
14433 | Register start = ToRegister(lir->start()); |
14434 | Register output = ToRegister(lir->output()); |
14435 | |
14436 | auto volatileRegs = liveVolatileRegs(lir); |
14437 | volatileRegs.takeUnchecked(output); |
14438 | |
14439 | masm.PushRegsInMask(volatileRegs); |
14440 | |
14441 | using Fn = int32_t (*)(const JSString*, int32_t); |
14442 | masm.setupAlignedABICall(); |
14443 | masm.passABIArg(string); |
14444 | masm.passABIArg(start); |
14445 | masm.callWithABI<Fn, jit::StringTrimEndIndex>(); |
14446 | masm.storeCallInt32Result(output); |
14447 | |
14448 | masm.PopRegsInMask(volatileRegs); |
14449 | } |
14450 | |
14451 | void CodeGenerator::visitStringSplit(LStringSplit* lir) { |
14452 | pushArg(Imm32(INT32_MAX(2147483647))); |
14453 | pushArg(ToRegister(lir->separator())); |
14454 | pushArg(ToRegister(lir->string())); |
14455 | |
14456 | using Fn = ArrayObject* (*)(JSContext*, HandleString, HandleString, uint32_t); |
14457 | callVM<Fn, js::StringSplitString>(lir); |
14458 | } |
14459 | |
14460 | void CodeGenerator::visitInitializedLength(LInitializedLength* lir) { |
14461 | Address initLength(ToRegister(lir->elements()), |
14462 | ObjectElements::offsetOfInitializedLength()); |
14463 | masm.load32(initLength, ToRegister(lir->output())); |
14464 | } |
14465 | |
14466 | void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) { |
14467 | Address initLength(ToRegister(lir->elements()), |
14468 | ObjectElements::offsetOfInitializedLength()); |
14469 | SetLengthFromIndex(masm, lir->index(), initLength); |
14470 | } |
14471 | |
14472 | void CodeGenerator::visitNotBI(LNotBI* lir) { |
14473 | Register input = ToRegister(lir->input()); |
14474 | Register output = ToRegister(lir->output()); |
14475 | |
14476 | masm.cmp32Set(Assembler::Equal, Address(input, BigInt::offsetOfLength()), |
14477 | Imm32(0), output); |
14478 | } |
14479 | |
14480 | void CodeGenerator::visitNotO(LNotO* lir) { |
14481 | Register objreg = ToRegister(lir->input()); |
14482 | Register output = ToRegister(lir->output()); |
14483 | |
14484 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
14485 | if (intact) { |
14486 | // Bug 1874905: It would be fantastic if this could be optimized out. |
14487 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
14488 | masm.move32(Imm32(0), output); |
14489 | } else { |
14490 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
14491 | addOutOfLineCode(ool, lir->mir()); |
14492 | |
14493 | Label* ifEmulatesUndefined = ool->label1(); |
14494 | Label* ifDoesntEmulateUndefined = ool->label2(); |
14495 | |
14496 | branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined, |
14497 | ifDoesntEmulateUndefined, output, ool); |
14498 | // fall through |
14499 | |
14500 | Label join; |
14501 | |
14502 | masm.move32(Imm32(0), output); |
14503 | masm.jump(&join); |
14504 | |
14505 | masm.bind(ifEmulatesUndefined); |
14506 | masm.move32(Imm32(1), output); |
14507 | |
14508 | masm.bind(&join); |
14509 | } |
14510 | } |
14511 | |
14512 | void CodeGenerator::visitNotV(LNotV* lir) { |
14513 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
14514 | addOutOfLineCode(ool, lir->mir()); |
14515 | |
14516 | Label* ifTruthy = ool->label1(); |
14517 | Label* ifFalsy = ool->label2(); |
14518 | |
14519 | ValueOperand input = ToValue(lir, LNotV::InputIndex); |
14520 | Register tempToUnbox = ToTempUnboxRegister(lir->temp1()); |
14521 | FloatRegister floatTemp = ToFloatRegister(lir->temp0()); |
14522 | Register output = ToRegister(lir->output()); |
14523 | const TypeDataList& observedTypes = lir->mir()->observedTypes(); |
14524 | |
14525 | testValueTruthy(input, tempToUnbox, output, floatTemp, observedTypes, |
14526 | ifTruthy, ifFalsy, ool); |
14527 | |
14528 | Label join; |
14529 | |
14530 | // Note that the testValueTruthy call above may choose to fall through |
14531 | // to ifTruthy instead of branching there. |
14532 | masm.bind(ifTruthy); |
14533 | masm.move32(Imm32(0), output); |
14534 | masm.jump(&join); |
14535 | |
14536 | masm.bind(ifFalsy); |
14537 | masm.move32(Imm32(1), output); |
14538 | |
14539 | // both branches meet here. |
14540 | masm.bind(&join); |
14541 | } |
14542 | |
14543 | void CodeGenerator::visitBoundsCheck(LBoundsCheck* lir) { |
14544 | const LAllocation* index = lir->index(); |
14545 | const LAllocation* length = lir->length(); |
14546 | LSnapshot* snapshot = lir->snapshot(); |
14547 | |
14548 | MIRType type = lir->mir()->type(); |
14549 | |
14550 | auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) { |
14551 | if (type == MIRType::Int32) { |
14552 | bailoutCmp32(cond, lhs, rhs, snapshot); |
14553 | } else { |
14554 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14554); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14554; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14555 | bailoutCmpPtr(cond, lhs, rhs, snapshot); |
14556 | } |
14557 | }; |
14558 | |
14559 | auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs, |
14560 | int32_t rhs) { |
14561 | if (type == MIRType::Int32) { |
14562 | bailoutCmp32(cond, lhs, Imm32(rhs), snapshot); |
14563 | } else { |
14564 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14564); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14564; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14565 | bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot); |
14566 | } |
14567 | }; |
14568 | |
14569 | if (index->isConstant()) { |
14570 | // Use uint32 so that the comparison is unsigned. |
14571 | uint32_t idx = ToInt32(index); |
14572 | if (length->isConstant()) { |
14573 | uint32_t len = ToInt32(lir->length()); |
14574 | if (idx < len) { |
14575 | return; |
14576 | } |
14577 | bailout(snapshot); |
14578 | return; |
14579 | } |
14580 | |
14581 | if (length->isRegister()) { |
14582 | bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), idx); |
14583 | } else { |
14584 | bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), idx); |
14585 | } |
14586 | return; |
14587 | } |
14588 | |
14589 | Register indexReg = ToRegister(index); |
14590 | if (length->isConstant()) { |
14591 | bailoutCmpConstant(Assembler::AboveOrEqual, indexReg, ToInt32(length)); |
14592 | } else if (length->isRegister()) { |
14593 | bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), indexReg); |
14594 | } else { |
14595 | bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), indexReg); |
14596 | } |
14597 | } |
14598 | |
14599 | void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) { |
14600 | int32_t min = lir->mir()->minimum(); |
14601 | int32_t max = lir->mir()->maximum(); |
14602 | MOZ_ASSERT(max >= min)do { static_assert( mozilla::detail::AssertionConditionType< decltype(max >= min)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(max >= min))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("max >= min", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14602); AnnotateMozCrashReason("MOZ_ASSERT" "(" "max >= min" ")"); do { *((volatile int*)__null) = 14602; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14603 | |
14604 | LSnapshot* snapshot = lir->snapshot(); |
14605 | MIRType type = lir->mir()->type(); |
14606 | |
14607 | const LAllocation* length = lir->length(); |
14608 | Register temp = ToRegister(lir->getTemp(0)); |
14609 | |
14610 | auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) { |
14611 | if (type == MIRType::Int32) { |
14612 | bailoutCmp32(cond, lhs, rhs, snapshot); |
14613 | } else { |
14614 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14614); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14614; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14615 | bailoutCmpPtr(cond, lhs, rhs, snapshot); |
14616 | } |
14617 | }; |
14618 | |
14619 | auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs, |
14620 | int32_t rhs) { |
14621 | if (type == MIRType::Int32) { |
14622 | bailoutCmp32(cond, lhs, Imm32(rhs), snapshot); |
14623 | } else { |
14624 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14624; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14625 | bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot); |
14626 | } |
14627 | }; |
14628 | |
14629 | if (lir->index()->isConstant()) { |
14630 | int32_t nmin, nmax; |
14631 | int32_t index = ToInt32(lir->index()); |
14632 | if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) { |
14633 | if (length->isRegister()) { |
14634 | bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax); |
14635 | } else { |
14636 | bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), nmax); |
14637 | } |
14638 | return; |
14639 | } |
14640 | masm.mov(ImmWord(index), temp); |
14641 | } else { |
14642 | masm.mov(ToRegister(lir->index()), temp); |
14643 | } |
14644 | |
14645 | // If the minimum and maximum differ then do an underflow check first. |
14646 | // If the two are the same then doing an unsigned comparison on the |
14647 | // length will also catch a negative index. |
14648 | if (min != max) { |
14649 | if (min != 0) { |
14650 | Label bail; |
14651 | if (type == MIRType::Int32) { |
14652 | masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail); |
14653 | } else { |
14654 | masm.branchAddPtr(Assembler::Overflow, Imm32(min), temp, &bail); |
14655 | } |
14656 | bailoutFrom(&bail, snapshot); |
14657 | } |
14658 | |
14659 | bailoutCmpConstant(Assembler::LessThan, temp, 0); |
14660 | |
14661 | if (min != 0) { |
14662 | int32_t diff; |
14663 | if (SafeSub(max, min, &diff)) { |
14664 | max = diff; |
14665 | } else { |
14666 | if (type == MIRType::Int32) { |
14667 | masm.sub32(Imm32(min), temp); |
14668 | } else { |
14669 | masm.subPtr(Imm32(min), temp); |
14670 | } |
14671 | } |
14672 | } |
14673 | } |
14674 | |
14675 | // Compute the maximum possible index. No overflow check is needed when |
14676 | // max > 0. We can only wraparound to a negative number, which will test as |
14677 | // larger than all nonnegative numbers in the unsigned comparison, and the |
14678 | // length is required to be nonnegative (else testing a negative length |
14679 | // would succeed on any nonnegative index). |
14680 | if (max != 0) { |
14681 | if (max < 0) { |
14682 | Label bail; |
14683 | if (type == MIRType::Int32) { |
14684 | masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail); |
14685 | } else { |
14686 | masm.branchAddPtr(Assembler::Overflow, Imm32(max), temp, &bail); |
14687 | } |
14688 | bailoutFrom(&bail, snapshot); |
14689 | } else { |
14690 | if (type == MIRType::Int32) { |
14691 | masm.add32(Imm32(max), temp); |
14692 | } else { |
14693 | masm.addPtr(Imm32(max), temp); |
14694 | } |
14695 | } |
14696 | } |
14697 | |
14698 | if (length->isRegister()) { |
14699 | bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), temp); |
14700 | } else { |
14701 | bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), temp); |
14702 | } |
14703 | } |
14704 | |
14705 | void CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir) { |
14706 | int32_t min = lir->mir()->minimum(); |
14707 | bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min), |
14708 | lir->snapshot()); |
14709 | } |
14710 | |
14711 | void CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir) { |
14712 | MOZ_ASSERT(JitOptions.spectreIndexMasking)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitOptions.spectreIndexMasking)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitOptions.spectreIndexMasking ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "JitOptions.spectreIndexMasking", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14712); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitOptions.spectreIndexMasking" ")"); do { *((volatile int*)__null) = 14712; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14713 | |
14714 | const LAllocation* length = lir->length(); |
14715 | Register index = ToRegister(lir->index()); |
14716 | Register output = ToRegister(lir->output()); |
14717 | |
14718 | if (lir->mir()->type() == MIRType::Int32) { |
14719 | if (length->isRegister()) { |
14720 | masm.spectreMaskIndex32(index, ToRegister(length), output); |
14721 | } else { |
14722 | masm.spectreMaskIndex32(index, ToAddress(length), output); |
14723 | } |
14724 | } else { |
14725 | MOZ_ASSERT(lir->mir()->type() == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::IntPtr)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::IntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14725); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14725; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14726 | if (length->isRegister()) { |
14727 | masm.spectreMaskIndexPtr(index, ToRegister(length), output); |
14728 | } else { |
14729 | masm.spectreMaskIndexPtr(index, ToAddress(length), output); |
14730 | } |
14731 | } |
14732 | } |
14733 | |
14734 | class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator> { |
14735 | LInstruction* ins_; |
14736 | |
14737 | public: |
14738 | explicit OutOfLineStoreElementHole(LInstruction* ins) : ins_(ins) { |
14739 | MOZ_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->isStoreElementHoleV() || ins->isStoreElementHoleT ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ins->isStoreElementHoleV() || ins->isStoreElementHoleT ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ins->isStoreElementHoleV() || ins->isStoreElementHoleT()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14739); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->isStoreElementHoleV() || ins->isStoreElementHoleT()" ")"); do { *((volatile int*)__null) = 14739; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14740 | } |
14741 | |
14742 | void accept(CodeGenerator* codegen) override { |
14743 | codegen->visitOutOfLineStoreElementHole(this); |
14744 | } |
14745 | |
14746 | MStoreElementHole* mir() const { |
14747 | return ins_->isStoreElementHoleV() ? ins_->toStoreElementHoleV()->mir() |
14748 | : ins_->toStoreElementHoleT()->mir(); |
14749 | } |
14750 | LInstruction* ins() const { return ins_; } |
14751 | }; |
14752 | |
14753 | void CodeGenerator::emitStoreHoleCheck(Register elements, |
14754 | const LAllocation* index, |
14755 | LSnapshot* snapshot) { |
14756 | Label bail; |
14757 | if (index->isConstant()) { |
14758 | Address dest(elements, ToInt32(index) * sizeof(js::Value)); |
14759 | masm.branchTestMagic(Assembler::Equal, dest, &bail); |
14760 | } else { |
14761 | BaseObjectElementIndex dest(elements, ToRegister(index)); |
14762 | masm.branchTestMagic(Assembler::Equal, dest, &bail); |
14763 | } |
14764 | bailoutFrom(&bail, snapshot); |
14765 | } |
14766 | |
14767 | void CodeGenerator::emitStoreElementTyped(const LAllocation* value, |
14768 | MIRType valueType, Register elements, |
14769 | const LAllocation* index) { |
14770 | MOZ_ASSERT(valueType != MIRType::MagicHole)do { static_assert( mozilla::detail::AssertionConditionType< decltype(valueType != MIRType::MagicHole)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(valueType != MIRType::MagicHole ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "valueType != MIRType::MagicHole", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14770); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole" ")"); do { *((volatile int*)__null) = 14770; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14771 | ConstantOrRegister v = ToConstantOrRegister(value, valueType); |
14772 | if (index->isConstant()) { |
14773 | Address dest(elements, ToInt32(index) * sizeof(js::Value)); |
14774 | masm.storeUnboxedValue(v, valueType, dest); |
14775 | } else { |
14776 | BaseObjectElementIndex dest(elements, ToRegister(index)); |
14777 | masm.storeUnboxedValue(v, valueType, dest); |
14778 | } |
14779 | } |
14780 | |
14781 | void CodeGenerator::visitStoreElementT(LStoreElementT* store) { |
14782 | Register elements = ToRegister(store->elements()); |
14783 | const LAllocation* index = store->index(); |
14784 | |
14785 | if (store->mir()->needsBarrier()) { |
14786 | emitPreBarrier(elements, index); |
14787 | } |
14788 | |
14789 | if (store->mir()->needsHoleCheck()) { |
14790 | emitStoreHoleCheck(elements, index, store->snapshot()); |
14791 | } |
14792 | |
14793 | emitStoreElementTyped(store->value(), store->mir()->value()->type(), elements, |
14794 | index); |
14795 | } |
14796 | |
14797 | void CodeGenerator::visitStoreElementV(LStoreElementV* lir) { |
14798 | const ValueOperand value = ToValue(lir, LStoreElementV::Value); |
14799 | Register elements = ToRegister(lir->elements()); |
14800 | const LAllocation* index = lir->index(); |
14801 | |
14802 | if (lir->mir()->needsBarrier()) { |
14803 | emitPreBarrier(elements, index); |
14804 | } |
14805 | |
14806 | if (lir->mir()->needsHoleCheck()) { |
14807 | emitStoreHoleCheck(elements, index, lir->snapshot()); |
14808 | } |
14809 | |
14810 | if (lir->index()->isConstant()) { |
14811 | Address dest(elements, ToInt32(lir->index()) * sizeof(js::Value)); |
14812 | masm.storeValue(value, dest); |
14813 | } else { |
14814 | BaseObjectElementIndex dest(elements, ToRegister(lir->index())); |
14815 | masm.storeValue(value, dest); |
14816 | } |
14817 | } |
14818 | |
14819 | void CodeGenerator::visitStoreHoleValueElement(LStoreHoleValueElement* lir) { |
14820 | Register elements = ToRegister(lir->elements()); |
14821 | Register index = ToRegister(lir->index()); |
14822 | |
14823 | Address elementsFlags(elements, ObjectElements::offsetOfFlags()); |
14824 | masm.or32(Imm32(ObjectElements::NON_PACKED), elementsFlags); |
14825 | |
14826 | BaseObjectElementIndex element(elements, index); |
14827 | masm.storeValue(MagicValue(JS_ELEMENTS_HOLE), element); |
14828 | } |
14829 | |
14830 | void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) { |
14831 | auto* ool = new (alloc()) OutOfLineStoreElementHole(lir); |
14832 | addOutOfLineCode(ool, lir->mir()); |
14833 | |
14834 | Register obj = ToRegister(lir->object()); |
14835 | Register elements = ToRegister(lir->elements()); |
14836 | Register index = ToRegister(lir->index()); |
14837 | Register temp = ToRegister(lir->temp0()); |
14838 | |
14839 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
14840 | masm.spectreBoundsCheck32(index, initLength, temp, ool->entry()); |
14841 | |
14842 | emitPreBarrier(elements, lir->index()); |
14843 | |
14844 | masm.bind(ool->rejoin()); |
14845 | emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), elements, |
14846 | lir->index()); |
14847 | |
14848 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
14849 | LiveRegisterSet regs = liveVolatileRegs(lir); |
14850 | ConstantOrRegister val = |
14851 | ToConstantOrRegister(lir->value(), lir->mir()->value()->type()); |
14852 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp, val); |
14853 | } |
14854 | } |
14855 | |
14856 | void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) { |
14857 | auto* ool = new (alloc()) OutOfLineStoreElementHole(lir); |
14858 | addOutOfLineCode(ool, lir->mir()); |
14859 | |
14860 | Register obj = ToRegister(lir->object()); |
14861 | Register elements = ToRegister(lir->elements()); |
14862 | Register index = ToRegister(lir->index()); |
14863 | const ValueOperand value = ToValue(lir, LStoreElementHoleV::ValueIndex); |
14864 | Register temp = ToRegister(lir->temp0()); |
14865 | |
14866 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
14867 | masm.spectreBoundsCheck32(index, initLength, temp, ool->entry()); |
14868 | |
14869 | emitPreBarrier(elements, lir->index()); |
14870 | |
14871 | masm.bind(ool->rejoin()); |
14872 | masm.storeValue(value, BaseObjectElementIndex(elements, index)); |
14873 | |
14874 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
14875 | LiveRegisterSet regs = liveVolatileRegs(lir); |
14876 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp, |
14877 | ConstantOrRegister(value)); |
14878 | } |
14879 | } |
14880 | |
14881 | void CodeGenerator::visitOutOfLineStoreElementHole( |
14882 | OutOfLineStoreElementHole* ool) { |
14883 | Register object, elements, index; |
14884 | LInstruction* ins = ool->ins(); |
14885 | mozilla::Maybe<ConstantOrRegister> value; |
14886 | Register temp; |
14887 | |
14888 | if (ins->isStoreElementHoleV()) { |
14889 | LStoreElementHoleV* store = ins->toStoreElementHoleV(); |
14890 | object = ToRegister(store->object()); |
14891 | elements = ToRegister(store->elements()); |
14892 | index = ToRegister(store->index()); |
14893 | value.emplace( |
14894 | TypedOrValueRegister(ToValue(store, LStoreElementHoleV::ValueIndex))); |
14895 | temp = ToRegister(store->temp0()); |
14896 | } else { |
14897 | LStoreElementHoleT* store = ins->toStoreElementHoleT(); |
14898 | object = ToRegister(store->object()); |
14899 | elements = ToRegister(store->elements()); |
14900 | index = ToRegister(store->index()); |
14901 | if (store->value()->isConstant()) { |
14902 | value.emplace( |
14903 | ConstantOrRegister(store->value()->toConstant()->toJSValue())); |
14904 | } else { |
14905 | MIRType valueType = store->mir()->value()->type(); |
14906 | value.emplace( |
14907 | TypedOrValueRegister(valueType, ToAnyRegister(store->value()))); |
14908 | } |
14909 | temp = ToRegister(store->temp0()); |
14910 | } |
14911 | |
14912 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
14913 | |
14914 | // We're out-of-bounds. We only handle the index == initlength case. |
14915 | // If index > initializedLength, bail out. Note that this relies on the |
14916 | // condition flags sticking from the incoming branch. |
14917 | // Also note: this branch does not need Spectre mitigations, doing that for |
14918 | // the capacity check below is sufficient. |
14919 | Label allocElement, addNewElement; |
14920 | #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ |
14921 | defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) |
14922 | // Had to reimplement for MIPS because there are no flags. |
14923 | bailoutCmp32(Assembler::NotEqual, initLength, index, ins->snapshot()); |
14924 | #else |
14925 | bailoutIf(Assembler::NotEqual, ins->snapshot()); |
14926 | #endif |
14927 | |
14928 | // If index < capacity, we can add a dense element inline. If not, we need |
14929 | // to allocate more elements first. |
14930 | masm.spectreBoundsCheck32( |
14931 | index, Address(elements, ObjectElements::offsetOfCapacity()), temp, |
14932 | &allocElement); |
14933 | masm.jump(&addNewElement); |
14934 | |
14935 | masm.bind(&allocElement); |
14936 | |
14937 | // Save all live volatile registers, except |temp|. |
14938 | LiveRegisterSet liveRegs = liveVolatileRegs(ins); |
14939 | liveRegs.takeUnchecked(temp); |
14940 | masm.PushRegsInMask(liveRegs); |
14941 | |
14942 | masm.setupAlignedABICall(); |
14943 | masm.loadJSContext(temp); |
14944 | masm.passABIArg(temp); |
14945 | masm.passABIArg(object); |
14946 | |
14947 | using Fn = bool (*)(JSContext*, NativeObject*); |
14948 | masm.callWithABI<Fn, NativeObject::addDenseElementPure>(); |
14949 | masm.storeCallPointerResult(temp); |
14950 | |
14951 | masm.PopRegsInMask(liveRegs); |
14952 | bailoutIfFalseBool(temp, ins->snapshot()); |
14953 | |
14954 | // Load the reallocated elements pointer. |
14955 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elements); |
14956 | |
14957 | masm.bind(&addNewElement); |
14958 | |
14959 | // Increment initLength |
14960 | masm.add32(Imm32(1), initLength); |
14961 | |
14962 | // If length is now <= index, increment length too. |
14963 | Label skipIncrementLength; |
14964 | Address length(elements, ObjectElements::offsetOfLength()); |
14965 | masm.branch32(Assembler::Above, length, index, &skipIncrementLength); |
14966 | masm.add32(Imm32(1), length); |
14967 | masm.bind(&skipIncrementLength); |
14968 | |
14969 | // Jump to the inline path where we will store the value. |
14970 | // We rejoin after the prebarrier, because the memory is uninitialized. |
14971 | masm.jump(ool->rejoin()); |
14972 | } |
14973 | |
14974 | void CodeGenerator::visitArrayPopShift(LArrayPopShift* lir) { |
14975 | Register obj = ToRegister(lir->object()); |
14976 | Register temp1 = ToRegister(lir->temp0()); |
14977 | Register temp2 = ToRegister(lir->temp1()); |
14978 | ValueOperand out = ToOutValue(lir); |
14979 | |
14980 | Label bail; |
14981 | if (lir->mir()->mode() == MArrayPopShift::Pop) { |
14982 | masm.packedArrayPop(obj, out, temp1, temp2, &bail); |
14983 | } else { |
14984 | MOZ_ASSERT(lir->mir()->mode() == MArrayPopShift::Shift)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->mode() == MArrayPopShift::Shift)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->mode() == MArrayPopShift::Shift))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->mode() == MArrayPopShift::Shift" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MArrayPopShift::Shift" ")"); do { *((volatile int*)__null) = 14984; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
14985 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
14986 | masm.packedArrayShift(obj, out, temp1, temp2, volatileRegs, &bail); |
14987 | } |
14988 | bailoutFrom(&bail, lir->snapshot()); |
14989 | } |
14990 | |
14991 | class OutOfLineArrayPush : public OutOfLineCodeBase<CodeGenerator> { |
14992 | LArrayPush* ins_; |
14993 | |
14994 | public: |
14995 | explicit OutOfLineArrayPush(LArrayPush* ins) : ins_(ins) {} |
14996 | |
14997 | void accept(CodeGenerator* codegen) override { |
14998 | codegen->visitOutOfLineArrayPush(this); |
14999 | } |
15000 | |
15001 | LArrayPush* ins() const { return ins_; } |
15002 | }; |
15003 | |
15004 | void CodeGenerator::visitArrayPush(LArrayPush* lir) { |
15005 | Register obj = ToRegister(lir->object()); |
15006 | Register elementsTemp = ToRegister(lir->temp0()); |
15007 | Register length = ToRegister(lir->output()); |
15008 | ValueOperand value = ToValue(lir, LArrayPush::ValueIndex); |
15009 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
15010 | |
15011 | auto* ool = new (alloc()) OutOfLineArrayPush(lir); |
15012 | addOutOfLineCode(ool, lir->mir()); |
15013 | |
15014 | // Load obj->elements in elementsTemp. |
15015 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); |
15016 | |
15017 | Address initLengthAddr(elementsTemp, |
15018 | ObjectElements::offsetOfInitializedLength()); |
15019 | Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength()); |
15020 | Address capacityAddr(elementsTemp, ObjectElements::offsetOfCapacity()); |
15021 | |
15022 | // Bail out if length != initLength. |
15023 | masm.load32(lengthAddr, length); |
15024 | bailoutCmp32(Assembler::NotEqual, initLengthAddr, length, lir->snapshot()); |
15025 | |
15026 | // If length < capacity, we can add a dense element inline. If not, we |
15027 | // need to allocate more elements. |
15028 | masm.spectreBoundsCheck32(length, capacityAddr, spectreTemp, ool->entry()); |
15029 | masm.bind(ool->rejoin()); |
15030 | |
15031 | // Store the value. |
15032 | masm.storeValue(value, BaseObjectElementIndex(elementsTemp, length)); |
15033 | |
15034 | // Update length and initialized length. |
15035 | masm.add32(Imm32(1), length); |
15036 | masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); |
15037 | masm.store32(length, Address(elementsTemp, |
15038 | ObjectElements::offsetOfInitializedLength())); |
15039 | |
15040 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
15041 | LiveRegisterSet regs = liveVolatileRegs(lir); |
15042 | regs.addUnchecked(length); |
15043 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->output()->output(), |
15044 | elementsTemp, ConstantOrRegister(value), |
15045 | /* indexDiff = */ -1); |
15046 | } |
15047 | } |
15048 | |
15049 | void CodeGenerator::visitOutOfLineArrayPush(OutOfLineArrayPush* ool) { |
15050 | LArrayPush* ins = ool->ins(); |
15051 | |
15052 | Register object = ToRegister(ins->object()); |
15053 | Register temp = ToRegister(ins->temp0()); |
15054 | |
15055 | LiveRegisterSet liveRegs = liveVolatileRegs(ins); |
15056 | liveRegs.takeUnchecked(temp); |
15057 | liveRegs.addUnchecked(ToRegister(ins->output())); |
15058 | liveRegs.addUnchecked(ToValue(ins, LArrayPush::ValueIndex)); |
15059 | |
15060 | masm.PushRegsInMask(liveRegs); |
15061 | |
15062 | masm.setupAlignedABICall(); |
15063 | masm.loadJSContext(temp); |
15064 | masm.passABIArg(temp); |
15065 | masm.passABIArg(object); |
15066 | |
15067 | using Fn = bool (*)(JSContext*, NativeObject* obj); |
15068 | masm.callWithABI<Fn, NativeObject::addDenseElementPure>(); |
15069 | masm.storeCallPointerResult(temp); |
15070 | |
15071 | masm.PopRegsInMask(liveRegs); |
15072 | bailoutIfFalseBool(temp, ins->snapshot()); |
15073 | |
15074 | // Load the reallocated elements pointer. |
15075 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
15076 | |
15077 | masm.jump(ool->rejoin()); |
15078 | } |
15079 | |
15080 | void CodeGenerator::visitArraySlice(LArraySlice* lir) { |
15081 | Register object = ToRegister(lir->object()); |
15082 | Register begin = ToRegister(lir->begin()); |
15083 | Register end = ToRegister(lir->end()); |
15084 | Register temp0 = ToRegister(lir->temp0()); |
15085 | Register temp1 = ToRegister(lir->temp1()); |
15086 | |
15087 | Label call, fail; |
15088 | |
15089 | Label bail; |
15090 | masm.branchArrayIsNotPacked(object, temp0, temp1, &bail); |
15091 | bailoutFrom(&bail, lir->snapshot()); |
15092 | |
15093 | // Try to allocate an object. |
15094 | TemplateObject templateObject(lir->mir()->templateObj()); |
15095 | masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(), |
15096 | &fail); |
15097 | |
15098 | masm.jump(&call); |
15099 | { |
15100 | masm.bind(&fail); |
15101 | masm.movePtr(ImmPtr(nullptr), temp0); |
15102 | } |
15103 | masm.bind(&call); |
15104 | |
15105 | pushArg(temp0); |
15106 | pushArg(end); |
15107 | pushArg(begin); |
15108 | pushArg(object); |
15109 | |
15110 | using Fn = |
15111 | JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); |
15112 | callVM<Fn, ArraySliceDense>(lir); |
15113 | } |
15114 | |
15115 | void CodeGenerator::visitArgumentsSlice(LArgumentsSlice* lir) { |
15116 | Register object = ToRegister(lir->object()); |
15117 | Register begin = ToRegister(lir->begin()); |
15118 | Register end = ToRegister(lir->end()); |
15119 | Register temp0 = ToRegister(lir->temp0()); |
15120 | Register temp1 = ToRegister(lir->temp1()); |
15121 | |
15122 | Label call, fail; |
15123 | |
15124 | // Try to allocate an object. |
15125 | TemplateObject templateObject(lir->mir()->templateObj()); |
15126 | masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(), |
15127 | &fail); |
15128 | |
15129 | masm.jump(&call); |
15130 | { |
15131 | masm.bind(&fail); |
15132 | masm.movePtr(ImmPtr(nullptr), temp0); |
15133 | } |
15134 | masm.bind(&call); |
15135 | |
15136 | pushArg(temp0); |
15137 | pushArg(end); |
15138 | pushArg(begin); |
15139 | pushArg(object); |
15140 | |
15141 | using Fn = |
15142 | JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); |
15143 | callVM<Fn, ArgumentsSliceDense>(lir); |
15144 | } |
15145 | |
15146 | #ifdef DEBUG1 |
15147 | void CodeGenerator::emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin, |
15148 | const RegisterOrInt32& count, |
15149 | Register numActualArgs) { |
15150 | // |begin| must be positive or zero. |
15151 | if (begin.is<Register>()) { |
15152 | Label beginOk; |
15153 | masm.branch32(Assembler::GreaterThanOrEqual, begin.as<Register>(), Imm32(0), |
15154 | &beginOk); |
15155 | masm.assumeUnreachable("begin < 0"); |
15156 | masm.bind(&beginOk); |
15157 | } else { |
15158 | MOZ_ASSERT(begin.as<int32_t>() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(begin.as<int32_t>() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(begin.as<int32_t>() >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("begin.as<int32_t>() >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15158); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() >= 0" ")"); do { *((volatile int*)__null) = 15158; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15159 | } |
15160 | |
15161 | // |count| must be positive or zero. |
15162 | if (count.is<Register>()) { |
15163 | Label countOk; |
15164 | masm.branch32(Assembler::GreaterThanOrEqual, count.as<Register>(), Imm32(0), |
15165 | &countOk); |
15166 | masm.assumeUnreachable("count < 0"); |
15167 | masm.bind(&countOk); |
15168 | } else { |
15169 | MOZ_ASSERT(count.as<int32_t>() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(count.as<int32_t>() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(count.as<int32_t>() >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("count.as<int32_t>() >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15169); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count.as<int32_t>() >= 0" ")"); do { *((volatile int*)__null) = 15169; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15170 | } |
15171 | |
15172 | // |begin| must be less-or-equal to |numActualArgs|. |
15173 | Label argsBeginOk; |
15174 | if (begin.is<Register>()) { |
15175 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(), |
15176 | &argsBeginOk); |
15177 | } else { |
15178 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
15179 | Imm32(begin.as<int32_t>()), &argsBeginOk); |
15180 | } |
15181 | masm.assumeUnreachable("begin <= numActualArgs"); |
15182 | masm.bind(&argsBeginOk); |
15183 | |
15184 | // |count| must be less-or-equal to |numActualArgs|. |
15185 | Label argsCountOk; |
15186 | if (count.is<Register>()) { |
15187 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, count.as<Register>(), |
15188 | &argsCountOk); |
15189 | } else { |
15190 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
15191 | Imm32(count.as<int32_t>()), &argsCountOk); |
15192 | } |
15193 | masm.assumeUnreachable("count <= numActualArgs"); |
15194 | masm.bind(&argsCountOk); |
15195 | |
15196 | // |begin| and |count| must be preserved, but |numActualArgs| can be changed. |
15197 | // |
15198 | // Pre-condition: |count| <= |numActualArgs| |
15199 | // Condition to test: |begin + count| <= |numActualArgs| |
15200 | // Transform to: |begin| <= |numActualArgs - count| |
15201 | if (count.is<Register>()) { |
15202 | masm.subPtr(count.as<Register>(), numActualArgs); |
15203 | } else { |
15204 | masm.subPtr(Imm32(count.as<int32_t>()), numActualArgs); |
15205 | } |
15206 | |
15207 | // |begin + count| must be less-or-equal to |numActualArgs|. |
15208 | Label argsBeginCountOk; |
15209 | if (begin.is<Register>()) { |
15210 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(), |
15211 | &argsBeginCountOk); |
15212 | } else { |
15213 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
15214 | Imm32(begin.as<int32_t>()), &argsBeginCountOk); |
15215 | } |
15216 | masm.assumeUnreachable("begin + count <= numActualArgs"); |
15217 | masm.bind(&argsBeginCountOk); |
15218 | } |
15219 | #endif |
15220 | |
15221 | template <class ArgumentsSlice> |
15222 | void CodeGenerator::emitNewArray(ArgumentsSlice* lir, |
15223 | const RegisterOrInt32& count, Register output, |
15224 | Register temp) { |
15225 | using Fn = ArrayObject* (*)(JSContext*, int32_t); |
15226 | auto* ool = count.match( |
15227 | [&](Register count) { |
15228 | return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>( |
15229 | lir, ArgList(count), StoreRegisterTo(output)); |
15230 | }, |
15231 | [&](int32_t count) { |
15232 | return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>( |
15233 | lir, ArgList(Imm32(count)), StoreRegisterTo(output)); |
15234 | }); |
15235 | |
15236 | TemplateObject templateObject(lir->mir()->templateObj()); |
15237 | MOZ_ASSERT(templateObject.isArrayObject())do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateObject.isArrayObject())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(templateObject.isArrayObject ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("templateObject.isArrayObject()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject.isArrayObject()" ")"); do { *((volatile int*)__null) = 15237; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15238 | |
15239 | auto templateNativeObj = templateObject.asTemplateNativeObject(); |
15240 | MOZ_ASSERT(templateNativeObj.getArrayLength() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateNativeObj.getArrayLength() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(templateNativeObj.getArrayLength() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("templateNativeObj.getArrayLength() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15240); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getArrayLength() == 0" ")"); do { *((volatile int*)__null) = 15240; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15241 | MOZ_ASSERT(templateNativeObj.getDenseInitializedLength() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateNativeObj.getDenseInitializedLength() == 0)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(templateNativeObj.getDenseInitializedLength() == 0)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("templateNativeObj.getDenseInitializedLength() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15241); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getDenseInitializedLength() == 0" ")"); do { *((volatile int*)__null) = 15241; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15242 | MOZ_ASSERT(!templateNativeObj.hasDynamicElements())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!templateNativeObj.hasDynamicElements())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(!templateNativeObj.hasDynamicElements()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!templateNativeObj.hasDynamicElements()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15242); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateNativeObj.hasDynamicElements()" ")"); do { *((volatile int*)__null) = 15242; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15243 | |
15244 | // Check array capacity. Call into the VM if the template object's capacity |
15245 | // is too small. |
15246 | bool tryAllocate = count.match( |
15247 | [&](Register count) { |
15248 | masm.branch32(Assembler::Above, count, |
15249 | Imm32(templateNativeObj.getDenseCapacity()), |
15250 | ool->entry()); |
15251 | return true; |
15252 | }, |
15253 | [&](int32_t count) { |
15254 | MOZ_ASSERT(count >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(count >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(count >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("count >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15254); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count >= 0" ")"); do { *((volatile int*)__null) = 15254; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15255 | if (uint32_t(count) > templateNativeObj.getDenseCapacity()) { |
15256 | masm.jump(ool->entry()); |
15257 | return false; |
15258 | } |
15259 | return true; |
15260 | }); |
15261 | |
15262 | if (tryAllocate) { |
15263 | // Try to allocate an object. |
15264 | masm.createGCObject(output, temp, templateObject, lir->mir()->initialHeap(), |
15265 | ool->entry()); |
15266 | |
15267 | auto setInitializedLengthAndLength = [&](auto count) { |
15268 | const int elementsOffset = NativeObject::offsetOfFixedElements(); |
15269 | |
15270 | // Update initialized length. |
15271 | Address initLength( |
15272 | output, elementsOffset + ObjectElements::offsetOfInitializedLength()); |
15273 | masm.store32(count, initLength); |
15274 | |
15275 | // Update length. |
15276 | Address length(output, elementsOffset + ObjectElements::offsetOfLength()); |
15277 | masm.store32(count, length); |
15278 | }; |
15279 | |
15280 | // The array object was successfully created. Set the length and initialized |
15281 | // length and then proceed to fill the elements. |
15282 | count.match([&](Register count) { setInitializedLengthAndLength(count); }, |
15283 | [&](int32_t count) { |
15284 | if (count > 0) { |
15285 | setInitializedLengthAndLength(Imm32(count)); |
15286 | } |
15287 | }); |
15288 | } |
15289 | |
15290 | masm.bind(ool->rejoin()); |
15291 | } |
15292 | |
15293 | void CodeGenerator::visitFrameArgumentsSlice(LFrameArgumentsSlice* lir) { |
15294 | Register begin = ToRegister(lir->begin()); |
15295 | Register count = ToRegister(lir->count()); |
15296 | Register temp = ToRegister(lir->temp0()); |
15297 | Register output = ToRegister(lir->output()); |
15298 | |
15299 | #ifdef DEBUG1 |
15300 | masm.loadNumActualArgs(FramePointer, temp); |
15301 | emitAssertArgumentsSliceBounds(RegisterOrInt32(begin), RegisterOrInt32(count), |
15302 | temp); |
15303 | #endif |
15304 | |
15305 | emitNewArray(lir, RegisterOrInt32(count), output, temp); |
15306 | |
15307 | Label done; |
15308 | masm.branch32(Assembler::Equal, count, Imm32(0), &done); |
15309 | { |
15310 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
15311 | allRegs.take(begin); |
15312 | allRegs.take(count); |
15313 | allRegs.take(temp); |
15314 | allRegs.take(output); |
15315 | |
15316 | ValueOperand value = allRegs.takeAnyValue(); |
15317 | |
15318 | LiveRegisterSet liveRegs; |
15319 | liveRegs.add(output); |
15320 | liveRegs.add(begin); |
15321 | liveRegs.add(value); |
15322 | |
15323 | masm.PushRegsInMask(liveRegs); |
15324 | |
15325 | // Initialize all elements. |
15326 | |
15327 | Register elements = output; |
15328 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
15329 | |
15330 | Register argIndex = begin; |
15331 | |
15332 | Register index = temp; |
15333 | masm.move32(Imm32(0), index); |
15334 | |
15335 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
15336 | BaseValueIndex argPtr(FramePointer, argIndex, argvOffset); |
15337 | |
15338 | Label loop; |
15339 | masm.bind(&loop); |
15340 | |
15341 | masm.loadValue(argPtr, value); |
15342 | |
15343 | // We don't need a pre-barrier, because the element at |index| is guaranteed |
15344 | // to be a non-GC thing (either uninitialized memory or the magic hole |
15345 | // value). |
15346 | masm.storeValue(value, BaseObjectElementIndex(elements, index)); |
15347 | |
15348 | masm.add32(Imm32(1), index); |
15349 | masm.add32(Imm32(1), argIndex); |
15350 | |
15351 | masm.branch32(Assembler::LessThan, index, count, &loop); |
15352 | |
15353 | masm.PopRegsInMask(liveRegs); |
15354 | |
15355 | // Emit a post-write barrier if |output| is tenured. |
15356 | // |
15357 | // We expect that |output| is nursery allocated, so it isn't worth the |
15358 | // trouble to check if no frame argument is a nursery thing, which would |
15359 | // allow to omit the post-write barrier. |
15360 | masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done); |
15361 | |
15362 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
15363 | volatileRegs.takeUnchecked(temp); |
15364 | if (output.volatile_()) { |
15365 | volatileRegs.addUnchecked(output); |
15366 | } |
15367 | |
15368 | masm.PushRegsInMask(volatileRegs); |
15369 | emitPostWriteBarrier(output); |
15370 | masm.PopRegsInMask(volatileRegs); |
15371 | } |
15372 | masm.bind(&done); |
15373 | } |
15374 | |
15375 | CodeGenerator::RegisterOrInt32 CodeGenerator::ToRegisterOrInt32( |
15376 | const LAllocation* allocation) { |
15377 | if (allocation->isConstant()) { |
15378 | return RegisterOrInt32(allocation->toConstant()->toInt32()); |
15379 | } |
15380 | return RegisterOrInt32(ToRegister(allocation)); |
15381 | } |
15382 | |
15383 | void CodeGenerator::visitInlineArgumentsSlice(LInlineArgumentsSlice* lir) { |
15384 | RegisterOrInt32 begin = ToRegisterOrInt32(lir->begin()); |
15385 | RegisterOrInt32 count = ToRegisterOrInt32(lir->count()); |
15386 | Register temp = ToRegister(lir->temp()); |
15387 | Register output = ToRegister(lir->output()); |
15388 | |
15389 | uint32_t numActuals = lir->mir()->numActuals(); |
15390 | |
15391 | #ifdef DEBUG1 |
15392 | masm.move32(Imm32(numActuals), temp); |
15393 | |
15394 | emitAssertArgumentsSliceBounds(begin, count, temp); |
15395 | #endif |
15396 | |
15397 | emitNewArray(lir, count, output, temp); |
15398 | |
15399 | // We're done if there are no actual arguments. |
15400 | if (numActuals == 0) { |
15401 | return; |
15402 | } |
15403 | |
15404 | // Check if any arguments have to be copied. |
15405 | Label done; |
15406 | if (count.is<Register>()) { |
15407 | masm.branch32(Assembler::Equal, count.as<Register>(), Imm32(0), &done); |
15408 | } else if (count.as<int32_t>() == 0) { |
15409 | return; |
15410 | } |
15411 | |
15412 | auto getArg = [&](uint32_t i) { |
15413 | return toConstantOrRegister(lir, LInlineArgumentsSlice::ArgIndex(i), |
15414 | lir->mir()->getArg(i)->type()); |
15415 | }; |
15416 | |
15417 | auto storeArg = [&](uint32_t i, auto dest) { |
15418 | // We don't need a pre-barrier because the element at |index| is guaranteed |
15419 | // to be a non-GC thing (either uninitialized memory or the magic hole |
15420 | // value). |
15421 | masm.storeConstantOrRegister(getArg(i), dest); |
15422 | }; |
15423 | |
15424 | // Initialize all elements. |
15425 | if (numActuals == 1) { |
15426 | // There's exactly one argument. We've checked that |count| is non-zero, |
15427 | // which implies that |begin| must be zero. |
15428 | MOZ_ASSERT_IF(begin.is<int32_t>(), begin.as<int32_t>() == 0)do { if (begin.is<int32_t>()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(begin.as<int32_t >() == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(begin.as<int32_t>() == 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("begin.as<int32_t>() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15428); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() == 0" ")"); do { *((volatile int*)__null) = 15428; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
15429 | |
15430 | Register elements = temp; |
15431 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
15432 | |
15433 | storeArg(0, Address(elements, 0)); |
15434 | } else if (begin.is<Register>()) { |
15435 | // There is more than one argument and |begin| isn't a compile-time |
15436 | // constant. Iterate through 0..numActuals to search for |begin| and then |
15437 | // start copying |count| arguments from that index. |
15438 | |
15439 | LiveGeneralRegisterSet liveRegs; |
15440 | liveRegs.add(output); |
15441 | liveRegs.add(begin.as<Register>()); |
15442 | |
15443 | masm.PushRegsInMask(liveRegs); |
15444 | |
15445 | Register elements = output; |
15446 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
15447 | |
15448 | Register argIndex = begin.as<Register>(); |
15449 | |
15450 | Register index = temp; |
15451 | masm.move32(Imm32(0), index); |
15452 | |
15453 | Label doneLoop; |
15454 | for (uint32_t i = 0; i < numActuals; ++i) { |
15455 | Label next; |
15456 | masm.branch32(Assembler::NotEqual, argIndex, Imm32(i), &next); |
15457 | |
15458 | storeArg(i, BaseObjectElementIndex(elements, index)); |
15459 | |
15460 | masm.add32(Imm32(1), index); |
15461 | masm.add32(Imm32(1), argIndex); |
15462 | |
15463 | if (count.is<Register>()) { |
15464 | masm.branch32(Assembler::GreaterThanOrEqual, index, |
15465 | count.as<Register>(), &doneLoop); |
15466 | } else { |
15467 | masm.branch32(Assembler::GreaterThanOrEqual, index, |
15468 | Imm32(count.as<int32_t>()), &doneLoop); |
15469 | } |
15470 | |
15471 | masm.bind(&next); |
15472 | } |
15473 | masm.bind(&doneLoop); |
15474 | |
15475 | masm.PopRegsInMask(liveRegs); |
15476 | } else { |
15477 | // There is more than one argument and |begin| is a compile-time constant. |
15478 | |
15479 | Register elements = temp; |
15480 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
15481 | |
15482 | int32_t argIndex = begin.as<int32_t>(); |
15483 | |
15484 | int32_t index = 0; |
15485 | |
15486 | Label doneLoop; |
15487 | for (uint32_t i = argIndex; i < numActuals; ++i) { |
15488 | storeArg(i, Address(elements, index * sizeof(Value))); |
15489 | |
15490 | index += 1; |
15491 | |
15492 | if (count.is<Register>()) { |
15493 | masm.branch32(Assembler::LessThanOrEqual, count.as<Register>(), |
15494 | Imm32(index), &doneLoop); |
15495 | } else { |
15496 | if (index >= count.as<int32_t>()) { |
15497 | break; |
15498 | } |
15499 | } |
15500 | } |
15501 | masm.bind(&doneLoop); |
15502 | } |
15503 | |
15504 | // Determine if we have to emit post-write barrier. |
15505 | // |
15506 | // If either |begin| or |count| is a constant, use their value directly. |
15507 | // Otherwise assume we copy all inline arguments from 0..numActuals. |
15508 | bool postWriteBarrier = false; |
15509 | uint32_t actualBegin = begin.match([](Register) { return 0; }, |
15510 | [](int32_t value) { return value; }); |
15511 | uint32_t actualCount = |
15512 | count.match([=](Register) { return numActuals; }, |
15513 | [](int32_t value) -> uint32_t { return value; }); |
15514 | for (uint32_t i = 0; i < actualCount; ++i) { |
15515 | ConstantOrRegister arg = getArg(actualBegin + i); |
15516 | if (arg.constant()) { |
15517 | Value v = arg.value(); |
15518 | if (v.isGCThing() && IsInsideNursery(v.toGCThing())) { |
15519 | postWriteBarrier = true; |
15520 | } |
15521 | } else { |
15522 | MIRType type = arg.reg().type(); |
15523 | if (type == MIRType::Value || NeedsPostBarrier(type)) { |
15524 | postWriteBarrier = true; |
15525 | } |
15526 | } |
15527 | } |
15528 | |
15529 | // Emit a post-write barrier if |output| is tenured and we couldn't |
15530 | // determine at compile-time that no barrier is needed. |
15531 | if (postWriteBarrier) { |
15532 | masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done); |
15533 | |
15534 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
15535 | volatileRegs.takeUnchecked(temp); |
15536 | if (output.volatile_()) { |
15537 | volatileRegs.addUnchecked(output); |
15538 | } |
15539 | |
15540 | masm.PushRegsInMask(volatileRegs); |
15541 | emitPostWriteBarrier(output); |
15542 | masm.PopRegsInMask(volatileRegs); |
15543 | } |
15544 | |
15545 | masm.bind(&done); |
15546 | } |
15547 | |
15548 | void CodeGenerator::visitNormalizeSliceTerm(LNormalizeSliceTerm* lir) { |
15549 | Register value = ToRegister(lir->value()); |
15550 | Register length = ToRegister(lir->length()); |
15551 | Register output = ToRegister(lir->output()); |
15552 | |
15553 | masm.move32(value, output); |
15554 | |
15555 | Label positive; |
15556 | masm.branch32(Assembler::GreaterThanOrEqual, value, Imm32(0), &positive); |
15557 | |
15558 | Label done; |
15559 | masm.add32(length, output); |
15560 | masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(0), &done); |
15561 | masm.move32(Imm32(0), output); |
15562 | masm.jump(&done); |
15563 | |
15564 | masm.bind(&positive); |
15565 | masm.cmp32Move32(Assembler::LessThan, length, value, length, output); |
15566 | |
15567 | masm.bind(&done); |
15568 | } |
15569 | |
15570 | void CodeGenerator::visitArrayJoin(LArrayJoin* lir) { |
15571 | Label skipCall; |
15572 | |
15573 | Register output = ToRegister(lir->output()); |
15574 | Register sep = ToRegister(lir->separator()); |
15575 | Register array = ToRegister(lir->array()); |
15576 | Register temp = ToRegister(lir->temp0()); |
15577 | |
15578 | // Fast path for simple length <= 1 cases. |
15579 | { |
15580 | masm.loadPtr(Address(array, NativeObject::offsetOfElements()), temp); |
15581 | Address length(temp, ObjectElements::offsetOfLength()); |
15582 | Address initLength(temp, ObjectElements::offsetOfInitializedLength()); |
15583 | |
15584 | // Check for length == 0 |
15585 | Label notEmpty; |
15586 | masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬Empty); |
15587 | const JSAtomState& names = gen->runtime->names(); |
15588 | masm.movePtr(ImmGCPtr(names.empty_), output); |
15589 | masm.jump(&skipCall); |
15590 | |
15591 | masm.bind(¬Empty); |
15592 | Label notSingleString; |
15593 | // Check for length == 1, initializedLength >= 1, arr[0].isString() |
15594 | masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleString); |
15595 | masm.branch32(Assembler::LessThan, initLength, Imm32(1), ¬SingleString); |
15596 | |
15597 | Address elem0(temp, 0); |
15598 | masm.branchTestString(Assembler::NotEqual, elem0, ¬SingleString); |
15599 | |
15600 | // At this point, 'output' can be used as a scratch register, since we're |
15601 | // guaranteed to succeed. |
15602 | masm.unboxString(elem0, output); |
15603 | masm.jump(&skipCall); |
15604 | masm.bind(¬SingleString); |
15605 | } |
15606 | |
15607 | pushArg(sep); |
15608 | pushArg(array); |
15609 | |
15610 | using Fn = JSString* (*)(JSContext*, HandleObject, HandleString); |
15611 | callVM<Fn, jit::ArrayJoin>(lir); |
15612 | masm.bind(&skipCall); |
15613 | } |
15614 | |
15615 | void CodeGenerator::visitObjectKeys(LObjectKeys* lir) { |
15616 | Register object = ToRegister(lir->object()); |
15617 | |
15618 | pushArg(object); |
15619 | |
15620 | using Fn = JSObject* (*)(JSContext*, HandleObject); |
15621 | callVM<Fn, jit::ObjectKeys>(lir); |
15622 | } |
15623 | |
15624 | void CodeGenerator::visitObjectKeysLength(LObjectKeysLength* lir) { |
15625 | Register object = ToRegister(lir->object()); |
15626 | |
15627 | pushArg(object); |
15628 | |
15629 | using Fn = bool (*)(JSContext*, HandleObject, int32_t*); |
15630 | callVM<Fn, jit::ObjectKeysLength>(lir); |
15631 | } |
15632 | |
15633 | void CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir) { |
15634 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
15635 | TypedOrValueRegister val = |
15636 | toConstantOrRegister(lir, LGetIteratorCache::ValueIndex, |
15637 | lir->mir()->value()->type()) |
15638 | .reg(); |
15639 | Register output = ToRegister(lir->output()); |
15640 | Register temp0 = ToRegister(lir->temp0()); |
15641 | Register temp1 = ToRegister(lir->temp1()); |
15642 | |
15643 | IonGetIteratorIC ic(liveRegs, val, output, temp0, temp1); |
15644 | addIC(lir, allocateIC(ic)); |
15645 | } |
15646 | |
15647 | void CodeGenerator::visitOptimizeSpreadCallCache( |
15648 | LOptimizeSpreadCallCache* lir) { |
15649 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
15650 | ValueOperand val = ToValue(lir, LOptimizeSpreadCallCache::ValueIndex); |
15651 | ValueOperand output = ToOutValue(lir); |
15652 | Register temp = ToRegister(lir->temp0()); |
15653 | |
15654 | IonOptimizeSpreadCallIC ic(liveRegs, val, output, temp); |
15655 | addIC(lir, allocateIC(ic)); |
15656 | } |
15657 | |
15658 | void CodeGenerator::visitCloseIterCache(LCloseIterCache* lir) { |
15659 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
15660 | Register iter = ToRegister(lir->iter()); |
15661 | Register temp = ToRegister(lir->temp0()); |
15662 | CompletionKind kind = CompletionKind(lir->mir()->completionKind()); |
15663 | |
15664 | IonCloseIterIC ic(liveRegs, iter, temp, kind); |
15665 | addIC(lir, allocateIC(ic)); |
15666 | } |
15667 | |
15668 | void CodeGenerator::visitOptimizeGetIteratorCache( |
15669 | LOptimizeGetIteratorCache* lir) { |
15670 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
15671 | ValueOperand val = ToValue(lir, LOptimizeGetIteratorCache::ValueIndex); |
15672 | Register output = ToRegister(lir->output()); |
15673 | Register temp = ToRegister(lir->temp0()); |
15674 | |
15675 | IonOptimizeGetIteratorIC ic(liveRegs, val, output, temp); |
15676 | addIC(lir, allocateIC(ic)); |
15677 | } |
15678 | |
15679 | void CodeGenerator::visitIteratorMore(LIteratorMore* lir) { |
15680 | const Register obj = ToRegister(lir->iterator()); |
15681 | const ValueOperand output = ToOutValue(lir); |
15682 | const Register temp = ToRegister(lir->temp0()); |
15683 | |
15684 | masm.iteratorMore(obj, output, temp); |
15685 | } |
15686 | |
15687 | void CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch* lir) { |
15688 | ValueOperand input = ToValue(lir, LIsNoIterAndBranch::Input); |
15689 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
15690 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
15691 | |
15692 | masm.branchTestMagic(Assembler::Equal, input, ifTrue); |
15693 | |
15694 | if (!isNextBlock(lir->ifFalse()->lir())) { |
15695 | masm.jump(ifFalse); |
15696 | } |
15697 | } |
15698 | |
15699 | void CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) { |
15700 | const Register obj = ToRegister(lir->object()); |
15701 | const Register temp0 = ToRegister(lir->temp0()); |
15702 | const Register temp1 = ToRegister(lir->temp1()); |
15703 | const Register temp2 = ToRegister(lir->temp2()); |
15704 | |
15705 | masm.iteratorClose(obj, temp0, temp1, temp2); |
15706 | } |
15707 | |
15708 | void CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) { |
15709 | // read number of actual arguments from the JS frame. |
15710 | Register argc = ToRegister(lir->output()); |
15711 | masm.loadNumActualArgs(FramePointer, argc); |
15712 | } |
15713 | |
15714 | void CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) { |
15715 | ValueOperand result = ToOutValue(lir); |
15716 | const LAllocation* index = lir->index(); |
15717 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
15718 | |
15719 | // This instruction is used to access actual arguments and formal arguments. |
15720 | // The number of Values on the stack is |max(numFormals, numActuals)|, so we |
15721 | // assert |index < numFormals || index < numActuals| in debug builds. |
15722 | DebugOnly<size_t> numFormals = gen->outerInfo().script()->function()->nargs(); |
15723 | |
15724 | if (index->isConstant()) { |
15725 | int32_t i = index->toConstant()->toInt32(); |
15726 | #ifdef DEBUG1 |
15727 | if (uint32_t(i) >= numFormals) { |
15728 | Label ok; |
15729 | Register argc = result.scratchReg(); |
15730 | masm.loadNumActualArgs(FramePointer, argc); |
15731 | masm.branch32(Assembler::Above, argc, Imm32(i), &ok); |
15732 | masm.assumeUnreachable("Invalid argument index"); |
15733 | masm.bind(&ok); |
15734 | } |
15735 | #endif |
15736 | Address argPtr(FramePointer, sizeof(Value) * i + argvOffset); |
15737 | masm.loadValue(argPtr, result); |
15738 | } else { |
15739 | Register i = ToRegister(index); |
15740 | #ifdef DEBUG1 |
15741 | Label ok; |
15742 | Register argc = result.scratchReg(); |
15743 | masm.branch32(Assembler::Below, i, Imm32(numFormals), &ok); |
15744 | masm.loadNumActualArgs(FramePointer, argc); |
15745 | masm.branch32(Assembler::Above, argc, i, &ok); |
15746 | masm.assumeUnreachable("Invalid argument index"); |
15747 | masm.bind(&ok); |
15748 | #endif |
15749 | BaseValueIndex argPtr(FramePointer, i, argvOffset); |
15750 | masm.loadValue(argPtr, result); |
15751 | } |
15752 | } |
15753 | |
15754 | void CodeGenerator::visitGetFrameArgumentHole(LGetFrameArgumentHole* lir) { |
15755 | ValueOperand result = ToOutValue(lir); |
15756 | Register index = ToRegister(lir->index()); |
15757 | Register length = ToRegister(lir->length()); |
15758 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0()); |
15759 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
15760 | |
15761 | Label outOfBounds, done; |
15762 | masm.spectreBoundsCheck32(index, length, spectreTemp, &outOfBounds); |
15763 | |
15764 | BaseValueIndex argPtr(FramePointer, index, argvOffset); |
15765 | masm.loadValue(argPtr, result); |
15766 | masm.jump(&done); |
15767 | |
15768 | masm.bind(&outOfBounds); |
15769 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
15770 | masm.moveValue(UndefinedValue(), result); |
15771 | |
15772 | masm.bind(&done); |
15773 | } |
15774 | |
15775 | void CodeGenerator::visitRest(LRest* lir) { |
15776 | Register numActuals = ToRegister(lir->numActuals()); |
15777 | Register temp0 = ToRegister(lir->temp0()); |
15778 | Register temp1 = ToRegister(lir->temp1()); |
15779 | Register temp2 = ToRegister(lir->temp2()); |
15780 | Register temp3 = ToRegister(lir->temp3()); |
15781 | unsigned numFormals = lir->mir()->numFormals(); |
15782 | |
15783 | constexpr uint32_t arrayCapacity = 2; |
15784 | |
15785 | if (Shape* shape = lir->mir()->shape()) { |
15786 | uint32_t arrayLength = 0; |
15787 | gc::AllocKind allocKind = GuessArrayGCKind(arrayCapacity); |
15788 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(allocKind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15788); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 15788; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15789 | allocKind = ForegroundToBackgroundAllocKind(allocKind); |
15790 | MOZ_ASSERT(GetGCKindSlots(allocKind) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements ::VALUES_PER_HEADER)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 15791; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
15791 | arrayCapacity + ObjectElements::VALUES_PER_HEADER)do { static_assert( mozilla::detail::AssertionConditionType< decltype(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements ::VALUES_PER_HEADER)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 15791; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15792 | |
15793 | Label joinAlloc, failAlloc; |
15794 | masm.movePtr(ImmGCPtr(shape), temp0); |
15795 | masm.createArrayWithFixedElements(temp2, temp0, temp1, InvalidReg, |
15796 | arrayLength, arrayCapacity, 0, 0, |
15797 | allocKind, gc::Heap::Default, &failAlloc); |
15798 | masm.jump(&joinAlloc); |
15799 | { |
15800 | masm.bind(&failAlloc); |
15801 | masm.movePtr(ImmPtr(nullptr), temp2); |
15802 | } |
15803 | masm.bind(&joinAlloc); |
15804 | } else { |
15805 | masm.movePtr(ImmPtr(nullptr), temp2); |
15806 | } |
15807 | |
15808 | // Set temp1 to the address of the first actual argument. |
15809 | size_t actualsOffset = JitFrameLayout::offsetOfActualArgs(); |
15810 | masm.computeEffectiveAddress(Address(FramePointer, actualsOffset), temp1); |
15811 | |
15812 | // Compute array length: max(numActuals - numFormals, 0). |
15813 | Register lengthReg; |
15814 | if (numFormals) { |
15815 | lengthReg = temp0; |
15816 | Label emptyLength, joinLength; |
15817 | masm.branch32(Assembler::LessThanOrEqual, numActuals, Imm32(numFormals), |
15818 | &emptyLength); |
15819 | { |
15820 | masm.move32(numActuals, lengthReg); |
15821 | masm.sub32(Imm32(numFormals), lengthReg); |
15822 | |
15823 | // Skip formal arguments. |
15824 | masm.addPtr(Imm32(sizeof(Value) * numFormals), temp1); |
15825 | |
15826 | masm.jump(&joinLength); |
15827 | } |
15828 | masm.bind(&emptyLength); |
15829 | { |
15830 | masm.move32(Imm32(0), lengthReg); |
15831 | |
15832 | // Leave temp1 pointed to the start of actuals() when the rest-array |
15833 | // length is zero. We don't use |actuals() + numFormals| because |
15834 | // |numFormals| can be any non-negative int32 value when this MRest was |
15835 | // created from scalar replacement optimizations. And it seems |
15836 | // questionable to compute a Value* pointer which points to who knows |
15837 | // where. |
15838 | } |
15839 | masm.bind(&joinLength); |
15840 | } else { |
15841 | // Use numActuals directly when there are no formals. |
15842 | lengthReg = numActuals; |
15843 | } |
15844 | |
15845 | // Try to initialize the array elements. |
15846 | Label vmCall, done; |
15847 | if (lir->mir()->shape()) { |
15848 | // Call into C++ if we failed to allocate an array or there are more than |
15849 | // |arrayCapacity| elements. |
15850 | masm.branchTestPtr(Assembler::Zero, temp2, temp2, &vmCall); |
15851 | masm.branch32(Assembler::Above, lengthReg, Imm32(arrayCapacity), &vmCall); |
15852 | |
15853 | // The array must be nursery allocated so no post barrier is needed. |
15854 | #ifdef DEBUG1 |
15855 | Label ok; |
15856 | masm.branchPtrInNurseryChunk(Assembler::Equal, temp2, temp3, &ok); |
15857 | masm.assumeUnreachable("Unexpected tenured object for LRest"); |
15858 | masm.bind(&ok); |
15859 | #endif |
15860 | |
15861 | Label initialized; |
15862 | masm.branch32(Assembler::Equal, lengthReg, Imm32(0), &initialized); |
15863 | |
15864 | // Store length and initializedLength. |
15865 | Register elements = temp3; |
15866 | masm.loadPtr(Address(temp2, NativeObject::offsetOfElements()), elements); |
15867 | Address lengthAddr(elements, ObjectElements::offsetOfLength()); |
15868 | Address initLengthAddr(elements, |
15869 | ObjectElements::offsetOfInitializedLength()); |
15870 | masm.store32(lengthReg, lengthAddr); |
15871 | masm.store32(lengthReg, initLengthAddr); |
15872 | |
15873 | // Store either one or two elements. This may clobber lengthReg (temp0). |
15874 | static_assert(arrayCapacity == 2, "code handles 1 or 2 elements"); |
15875 | Label storeFirst; |
15876 | masm.branch32(Assembler::Equal, lengthReg, Imm32(1), &storeFirst); |
15877 | masm.storeValue(Address(temp1, sizeof(Value)), |
15878 | Address(elements, sizeof(Value)), temp0); |
15879 | masm.bind(&storeFirst); |
15880 | masm.storeValue(Address(temp1, 0), Address(elements, 0), temp0); |
15881 | |
15882 | // Done. |
15883 | masm.bind(&initialized); |
15884 | masm.movePtr(temp2, ReturnReg); |
15885 | masm.jump(&done); |
15886 | } |
15887 | |
15888 | masm.bind(&vmCall); |
15889 | |
15890 | pushArg(temp2); |
15891 | pushArg(temp1); |
15892 | pushArg(lengthReg); |
15893 | |
15894 | using Fn = |
15895 | ArrayObject* (*)(JSContext*, uint32_t, Value*, Handle<ArrayObject*>); |
15896 | callVM<Fn, InitRestParameter>(lir); |
15897 | |
15898 | masm.bind(&done); |
15899 | } |
15900 | |
15901 | // Create a stackmap from the given safepoint, with the structure: |
15902 | // |
15903 | // <reg dump, if any> |
15904 | // | ++ <body (general spill)> |
15905 | // | | ++ <space for Frame> |
15906 | // | | ++ <inbound args> |
15907 | // | | | |
15908 | // Lowest Addr Highest Addr |
15909 | // | |
15910 | // framePushedAtStackMapBase |
15911 | // |
15912 | // The caller owns the resulting stackmap. This assumes a grow-down stack. |
15913 | // |
15914 | // For non-debug builds, if the stackmap would contain no pointers, no |
15915 | // stackmap is created, and nullptr is returned. For a debug build, a |
15916 | // stackmap is always created and returned. |
15917 | // |
15918 | // Depending on the type of safepoint, the stackmap may need to account for |
15919 | // spilled registers. WasmSafepointKind::LirCall corresponds to LIR nodes where |
15920 | // isCall() == true, for which the register allocator will spill/restore all |
15921 | // live registers at the LIR level - in this case, the LSafepoint sees only live |
15922 | // values on the stack, never in registers. WasmSafepointKind::CodegenCall, on |
15923 | // the other hand, is for LIR nodes which may manually spill/restore live |
15924 | // registers in codegen, in which case the stackmap must account for this. Traps |
15925 | // also require tracking of live registers, but spilling is handled by the trap |
15926 | // mechanism. |
15927 | static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint, |
15928 | const RegisterOffsets& trapExitLayout, |
15929 | size_t trapExitLayoutNumWords, |
15930 | size_t nInboundStackArgBytes, |
15931 | wasm::StackMap** result) { |
15932 | // Ensure this is defined on all return paths. |
15933 | *result = nullptr; |
15934 | |
15935 | // The size of the wasm::Frame itself. |
15936 | const size_t nFrameBytes = sizeof(wasm::Frame); |
15937 | |
15938 | // This is the number of bytes spilled for live registers, outside of a trap. |
15939 | // For traps, trapExitLayout and trapExitLayoutNumWords will be used. |
15940 | const size_t nRegisterDumpBytes = |
15941 | MacroAssembler::PushRegsInMaskSizeInBytes(safepoint.liveRegs()); |
15942 | |
15943 | // As mentioned above, for WasmSafepointKind::LirCall, register spills and |
15944 | // restores are handled at the LIR level and there should therefore be no live |
15945 | // registers to handle here. |
15946 | MOZ_ASSERT_IF(safepoint.wasmSafepointKind() == WasmSafepointKind::LirCall,do { if (safepoint.wasmSafepointKind() == WasmSafepointKind:: LirCall) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(nRegisterDumpBytes == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(nRegisterDumpBytes == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15947); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { *((volatile int*)__null) = 15947; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
15947 | nRegisterDumpBytes == 0)do { if (safepoint.wasmSafepointKind() == WasmSafepointKind:: LirCall) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(nRegisterDumpBytes == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(nRegisterDumpBytes == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15947); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { *((volatile int*)__null) = 15947; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
15948 | MOZ_ASSERT(nRegisterDumpBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nRegisterDumpBytes % sizeof(void*) == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nRegisterDumpBytes % sizeof(void*) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes % sizeof(void*) == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15948); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 15948; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15949 | |
15950 | // This is the number of bytes in the general spill area, below the Frame. |
15951 | const size_t nBodyBytes = safepoint.framePushedAtStackMapBase(); |
15952 | |
15953 | // The stack map owns any alignment padding around inbound stack args. |
15954 | const size_t nInboundStackArgBytesAligned = |
15955 | wasm::AlignStackArgAreaSize(nInboundStackArgBytes); |
15956 | |
15957 | // This is the number of bytes in the general spill area, the Frame, and the |
15958 | // incoming args, but not including any register dump area. |
15959 | const size_t nNonRegisterBytes = |
15960 | nBodyBytes + nFrameBytes + nInboundStackArgBytesAligned; |
15961 | MOZ_ASSERT(nNonRegisterBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nNonRegisterBytes % sizeof(void*) == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nNonRegisterBytes % sizeof(void*) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nNonRegisterBytes % sizeof(void*) == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15961); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nNonRegisterBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 15961; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
15962 | |
15963 | // This is the number of bytes in the register dump area, if any, below the |
15964 | // general spill area. |
15965 | const size_t nRegisterBytes = |
15966 | (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) |
15967 | ? (trapExitLayoutNumWords * sizeof(void*)) |
15968 | : nRegisterDumpBytes; |
15969 | |
15970 | // This is the total number of bytes covered by the map. |
15971 | const size_t nTotalBytes = nNonRegisterBytes + nRegisterBytes; |
15972 | |
15973 | #ifndef DEBUG1 |
15974 | bool needStackMap = !(safepoint.wasmAnyRefRegs().empty() && |
15975 | safepoint.wasmAnyRefSlots().empty() && |
15976 | safepoint.slotsOrElementsSlots().empty()); |
15977 | |
15978 | // There are no references, and this is a non-debug build, so don't bother |
15979 | // building the stackmap. |
15980 | if (!needStackMap) { |
15981 | return true; |
15982 | } |
15983 | #endif |
15984 | |
15985 | wasm::StackMap* stackMap = |
15986 | wasm::StackMap::create(nTotalBytes / sizeof(void*)); |
15987 | if (!stackMap) { |
15988 | return false; |
15989 | } |
15990 | if (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) { |
15991 | stackMap->setExitStubWords(trapExitLayoutNumWords); |
15992 | } |
15993 | |
15994 | // REG DUMP AREA, if any. |
15995 | size_t regDumpWords = 0; |
15996 | const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs(); |
15997 | const LiveGeneralRegisterSet slotsOrElementsRegs = |
15998 | safepoint.slotsOrElementsRegs(); |
15999 | const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union( |
16000 | wasmAnyRefRegs.set(), slotsOrElementsRegs.set())); |
16001 | GeneralRegisterForwardIterator refRegsIter(refRegs); |
16002 | switch (safepoint.wasmSafepointKind()) { |
16003 | case WasmSafepointKind::LirCall: |
16004 | case WasmSafepointKind::StackSwitch: |
16005 | case WasmSafepointKind::CodegenCall: { |
16006 | size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*); |
16007 | regDumpWords += spilledNumWords; |
16008 | |
16009 | for (; refRegsIter.more(); ++refRegsIter) { |
16010 | Register reg = *refRegsIter; |
16011 | size_t offsetFromSpillBase = |
16012 | safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) / |
16013 | sizeof(void*); |
16014 | MOZ_ASSERT(0 < offsetFromSpillBase &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16015); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" ")"); do { *((volatile int*)__null) = 16015; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
16015 | offsetFromSpillBase <= spilledNumWords)do { static_assert( mozilla::detail::AssertionConditionType< decltype(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16015); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" ")"); do { *((volatile int*)__null) = 16015; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16016 | size_t index = spilledNumWords - offsetFromSpillBase; |
16017 | |
16018 | if (wasmAnyRefRegs.has(reg)) { |
16019 | stackMap->set(index, wasm::StackMap::AnyRef); |
16020 | } else { |
16021 | MOZ_ASSERT(slotsOrElementsRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotsOrElementsRegs.has(reg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slotsOrElementsRegs.has(reg) ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "slotsOrElementsRegs.has(reg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16021); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)" ")"); do { *((volatile int*)__null) = 16021; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16022 | stackMap->set(index, wasm::StackMap::ArrayDataPointer); |
16023 | } |
16024 | } |
16025 | // Float and vector registers do not have to be handled; they cannot |
16026 | // contain wasm anyrefs, and they are spilled after general-purpose |
16027 | // registers. Gprs are therefore closest to the spill base and thus their |
16028 | // offset calculation does not need to account for other spills. |
16029 | } break; |
16030 | case WasmSafepointKind::Trap: { |
16031 | regDumpWords += trapExitLayoutNumWords; |
16032 | |
16033 | for (; refRegsIter.more(); ++refRegsIter) { |
16034 | Register reg = *refRegsIter; |
16035 | size_t offsetFromTop = trapExitLayout.getOffset(reg); |
16036 | |
16037 | // If this doesn't hold, the associated register wasn't saved by |
16038 | // the trap exit stub. Better to crash now than much later, in |
16039 | // some obscure place, and possibly with security consequences. |
16040 | MOZ_RELEASE_ASSERT(offsetFromTop < trapExitLayoutNumWords)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetFromTop < trapExitLayoutNumWords)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(offsetFromTop < trapExitLayoutNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("offsetFromTop < trapExitLayoutNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16040); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "offsetFromTop < trapExitLayoutNumWords" ")"); do { *((volatile int*)__null) = 16040; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16041 | |
16042 | // offsetFromTop is an offset in words down from the highest |
16043 | // address in the exit stub save area. Switch it around to be an |
16044 | // offset up from the bottom of the (integer register) save area. |
16045 | size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop; |
16046 | |
16047 | if (wasmAnyRefRegs.has(reg)) { |
16048 | stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef); |
16049 | } else { |
16050 | MOZ_ASSERT(slotsOrElementsRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotsOrElementsRegs.has(reg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slotsOrElementsRegs.has(reg) ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "slotsOrElementsRegs.has(reg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16050); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)" ")"); do { *((volatile int*)__null) = 16050; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16051 | stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer); |
16052 | } |
16053 | } |
16054 | } break; |
16055 | default: |
16056 | MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16056); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")" ); do { *((volatile int*)__null) = 16056; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
16057 | } |
16058 | |
16059 | // Ensure other reg/slot collections on LSafepoint are empty. |
16060 | MOZ_ASSERT(safepoint.gcRegs().empty() && safepoint.gcSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.gcRegs().empty() && safepoint.gcSlots ().empty())>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(safepoint.gcRegs().empty() && safepoint.gcSlots().empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("safepoint.gcRegs().empty() && safepoint.gcSlots().empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16060); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.gcRegs().empty() && safepoint.gcSlots().empty()" ")"); do { *((volatile int*)__null) = 16060; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16061 | #ifdef JS_NUNBOX32 |
16062 | MOZ_ASSERT(safepoint.nunboxParts().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.nunboxParts().empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(safepoint.nunboxParts().empty ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("safepoint.nunboxParts().empty()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16062); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.nunboxParts().empty()" ")"); do { *((volatile int*)__null) = 16062; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16063 | #elif JS_PUNBOX641 |
16064 | MOZ_ASSERT(safepoint.valueRegs().empty() && safepoint.valueSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.valueRegs().empty() && safepoint.valueSlots ().empty())>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(safepoint.valueRegs().empty() && safepoint.valueSlots().empty()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("safepoint.valueRegs().empty() && safepoint.valueSlots().empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16064); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.valueRegs().empty() && safepoint.valueSlots().empty()" ")"); do { *((volatile int*)__null) = 16064; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16065 | #endif |
16066 | |
16067 | // BODY (GENERAL SPILL) AREA and FRAME and INCOMING ARGS |
16068 | // Deal with roots on the stack. |
16069 | const LSafepoint::SlotList& wasmAnyRefSlots = safepoint.wasmAnyRefSlots(); |
16070 | for (SafepointSlotEntry wasmAnyRefSlot : wasmAnyRefSlots) { |
16071 | // The following needs to correspond with JitFrameLayout::slotRef |
16072 | // wasmAnyRefSlot.stack == 0 means the slot is in the args area |
16073 | if (wasmAnyRefSlot.stack) { |
16074 | // It's a slot in the body allocation, so .slot is interpreted |
16075 | // as an index downwards from the Frame* |
16076 | MOZ_ASSERT(wasmAnyRefSlot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasmAnyRefSlot.slot <= nBodyBytes)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wasmAnyRefSlot.slot <= nBodyBytes ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wasmAnyRefSlot.slot <= nBodyBytes", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16076); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot <= nBodyBytes" ")"); do { *((volatile int*)__null) = 16076; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16077 | uint32_t offsetInBytes = nBodyBytes - wasmAnyRefSlot.slot; |
16078 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16078; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16079 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
16080 | wasm::StackMap::AnyRef); |
16081 | } else { |
16082 | // It's an argument slot |
16083 | MOZ_ASSERT(wasmAnyRefSlot.slot < nInboundStackArgBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasmAnyRefSlot.slot < nInboundStackArgBytes)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(wasmAnyRefSlot.slot < nInboundStackArgBytes))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("wasmAnyRefSlot.slot < nInboundStackArgBytes" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16083); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot < nInboundStackArgBytes" ")"); do { *((volatile int*)__null) = 16083; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16084 | uint32_t offsetInBytes = nBodyBytes + nFrameBytes + wasmAnyRefSlot.slot; |
16085 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16085); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16085; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16086 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
16087 | wasm::StackMap::AnyRef); |
16088 | } |
16089 | } |
16090 | |
16091 | // Track array data pointers on the stack |
16092 | const LSafepoint::SlotList& slots = safepoint.slotsOrElementsSlots(); |
16093 | for (SafepointSlotEntry slot : slots) { |
16094 | MOZ_ASSERT(slot.stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot.stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot.stack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot.stack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16094); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack" ")"); do { *((volatile int*)__null) = 16094; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16095 | |
16096 | // It's a slot in the body allocation, so .slot is interpreted |
16097 | // as an index downwards from the Frame* |
16098 | MOZ_ASSERT(slot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot.slot <= nBodyBytes)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot.slot <= nBodyBytes)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot.slot <= nBodyBytes" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16098); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { *((volatile int*)__null) = 16098; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16099 | uint32_t offsetInBytes = nBodyBytes - slot.slot; |
16100 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16100); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16100; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16101 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
16102 | wasm::StackMap::Kind::ArrayDataPointer); |
16103 | } |
16104 | |
16105 | // Record in the map, how far down from the highest address the Frame* is. |
16106 | // Take the opportunity to check that we haven't marked any part of the |
16107 | // Frame itself as a pointer. |
16108 | stackMap->setFrameOffsetFromTop((nInboundStackArgBytesAligned + nFrameBytes) / |
16109 | sizeof(void*)); |
16110 | #ifdef DEBUG1 |
16111 | for (uint32_t i = 0; i < nFrameBytes / sizeof(void*); i++) { |
16112 | MOZ_ASSERT(stackMap->get(stackMap->header.numMappedWords -do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16114); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16114; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
16113 | stackMap->header.frameOffsetFromTop + i) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16114); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16114; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
16114 | wasm::StackMap::Kind::POD)do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16114); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16114; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16115 | } |
16116 | #endif |
16117 | |
16118 | *result = stackMap; |
16119 | return true; |
16120 | } |
16121 | |
16122 | bool CodeGenerator::generateWasm( |
16123 | wasm::CallIndirectId callIndirectId, wasm::BytecodeOffset trapOffset, |
16124 | const wasm::ArgTypeVector& argTypes, const RegisterOffsets& trapExitLayout, |
16125 | size_t trapExitLayoutNumWords, wasm::FuncOffsets* offsets, |
16126 | wasm::StackMaps* stackMaps, wasm::Decoder* decoder) { |
16127 | AutoCreatedBy acb(masm, "CodeGenerator::generateWasm"); |
16128 | |
16129 | JitSpew(JitSpew_Codegen, "# Emitting wasm code"); |
16130 | |
16131 | size_t nInboundStackArgBytes = StackArgAreaSizeUnaligned(argTypes); |
16132 | inboundStackArgBytes_ = nInboundStackArgBytes; |
16133 | |
16134 | wasm::GenerateFunctionPrologue(masm, callIndirectId, mozilla::Nothing(), |
16135 | offsets); |
16136 | |
16137 | MOZ_ASSERT(masm.framePushed() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("masm.framePushed() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { *((volatile int*)__null) = 16137; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16138 | |
16139 | // Very large frames are implausible, probably an attack. |
16140 | if (frameSize() > wasm::MaxFrameSize) { |
16141 | return decoder->fail(decoder->beginOffset(), "stack frame is too large"); |
16142 | } |
16143 | |
16144 | if (omitOverRecursedCheck()) { |
16145 | masm.reserveStack(frameSize()); |
16146 | } else { |
16147 | std::pair<CodeOffset, uint32_t> pair = |
16148 | masm.wasmReserveStackChecked(frameSize(), trapOffset); |
16149 | CodeOffset trapInsnOffset = pair.first; |
16150 | size_t nBytesReservedBeforeTrap = pair.second; |
16151 | |
16152 | wasm::StackMap* functionEntryStackMap = nullptr; |
16153 | if (!CreateStackMapForFunctionEntryTrap( |
16154 | argTypes, trapExitLayout, trapExitLayoutNumWords, |
16155 | nBytesReservedBeforeTrap, nInboundStackArgBytes, |
16156 | &functionEntryStackMap)) { |
16157 | return false; |
16158 | } |
16159 | |
16160 | // In debug builds, we'll always have a stack map, even if there are no |
16161 | // refs to track. |
16162 | MOZ_ASSERT(functionEntryStackMap)do { static_assert( mozilla::detail::AssertionConditionType< decltype(functionEntryStackMap)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(functionEntryStackMap))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("functionEntryStackMap" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16162); AnnotateMozCrashReason("MOZ_ASSERT" "(" "functionEntryStackMap" ")"); do { *((volatile int*)__null) = 16162; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16163 | |
16164 | if (functionEntryStackMap && |
16165 | !stackMaps->add((uint8_t*)(uintptr_t)trapInsnOffset.offset(), |
16166 | functionEntryStackMap)) { |
16167 | functionEntryStackMap->destroy(); |
16168 | return false; |
16169 | } |
16170 | } |
16171 | |
16172 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16172); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 16172; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16173 | |
16174 | if (!generateBody()) { |
16175 | return false; |
16176 | } |
16177 | |
16178 | masm.bind(&returnLabel_); |
16179 | wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets); |
16180 | |
16181 | if (!generateOutOfLineCode()) { |
16182 | return false; |
16183 | } |
16184 | |
16185 | masm.flush(); |
16186 | if (masm.oom()) { |
16187 | return false; |
16188 | } |
16189 | |
16190 | offsets->end = masm.currentOffset(); |
16191 | |
16192 | MOZ_ASSERT(!masm.failureLabel()->used())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!masm.failureLabel()->used())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!masm.failureLabel()->used ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!masm.failureLabel()->used()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!masm.failureLabel()->used()" ")"); do { *((volatile int*)__null) = 16192; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16193 | MOZ_ASSERT(snapshots_.listSize() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(snapshots_.listSize() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(snapshots_.listSize() == 0)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("snapshots_.listSize() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.listSize() == 0" ")"); do { *((volatile int*)__null) = 16193; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16194 | MOZ_ASSERT(snapshots_.RVATableSize() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(snapshots_.RVATableSize() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(snapshots_.RVATableSize() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("snapshots_.RVATableSize() == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16194); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.RVATableSize() == 0" ")"); do { *((volatile int*)__null) = 16194; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16195 | MOZ_ASSERT(recovers_.size() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(recovers_.size() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(recovers_.size() == 0))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("recovers_.size() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16195); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size() == 0" ")"); do { *((volatile int*)__null) = 16195; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16196 | MOZ_ASSERT(graph.numConstants() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(graph.numConstants() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(graph.numConstants() == 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("graph.numConstants() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16196); AnnotateMozCrashReason("MOZ_ASSERT" "(" "graph.numConstants() == 0" ")"); do { *((volatile int*)__null) = 16196; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16197 | MOZ_ASSERT(osiIndices_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(osiIndices_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(osiIndices_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("osiIndices_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16197); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osiIndices_.empty()" ")"); do { *((volatile int*)__null) = 16197; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16198 | MOZ_ASSERT(icList_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(icList_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(icList_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("icList_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "icList_.empty()" ")"); do { *((volatile int*)__null) = 16198; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16199 | MOZ_ASSERT(safepoints_.size() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoints_.size() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(safepoints_.size() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("safepoints_.size() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16199); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoints_.size() == 0" ")"); do { *((volatile int*)__null) = 16199; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16200 | MOZ_ASSERT(!scriptCounts_)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!scriptCounts_)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!scriptCounts_))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!scriptCounts_" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16200); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!scriptCounts_" ")"); do { *((volatile int*)__null) = 16200; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16201 | |
16202 | // Convert the safepoints to stackmaps and add them to our running |
16203 | // collection thereof. |
16204 | for (CodegenSafepointIndex& index : safepointIndices_) { |
16205 | wasm::StackMap* stackMap = nullptr; |
16206 | if (!CreateStackMapFromLSafepoint(*index.safepoint(), trapExitLayout, |
16207 | trapExitLayoutNumWords, |
16208 | nInboundStackArgBytes, &stackMap)) { |
16209 | return false; |
16210 | } |
16211 | |
16212 | // In debug builds, we'll always have a stack map. |
16213 | MOZ_ASSERT(stackMap)do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(stackMap))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("stackMap", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16213); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap" ")" ); do { *((volatile int*)__null) = 16213; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
16214 | if (!stackMap) { |
16215 | continue; |
16216 | } |
16217 | |
16218 | if (!stackMaps->add((uint8_t*)(uintptr_t)index.displacement(), stackMap)) { |
16219 | stackMap->destroy(); |
16220 | return false; |
16221 | } |
16222 | } |
16223 | |
16224 | return true; |
16225 | } |
16226 | |
16227 | bool CodeGenerator::generate() { |
16228 | AutoCreatedBy acb(masm, "CodeGenerator::generate"); |
16229 | |
16230 | JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%u:%u", |
16231 | gen->outerInfo().script()->filename(), |
16232 | gen->outerInfo().script()->lineno(), |
16233 | gen->outerInfo().script()->column().oneOriginValue()); |
16234 | |
16235 | // Initialize native code table with an entry to the start of |
16236 | // top-level script. |
16237 | InlineScriptTree* tree = gen->outerInfo().inlineScriptTree(); |
16238 | jsbytecode* startPC = tree->script()->code(); |
16239 | BytecodeSite* startSite = new (gen->alloc()) BytecodeSite(tree, startPC); |
16240 | if (!addNativeToBytecodeEntry(startSite)) { |
16241 | return false; |
16242 | } |
16243 | |
16244 | if (!safepoints_.init(gen->alloc())) { |
16245 | return false; |
16246 | } |
16247 | |
16248 | perfSpewer_.recordOffset(masm, "Prologue"); |
16249 | if (!generatePrologue()) { |
16250 | return false; |
16251 | } |
16252 | |
16253 | // Reset native => bytecode map table with top-level script and startPc. |
16254 | if (!addNativeToBytecodeEntry(startSite)) { |
16255 | return false; |
16256 | } |
16257 | |
16258 | if (!generateBody()) { |
16259 | return false; |
16260 | } |
16261 | |
16262 | // Reset native => bytecode map table with top-level script and startPc. |
16263 | if (!addNativeToBytecodeEntry(startSite)) { |
16264 | return false; |
16265 | } |
16266 | |
16267 | perfSpewer_.recordOffset(masm, "Epilogue"); |
16268 | if (!generateEpilogue()) { |
16269 | return false; |
16270 | } |
16271 | |
16272 | // Reset native => bytecode map table with top-level script and startPc. |
16273 | if (!addNativeToBytecodeEntry(startSite)) { |
16274 | return false; |
16275 | } |
16276 | |
16277 | perfSpewer_.recordOffset(masm, "InvalidateEpilogue"); |
16278 | generateInvalidateEpilogue(); |
16279 | |
16280 | // native => bytecode entries for OOL code will be added |
16281 | // by CodeGeneratorShared::generateOutOfLineCode |
16282 | perfSpewer_.recordOffset(masm, "OOLCode"); |
16283 | if (!generateOutOfLineCode()) { |
16284 | return false; |
16285 | } |
16286 | |
16287 | // Add terminal entry. |
16288 | if (!addNativeToBytecodeEntry(startSite)) { |
16289 | return false; |
16290 | } |
16291 | |
16292 | // Dump Native to bytecode entries to spew. |
16293 | dumpNativeToBytecodeEntries(); |
16294 | |
16295 | // We encode safepoints after the OSI-point offsets have been determined. |
16296 | if (!encodeSafepoints()) { |
16297 | return false; |
16298 | } |
16299 | |
16300 | return !masm.oom(); |
16301 | } |
16302 | |
16303 | static bool AddInlinedCompilations(JSContext* cx, HandleScript script, |
16304 | IonCompilationId compilationId, |
16305 | const WarpSnapshot* snapshot, |
16306 | bool* isValid) { |
16307 | MOZ_ASSERT(!*isValid)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!*isValid)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!*isValid))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!*isValid", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16307); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*isValid" ")"); do { *((volatile int*)__null) = 16307; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16308 | RecompileInfo recompileInfo(script, compilationId); |
16309 | |
16310 | JitZone* jitZone = cx->zone()->jitZone(); |
16311 | |
16312 | for (const auto* scriptSnapshot : snapshot->scripts()) { |
16313 | JSScript* inlinedScript = scriptSnapshot->script(); |
16314 | if (inlinedScript == script) { |
16315 | continue; |
16316 | } |
16317 | |
16318 | // TODO(post-Warp): This matches FinishCompilation and is necessary to |
16319 | // ensure in-progress compilations are canceled when an inlined functon |
16320 | // becomes a debuggee. See the breakpoint-14.js jit-test. |
16321 | // When TI is gone, try to clean this up by moving AddInlinedCompilations to |
16322 | // WarpOracle so that we can handle this as part of addPendingRecompile |
16323 | // instead of requiring this separate check. |
16324 | if (inlinedScript->isDebuggee()) { |
16325 | *isValid = false; |
16326 | return true; |
16327 | } |
16328 | |
16329 | if (!jitZone->addInlinedCompilation(recompileInfo, inlinedScript)) { |
16330 | return false; |
16331 | } |
16332 | } |
16333 | |
16334 | *isValid = true; |
16335 | return true; |
16336 | } |
16337 | |
16338 | void CodeGenerator::validateAndRegisterFuseDependencies(JSContext* cx, |
16339 | HandleScript script, |
16340 | bool* isValid) { |
16341 | // No need to validate as we will toss this compilation anyhow. |
16342 | if (!*isValid) { |
16343 | return; |
16344 | } |
16345 | |
16346 | for (auto dependency : fuseDependencies) { |
16347 | switch (dependency) { |
16348 | case FuseDependencyKind::HasSeenObjectEmulateUndefinedFuse: { |
16349 | auto& hasSeenObjectEmulateUndefinedFuse = |
16350 | cx->runtime()->hasSeenObjectEmulateUndefinedFuse.ref(); |
16351 | |
16352 | if (!hasSeenObjectEmulateUndefinedFuse.intact()) { |
16353 | JitSpew(JitSpew_Codegen, |
16354 | "tossing compilation; hasSeenObjectEmulateUndefinedFuse fuse " |
16355 | "dependency no longer valid\n"); |
16356 | *isValid = false; |
16357 | return; |
16358 | } |
16359 | |
16360 | if (!hasSeenObjectEmulateUndefinedFuse.addFuseDependency(cx, script)) { |
16361 | JitSpew(JitSpew_Codegen, |
16362 | "tossing compilation; failed to register " |
16363 | "hasSeenObjectEmulateUndefinedFuse script dependency\n"); |
16364 | *isValid = false; |
16365 | return; |
16366 | } |
16367 | break; |
16368 | } |
16369 | |
16370 | case FuseDependencyKind::OptimizeGetIteratorFuse: { |
16371 | auto& optimizeGetIteratorFuse = |
16372 | cx->realm()->realmFuses.optimizeGetIteratorFuse; |
16373 | if (!optimizeGetIteratorFuse.intact()) { |
16374 | JitSpew(JitSpew_Codegen, |
16375 | "tossing compilation; optimizeGetIteratorFuse fuse " |
16376 | "dependency no longer valid\n"); |
16377 | *isValid = false; |
16378 | return; |
16379 | } |
16380 | |
16381 | if (!optimizeGetIteratorFuse.addFuseDependency(cx, script)) { |
16382 | JitSpew(JitSpew_Codegen, |
16383 | "tossing compilation; failed to register " |
16384 | "optimizeGetIteratorFuse script dependency\n"); |
16385 | *isValid = false; |
16386 | return; |
16387 | } |
16388 | break; |
16389 | } |
16390 | |
16391 | default: |
16392 | MOZ_CRASH("Unknown Dependency Kind")do { do { } while (false); MOZ_ReportCrash("" "Unknown Dependency Kind" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16392); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown Dependency Kind" ")"); do { *((volatile int*)__null) = 16392; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
16393 | } |
16394 | } |
16395 | } |
16396 | |
16397 | bool CodeGenerator::link(JSContext* cx, const WarpSnapshot* snapshot) { |
16398 | AutoCreatedBy acb(masm, "CodeGenerator::link"); |
16399 | |
16400 | // We cancel off-thread Ion compilations in a few places during GC, but if |
16401 | // this compilation was performed off-thread it will already have been |
16402 | // removed from the relevant lists by this point. Don't allow GC here. |
16403 | JS::AutoAssertNoGC nogc(cx); |
16404 | |
16405 | RootedScript script(cx, gen->outerInfo().script()); |
16406 | MOZ_ASSERT(!script->hasIonScript())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!script->hasIonScript())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!script->hasIonScript())) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!script->hasIonScript()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16406); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!script->hasIonScript()" ")"); do { *((volatile int*)__null) = 16406; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16407 | |
16408 | // Perform any read barriers which were skipped while compiling the |
16409 | // script, which may have happened off-thread. |
16410 | JitZone* jitZone = cx->zone()->jitZone(); |
16411 | jitZone->performStubReadBarriers(zoneStubsToReadBarrier_); |
16412 | |
16413 | if (scriptCounts_ && !script->hasScriptCounts() && |
16414 | !script->initScriptCounts(cx)) { |
16415 | return false; |
16416 | } |
16417 | |
16418 | IonCompilationId compilationId = |
16419 | cx->runtime()->jitRuntime()->nextCompilationId(); |
16420 | jitZone->currentCompilationIdRef().emplace(compilationId); |
16421 | auto resetCurrentId = mozilla::MakeScopeExit( |
16422 | [jitZone] { jitZone->currentCompilationIdRef().reset(); }); |
16423 | |
16424 | // Record constraints. If an error occured, returns false and potentially |
16425 | // prevent future compilations. Otherwise, if an invalidation occured, then |
16426 | // skip the current compilation. |
16427 | bool isValid = false; |
16428 | |
16429 | // If an inlined script is invalidated (for example, by attaching |
16430 | // a debugger), we must also invalidate the parent IonScript. |
16431 | if (!AddInlinedCompilations(cx, script, compilationId, snapshot, &isValid)) { |
16432 | return false; |
16433 | } |
16434 | |
16435 | // Validate fuse dependencies here; if a fuse has popped since we registered a |
16436 | // dependency then we need to toss this compilation as it assumes things which |
16437 | // are not valid. |
16438 | // |
16439 | // Eagerly register a fuse dependency here too; this way if we OOM we can |
16440 | // instead simply remove the compilation and move on with our lives. |
16441 | validateAndRegisterFuseDependencies(cx, script, &isValid); |
16442 | |
16443 | // This compilation is no longer valid; don't proceed, but return true as this |
16444 | // isn't an error case either. |
16445 | if (!isValid) { |
16446 | return true; |
16447 | } |
16448 | |
16449 | uint32_t argumentSlots = (gen->outerInfo().nargs() + 1) * sizeof(Value); |
16450 | |
16451 | size_t numNurseryObjects = snapshot->nurseryObjects().length(); |
16452 | |
16453 | IonScript* ionScript = IonScript::New( |
16454 | cx, compilationId, graph.localSlotsSize(), argumentSlots, frameDepth_, |
16455 | snapshots_.listSize(), snapshots_.RVATableSize(), recovers_.size(), |
16456 | graph.numConstants(), numNurseryObjects, safepointIndices_.length(), |
16457 | osiIndices_.length(), icList_.length(), runtimeData_.length(), |
16458 | safepoints_.size()); |
16459 | if (!ionScript) { |
16460 | return false; |
16461 | } |
16462 | #ifdef DEBUG1 |
16463 | ionScript->setICHash(snapshot->icHash()); |
16464 | #endif |
16465 | |
16466 | auto freeIonScript = mozilla::MakeScopeExit([&ionScript] { |
16467 | // Use js_free instead of IonScript::Destroy: the cache list is still |
16468 | // uninitialized. |
16469 | js_free(ionScript); |
16470 | }); |
16471 | |
16472 | Linker linker(masm); |
16473 | JitCode* code = linker.newCode(cx, CodeKind::Ion); |
16474 | if (!code) { |
16475 | return false; |
16476 | } |
16477 | |
16478 | // Encode native to bytecode map if profiling is enabled. |
16479 | if (isProfilerInstrumentationEnabled()) { |
16480 | // Generate native-to-bytecode main table. |
16481 | IonEntry::ScriptList scriptList; |
16482 | if (!generateCompactNativeToBytecodeMap(cx, code, scriptList)) { |
16483 | return false; |
16484 | } |
16485 | |
16486 | uint8_t* ionTableAddr = |
16487 | ((uint8_t*)nativeToBytecodeMap_.get()) + nativeToBytecodeTableOffset_; |
16488 | JitcodeIonTable* ionTable = (JitcodeIonTable*)ionTableAddr; |
16489 | |
16490 | // Construct the IonEntry that will go into the global table. |
16491 | auto entry = MakeJitcodeGlobalEntry<IonEntry>( |
16492 | cx, code, code->raw(), code->rawEnd(), std::move(scriptList), ionTable); |
16493 | if (!entry) { |
16494 | return false; |
16495 | } |
16496 | (void)nativeToBytecodeMap_.release(); // Table is now owned by |entry|. |
16497 | |
16498 | // Add entry to the global table. |
16499 | JitcodeGlobalTable* globalTable = |
16500 | cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); |
16501 | if (!globalTable->addEntry(std::move(entry))) { |
16502 | return false; |
16503 | } |
16504 | |
16505 | // Mark the jitcode as having a bytecode map. |
16506 | code->setHasBytecodeMap(); |
16507 | } else { |
16508 | // Add a dumy jitcodeGlobalTable entry. |
16509 | auto entry = MakeJitcodeGlobalEntry<DummyEntry>(cx, code, code->raw(), |
16510 | code->rawEnd()); |
16511 | if (!entry) { |
16512 | return false; |
16513 | } |
16514 | |
16515 | // Add entry to the global table. |
16516 | JitcodeGlobalTable* globalTable = |
16517 | cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); |
16518 | if (!globalTable->addEntry(std::move(entry))) { |
16519 | return false; |
16520 | } |
16521 | |
16522 | // Mark the jitcode as having a bytecode map. |
16523 | code->setHasBytecodeMap(); |
16524 | } |
16525 | |
16526 | ionScript->setMethod(code); |
16527 | |
16528 | // If the Gecko Profiler is enabled, mark IonScript as having been |
16529 | // instrumented accordingly. |
16530 | if (isProfilerInstrumentationEnabled()) { |
16531 | ionScript->setHasProfilingInstrumentation(); |
16532 | } |
16533 | |
16534 | Assembler::PatchDataWithValueCheck( |
16535 | CodeLocationLabel(code, invalidateEpilogueData_), ImmPtr(ionScript), |
16536 | ImmPtr((void*)-1)); |
16537 | |
16538 | for (CodeOffset offset : ionScriptLabels_) { |
16539 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, offset), |
16540 | ImmPtr(ionScript), ImmPtr((void*)-1)); |
16541 | } |
16542 | |
16543 | for (NurseryObjectLabel label : ionNurseryObjectLabels_) { |
16544 | void* entry = ionScript->addressOfNurseryObject(label.nurseryIndex); |
16545 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset), |
16546 | ImmPtr(entry), ImmPtr((void*)-1)); |
16547 | } |
16548 | |
16549 | // for generating inline caches during the execution. |
16550 | if (runtimeData_.length()) { |
16551 | ionScript->copyRuntimeData(&runtimeData_[0]); |
16552 | } |
16553 | if (icList_.length()) { |
16554 | ionScript->copyICEntries(&icList_[0]); |
16555 | } |
16556 | |
16557 | for (size_t i = 0; i < icInfo_.length(); i++) { |
16558 | IonIC& ic = ionScript->getICFromIndex(i); |
16559 | Assembler::PatchDataWithValueCheck( |
16560 | CodeLocationLabel(code, icInfo_[i].icOffsetForJump), |
16561 | ImmPtr(ic.codeRawPtr()), ImmPtr((void*)-1)); |
16562 | Assembler::PatchDataWithValueCheck( |
16563 | CodeLocationLabel(code, icInfo_[i].icOffsetForPush), ImmPtr(&ic), |
16564 | ImmPtr((void*)-1)); |
16565 | } |
16566 | |
16567 | JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)", (void*)ionScript, |
16568 | (void*)code->raw()); |
16569 | |
16570 | ionScript->setInvalidationEpilogueDataOffset( |
16571 | invalidateEpilogueData_.offset()); |
16572 | if (jsbytecode* osrPc = gen->outerInfo().osrPc()) { |
16573 | ionScript->setOsrPc(osrPc); |
16574 | ionScript->setOsrEntryOffset(getOsrEntryOffset()); |
16575 | } |
16576 | ionScript->setInvalidationEpilogueOffset(invalidate_.offset()); |
16577 | |
16578 | perfSpewer_.saveProfile(cx, script, code); |
16579 | |
16580 | #ifdef MOZ_VTUNE1 |
16581 | vtune::MarkScript(code, script, "ion"); |
16582 | #endif |
16583 | |
16584 | // Set a Ion counter hint for this script. |
16585 | if (cx->runtime()->jitRuntime()->hasJitHintsMap()) { |
16586 | JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap(); |
16587 | jitHints->recordIonCompilation(script); |
16588 | } |
16589 | |
16590 | // for marking during GC. |
16591 | if (safepointIndices_.length()) { |
16592 | ionScript->copySafepointIndices(&safepointIndices_[0]); |
16593 | } |
16594 | if (safepoints_.size()) { |
16595 | ionScript->copySafepoints(&safepoints_); |
16596 | } |
16597 | |
16598 | // for recovering from an Ion Frame. |
16599 | if (osiIndices_.length()) { |
16600 | ionScript->copyOsiIndices(&osiIndices_[0]); |
16601 | } |
16602 | if (snapshots_.listSize()) { |
16603 | ionScript->copySnapshots(&snapshots_); |
16604 | } |
16605 | MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size())do { if (snapshots_.listSize()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(recovers_.size() )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(recovers_.size()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("recovers_.size()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size()" ")"); do { *((volatile int*)__null) = 16605; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
16606 | if (recovers_.size()) { |
16607 | ionScript->copyRecovers(&recovers_); |
16608 | } |
16609 | if (graph.numConstants()) { |
16610 | const Value* vp = graph.constantPool(); |
16611 | ionScript->copyConstants(vp); |
16612 | for (size_t i = 0; i < graph.numConstants(); i++) { |
16613 | const Value& v = vp[i]; |
16614 | if (v.isGCThing()) { |
16615 | if (gc::StoreBuffer* sb = v.toGCThing()->storeBuffer()) { |
16616 | sb->putWholeCell(script); |
16617 | break; |
16618 | } |
16619 | } |
16620 | } |
16621 | } |
16622 | |
16623 | // Attach any generated script counts to the script. |
16624 | if (IonScriptCounts* counts = extractScriptCounts()) { |
16625 | script->addIonCounts(counts); |
16626 | } |
16627 | // WARNING: Code after this point must be infallible! |
16628 | |
16629 | // Copy the list of nursery objects. Note that the store buffer can add |
16630 | // HeapPtr edges that must be cleared in IonScript::Destroy. See the |
16631 | // infallibility warning above. |
16632 | const auto& nurseryObjects = snapshot->nurseryObjects(); |
16633 | for (size_t i = 0; i < nurseryObjects.length(); i++) { |
16634 | ionScript->nurseryObjects()[i].init(nurseryObjects[i]); |
16635 | } |
16636 | |
16637 | // Transfer ownership of the IonScript to the JitScript. At this point enough |
16638 | // of the IonScript must be initialized for IonScript::Destroy to work. |
16639 | freeIonScript.release(); |
16640 | script->jitScript()->setIonScript(script, ionScript); |
16641 | |
16642 | return true; |
16643 | } |
16644 | |
16645 | // An out-of-line path to convert a boxed int32 to either a float or double. |
16646 | class OutOfLineUnboxFloatingPoint : public OutOfLineCodeBase<CodeGenerator> { |
16647 | LUnboxFloatingPoint* unboxFloatingPoint_; |
16648 | |
16649 | public: |
16650 | explicit OutOfLineUnboxFloatingPoint(LUnboxFloatingPoint* unboxFloatingPoint) |
16651 | : unboxFloatingPoint_(unboxFloatingPoint) {} |
16652 | |
16653 | void accept(CodeGenerator* codegen) override { |
16654 | codegen->visitOutOfLineUnboxFloatingPoint(this); |
16655 | } |
16656 | |
16657 | LUnboxFloatingPoint* unboxFloatingPoint() const { |
16658 | return unboxFloatingPoint_; |
16659 | } |
16660 | }; |
16661 | |
16662 | void CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint* lir) { |
16663 | const ValueOperand box = ToValue(lir, LUnboxFloatingPoint::Input); |
16664 | const LDefinition* result = lir->output(); |
16665 | |
16666 | // Out-of-line path to convert int32 to double or bailout |
16667 | // if this instruction is fallible. |
16668 | OutOfLineUnboxFloatingPoint* ool = |
16669 | new (alloc()) OutOfLineUnboxFloatingPoint(lir); |
16670 | addOutOfLineCode(ool, lir->mir()); |
16671 | |
16672 | FloatRegister resultReg = ToFloatRegister(result); |
16673 | masm.branchTestDouble(Assembler::NotEqual, box, ool->entry()); |
16674 | masm.unboxDouble(box, resultReg); |
16675 | if (lir->type() == MIRType::Float32) { |
16676 | masm.convertDoubleToFloat32(resultReg, resultReg); |
16677 | } |
16678 | masm.bind(ool->rejoin()); |
16679 | } |
16680 | |
16681 | void CodeGenerator::visitOutOfLineUnboxFloatingPoint( |
16682 | OutOfLineUnboxFloatingPoint* ool) { |
16683 | LUnboxFloatingPoint* ins = ool->unboxFloatingPoint(); |
16684 | const ValueOperand value = ToValue(ins, LUnboxFloatingPoint::Input); |
16685 | |
16686 | if (ins->mir()->fallible()) { |
16687 | Label bail; |
16688 | masm.branchTestInt32(Assembler::NotEqual, value, &bail); |
16689 | bailoutFrom(&bail, ins->snapshot()); |
16690 | } |
16691 | if (ins->type() == MIRType::Float32) { |
16692 | masm.convertInt32ToFloat32(value.payloadOrValueReg(), |
16693 | ToFloatRegister(ins->output())); |
16694 | } else { |
16695 | masm.convertInt32ToDouble(value.payloadOrValueReg(), |
16696 | ToFloatRegister(ins->output())); |
16697 | } |
16698 | masm.jump(ool->rejoin()); |
16699 | } |
16700 | |
16701 | void CodeGenerator::visitCallBindVar(LCallBindVar* lir) { |
16702 | pushArg(ToRegister(lir->environmentChain())); |
16703 | |
16704 | using Fn = JSObject* (*)(JSContext*, JSObject*); |
16705 | callVM<Fn, BindVarOperation>(lir); |
16706 | } |
16707 | |
16708 | void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) { |
16709 | Register obj = ToRegister(lir->getOperand(0)); |
16710 | ValueOperand idVal = ToValue(lir, LMegamorphicSetElement::IndexIndex); |
16711 | ValueOperand value = ToValue(lir, LMegamorphicSetElement::ValueIndex); |
16712 | |
16713 | Register temp0 = ToRegister(lir->temp0()); |
16714 | // See comment in LIROps.yaml (x86 is short on registers) |
16715 | #ifndef JS_CODEGEN_X86 |
16716 | Register temp1 = ToRegister(lir->temp1()); |
16717 | Register temp2 = ToRegister(lir->temp2()); |
16718 | #endif |
16719 | |
16720 | Label cacheHit, done; |
16721 | #ifdef JS_CODEGEN_X86 |
16722 | masm.emitMegamorphicCachedSetSlot( |
16723 | idVal, obj, temp0, value, &cacheHit, |
16724 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
16725 | EmitPreBarrier(masm, addr, mirType); |
16726 | }); |
16727 | #else |
16728 | masm.emitMegamorphicCachedSetSlot( |
16729 | idVal, obj, temp0, temp1, temp2, value, &cacheHit, |
16730 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
16731 | EmitPreBarrier(masm, addr, mirType); |
16732 | }); |
16733 | #endif |
16734 | |
16735 | pushArg(Imm32(lir->mir()->strict())); |
16736 | pushArg(ToValue(lir, LMegamorphicSetElement::ValueIndex)); |
16737 | pushArg(ToValue(lir, LMegamorphicSetElement::IndexIndex)); |
16738 | pushArg(obj); |
16739 | |
16740 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool); |
16741 | callVM<Fn, js::jit::SetElementMegamorphic<true>>(lir); |
16742 | |
16743 | masm.jump(&done); |
16744 | masm.bind(&cacheHit); |
16745 | |
16746 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done); |
16747 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done); |
16748 | |
16749 | saveVolatile(temp0); |
16750 | emitPostWriteBarrier(obj); |
16751 | restoreVolatile(temp0); |
16752 | |
16753 | masm.bind(&done); |
16754 | } |
16755 | |
16756 | void CodeGenerator::visitLoadScriptedProxyHandler( |
16757 | LLoadScriptedProxyHandler* ins) { |
16758 | Register obj = ToRegister(ins->getOperand(0)); |
16759 | Register output = ToRegister(ins->output()); |
16760 | |
16761 | masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), output); |
16762 | |
16763 | Label bail; |
16764 | Address handlerAddr(output, js::detail::ProxyReservedSlots::offsetOfSlot( |
16765 | ScriptedProxyHandler::HANDLER_EXTRA)); |
16766 | masm.fallibleUnboxObject(handlerAddr, output, &bail); |
16767 | bailoutFrom(&bail, ins->snapshot()); |
16768 | } |
16769 | |
16770 | #ifdef JS_PUNBOX641 |
16771 | void CodeGenerator::visitCheckScriptedProxyGetResult( |
16772 | LCheckScriptedProxyGetResult* ins) { |
16773 | ValueOperand target = ToValue(ins, LCheckScriptedProxyGetResult::TargetIndex); |
16774 | ValueOperand value = ToValue(ins, LCheckScriptedProxyGetResult::ValueIndex); |
16775 | ValueOperand id = ToValue(ins, LCheckScriptedProxyGetResult::IdIndex); |
16776 | Register scratch = ToRegister(ins->temp0()); |
16777 | Register scratch2 = ToRegister(ins->temp1()); |
16778 | |
16779 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, |
16780 | MutableHandleValue); |
16781 | OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>( |
16782 | ins, ArgList(scratch, id, value), StoreValueTo(value)); |
16783 | |
16784 | masm.unboxObject(target, scratch); |
16785 | masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch, |
16786 | scratch2, ool->entry()); |
16787 | masm.bind(ool->rejoin()); |
16788 | } |
16789 | #endif |
16790 | |
16791 | void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) { |
16792 | ValueOperand id = ToValue(ins, LIdToStringOrSymbol::IdIndex); |
16793 | ValueOperand output = ToOutValue(ins); |
16794 | Register scratch = ToRegister(ins->temp0()); |
16795 | |
16796 | masm.moveValue(id, output); |
16797 | |
16798 | Label done, callVM; |
16799 | Label bail; |
16800 | { |
16801 | ScratchTagScope tag(masm, output); |
16802 | masm.splitTagForTest(output, tag); |
16803 | masm.branchTestString(Assembler::Equal, tag, &done); |
16804 | masm.branchTestSymbol(Assembler::Equal, tag, &done); |
16805 | masm.branchTestInt32(Assembler::NotEqual, tag, &bail); |
16806 | } |
16807 | |
16808 | masm.unboxInt32(output, scratch); |
16809 | |
16810 | using Fn = JSLinearString* (*)(JSContext*, int); |
16811 | OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>( |
16812 | ins, ArgList(scratch), StoreRegisterTo(output.scratchReg())); |
16813 | |
16814 | masm.lookupStaticIntString(scratch, output.scratchReg(), |
16815 | gen->runtime->staticStrings(), ool->entry()); |
16816 | |
16817 | masm.bind(ool->rejoin()); |
16818 | masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output); |
16819 | masm.bind(&done); |
16820 | |
16821 | bailoutFrom(&bail, ins->snapshot()); |
16822 | } |
16823 | |
16824 | void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) { |
16825 | const Register obj = ToRegister(ins->getOperand(0)); |
16826 | size_t slot = ins->mir()->slot(); |
16827 | ValueOperand result = ToOutValue(ins); |
16828 | |
16829 | masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result); |
16830 | } |
16831 | |
16832 | void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) { |
16833 | const Register obj = ToRegister(ins->getOperand(0)); |
16834 | size_t slot = ins->mir()->slot(); |
16835 | AnyRegister result = ToAnyRegister(ins->getDef(0)); |
16836 | MIRType type = ins->mir()->type(); |
16837 | |
16838 | masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), |
16839 | type, result); |
16840 | } |
16841 | |
16842 | template <typename T> |
16843 | static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type, |
16844 | bool fallible, AnyRegister dest, Label* fail) { |
16845 | if (type == MIRType::Double) { |
16846 | MOZ_ASSERT(dest.isFloat())do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.isFloat())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.isFloat()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("dest.isFloat()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16846); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.isFloat()" ")"); do { *((volatile int*)__null) = 16846; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16847 | masm.ensureDouble(src, dest.fpu(), fail); |
16848 | return; |
16849 | } |
16850 | if (fallible) { |
16851 | switch (type) { |
16852 | case MIRType::Int32: |
16853 | masm.fallibleUnboxInt32(src, dest.gpr(), fail); |
16854 | break; |
16855 | case MIRType::Boolean: |
16856 | masm.fallibleUnboxBoolean(src, dest.gpr(), fail); |
16857 | break; |
16858 | case MIRType::Object: |
16859 | masm.fallibleUnboxObject(src, dest.gpr(), fail); |
16860 | break; |
16861 | case MIRType::String: |
16862 | masm.fallibleUnboxString(src, dest.gpr(), fail); |
16863 | break; |
16864 | case MIRType::Symbol: |
16865 | masm.fallibleUnboxSymbol(src, dest.gpr(), fail); |
16866 | break; |
16867 | case MIRType::BigInt: |
16868 | masm.fallibleUnboxBigInt(src, dest.gpr(), fail); |
16869 | break; |
16870 | default: |
16871 | MOZ_CRASH("Unexpected MIRType")do { do { } while (false); MOZ_ReportCrash("" "Unexpected MIRType" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16871); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected MIRType" ")"); do { *((volatile int*)__null) = 16871; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
16872 | } |
16873 | return; |
16874 | } |
16875 | masm.loadUnboxedValue(src, type, dest); |
16876 | } |
16877 | |
16878 | void CodeGenerator::visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* ins) { |
16879 | const MLoadFixedSlotAndUnbox* mir = ins->mir(); |
16880 | MIRType type = mir->type(); |
16881 | Register input = ToRegister(ins->object()); |
16882 | AnyRegister result = ToAnyRegister(ins->output()); |
16883 | size_t slot = mir->slot(); |
16884 | |
16885 | Address address(input, NativeObject::getFixedSlotOffset(slot)); |
16886 | |
16887 | Label bail; |
16888 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
16889 | if (mir->fallible()) { |
16890 | bailoutFrom(&bail, ins->snapshot()); |
16891 | } |
16892 | } |
16893 | |
16894 | void CodeGenerator::visitLoadDynamicSlotAndUnbox( |
16895 | LLoadDynamicSlotAndUnbox* ins) { |
16896 | const MLoadDynamicSlotAndUnbox* mir = ins->mir(); |
16897 | MIRType type = mir->type(); |
16898 | Register input = ToRegister(ins->slots()); |
16899 | AnyRegister result = ToAnyRegister(ins->output()); |
16900 | size_t slot = mir->slot(); |
16901 | |
16902 | Address address(input, slot * sizeof(JS::Value)); |
16903 | |
16904 | Label bail; |
16905 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
16906 | if (mir->fallible()) { |
16907 | bailoutFrom(&bail, ins->snapshot()); |
16908 | } |
16909 | } |
16910 | |
16911 | void CodeGenerator::visitLoadElementAndUnbox(LLoadElementAndUnbox* ins) { |
16912 | const MLoadElementAndUnbox* mir = ins->mir(); |
16913 | MIRType type = mir->type(); |
16914 | Register elements = ToRegister(ins->elements()); |
16915 | AnyRegister result = ToAnyRegister(ins->output()); |
16916 | |
16917 | Label bail; |
16918 | if (ins->index()->isConstant()) { |
16919 | NativeObject::elementsSizeMustNotOverflow(); |
16920 | int32_t offset = ToInt32(ins->index()) * sizeof(Value); |
16921 | Address address(elements, offset); |
16922 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
16923 | } else { |
16924 | BaseObjectElementIndex address(elements, ToRegister(ins->index())); |
16925 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
16926 | } |
16927 | |
16928 | if (mir->fallible()) { |
16929 | bailoutFrom(&bail, ins->snapshot()); |
16930 | } |
16931 | } |
16932 | |
16933 | class OutOfLineAtomizeSlot : public OutOfLineCodeBase<CodeGenerator> { |
16934 | LInstruction* lir_; |
16935 | Register stringReg_; |
16936 | Address slotAddr_; |
16937 | TypedOrValueRegister dest_; |
16938 | |
16939 | public: |
16940 | OutOfLineAtomizeSlot(LInstruction* lir, Register stringReg, Address slotAddr, |
16941 | TypedOrValueRegister dest) |
16942 | : lir_(lir), stringReg_(stringReg), slotAddr_(slotAddr), dest_(dest) {} |
16943 | |
16944 | void accept(CodeGenerator* codegen) final { |
16945 | codegen->visitOutOfLineAtomizeSlot(this); |
16946 | } |
16947 | LInstruction* lir() const { return lir_; } |
16948 | Register stringReg() const { return stringReg_; } |
16949 | Address slotAddr() const { return slotAddr_; } |
16950 | TypedOrValueRegister dest() const { return dest_; } |
16951 | }; |
16952 | |
16953 | void CodeGenerator::visitOutOfLineAtomizeSlot(OutOfLineAtomizeSlot* ool) { |
16954 | LInstruction* lir = ool->lir(); |
16955 | Register stringReg = ool->stringReg(); |
16956 | Address slotAddr = ool->slotAddr(); |
16957 | TypedOrValueRegister dest = ool->dest(); |
16958 | |
16959 | // This code is called with a non-atomic string in |stringReg|. |
16960 | // When it returns, |stringReg| contains an unboxed pointer to an |
16961 | // atomized version of that string, and |slotAddr| contains a |
16962 | // StringValue pointing to that atom. If |dest| is a ValueOperand, |
16963 | // it contains the same StringValue; otherwise we assert that |dest| |
16964 | // is |stringReg|. |
16965 | |
16966 | saveLive(lir); |
16967 | pushArg(stringReg); |
16968 | |
16969 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
16970 | callVM<Fn, js::AtomizeString>(lir); |
16971 | StoreRegisterTo(stringReg).generate(this); |
16972 | restoreLiveIgnore(lir, StoreRegisterTo(stringReg).clobbered()); |
16973 | |
16974 | if (dest.hasValue()) { |
16975 | masm.moveValue( |
16976 | TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)), |
16977 | dest.valueReg()); |
16978 | } else { |
16979 | MOZ_ASSERT(dest.typedReg().gpr() == stringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.typedReg().gpr() == stringReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.typedReg().gpr() == stringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "dest.typedReg().gpr() == stringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16979); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg" ")"); do { *((volatile int*)__null) = 16979; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
16980 | } |
16981 | |
16982 | emitPreBarrier(slotAddr); |
16983 | masm.storeTypedOrValue(dest, slotAddr); |
16984 | |
16985 | // We don't need a post-barrier because atoms aren't nursery-allocated. |
16986 | #ifdef DEBUG1 |
16987 | // We need a temp register for the nursery check. Spill something. |
16988 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
16989 | allRegs.take(stringReg); |
16990 | Register temp = allRegs.takeAny(); |
16991 | masm.push(temp); |
16992 | |
16993 | Label tenured; |
16994 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, stringReg, temp, &tenured); |
16995 | masm.assumeUnreachable("AtomizeString returned a nursery pointer"); |
16996 | masm.bind(&tenured); |
16997 | |
16998 | masm.pop(temp); |
16999 | #endif |
17000 | |
17001 | masm.jump(ool->rejoin()); |
17002 | } |
17003 | |
17004 | void CodeGenerator::emitMaybeAtomizeSlot(LInstruction* ins, Register stringReg, |
17005 | Address slotAddr, |
17006 | TypedOrValueRegister dest) { |
17007 | OutOfLineAtomizeSlot* ool = |
17008 | new (alloc()) OutOfLineAtomizeSlot(ins, stringReg, slotAddr, dest); |
17009 | addOutOfLineCode(ool, ins->mirRaw()->toInstruction()); |
17010 | masm.branchTest32(Assembler::NonZero, |
17011 | Address(stringReg, JSString::offsetOfFlags()), |
17012 | Imm32(JSString::ATOM_BIT), ool->rejoin()); |
17013 | |
17014 | masm.branchTest32(Assembler::Zero, |
17015 | Address(stringReg, JSString::offsetOfFlags()), |
17016 | Imm32(JSString::ATOM_REF_BIT), ool->entry()); |
17017 | masm.loadPtr(Address(stringReg, JSAtomRefString::offsetOfAtom()), stringReg); |
17018 | |
17019 | if (dest.hasValue()) { |
17020 | masm.moveValue( |
17021 | TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)), |
17022 | dest.valueReg()); |
17023 | } else { |
17024 | MOZ_ASSERT(dest.typedReg().gpr() == stringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.typedReg().gpr() == stringReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.typedReg().gpr() == stringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "dest.typedReg().gpr() == stringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17024); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg" ")"); do { *((volatile int*)__null) = 17024; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17025 | } |
17026 | |
17027 | emitPreBarrier(slotAddr); |
17028 | masm.storeTypedOrValue(dest, slotAddr); |
17029 | |
17030 | masm.bind(ool->rejoin()); |
17031 | } |
17032 | |
17033 | void CodeGenerator::visitLoadFixedSlotAndAtomize( |
17034 | LLoadFixedSlotAndAtomize* ins) { |
17035 | Register obj = ToRegister(ins->getOperand(0)); |
17036 | Register temp = ToRegister(ins->temp0()); |
17037 | size_t slot = ins->mir()->slot(); |
17038 | ValueOperand result = ToOutValue(ins); |
17039 | |
17040 | Address slotAddr(obj, NativeObject::getFixedSlotOffset(slot)); |
17041 | masm.loadValue(slotAddr, result); |
17042 | |
17043 | Label notString; |
17044 | masm.branchTestString(Assembler::NotEqual, result, ¬String); |
17045 | masm.unboxString(result, temp); |
17046 | emitMaybeAtomizeSlot(ins, temp, slotAddr, result); |
17047 | masm.bind(¬String); |
17048 | } |
17049 | |
17050 | void CodeGenerator::visitLoadDynamicSlotAndAtomize( |
17051 | LLoadDynamicSlotAndAtomize* ins) { |
17052 | ValueOperand result = ToOutValue(ins); |
17053 | Register temp = ToRegister(ins->temp0()); |
17054 | Register base = ToRegister(ins->input()); |
17055 | int32_t offset = ins->mir()->slot() * sizeof(js::Value); |
17056 | |
17057 | Address slotAddr(base, offset); |
17058 | masm.loadValue(slotAddr, result); |
17059 | |
17060 | Label notString; |
17061 | masm.branchTestString(Assembler::NotEqual, result, ¬String); |
17062 | masm.unboxString(result, temp); |
17063 | emitMaybeAtomizeSlot(ins, temp, slotAddr, result); |
17064 | masm.bind(¬String); |
17065 | } |
17066 | |
17067 | void CodeGenerator::visitLoadFixedSlotUnboxAndAtomize( |
17068 | LLoadFixedSlotUnboxAndAtomize* ins) { |
17069 | const MLoadFixedSlotAndUnbox* mir = ins->mir(); |
17070 | MOZ_ASSERT(mir->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->type() == MIRType::String)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mir->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17070); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 17070; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17071 | Register input = ToRegister(ins->object()); |
17072 | AnyRegister result = ToAnyRegister(ins->output()); |
17073 | size_t slot = mir->slot(); |
17074 | |
17075 | Address slotAddr(input, NativeObject::getFixedSlotOffset(slot)); |
17076 | |
17077 | Label bail; |
17078 | EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result, |
17079 | &bail); |
17080 | emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr, |
17081 | TypedOrValueRegister(MIRType::String, result)); |
17082 | |
17083 | if (mir->fallible()) { |
17084 | bailoutFrom(&bail, ins->snapshot()); |
17085 | } |
17086 | } |
17087 | |
17088 | void CodeGenerator::visitLoadDynamicSlotUnboxAndAtomize( |
17089 | LLoadDynamicSlotUnboxAndAtomize* ins) { |
17090 | const MLoadDynamicSlotAndUnbox* mir = ins->mir(); |
17091 | MOZ_ASSERT(mir->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->type() == MIRType::String)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mir->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 17091; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17092 | Register input = ToRegister(ins->slots()); |
17093 | AnyRegister result = ToAnyRegister(ins->output()); |
17094 | size_t slot = mir->slot(); |
17095 | |
17096 | Address slotAddr(input, slot * sizeof(JS::Value)); |
17097 | |
17098 | Label bail; |
17099 | EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result, |
17100 | &bail); |
17101 | emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr, |
17102 | TypedOrValueRegister(MIRType::String, result)); |
17103 | |
17104 | if (mir->fallible()) { |
17105 | bailoutFrom(&bail, ins->snapshot()); |
17106 | } |
17107 | } |
17108 | |
17109 | void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) { |
17110 | const Register obj = ToRegister(ins->getOperand(0)); |
17111 | const ValueOperand value = ToValue(ins, LAddAndStoreSlot::ValueIndex); |
17112 | const Register maybeTemp = ToTempRegisterOrInvalid(ins->temp0()); |
17113 | |
17114 | Shape* shape = ins->mir()->shape(); |
17115 | masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) { |
17116 | EmitPreBarrier(masm, addr, MIRType::Shape); |
17117 | }); |
17118 | |
17119 | // Perform the store. No pre-barrier required since this is a new |
17120 | // initialization. |
17121 | |
17122 | uint32_t offset = ins->mir()->slotOffset(); |
17123 | if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) { |
17124 | Address slot(obj, offset); |
17125 | masm.storeValue(value, slot); |
17126 | } else { |
17127 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), maybeTemp); |
17128 | Address slot(maybeTemp, offset); |
17129 | masm.storeValue(value, slot); |
17130 | } |
17131 | } |
17132 | |
17133 | void CodeGenerator::visitAllocateAndStoreSlot(LAllocateAndStoreSlot* ins) { |
17134 | const Register obj = ToRegister(ins->getOperand(0)); |
17135 | const ValueOperand value = ToValue(ins, LAllocateAndStoreSlot::ValueIndex); |
17136 | const Register temp0 = ToRegister(ins->temp0()); |
17137 | const Register temp1 = ToRegister(ins->temp1()); |
17138 | |
17139 | masm.Push(obj); |
17140 | masm.Push(value); |
17141 | |
17142 | using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount); |
17143 | masm.setupAlignedABICall(); |
17144 | masm.loadJSContext(temp0); |
17145 | masm.passABIArg(temp0); |
17146 | masm.passABIArg(obj); |
17147 | masm.move32(Imm32(ins->mir()->numNewSlots()), temp1); |
17148 | masm.passABIArg(temp1); |
17149 | masm.callWithABI<Fn, NativeObject::growSlotsPure>(); |
17150 | masm.storeCallPointerResult(temp0); |
17151 | |
17152 | masm.Pop(value); |
17153 | masm.Pop(obj); |
17154 | |
17155 | bailoutIfFalseBool(temp0, ins->snapshot()); |
17156 | |
17157 | masm.storeObjShape(ins->mir()->shape(), obj, |
17158 | [](MacroAssembler& masm, const Address& addr) { |
17159 | EmitPreBarrier(masm, addr, MIRType::Shape); |
17160 | }); |
17161 | |
17162 | // Perform the store. No pre-barrier required since this is a new |
17163 | // initialization. |
17164 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0); |
17165 | Address slot(temp0, ins->mir()->slotOffset()); |
17166 | masm.storeValue(value, slot); |
17167 | } |
17168 | |
17169 | void CodeGenerator::visitAddSlotAndCallAddPropHook( |
17170 | LAddSlotAndCallAddPropHook* ins) { |
17171 | const Register obj = ToRegister(ins->object()); |
17172 | const ValueOperand value = |
17173 | ToValue(ins, LAddSlotAndCallAddPropHook::ValueIndex); |
17174 | |
17175 | pushArg(ImmGCPtr(ins->mir()->shape())); |
17176 | pushArg(value); |
17177 | pushArg(obj); |
17178 | |
17179 | using Fn = |
17180 | bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, Handle<Shape*>); |
17181 | callVM<Fn, AddSlotAndCallAddPropHook>(ins); |
17182 | } |
17183 | |
17184 | void CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV* ins) { |
17185 | const Register obj = ToRegister(ins->getOperand(0)); |
17186 | size_t slot = ins->mir()->slot(); |
17187 | |
17188 | const ValueOperand value = ToValue(ins, LStoreFixedSlotV::ValueIndex); |
17189 | |
17190 | Address address(obj, NativeObject::getFixedSlotOffset(slot)); |
17191 | if (ins->mir()->needsBarrier()) { |
17192 | emitPreBarrier(address); |
17193 | } |
17194 | |
17195 | masm.storeValue(value, address); |
17196 | } |
17197 | |
17198 | void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) { |
17199 | const Register obj = ToRegister(ins->getOperand(0)); |
17200 | size_t slot = ins->mir()->slot(); |
17201 | |
17202 | const LAllocation* value = ins->value(); |
17203 | MIRType valueType = ins->mir()->value()->type(); |
17204 | |
17205 | Address address(obj, NativeObject::getFixedSlotOffset(slot)); |
17206 | if (ins->mir()->needsBarrier()) { |
17207 | emitPreBarrier(address); |
17208 | } |
17209 | |
17210 | ConstantOrRegister nvalue = |
17211 | value->isConstant() |
17212 | ? ConstantOrRegister(value->toConstant()->toJSValue()) |
17213 | : TypedOrValueRegister(valueType, ToAnyRegister(value)); |
17214 | masm.storeConstantOrRegister(nvalue, address); |
17215 | } |
17216 | |
17217 | void CodeGenerator::visitGetNameCache(LGetNameCache* ins) { |
17218 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17219 | Register envChain = ToRegister(ins->envObj()); |
17220 | ValueOperand output = ToOutValue(ins); |
17221 | Register temp = ToRegister(ins->temp0()); |
17222 | |
17223 | IonGetNameIC ic(liveRegs, envChain, output, temp); |
17224 | addIC(ins, allocateIC(ic)); |
17225 | } |
17226 | |
17227 | void CodeGenerator::addGetPropertyCache(LInstruction* ins, |
17228 | LiveRegisterSet liveRegs, |
17229 | TypedOrValueRegister value, |
17230 | const ConstantOrRegister& id, |
17231 | ValueOperand output) { |
17232 | CacheKind kind = CacheKind::GetElem; |
17233 | if (id.constant() && id.value().isString()) { |
17234 | JSString* idString = id.value().toString(); |
17235 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
17236 | kind = CacheKind::GetProp; |
17237 | } |
17238 | } |
17239 | IonGetPropertyIC cache(kind, liveRegs, value, id, output); |
17240 | addIC(ins, allocateIC(cache)); |
17241 | } |
17242 | |
17243 | void CodeGenerator::addSetPropertyCache(LInstruction* ins, |
17244 | LiveRegisterSet liveRegs, |
17245 | Register objReg, Register temp, |
17246 | const ConstantOrRegister& id, |
17247 | const ConstantOrRegister& value, |
17248 | bool strict) { |
17249 | CacheKind kind = CacheKind::SetElem; |
17250 | if (id.constant() && id.value().isString()) { |
17251 | JSString* idString = id.value().toString(); |
17252 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
17253 | kind = CacheKind::SetProp; |
17254 | } |
17255 | } |
17256 | IonSetPropertyIC cache(kind, liveRegs, objReg, temp, id, value, strict); |
17257 | addIC(ins, allocateIC(cache)); |
17258 | } |
17259 | |
17260 | ConstantOrRegister CodeGenerator::toConstantOrRegister(LInstruction* lir, |
17261 | size_t n, MIRType type) { |
17262 | if (type == MIRType::Value) { |
17263 | return TypedOrValueRegister(ToValue(lir, n)); |
17264 | } |
17265 | |
17266 | const LAllocation* value = lir->getOperand(n); |
17267 | if (value->isConstant()) { |
17268 | return ConstantOrRegister(value->toConstant()->toJSValue()); |
17269 | } |
17270 | |
17271 | return TypedOrValueRegister(type, ToAnyRegister(value)); |
17272 | } |
17273 | |
17274 | void CodeGenerator::visitGetPropertyCache(LGetPropertyCache* ins) { |
17275 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17276 | TypedOrValueRegister value = |
17277 | toConstantOrRegister(ins, LGetPropertyCache::ValueIndex, |
17278 | ins->mir()->value()->type()) |
17279 | .reg(); |
17280 | ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCache::IdIndex, |
17281 | ins->mir()->idval()->type()); |
17282 | ValueOperand output = ToOutValue(ins); |
17283 | addGetPropertyCache(ins, liveRegs, value, id, output); |
17284 | } |
17285 | |
17286 | void CodeGenerator::visitGetPropSuperCache(LGetPropSuperCache* ins) { |
17287 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17288 | Register obj = ToRegister(ins->obj()); |
17289 | TypedOrValueRegister receiver = |
17290 | toConstantOrRegister(ins, LGetPropSuperCache::ReceiverIndex, |
17291 | ins->mir()->receiver()->type()) |
17292 | .reg(); |
17293 | ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCache::IdIndex, |
17294 | ins->mir()->idval()->type()); |
17295 | ValueOperand output = ToOutValue(ins); |
17296 | |
17297 | CacheKind kind = CacheKind::GetElemSuper; |
17298 | if (id.constant() && id.value().isString()) { |
17299 | JSString* idString = id.value().toString(); |
17300 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
17301 | kind = CacheKind::GetPropSuper; |
17302 | } |
17303 | } |
17304 | |
17305 | IonGetPropSuperIC cache(kind, liveRegs, obj, receiver, id, output); |
17306 | addIC(ins, allocateIC(cache)); |
17307 | } |
17308 | |
17309 | void CodeGenerator::visitBindNameCache(LBindNameCache* ins) { |
17310 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17311 | Register envChain = ToRegister(ins->environmentChain()); |
17312 | Register output = ToRegister(ins->output()); |
17313 | Register temp = ToRegister(ins->temp0()); |
17314 | |
17315 | IonBindNameIC ic(liveRegs, envChain, output, temp); |
17316 | addIC(ins, allocateIC(ic)); |
17317 | } |
17318 | |
17319 | void CodeGenerator::visitHasOwnCache(LHasOwnCache* ins) { |
17320 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17321 | TypedOrValueRegister value = |
17322 | toConstantOrRegister(ins, LHasOwnCache::ValueIndex, |
17323 | ins->mir()->value()->type()) |
17324 | .reg(); |
17325 | TypedOrValueRegister id = toConstantOrRegister(ins, LHasOwnCache::IdIndex, |
17326 | ins->mir()->idval()->type()) |
17327 | .reg(); |
17328 | Register output = ToRegister(ins->output()); |
17329 | |
17330 | IonHasOwnIC cache(liveRegs, value, id, output); |
17331 | addIC(ins, allocateIC(cache)); |
17332 | } |
17333 | |
17334 | void CodeGenerator::visitCheckPrivateFieldCache(LCheckPrivateFieldCache* ins) { |
17335 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17336 | TypedOrValueRegister value = |
17337 | toConstantOrRegister(ins, LCheckPrivateFieldCache::ValueIndex, |
17338 | ins->mir()->value()->type()) |
17339 | .reg(); |
17340 | TypedOrValueRegister id = |
17341 | toConstantOrRegister(ins, LCheckPrivateFieldCache::IdIndex, |
17342 | ins->mir()->idval()->type()) |
17343 | .reg(); |
17344 | Register output = ToRegister(ins->output()); |
17345 | |
17346 | IonCheckPrivateFieldIC cache(liveRegs, value, id, output); |
17347 | addIC(ins, allocateIC(cache)); |
17348 | } |
17349 | |
17350 | void CodeGenerator::visitNewPrivateName(LNewPrivateName* ins) { |
17351 | pushArg(ImmGCPtr(ins->mir()->name())); |
17352 | |
17353 | using Fn = JS::Symbol* (*)(JSContext*, Handle<JSAtom*>); |
17354 | callVM<Fn, NewPrivateName>(ins); |
17355 | } |
17356 | |
17357 | void CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty* lir) { |
17358 | pushArg(ImmGCPtr(lir->mir()->name())); |
17359 | pushArg(ToValue(lir, LCallDeleteProperty::ValueIndex)); |
17360 | |
17361 | using Fn = bool (*)(JSContext*, HandleValue, Handle<PropertyName*>, bool*); |
17362 | if (lir->mir()->strict()) { |
17363 | callVM<Fn, DelPropOperation<true>>(lir); |
17364 | } else { |
17365 | callVM<Fn, DelPropOperation<false>>(lir); |
17366 | } |
17367 | } |
17368 | |
17369 | void CodeGenerator::visitCallDeleteElement(LCallDeleteElement* lir) { |
17370 | pushArg(ToValue(lir, LCallDeleteElement::IndexIndex)); |
17371 | pushArg(ToValue(lir, LCallDeleteElement::ValueIndex)); |
17372 | |
17373 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*); |
17374 | if (lir->mir()->strict()) { |
17375 | callVM<Fn, DelElemOperation<true>>(lir); |
17376 | } else { |
17377 | callVM<Fn, DelElemOperation<false>>(lir); |
17378 | } |
17379 | } |
17380 | |
17381 | void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) { |
17382 | Register obj = ToRegister(lir->object()); |
17383 | Register iterObj = ToRegister(lir->output()); |
17384 | Register temp = ToRegister(lir->temp0()); |
17385 | Register temp2 = ToRegister(lir->temp1()); |
17386 | Register temp3 = ToRegister(lir->temp2()); |
17387 | |
17388 | using Fn = PropertyIteratorObject* (*)(JSContext*, HandleObject); |
17389 | OutOfLineCode* ool = (lir->mir()->wantsIndices()) |
17390 | ? oolCallVM<Fn, GetIteratorWithIndices>( |
17391 | lir, ArgList(obj), StoreRegisterTo(iterObj)) |
17392 | : oolCallVM<Fn, GetIterator>( |
17393 | lir, ArgList(obj), StoreRegisterTo(iterObj)); |
17394 | |
17395 | masm.maybeLoadIteratorFromShape(obj, iterObj, temp, temp2, temp3, |
17396 | ool->entry()); |
17397 | |
17398 | Register nativeIter = temp; |
17399 | masm.loadPrivate( |
17400 | Address(iterObj, PropertyIteratorObject::offsetOfIteratorSlot()), |
17401 | nativeIter); |
17402 | |
17403 | if (lir->mir()->wantsIndices()) { |
17404 | // At least one consumer of the output of this iterator has been optimized |
17405 | // to use iterator indices. If the cached iterator doesn't include indices, |
17406 | // but it was marked to indicate that we can create them if needed, then we |
17407 | // do a VM call to replace the cached iterator with a fresh iterator |
17408 | // including indices. |
17409 | masm.branchNativeIteratorIndices(Assembler::Equal, nativeIter, temp2, |
17410 | NativeIteratorIndices::AvailableOnRequest, |
17411 | ool->entry()); |
17412 | } |
17413 | |
17414 | Address iterFlagsAddr(nativeIter, NativeIterator::offsetOfFlagsAndCount()); |
17415 | masm.storePtr( |
17416 | obj, Address(nativeIter, NativeIterator::offsetOfObjectBeingIterated())); |
17417 | masm.or32(Imm32(NativeIterator::Flags::Active), iterFlagsAddr); |
17418 | |
17419 | Register enumeratorsAddr = temp2; |
17420 | masm.movePtr(ImmPtr(lir->mir()->enumeratorsAddr()), enumeratorsAddr); |
17421 | masm.registerIterator(enumeratorsAddr, nativeIter, temp3); |
17422 | |
17423 | // Generate post-write barrier for storing to |iterObj->objectBeingIterated_|. |
17424 | // We already know that |iterObj| is tenured, so we only have to check |obj|. |
17425 | Label skipBarrier; |
17426 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp2, &skipBarrier); |
17427 | { |
17428 | LiveRegisterSet save = liveVolatileRegs(lir); |
17429 | save.takeUnchecked(temp); |
17430 | save.takeUnchecked(temp2); |
17431 | save.takeUnchecked(temp3); |
17432 | if (iterObj.volatile_()) { |
17433 | save.addUnchecked(iterObj); |
17434 | } |
17435 | |
17436 | masm.PushRegsInMask(save); |
17437 | emitPostWriteBarrier(iterObj); |
17438 | masm.PopRegsInMask(save); |
17439 | } |
17440 | masm.bind(&skipBarrier); |
17441 | |
17442 | masm.bind(ool->rejoin()); |
17443 | } |
17444 | |
17445 | void CodeGenerator::visitValueToIterator(LValueToIterator* lir) { |
17446 | pushArg(ToValue(lir, LValueToIterator::ValueIndex)); |
17447 | |
17448 | using Fn = PropertyIteratorObject* (*)(JSContext*, HandleValue); |
17449 | callVM<Fn, ValueToIterator>(lir); |
17450 | } |
17451 | |
17452 | void CodeGenerator::visitIteratorHasIndicesAndBranch( |
17453 | LIteratorHasIndicesAndBranch* lir) { |
17454 | Register iterator = ToRegister(lir->iterator()); |
17455 | Register object = ToRegister(lir->object()); |
17456 | Register temp = ToRegister(lir->temp()); |
17457 | Register temp2 = ToRegister(lir->temp2()); |
17458 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
17459 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
17460 | |
17461 | // Check that the iterator has indices available. |
17462 | Address nativeIterAddr(iterator, |
17463 | PropertyIteratorObject::offsetOfIteratorSlot()); |
17464 | masm.loadPrivate(nativeIterAddr, temp); |
17465 | masm.branchNativeIteratorIndices(Assembler::NotEqual, temp, temp2, |
17466 | NativeIteratorIndices::Valid, ifFalse); |
17467 | |
17468 | // Guard that the first shape stored in the iterator matches the current |
17469 | // shape of the iterated object. |
17470 | Address firstShapeAddr(temp, NativeIterator::offsetOfFirstShape()); |
17471 | masm.loadPtr(firstShapeAddr, temp); |
17472 | masm.branchTestObjShape(Assembler::NotEqual, object, temp, temp2, object, |
17473 | ifFalse); |
17474 | |
17475 | if (!isNextBlock(lir->ifTrue()->lir())) { |
17476 | masm.jump(ifTrue); |
17477 | } |
17478 | } |
17479 | |
17480 | void CodeGenerator::visitLoadSlotByIteratorIndex( |
17481 | LLoadSlotByIteratorIndex* lir) { |
17482 | Register object = ToRegister(lir->object()); |
17483 | Register iterator = ToRegister(lir->iterator()); |
17484 | Register temp = ToRegister(lir->temp0()); |
17485 | Register temp2 = ToRegister(lir->temp1()); |
17486 | ValueOperand result = ToOutValue(lir); |
17487 | |
17488 | masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2); |
17489 | |
17490 | Label notDynamicSlot, notFixedSlot, done; |
17491 | masm.branch32(Assembler::NotEqual, temp2, |
17492 | Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), |
17493 | ¬DynamicSlot); |
17494 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
17495 | masm.loadValue(BaseValueIndex(temp2, temp), result); |
17496 | masm.jump(&done); |
17497 | |
17498 | masm.bind(¬DynamicSlot); |
17499 | masm.branch32(Assembler::NotEqual, temp2, |
17500 | Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), ¬FixedSlot); |
17501 | // Fixed slot |
17502 | masm.loadValue(BaseValueIndex(object, temp, sizeof(NativeObject)), result); |
17503 | masm.jump(&done); |
17504 | masm.bind(¬FixedSlot); |
17505 | |
17506 | #ifdef DEBUG1 |
17507 | Label kindOkay; |
17508 | masm.branch32(Assembler::Equal, temp2, |
17509 | Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); |
17510 | masm.assumeUnreachable("Invalid PropertyIndex::Kind"); |
17511 | masm.bind(&kindOkay); |
17512 | #endif |
17513 | |
17514 | // Dense element |
17515 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2); |
17516 | Label indexOkay; |
17517 | Address initLength(temp2, ObjectElements::offsetOfInitializedLength()); |
17518 | masm.branch32(Assembler::Above, initLength, temp, &indexOkay); |
17519 | masm.assumeUnreachable("Dense element out of bounds"); |
17520 | masm.bind(&indexOkay); |
17521 | |
17522 | masm.loadValue(BaseObjectElementIndex(temp2, temp), result); |
17523 | masm.bind(&done); |
17524 | } |
17525 | |
17526 | void CodeGenerator::visitStoreSlotByIteratorIndex( |
17527 | LStoreSlotByIteratorIndex* lir) { |
17528 | Register object = ToRegister(lir->object()); |
17529 | Register iterator = ToRegister(lir->iterator()); |
17530 | ValueOperand value = ToValue(lir, LStoreSlotByIteratorIndex::ValueIndex); |
17531 | Register temp = ToRegister(lir->temp0()); |
17532 | Register temp2 = ToRegister(lir->temp1()); |
17533 | |
17534 | masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2); |
17535 | |
17536 | Label notDynamicSlot, notFixedSlot, done, doStore; |
17537 | masm.branch32(Assembler::NotEqual, temp2, |
17538 | Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), |
17539 | ¬DynamicSlot); |
17540 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
17541 | masm.computeEffectiveAddress(BaseValueIndex(temp2, temp), temp); |
17542 | masm.jump(&doStore); |
17543 | |
17544 | masm.bind(¬DynamicSlot); |
17545 | masm.branch32(Assembler::NotEqual, temp2, |
17546 | Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), ¬FixedSlot); |
17547 | // Fixed slot |
17548 | masm.computeEffectiveAddress( |
17549 | BaseValueIndex(object, temp, sizeof(NativeObject)), temp); |
17550 | masm.jump(&doStore); |
17551 | masm.bind(¬FixedSlot); |
17552 | |
17553 | #ifdef DEBUG1 |
17554 | Label kindOkay; |
17555 | masm.branch32(Assembler::Equal, temp2, |
17556 | Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); |
17557 | masm.assumeUnreachable("Invalid PropertyIndex::Kind"); |
17558 | masm.bind(&kindOkay); |
17559 | #endif |
17560 | |
17561 | // Dense element |
17562 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2); |
17563 | Label indexOkay; |
17564 | Address initLength(temp2, ObjectElements::offsetOfInitializedLength()); |
17565 | masm.branch32(Assembler::Above, initLength, temp, &indexOkay); |
17566 | masm.assumeUnreachable("Dense element out of bounds"); |
17567 | masm.bind(&indexOkay); |
17568 | |
17569 | BaseObjectElementIndex elementAddress(temp2, temp); |
17570 | masm.computeEffectiveAddress(elementAddress, temp); |
17571 | |
17572 | masm.bind(&doStore); |
17573 | Address storeAddress(temp, 0); |
17574 | emitPreBarrier(storeAddress); |
17575 | masm.storeValue(value, storeAddress); |
17576 | |
17577 | masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp2, &done); |
17578 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp2, &done); |
17579 | |
17580 | saveVolatile(temp2); |
17581 | emitPostWriteBarrier(object); |
17582 | restoreVolatile(temp2); |
17583 | |
17584 | masm.bind(&done); |
17585 | } |
17586 | |
17587 | void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) { |
17588 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
17589 | Register objReg = ToRegister(ins->object()); |
17590 | Register temp = ToRegister(ins->temp0()); |
17591 | |
17592 | ConstantOrRegister id = toConstantOrRegister(ins, LSetPropertyCache::IdIndex, |
17593 | ins->mir()->idval()->type()); |
17594 | ConstantOrRegister value = toConstantOrRegister( |
17595 | ins, LSetPropertyCache::ValueIndex, ins->mir()->value()->type()); |
17596 | |
17597 | addSetPropertyCache(ins, liveRegs, objReg, temp, id, value, |
17598 | ins->mir()->strict()); |
17599 | } |
17600 | |
17601 | void CodeGenerator::visitThrow(LThrow* lir) { |
17602 | pushArg(ToValue(lir, LThrow::ValueIndex)); |
17603 | |
17604 | using Fn = bool (*)(JSContext*, HandleValue); |
17605 | callVM<Fn, js::ThrowOperation>(lir); |
17606 | } |
17607 | |
17608 | void CodeGenerator::visitThrowWithStack(LThrowWithStack* lir) { |
17609 | pushArg(ToValue(lir, LThrowWithStack::StackIndex)); |
17610 | pushArg(ToValue(lir, LThrowWithStack::ValueIndex)); |
17611 | |
17612 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue); |
17613 | callVM<Fn, js::ThrowWithStackOperation>(lir); |
17614 | } |
17615 | |
17616 | class OutOfLineTypeOfV : public OutOfLineCodeBase<CodeGenerator> { |
17617 | LTypeOfV* ins_; |
17618 | |
17619 | public: |
17620 | explicit OutOfLineTypeOfV(LTypeOfV* ins) : ins_(ins) {} |
17621 | |
17622 | void accept(CodeGenerator* codegen) override { |
17623 | codegen->visitOutOfLineTypeOfV(this); |
17624 | } |
17625 | LTypeOfV* ins() const { return ins_; } |
17626 | }; |
17627 | |
17628 | void CodeGenerator::emitTypeOfJSType(JSValueType type, Register output) { |
17629 | switch (type) { |
17630 | case JSVAL_TYPE_OBJECT: |
17631 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
17632 | break; |
17633 | case JSVAL_TYPE_DOUBLE: |
17634 | case JSVAL_TYPE_INT32: |
17635 | masm.move32(Imm32(JSTYPE_NUMBER), output); |
17636 | break; |
17637 | case JSVAL_TYPE_BOOLEAN: |
17638 | masm.move32(Imm32(JSTYPE_BOOLEAN), output); |
17639 | break; |
17640 | case JSVAL_TYPE_UNDEFINED: |
17641 | masm.move32(Imm32(JSTYPE_UNDEFINED), output); |
17642 | break; |
17643 | case JSVAL_TYPE_NULL: |
17644 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
17645 | break; |
17646 | case JSVAL_TYPE_STRING: |
17647 | masm.move32(Imm32(JSTYPE_STRING), output); |
17648 | break; |
17649 | case JSVAL_TYPE_SYMBOL: |
17650 | masm.move32(Imm32(JSTYPE_SYMBOL), output); |
17651 | break; |
17652 | case JSVAL_TYPE_BIGINT: |
17653 | masm.move32(Imm32(JSTYPE_BIGINT), output); |
17654 | break; |
17655 | default: |
17656 | MOZ_CRASH("Unsupported JSValueType")do { do { } while (false); MOZ_ReportCrash("" "Unsupported JSValueType" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17656); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported JSValueType" ")"); do { *((volatile int*)__null) = 17656; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
17657 | } |
17658 | } |
17659 | |
17660 | void CodeGenerator::emitTypeOfCheck(JSValueType type, Register tag, |
17661 | Register output, Label* done, |
17662 | Label* oolObject) { |
17663 | Label notMatch; |
17664 | switch (type) { |
17665 | case JSVAL_TYPE_OBJECT: |
17666 | // The input may be a callable object (result is "function") or |
17667 | // may emulate undefined (result is "undefined"). Use an OOL path. |
17668 | masm.branchTestObject(Assembler::Equal, tag, oolObject); |
17669 | return; |
17670 | case JSVAL_TYPE_DOUBLE: |
17671 | case JSVAL_TYPE_INT32: |
17672 | masm.branchTestNumber(Assembler::NotEqual, tag, ¬Match); |
17673 | break; |
17674 | default: |
17675 | masm.branchTestType(Assembler::NotEqual, tag, type, ¬Match); |
17676 | break; |
17677 | } |
17678 | |
17679 | emitTypeOfJSType(type, output); |
17680 | masm.jump(done); |
17681 | masm.bind(¬Match); |
17682 | } |
17683 | |
17684 | void CodeGenerator::visitTypeOfV(LTypeOfV* lir) { |
17685 | const ValueOperand value = ToValue(lir, LTypeOfV::InputIndex); |
17686 | Register output = ToRegister(lir->output()); |
17687 | Register tag = masm.extractTag(value, output); |
17688 | |
17689 | Label done; |
17690 | |
17691 | auto* ool = new (alloc()) OutOfLineTypeOfV(lir); |
17692 | addOutOfLineCode(ool, lir->mir()); |
17693 | |
17694 | const std::initializer_list<JSValueType> defaultOrder = { |
17695 | JSVAL_TYPE_OBJECT, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_UNDEFINED, |
17696 | JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_STRING, |
17697 | JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT}; |
17698 | |
17699 | mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder); |
17700 | |
17701 | // Generate checks for previously observed types first. |
17702 | // The TypeDataList is sorted by descending frequency. |
17703 | for (auto& observed : lir->mir()->observedTypes()) { |
17704 | JSValueType type = observed.type(); |
17705 | |
17706 | // Unify number types. |
17707 | if (type == JSVAL_TYPE_INT32) { |
17708 | type = JSVAL_TYPE_DOUBLE; |
17709 | } |
17710 | |
17711 | remaining -= type; |
17712 | |
17713 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
17714 | } |
17715 | |
17716 | // Generate checks for remaining types. |
17717 | for (auto type : defaultOrder) { |
17718 | if (!remaining.contains(type)) { |
17719 | continue; |
17720 | } |
17721 | remaining -= type; |
17722 | |
17723 | if (remaining.isEmpty() && type != JSVAL_TYPE_OBJECT) { |
17724 | // We can skip the check for the last remaining type, unless the type is |
17725 | // JSVAL_TYPE_OBJECT, which may have to go through the OOL path. |
17726 | #ifdef DEBUG1 |
17727 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
17728 | masm.assumeUnreachable("Unexpected Value type in visitTypeOfV"); |
17729 | #else |
17730 | emitTypeOfJSType(type, output); |
17731 | #endif |
17732 | } else { |
17733 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
17734 | } |
17735 | } |
17736 | MOZ_ASSERT(remaining.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(remaining.isEmpty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(remaining.isEmpty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("remaining.isEmpty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17736); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { *((volatile int*)__null) = 17736; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
17737 | |
17738 | masm.bind(&done); |
17739 | masm.bind(ool->rejoin()); |
17740 | } |
17741 | |
17742 | void CodeGenerator::emitTypeOfObject(Register obj, Register output, |
17743 | Label* done) { |
17744 | Label slowCheck, isObject, isCallable, isUndefined; |
17745 | masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable, |
17746 | &isUndefined); |
17747 | |
17748 | masm.bind(&isCallable); |
17749 | masm.move32(Imm32(JSTYPE_FUNCTION), output); |
17750 | masm.jump(done); |
17751 | |
17752 | masm.bind(&isUndefined); |
17753 | masm.move32(Imm32(JSTYPE_UNDEFINED), output); |
17754 | masm.jump(done); |
17755 | |
17756 | masm.bind(&isObject); |
17757 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
17758 | masm.jump(done); |
17759 | |
17760 | masm.bind(&slowCheck); |
17761 | |
17762 | saveVolatile(output); |
17763 | using Fn = JSType (*)(JSObject*); |
17764 | masm.setupAlignedABICall(); |
17765 | masm.passABIArg(obj); |
17766 | masm.callWithABI<Fn, js::TypeOfObject>(); |
17767 | masm.storeCallInt32Result(output); |
17768 | restoreVolatile(output); |
17769 | } |
17770 | |
17771 | void CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) { |
17772 | LTypeOfV* ins = ool->ins(); |
17773 | |
17774 | ValueOperand input = ToValue(ins, LTypeOfV::InputIndex); |
17775 | Register temp = ToTempUnboxRegister(ins->temp0()); |
17776 | Register output = ToRegister(ins->output()); |
17777 | |
17778 | Register obj = masm.extractObject(input, temp); |
17779 | emitTypeOfObject(obj, output, ool->rejoin()); |
17780 | masm.jump(ool->rejoin()); |
17781 | } |
17782 | |
17783 | void CodeGenerator::visitTypeOfO(LTypeOfO* lir) { |
17784 | Register obj = ToRegister(lir->object()); |
17785 | Register output = ToRegister(lir->output()); |
17786 | |
17787 | Label done; |
17788 | emitTypeOfObject(obj, output, &done); |
17789 | masm.bind(&done); |
17790 | } |
17791 | |
17792 | void CodeGenerator::visitTypeOfName(LTypeOfName* lir) { |
17793 | Register input = ToRegister(lir->input()); |
17794 | Register output = ToRegister(lir->output()); |
17795 | |
17796 | #ifdef DEBUG1 |
17797 | Label ok; |
17798 | masm.branch32(Assembler::Below, input, Imm32(JSTYPE_LIMIT), &ok); |
17799 | masm.assumeUnreachable("bad JSType"); |
17800 | masm.bind(&ok); |
17801 | #endif |
17802 | |
17803 | static_assert(JSTYPE_UNDEFINED == 0); |
17804 | |
17805 | masm.movePtr(ImmPtr(&gen->runtime->names().undefined), output); |
17806 | masm.loadPtr(BaseIndex(output, input, ScalePointer), output); |
17807 | } |
17808 | |
17809 | class OutOfLineTypeOfIsNonPrimitiveV : public OutOfLineCodeBase<CodeGenerator> { |
17810 | LTypeOfIsNonPrimitiveV* ins_; |
17811 | |
17812 | public: |
17813 | explicit OutOfLineTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* ins) |
17814 | : ins_(ins) {} |
17815 | |
17816 | void accept(CodeGenerator* codegen) override { |
17817 | codegen->visitOutOfLineTypeOfIsNonPrimitiveV(this); |
17818 | } |
17819 | auto* ins() const { return ins_; } |
17820 | }; |
17821 | |
17822 | class OutOfLineTypeOfIsNonPrimitiveO : public OutOfLineCodeBase<CodeGenerator> { |
17823 | LTypeOfIsNonPrimitiveO* ins_; |
17824 | |
17825 | public: |
17826 | explicit OutOfLineTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* ins) |
17827 | : ins_(ins) {} |
17828 | |
17829 | void accept(CodeGenerator* codegen) override { |
17830 | codegen->visitOutOfLineTypeOfIsNonPrimitiveO(this); |
17831 | } |
17832 | auto* ins() const { return ins_; } |
17833 | }; |
17834 | |
17835 | void CodeGenerator::emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj, |
17836 | Register output) { |
17837 | saveVolatile(output); |
17838 | using Fn = JSType (*)(JSObject*); |
17839 | masm.setupAlignedABICall(); |
17840 | masm.passABIArg(obj); |
17841 | masm.callWithABI<Fn, js::TypeOfObject>(); |
17842 | masm.storeCallInt32Result(output); |
17843 | restoreVolatile(output); |
17844 | |
17845 | auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false); |
17846 | masm.cmp32Set(cond, output, Imm32(mir->jstype()), output); |
17847 | } |
17848 | |
17849 | void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveV( |
17850 | OutOfLineTypeOfIsNonPrimitiveV* ool) { |
17851 | auto* ins = ool->ins(); |
17852 | ValueOperand input = ToValue(ins, LTypeOfIsNonPrimitiveV::InputIndex); |
17853 | Register output = ToRegister(ins->output()); |
17854 | Register temp = ToTempUnboxRegister(ins->temp0()); |
17855 | |
17856 | Register obj = masm.extractObject(input, temp); |
17857 | |
17858 | emitTypeOfIsObjectOOL(ins->mir(), obj, output); |
17859 | |
17860 | masm.jump(ool->rejoin()); |
17861 | } |
17862 | |
17863 | void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveO( |
17864 | OutOfLineTypeOfIsNonPrimitiveO* ool) { |
17865 | auto* ins = ool->ins(); |
17866 | Register input = ToRegister(ins->input()); |
17867 | Register output = ToRegister(ins->output()); |
17868 | |
17869 | emitTypeOfIsObjectOOL(ins->mir(), input, output); |
17870 | |
17871 | masm.jump(ool->rejoin()); |
17872 | } |
17873 | |
17874 | void CodeGenerator::emitTypeOfIsObject(MTypeOfIs* mir, Register obj, |
17875 | Register output, Label* success, |
17876 | Label* fail, Label* slowCheck) { |
17877 | Label* isObject = fail; |
17878 | Label* isFunction = fail; |
17879 | Label* isUndefined = fail; |
17880 | |
17881 | switch (mir->jstype()) { |
17882 | case JSTYPE_UNDEFINED: |
17883 | isUndefined = success; |
17884 | break; |
17885 | |
17886 | case JSTYPE_OBJECT: |
17887 | isObject = success; |
17888 | break; |
17889 | |
17890 | case JSTYPE_FUNCTION: |
17891 | isFunction = success; |
17892 | break; |
17893 | |
17894 | case JSTYPE_STRING: |
17895 | case JSTYPE_NUMBER: |
17896 | case JSTYPE_BOOLEAN: |
17897 | case JSTYPE_SYMBOL: |
17898 | case JSTYPE_BIGINT: |
17899 | #ifdef ENABLE_RECORD_TUPLE |
17900 | case JSTYPE_RECORD: |
17901 | case JSTYPE_TUPLE: |
17902 | #endif |
17903 | case JSTYPE_LIMIT: |
17904 | MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17904); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type" ")"); do { *((volatile int*)__null) = 17904; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
17905 | } |
17906 | |
17907 | masm.typeOfObject(obj, output, slowCheck, isObject, isFunction, isUndefined); |
17908 | |
17909 | auto op = mir->jsop(); |
17910 | |
17911 | Label done; |
17912 | masm.bind(fail); |
17913 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
17914 | masm.jump(&done); |
17915 | masm.bind(success); |
17916 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
17917 | masm.bind(&done); |
17918 | } |
17919 | |
17920 | void CodeGenerator::visitTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* lir) { |
17921 | ValueOperand input = ToValue(lir, LTypeOfIsNonPrimitiveV::InputIndex); |
17922 | Register output = ToRegister(lir->output()); |
17923 | Register temp = ToTempUnboxRegister(lir->temp0()); |
17924 | |
17925 | auto* mir = lir->mir(); |
17926 | |
17927 | auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveV(lir); |
17928 | addOutOfLineCode(ool, mir); |
17929 | |
17930 | Label success, fail; |
17931 | |
17932 | switch (mir->jstype()) { |
17933 | case JSTYPE_UNDEFINED: { |
17934 | ScratchTagScope tag(masm, input); |
17935 | masm.splitTagForTest(input, tag); |
17936 | |
17937 | masm.branchTestUndefined(Assembler::Equal, tag, &success); |
17938 | masm.branchTestObject(Assembler::NotEqual, tag, &fail); |
17939 | break; |
17940 | } |
17941 | |
17942 | case JSTYPE_OBJECT: { |
17943 | ScratchTagScope tag(masm, input); |
17944 | masm.splitTagForTest(input, tag); |
17945 | |
17946 | masm.branchTestNull(Assembler::Equal, tag, &success); |
17947 | masm.branchTestObject(Assembler::NotEqual, tag, &fail); |
17948 | break; |
17949 | } |
17950 | |
17951 | case JSTYPE_FUNCTION: { |
17952 | masm.branchTestObject(Assembler::NotEqual, input, &fail); |
17953 | break; |
17954 | } |
17955 | |
17956 | case JSTYPE_STRING: |
17957 | case JSTYPE_NUMBER: |
17958 | case JSTYPE_BOOLEAN: |
17959 | case JSTYPE_SYMBOL: |
17960 | case JSTYPE_BIGINT: |
17961 | #ifdef ENABLE_RECORD_TUPLE |
17962 | case JSTYPE_RECORD: |
17963 | case JSTYPE_TUPLE: |
17964 | #endif |
17965 | case JSTYPE_LIMIT: |
17966 | MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17966); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type" ")"); do { *((volatile int*)__null) = 17966; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
17967 | } |
17968 | |
17969 | Register obj = masm.extractObject(input, temp); |
17970 | |
17971 | emitTypeOfIsObject(mir, obj, output, &success, &fail, ool->entry()); |
17972 | |
17973 | masm.bind(ool->rejoin()); |
17974 | } |
17975 | |
17976 | void CodeGenerator::visitTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* lir) { |
17977 | Register input = ToRegister(lir->input()); |
17978 | Register output = ToRegister(lir->output()); |
17979 | |
17980 | auto* mir = lir->mir(); |
17981 | |
17982 | auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveO(lir); |
17983 | addOutOfLineCode(ool, mir); |
17984 | |
17985 | Label success, fail; |
17986 | emitTypeOfIsObject(mir, input, output, &success, &fail, ool->entry()); |
17987 | |
17988 | masm.bind(ool->rejoin()); |
17989 | } |
17990 | |
17991 | void CodeGenerator::visitTypeOfIsPrimitive(LTypeOfIsPrimitive* lir) { |
17992 | ValueOperand input = ToValue(lir, LTypeOfIsPrimitive::InputIndex); |
17993 | Register output = ToRegister(lir->output()); |
17994 | |
17995 | auto* mir = lir->mir(); |
17996 | auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false); |
17997 | |
17998 | switch (mir->jstype()) { |
17999 | case JSTYPE_STRING: |
18000 | masm.testStringSet(cond, input, output); |
18001 | break; |
18002 | case JSTYPE_NUMBER: |
18003 | masm.testNumberSet(cond, input, output); |
18004 | break; |
18005 | case JSTYPE_BOOLEAN: |
18006 | masm.testBooleanSet(cond, input, output); |
18007 | break; |
18008 | case JSTYPE_SYMBOL: |
18009 | masm.testSymbolSet(cond, input, output); |
18010 | break; |
18011 | case JSTYPE_BIGINT: |
18012 | masm.testBigIntSet(cond, input, output); |
18013 | break; |
18014 | |
18015 | case JSTYPE_UNDEFINED: |
18016 | case JSTYPE_OBJECT: |
18017 | case JSTYPE_FUNCTION: |
18018 | #ifdef ENABLE_RECORD_TUPLE |
18019 | case JSTYPE_RECORD: |
18020 | case JSTYPE_TUPLE: |
18021 | #endif |
18022 | case JSTYPE_LIMIT: |
18023 | MOZ_CRASH("Non-primitive type")do { do { } while (false); MOZ_ReportCrash("" "Non-primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18023); AnnotateMozCrashReason("MOZ_CRASH(" "Non-primitive type" ")"); do { *((volatile int*)__null) = 18023; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18024 | } |
18025 | } |
18026 | |
18027 | void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) { |
18028 | pushArg(ToValue(lir, LToAsyncIter::NextMethodIndex)); |
18029 | pushArg(ToRegister(lir->iterator())); |
18030 | |
18031 | using Fn = JSObject* (*)(JSContext*, HandleObject, HandleValue); |
18032 | callVM<Fn, js::CreateAsyncFromSyncIterator>(lir); |
18033 | } |
18034 | |
18035 | void CodeGenerator::visitToPropertyKeyCache(LToPropertyKeyCache* lir) { |
18036 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
18037 | ValueOperand input = ToValue(lir, LToPropertyKeyCache::InputIndex); |
18038 | ValueOperand output = ToOutValue(lir); |
18039 | |
18040 | IonToPropertyKeyIC ic(liveRegs, input, output); |
18041 | addIC(lir, allocateIC(ic)); |
18042 | } |
18043 | |
18044 | void CodeGenerator::visitLoadElementV(LLoadElementV* load) { |
18045 | Register elements = ToRegister(load->elements()); |
18046 | const ValueOperand out = ToOutValue(load); |
18047 | |
18048 | if (load->index()->isConstant()) { |
18049 | NativeObject::elementsSizeMustNotOverflow(); |
18050 | int32_t offset = ToInt32(load->index()) * sizeof(Value); |
18051 | masm.loadValue(Address(elements, offset), out); |
18052 | } else { |
18053 | masm.loadValue(BaseObjectElementIndex(elements, ToRegister(load->index())), |
18054 | out); |
18055 | } |
18056 | |
18057 | Label testMagic; |
18058 | masm.branchTestMagic(Assembler::Equal, out, &testMagic); |
18059 | bailoutFrom(&testMagic, load->snapshot()); |
18060 | } |
18061 | |
18062 | void CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) { |
18063 | Register elements = ToRegister(lir->elements()); |
18064 | Register index = ToRegister(lir->index()); |
18065 | Register initLength = ToRegister(lir->initLength()); |
18066 | const ValueOperand out = ToOutValue(lir); |
18067 | |
18068 | const MLoadElementHole* mir = lir->mir(); |
18069 | |
18070 | // If the index is out of bounds, load |undefined|. Otherwise, load the |
18071 | // value. |
18072 | Label outOfBounds, done; |
18073 | masm.spectreBoundsCheck32(index, initLength, out.scratchReg(), &outOfBounds); |
18074 | |
18075 | masm.loadValue(BaseObjectElementIndex(elements, index), out); |
18076 | |
18077 | // If the value wasn't a hole, we're done. Otherwise, we'll load undefined. |
18078 | masm.branchTestMagic(Assembler::NotEqual, out, &done); |
18079 | |
18080 | if (mir->needsNegativeIntCheck()) { |
18081 | Label loadUndefined; |
18082 | masm.jump(&loadUndefined); |
18083 | |
18084 | masm.bind(&outOfBounds); |
18085 | |
18086 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
18087 | |
18088 | masm.bind(&loadUndefined); |
18089 | } else { |
18090 | masm.bind(&outOfBounds); |
18091 | } |
18092 | masm.moveValue(UndefinedValue(), out); |
18093 | |
18094 | masm.bind(&done); |
18095 | } |
18096 | |
18097 | void CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) { |
18098 | Register elements = ToRegister(lir->elements()); |
18099 | Register temp0 = ToTempRegisterOrInvalid(lir->temp0()); |
18100 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
18101 | AnyRegister out = ToAnyRegister(lir->output()); |
18102 | |
18103 | const MLoadUnboxedScalar* mir = lir->mir(); |
18104 | |
18105 | Scalar::Type storageType = mir->storageType(); |
18106 | |
18107 | LiveRegisterSet volatileRegs; |
18108 | if (MacroAssembler::LoadRequiresCall(storageType)) { |
18109 | volatileRegs = liveVolatileRegs(lir); |
18110 | } |
18111 | |
18112 | Label fail; |
18113 | if (lir->index()->isConstant()) { |
18114 | Address source = |
18115 | ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment()); |
18116 | masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail, |
18117 | volatileRegs); |
18118 | } else { |
18119 | BaseIndex source(elements, ToRegister(lir->index()), |
18120 | ScaleFromScalarType(storageType), mir->offsetAdjustment()); |
18121 | masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail, |
18122 | volatileRegs); |
18123 | } |
18124 | |
18125 | if (fail.used()) { |
18126 | bailoutFrom(&fail, lir->snapshot()); |
18127 | } |
18128 | } |
18129 | |
18130 | void CodeGenerator::visitLoadUnboxedBigInt(LLoadUnboxedBigInt* lir) { |
18131 | Register elements = ToRegister(lir->elements()); |
18132 | Register temp = ToRegister(lir->temp()); |
18133 | Register64 temp64 = ToRegister64(lir->temp64()); |
18134 | Register out = ToRegister(lir->output()); |
18135 | |
18136 | const MLoadUnboxedScalar* mir = lir->mir(); |
18137 | |
18138 | Scalar::Type storageType = mir->storageType(); |
18139 | |
18140 | if (lir->index()->isConstant()) { |
18141 | Address source = |
18142 | ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment()); |
18143 | masm.load64(source, temp64); |
18144 | } else { |
18145 | BaseIndex source(elements, ToRegister(lir->index()), |
18146 | ScaleFromScalarType(storageType), mir->offsetAdjustment()); |
18147 | masm.load64(source, temp64); |
18148 | } |
18149 | |
18150 | emitCreateBigInt(lir, storageType, temp64, out, temp); |
18151 | } |
18152 | |
18153 | void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) { |
18154 | Register elements = ToRegister(lir->elements()); |
18155 | const LAllocation* littleEndian = lir->littleEndian(); |
18156 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
18157 | Register temp2 = ToTempRegisterOrInvalid(lir->temp2()); |
18158 | Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64()); |
18159 | AnyRegister out = ToAnyRegister(lir->output()); |
18160 | |
18161 | const MLoadDataViewElement* mir = lir->mir(); |
18162 | Scalar::Type storageType = mir->storageType(); |
18163 | |
18164 | LiveRegisterSet volatileRegs; |
18165 | if (MacroAssembler::LoadRequiresCall(storageType)) { |
18166 | volatileRegs = liveVolatileRegs(lir); |
18167 | } |
18168 | |
18169 | BaseIndex source(elements, ToRegister(lir->index()), TimesOne); |
18170 | |
18171 | bool noSwap = littleEndian->isConstant() && |
18172 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
18173 | |
18174 | // Directly load if no byte swap is needed and the platform supports unaligned |
18175 | // accesses for the access. (Such support is assumed for integer types.) |
18176 | if (noSwap && (!Scalar::isFloatingType(storageType) || |
18177 | MacroAssembler::SupportsFastUnalignedFPAccesses())) { |
18178 | if (!Scalar::isBigIntType(storageType)) { |
18179 | Label fail; |
18180 | masm.loadFromTypedArray(storageType, source, out, temp1, temp2, &fail, |
18181 | volatileRegs); |
18182 | |
18183 | if (fail.used()) { |
18184 | bailoutFrom(&fail, lir->snapshot()); |
18185 | } |
18186 | } else { |
18187 | masm.load64(source, temp64); |
18188 | |
18189 | emitCreateBigInt(lir, storageType, temp64, out.gpr(), temp1); |
18190 | } |
18191 | return; |
18192 | } |
18193 | |
18194 | // Load the value into a gpr register. |
18195 | switch (storageType) { |
18196 | case Scalar::Int16: |
18197 | masm.load16UnalignedSignExtend(source, out.gpr()); |
18198 | break; |
18199 | case Scalar::Uint16: |
18200 | masm.load16UnalignedZeroExtend(source, out.gpr()); |
18201 | break; |
18202 | case Scalar::Int32: |
18203 | masm.load32Unaligned(source, out.gpr()); |
18204 | break; |
18205 | case Scalar::Uint32: |
18206 | masm.load32Unaligned(source, out.isFloat() ? temp1 : out.gpr()); |
18207 | break; |
18208 | case Scalar::Float16: |
18209 | masm.load16UnalignedZeroExtend(source, temp1); |
18210 | break; |
18211 | case Scalar::Float32: |
18212 | masm.load32Unaligned(source, temp1); |
18213 | break; |
18214 | case Scalar::Float64: |
18215 | case Scalar::BigInt64: |
18216 | case Scalar::BigUint64: |
18217 | masm.load64Unaligned(source, temp64); |
18218 | break; |
18219 | case Scalar::Int8: |
18220 | case Scalar::Uint8: |
18221 | case Scalar::Uint8Clamped: |
18222 | default: |
18223 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18223); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18223; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18224 | } |
18225 | |
18226 | if (!noSwap) { |
18227 | // Swap the bytes in the loaded value. |
18228 | Label skip; |
18229 | if (!littleEndian->isConstant()) { |
18230 | masm.branch32( |
18231 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
18232 | ToRegister(littleEndian), Imm32(0), &skip); |
18233 | } |
18234 | |
18235 | switch (storageType) { |
18236 | case Scalar::Int16: |
18237 | masm.byteSwap16SignExtend(out.gpr()); |
18238 | break; |
18239 | case Scalar::Uint16: |
18240 | masm.byteSwap16ZeroExtend(out.gpr()); |
18241 | break; |
18242 | case Scalar::Int32: |
18243 | masm.byteSwap32(out.gpr()); |
18244 | break; |
18245 | case Scalar::Uint32: |
18246 | masm.byteSwap32(out.isFloat() ? temp1 : out.gpr()); |
18247 | break; |
18248 | case Scalar::Float16: |
18249 | masm.byteSwap16ZeroExtend(temp1); |
18250 | break; |
18251 | case Scalar::Float32: |
18252 | masm.byteSwap32(temp1); |
18253 | break; |
18254 | case Scalar::Float64: |
18255 | case Scalar::BigInt64: |
18256 | case Scalar::BigUint64: |
18257 | masm.byteSwap64(temp64); |
18258 | break; |
18259 | case Scalar::Int8: |
18260 | case Scalar::Uint8: |
18261 | case Scalar::Uint8Clamped: |
18262 | default: |
18263 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18263); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18263; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18264 | } |
18265 | |
18266 | if (skip.used()) { |
18267 | masm.bind(&skip); |
18268 | } |
18269 | } |
18270 | |
18271 | // Move the value into the output register. |
18272 | switch (storageType) { |
18273 | case Scalar::Int16: |
18274 | case Scalar::Uint16: |
18275 | case Scalar::Int32: |
18276 | break; |
18277 | case Scalar::Uint32: |
18278 | if (out.isFloat()) { |
18279 | masm.convertUInt32ToDouble(temp1, out.fpu()); |
18280 | } else { |
18281 | // Bail out if the value doesn't fit into a signed int32 value. This |
18282 | // is what allows MLoadDataViewElement to have a type() of |
18283 | // MIRType::Int32 for UInt32 array loads. |
18284 | bailoutTest32(Assembler::Signed, out.gpr(), out.gpr(), lir->snapshot()); |
18285 | } |
18286 | break; |
18287 | case Scalar::Float16: |
18288 | masm.moveGPRToFloat16(temp1, out.fpu(), temp2, volatileRegs); |
18289 | masm.canonicalizeFloat(out.fpu()); |
18290 | break; |
18291 | case Scalar::Float32: |
18292 | masm.moveGPRToFloat32(temp1, out.fpu()); |
18293 | masm.canonicalizeFloat(out.fpu()); |
18294 | break; |
18295 | case Scalar::Float64: |
18296 | masm.moveGPR64ToDouble(temp64, out.fpu()); |
18297 | masm.canonicalizeDouble(out.fpu()); |
18298 | break; |
18299 | case Scalar::BigInt64: |
18300 | case Scalar::BigUint64: |
18301 | emitCreateBigInt(lir, storageType, temp64, out.gpr(), temp1); |
18302 | break; |
18303 | case Scalar::Int8: |
18304 | case Scalar::Uint8: |
18305 | case Scalar::Uint8Clamped: |
18306 | default: |
18307 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18307); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18307; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18308 | } |
18309 | } |
18310 | |
18311 | void CodeGenerator::visitLoadTypedArrayElementHole( |
18312 | LLoadTypedArrayElementHole* lir) { |
18313 | Register elements = ToRegister(lir->elements()); |
18314 | Register index = ToRegister(lir->index()); |
18315 | Register length = ToRegister(lir->length()); |
18316 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
18317 | const ValueOperand out = ToOutValue(lir); |
18318 | |
18319 | Register scratch = out.scratchReg(); |
18320 | |
18321 | // Load undefined if index >= length. |
18322 | Label outOfBounds, done; |
18323 | masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds); |
18324 | |
18325 | Scalar::Type arrayType = lir->mir()->arrayType(); |
18326 | |
18327 | LiveRegisterSet volatileRegs; |
18328 | if (MacroAssembler::LoadRequiresCall(arrayType)) { |
18329 | volatileRegs = liveVolatileRegs(lir); |
18330 | } |
18331 | |
18332 | Label fail; |
18333 | BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); |
18334 | MacroAssembler::Uint32Mode uint32Mode = |
18335 | lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble |
18336 | : MacroAssembler::Uint32Mode::FailOnDouble; |
18337 | masm.loadFromTypedArray(arrayType, source, out, uint32Mode, temp, &fail, |
18338 | volatileRegs); |
18339 | masm.jump(&done); |
18340 | |
18341 | masm.bind(&outOfBounds); |
18342 | masm.moveValue(UndefinedValue(), out); |
18343 | |
18344 | if (fail.used()) { |
18345 | bailoutFrom(&fail, lir->snapshot()); |
18346 | } |
18347 | |
18348 | masm.bind(&done); |
18349 | } |
18350 | |
18351 | void CodeGenerator::visitLoadTypedArrayElementHoleBigInt( |
18352 | LLoadTypedArrayElementHoleBigInt* lir) { |
18353 | Register elements = ToRegister(lir->elements()); |
18354 | Register index = ToRegister(lir->index()); |
18355 | Register length = ToRegister(lir->length()); |
18356 | const ValueOperand out = ToOutValue(lir); |
18357 | |
18358 | Register temp = ToRegister(lir->temp()); |
18359 | |
18360 | // On x86 there are not enough registers. In that case reuse the output |
18361 | // registers as temporaries. |
18362 | #ifdef JS_CODEGEN_X86 |
18363 | MOZ_ASSERT(lir->temp64().isBogusTemp())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->temp64().isBogusTemp())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->temp64().isBogusTemp ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->temp64().isBogusTemp()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18363); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->temp64().isBogusTemp()" ")"); do { *((volatile int*)__null) = 18363; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
18364 | Register64 temp64 = out.toRegister64(); |
18365 | #else |
18366 | Register64 temp64 = ToRegister64(lir->temp64()); |
18367 | #endif |
18368 | |
18369 | // Load undefined if index >= length. |
18370 | Label outOfBounds, done; |
18371 | masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds); |
18372 | |
18373 | Scalar::Type arrayType = lir->mir()->arrayType(); |
18374 | BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); |
18375 | masm.load64(source, temp64); |
18376 | |
18377 | #ifdef JS_CODEGEN_X86 |
18378 | Register bigInt = temp; |
18379 | Register maybeTemp = InvalidReg; |
18380 | #else |
18381 | Register bigInt = out.scratchReg(); |
18382 | Register maybeTemp = temp; |
18383 | #endif |
18384 | emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp); |
18385 | |
18386 | masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out); |
18387 | masm.jump(&done); |
18388 | |
18389 | masm.bind(&outOfBounds); |
18390 | masm.moveValue(UndefinedValue(), out); |
18391 | |
18392 | masm.bind(&done); |
18393 | } |
18394 | |
18395 | template <SwitchTableType tableType> |
18396 | class OutOfLineSwitch : public OutOfLineCodeBase<CodeGenerator> { |
18397 | using LabelsVector = Vector<Label, 0, JitAllocPolicy>; |
18398 | using CodeLabelsVector = Vector<CodeLabel, 0, JitAllocPolicy>; |
18399 | LabelsVector labels_; |
18400 | CodeLabelsVector codeLabels_; |
18401 | CodeLabel start_; |
18402 | bool isOutOfLine_; |
18403 | |
18404 | void accept(CodeGenerator* codegen) override { |
18405 | codegen->visitOutOfLineSwitch(this); |
18406 | } |
18407 | |
18408 | public: |
18409 | explicit OutOfLineSwitch(TempAllocator& alloc) |
18410 | : labels_(alloc), codeLabels_(alloc), isOutOfLine_(false) {} |
18411 | |
18412 | CodeLabel* start() { return &start_; } |
18413 | |
18414 | CodeLabelsVector& codeLabels() { return codeLabels_; } |
18415 | LabelsVector& labels() { return labels_; } |
18416 | |
18417 | void jumpToCodeEntries(MacroAssembler& masm, Register index, Register temp) { |
18418 | Register base; |
18419 | if (tableType == SwitchTableType::Inline) { |
18420 | #if defined(JS_CODEGEN_ARM) |
18421 | base = ::js::jit::pc; |
18422 | #else |
18423 | MOZ_CRASH("NYI: SwitchTableType::Inline")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::Inline" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18423); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::Inline" ")"); do { *((volatile int*)__null) = 18423; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18424 | #endif |
18425 | } else { |
18426 | #if defined(JS_CODEGEN_ARM) |
18427 | MOZ_CRASH("NYI: SwitchTableType::OutOfLine")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::OutOfLine" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18427); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine" ")"); do { *((volatile int*)__null) = 18427; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18428 | #else |
18429 | masm.mov(start(), temp); |
18430 | base = temp; |
18431 | #endif |
18432 | } |
18433 | BaseIndex jumpTarget(base, index, ScalePointer); |
18434 | masm.branchToComputedAddress(jumpTarget); |
18435 | } |
18436 | |
18437 | // Register an entry in the switch table. |
18438 | void addTableEntry(MacroAssembler& masm) { |
18439 | if ((!isOutOfLine_ && tableType == SwitchTableType::Inline) || |
18440 | (isOutOfLine_ && tableType == SwitchTableType::OutOfLine)) { |
18441 | CodeLabel cl; |
18442 | masm.writeCodePointer(&cl); |
18443 | masm.propagateOOM(codeLabels_.append(std::move(cl))); |
18444 | } |
18445 | } |
18446 | // Register the code, to which the table will jump to. |
18447 | void addCodeEntry(MacroAssembler& masm) { |
18448 | Label entry; |
18449 | masm.bind(&entry); |
18450 | masm.propagateOOM(labels_.append(std::move(entry))); |
18451 | } |
18452 | |
18453 | void setOutOfLine() { isOutOfLine_ = true; } |
18454 | }; |
18455 | |
18456 | template <SwitchTableType tableType> |
18457 | void CodeGenerator::visitOutOfLineSwitch( |
18458 | OutOfLineSwitch<tableType>* jumpTable) { |
18459 | jumpTable->setOutOfLine(); |
18460 | auto& labels = jumpTable->labels(); |
18461 | |
18462 | if (tableType == SwitchTableType::OutOfLine) { |
18463 | #if defined(JS_CODEGEN_ARM) |
18464 | MOZ_CRASH("NYI: SwitchTableType::OutOfLine")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::OutOfLine" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18464); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine" ")"); do { *((volatile int*)__null) = 18464; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18465 | #elif defined(JS_CODEGEN_NONE) |
18466 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18466); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 18466; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
18467 | #else |
18468 | |
18469 | # if defined(JS_CODEGEN_ARM64) |
18470 | AutoForbidPoolsAndNops afp( |
18471 | &masm, |
18472 | (labels.length() + 1) * (sizeof(void*) / vixl::kInstructionSize)); |
18473 | # endif |
18474 | |
18475 | masm.haltingAlign(sizeof(void*)); |
18476 | |
18477 | // Bind the address of the jump table and reserve the space for code |
18478 | // pointers to jump in the newly generated code. |
18479 | masm.bind(jumpTable->start()); |
18480 | masm.addCodeLabel(*jumpTable->start()); |
18481 | for (size_t i = 0, e = labels.length(); i < e; i++) { |
18482 | jumpTable->addTableEntry(masm); |
18483 | } |
18484 | #endif |
18485 | } |
18486 | |
18487 | // Register all reserved pointers of the jump table to target labels. The |
18488 | // entries of the jump table need to be absolute addresses and thus must be |
18489 | // patched after codegen is finished. |
18490 | auto& codeLabels = jumpTable->codeLabels(); |
18491 | for (size_t i = 0, e = codeLabels.length(); i < e; i++) { |
18492 | auto& cl = codeLabels[i]; |
18493 | cl.target()->bind(labels[i].offset()); |
18494 | masm.addCodeLabel(cl); |
18495 | } |
18496 | } |
18497 | |
18498 | template void CodeGenerator::visitOutOfLineSwitch( |
18499 | OutOfLineSwitch<SwitchTableType::Inline>* jumpTable); |
18500 | template void CodeGenerator::visitOutOfLineSwitch( |
18501 | OutOfLineSwitch<SwitchTableType::OutOfLine>* jumpTable); |
18502 | |
18503 | template <typename T> |
18504 | static inline void StoreToTypedArray(MacroAssembler& masm, |
18505 | Scalar::Type writeType, |
18506 | const LAllocation* value, const T& dest, |
18507 | Register temp, |
18508 | LiveRegisterSet volatileRegs) { |
18509 | if (Scalar::isFloatingType(writeType)) { |
18510 | masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, temp, |
18511 | volatileRegs); |
18512 | } else { |
18513 | if (value->isConstant()) { |
18514 | masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest); |
18515 | } else { |
18516 | masm.storeToTypedIntArray(writeType, ToRegister(value), dest); |
18517 | } |
18518 | } |
18519 | } |
18520 | |
18521 | void CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) { |
18522 | Register elements = ToRegister(lir->elements()); |
18523 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
18524 | const LAllocation* value = lir->value(); |
18525 | |
18526 | const MStoreUnboxedScalar* mir = lir->mir(); |
18527 | |
18528 | Scalar::Type writeType = mir->writeType(); |
18529 | |
18530 | LiveRegisterSet volatileRegs; |
18531 | if (MacroAssembler::StoreRequiresCall(writeType)) { |
18532 | volatileRegs = liveVolatileRegs(lir); |
18533 | } |
18534 | |
18535 | if (lir->index()->isConstant()) { |
18536 | Address dest = ToAddress(elements, lir->index(), writeType); |
18537 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
18538 | } else { |
18539 | BaseIndex dest(elements, ToRegister(lir->index()), |
18540 | ScaleFromScalarType(writeType)); |
18541 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
18542 | } |
18543 | } |
18544 | |
18545 | void CodeGenerator::visitStoreUnboxedBigInt(LStoreUnboxedBigInt* lir) { |
18546 | Register elements = ToRegister(lir->elements()); |
18547 | Register value = ToRegister(lir->value()); |
18548 | Register64 temp = ToRegister64(lir->temp()); |
18549 | |
18550 | Scalar::Type writeType = lir->mir()->writeType(); |
18551 | |
18552 | masm.loadBigInt64(value, temp); |
18553 | |
18554 | if (lir->index()->isConstant()) { |
18555 | Address dest = ToAddress(elements, lir->index(), writeType); |
18556 | masm.storeToTypedBigIntArray(writeType, temp, dest); |
18557 | } else { |
18558 | BaseIndex dest(elements, ToRegister(lir->index()), |
18559 | ScaleFromScalarType(writeType)); |
18560 | masm.storeToTypedBigIntArray(writeType, temp, dest); |
18561 | } |
18562 | } |
18563 | |
18564 | void CodeGenerator::visitStoreDataViewElement(LStoreDataViewElement* lir) { |
18565 | Register elements = ToRegister(lir->elements()); |
18566 | const LAllocation* value = lir->value(); |
18567 | const LAllocation* littleEndian = lir->littleEndian(); |
18568 | Register temp = ToTempRegisterOrInvalid(lir->temp()); |
18569 | Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64()); |
18570 | |
18571 | const MStoreDataViewElement* mir = lir->mir(); |
18572 | Scalar::Type writeType = mir->writeType(); |
18573 | |
18574 | LiveRegisterSet volatileRegs; |
18575 | if (MacroAssembler::StoreRequiresCall(writeType)) { |
18576 | volatileRegs = liveVolatileRegs(lir); |
18577 | } |
18578 | |
18579 | BaseIndex dest(elements, ToRegister(lir->index()), TimesOne); |
18580 | |
18581 | bool noSwap = littleEndian->isConstant() && |
18582 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
18583 | |
18584 | // Directly store if no byte swap is needed and the platform supports |
18585 | // unaligned accesses for the access. (Such support is assumed for integer |
18586 | // types.) |
18587 | if (noSwap && (!Scalar::isFloatingType(writeType) || |
18588 | MacroAssembler::SupportsFastUnalignedFPAccesses())) { |
18589 | if (!Scalar::isBigIntType(writeType)) { |
18590 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
18591 | } else { |
18592 | masm.loadBigInt64(ToRegister(value), temp64); |
18593 | masm.storeToTypedBigIntArray(writeType, temp64, dest); |
18594 | } |
18595 | return; |
18596 | } |
18597 | |
18598 | // Load the value into a gpr register. |
18599 | switch (writeType) { |
18600 | case Scalar::Int16: |
18601 | case Scalar::Uint16: |
18602 | case Scalar::Int32: |
18603 | case Scalar::Uint32: |
18604 | if (value->isConstant()) { |
18605 | masm.move32(Imm32(ToInt32(value)), temp); |
18606 | } else { |
18607 | masm.move32(ToRegister(value), temp); |
18608 | } |
18609 | break; |
18610 | case Scalar::Float16: { |
18611 | FloatRegister fvalue = ToFloatRegister(value); |
18612 | masm.canonicalizeFloatIfDeterministic(fvalue); |
18613 | masm.moveFloat16ToGPR(fvalue, temp, volatileRegs); |
18614 | break; |
18615 | } |
18616 | case Scalar::Float32: { |
18617 | FloatRegister fvalue = ToFloatRegister(value); |
18618 | masm.canonicalizeFloatIfDeterministic(fvalue); |
18619 | masm.moveFloat32ToGPR(fvalue, temp); |
18620 | break; |
18621 | } |
18622 | case Scalar::Float64: { |
18623 | FloatRegister fvalue = ToFloatRegister(value); |
18624 | masm.canonicalizeDoubleIfDeterministic(fvalue); |
18625 | masm.moveDoubleToGPR64(fvalue, temp64); |
18626 | break; |
18627 | } |
18628 | case Scalar::BigInt64: |
18629 | case Scalar::BigUint64: |
18630 | masm.loadBigInt64(ToRegister(value), temp64); |
18631 | break; |
18632 | case Scalar::Int8: |
18633 | case Scalar::Uint8: |
18634 | case Scalar::Uint8Clamped: |
18635 | default: |
18636 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18636); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18636; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18637 | } |
18638 | |
18639 | if (!noSwap) { |
18640 | // Swap the bytes in the loaded value. |
18641 | Label skip; |
18642 | if (!littleEndian->isConstant()) { |
18643 | masm.branch32( |
18644 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
18645 | ToRegister(littleEndian), Imm32(0), &skip); |
18646 | } |
18647 | |
18648 | switch (writeType) { |
18649 | case Scalar::Int16: |
18650 | masm.byteSwap16SignExtend(temp); |
18651 | break; |
18652 | case Scalar::Uint16: |
18653 | case Scalar::Float16: |
18654 | masm.byteSwap16ZeroExtend(temp); |
18655 | break; |
18656 | case Scalar::Int32: |
18657 | case Scalar::Uint32: |
18658 | case Scalar::Float32: |
18659 | masm.byteSwap32(temp); |
18660 | break; |
18661 | case Scalar::Float64: |
18662 | case Scalar::BigInt64: |
18663 | case Scalar::BigUint64: |
18664 | masm.byteSwap64(temp64); |
18665 | break; |
18666 | case Scalar::Int8: |
18667 | case Scalar::Uint8: |
18668 | case Scalar::Uint8Clamped: |
18669 | default: |
18670 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18670); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18670; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18671 | } |
18672 | |
18673 | if (skip.used()) { |
18674 | masm.bind(&skip); |
18675 | } |
18676 | } |
18677 | |
18678 | // Store the value into the destination. |
18679 | switch (writeType) { |
18680 | case Scalar::Int16: |
18681 | case Scalar::Uint16: |
18682 | case Scalar::Float16: |
18683 | masm.store16Unaligned(temp, dest); |
18684 | break; |
18685 | case Scalar::Int32: |
18686 | case Scalar::Uint32: |
18687 | case Scalar::Float32: |
18688 | masm.store32Unaligned(temp, dest); |
18689 | break; |
18690 | case Scalar::Float64: |
18691 | case Scalar::BigInt64: |
18692 | case Scalar::BigUint64: |
18693 | masm.store64Unaligned(temp64, dest); |
18694 | break; |
18695 | case Scalar::Int8: |
18696 | case Scalar::Uint8: |
18697 | case Scalar::Uint8Clamped: |
18698 | default: |
18699 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18699); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18699; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
18700 | } |
18701 | } |
18702 | |
18703 | void CodeGenerator::visitStoreTypedArrayElementHole( |
18704 | LStoreTypedArrayElementHole* lir) { |
18705 | Register elements = ToRegister(lir->elements()); |
18706 | const LAllocation* value = lir->value(); |
18707 | |
18708 | Scalar::Type arrayType = lir->mir()->arrayType(); |
18709 | |
18710 | Register index = ToRegister(lir->index()); |
18711 | const LAllocation* length = lir->length(); |
18712 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
18713 | |
18714 | LiveRegisterSet volatileRegs; |
18715 | if (MacroAssembler::StoreRequiresCall(arrayType)) { |
18716 | volatileRegs = liveVolatileRegs(lir); |
18717 | } |
18718 | |
18719 | Label skip; |
18720 | if (length->isRegister()) { |
18721 | masm.spectreBoundsCheckPtr(index, ToRegister(length), temp, &skip); |
18722 | } else { |
18723 | masm.spectreBoundsCheckPtr(index, ToAddress(length), temp, &skip); |
18724 | } |
18725 | |
18726 | BaseIndex dest(elements, index, ScaleFromScalarType(arrayType)); |
18727 | StoreToTypedArray(masm, arrayType, value, dest, temp, volatileRegs); |
18728 | |
18729 | masm.bind(&skip); |
18730 | } |
18731 | |
18732 | void CodeGenerator::visitStoreTypedArrayElementHoleBigInt( |
18733 | LStoreTypedArrayElementHoleBigInt* lir) { |
18734 | Register elements = ToRegister(lir->elements()); |
18735 | Register value = ToRegister(lir->value()); |
18736 | Register64 temp = ToRegister64(lir->temp()); |
18737 | |
18738 | Scalar::Type arrayType = lir->mir()->arrayType(); |
18739 | |
18740 | Register index = ToRegister(lir->index()); |
18741 | const LAllocation* length = lir->length(); |
18742 | Register spectreTemp = temp.scratchReg(); |
18743 | |
18744 | Label skip; |
18745 | if (length->isRegister()) { |
18746 | masm.spectreBoundsCheckPtr(index, ToRegister(length), spectreTemp, &skip); |
18747 | } else { |
18748 | masm.spectreBoundsCheckPtr(index, ToAddress(length), spectreTemp, &skip); |
18749 | } |
18750 | |
18751 | masm.loadBigInt64(value, temp); |
18752 | |
18753 | BaseIndex dest(elements, index, ScaleFromScalarType(arrayType)); |
18754 | masm.storeToTypedBigIntArray(arrayType, temp, dest); |
18755 | |
18756 | masm.bind(&skip); |
18757 | } |
18758 | |
18759 | void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { |
18760 | masm.memoryBarrier(ins->type()); |
18761 | } |
18762 | |
18763 | void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) { |
18764 | Register value = ToRegister(lir->value()); |
18765 | Register output = ToRegister(lir->output()); |
18766 | |
18767 | masm.atomicIsLockFreeJS(value, output); |
18768 | } |
18769 | |
18770 | void CodeGenerator::visitClampIToUint8(LClampIToUint8* lir) { |
18771 | Register output = ToRegister(lir->output()); |
18772 | MOZ_ASSERT(output == ToRegister(lir->input()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(output == ToRegister(lir->input()))>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(output == ToRegister(lir->input())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output == ToRegister(lir->input())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18772); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == ToRegister(lir->input())" ")"); do { *((volatile int*)__null) = 18772; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
18773 | masm.clampIntToUint8(output); |
18774 | } |
18775 | |
18776 | void CodeGenerator::visitClampDToUint8(LClampDToUint8* lir) { |
18777 | FloatRegister input = ToFloatRegister(lir->input()); |
18778 | Register output = ToRegister(lir->output()); |
18779 | masm.clampDoubleToUint8(input, output); |
18780 | } |
18781 | |
18782 | void CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) { |
18783 | ValueOperand operand = ToValue(lir, LClampVToUint8::InputIndex); |
18784 | FloatRegister tempFloat = ToFloatRegister(lir->temp0()); |
18785 | Register output = ToRegister(lir->output()); |
18786 | |
18787 | using Fn = bool (*)(JSContext*, JSString*, double*); |
18788 | OutOfLineCode* oolString = oolCallVM<Fn, StringToNumber>( |
18789 | lir, ArgList(output), StoreFloatRegisterTo(tempFloat)); |
18790 | Label* stringEntry = oolString->entry(); |
18791 | Label* stringRejoin = oolString->rejoin(); |
18792 | |
18793 | Label fails; |
18794 | masm.clampValueToUint8(operand, stringEntry, stringRejoin, output, tempFloat, |
18795 | output, &fails); |
18796 | |
18797 | bailoutFrom(&fails, lir->snapshot()); |
18798 | } |
18799 | |
18800 | void CodeGenerator::visitInCache(LInCache* ins) { |
18801 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
18802 | |
18803 | ConstantOrRegister key = |
18804 | toConstantOrRegister(ins, LInCache::LhsIndex, ins->mir()->key()->type()); |
18805 | Register object = ToRegister(ins->rhs()); |
18806 | Register output = ToRegister(ins->output()); |
18807 | Register temp = ToRegister(ins->temp0()); |
18808 | |
18809 | IonInIC cache(liveRegs, key, object, output, temp); |
18810 | addIC(ins, allocateIC(cache)); |
18811 | } |
18812 | |
18813 | void CodeGenerator::visitInArray(LInArray* lir) { |
18814 | const MInArray* mir = lir->mir(); |
18815 | Register elements = ToRegister(lir->elements()); |
18816 | Register initLength = ToRegister(lir->initLength()); |
18817 | Register output = ToRegister(lir->output()); |
18818 | |
18819 | Label falseBranch, done, trueBranch; |
18820 | |
18821 | if (lir->index()->isConstant()) { |
18822 | int32_t index = ToInt32(lir->index()); |
18823 | |
18824 | if (index < 0) { |
18825 | MOZ_ASSERT(mir->needsNegativeIntCheck())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->needsNegativeIntCheck())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->needsNegativeIntCheck ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mir->needsNegativeIntCheck()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18825); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->needsNegativeIntCheck()" ")"); do { *((volatile int*)__null) = 18825; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
18826 | bailout(lir->snapshot()); |
18827 | return; |
18828 | } |
18829 | |
18830 | masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), |
18831 | &falseBranch); |
18832 | |
18833 | NativeObject::elementsSizeMustNotOverflow(); |
18834 | Address address = Address(elements, index * sizeof(Value)); |
18835 | masm.branchTestMagic(Assembler::Equal, address, &falseBranch); |
18836 | } else { |
18837 | Register index = ToRegister(lir->index()); |
18838 | |
18839 | Label negativeIntCheck; |
18840 | Label* failedInitLength = &falseBranch; |
18841 | if (mir->needsNegativeIntCheck()) { |
18842 | failedInitLength = &negativeIntCheck; |
18843 | } |
18844 | |
18845 | masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength); |
18846 | |
18847 | BaseObjectElementIndex address(elements, index); |
18848 | masm.branchTestMagic(Assembler::Equal, address, &falseBranch); |
18849 | |
18850 | if (mir->needsNegativeIntCheck()) { |
18851 | masm.jump(&trueBranch); |
18852 | masm.bind(&negativeIntCheck); |
18853 | |
18854 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
18855 | |
18856 | masm.jump(&falseBranch); |
18857 | } |
18858 | } |
18859 | |
18860 | masm.bind(&trueBranch); |
18861 | masm.move32(Imm32(1), output); |
18862 | masm.jump(&done); |
18863 | |
18864 | masm.bind(&falseBranch); |
18865 | masm.move32(Imm32(0), output); |
18866 | masm.bind(&done); |
18867 | } |
18868 | |
18869 | void CodeGenerator::visitGuardElementNotHole(LGuardElementNotHole* lir) { |
18870 | Register elements = ToRegister(lir->elements()); |
18871 | const LAllocation* index = lir->index(); |
18872 | |
18873 | Label testMagic; |
18874 | if (index->isConstant()) { |
18875 | Address address(elements, ToInt32(index) * sizeof(js::Value)); |
18876 | masm.branchTestMagic(Assembler::Equal, address, &testMagic); |
18877 | } else { |
18878 | BaseObjectElementIndex address(elements, ToRegister(index)); |
18879 | masm.branchTestMagic(Assembler::Equal, address, &testMagic); |
18880 | } |
18881 | bailoutFrom(&testMagic, lir->snapshot()); |
18882 | } |
18883 | |
18884 | void CodeGenerator::visitInstanceOfO(LInstanceOfO* ins) { |
18885 | Register protoReg = ToRegister(ins->rhs()); |
18886 | emitInstanceOf(ins, protoReg); |
18887 | } |
18888 | |
18889 | void CodeGenerator::visitInstanceOfV(LInstanceOfV* ins) { |
18890 | Register protoReg = ToRegister(ins->rhs()); |
18891 | emitInstanceOf(ins, protoReg); |
18892 | } |
18893 | |
18894 | void CodeGenerator::emitInstanceOf(LInstruction* ins, Register protoReg) { |
18895 | // This path implements fun_hasInstance when the function's prototype is |
18896 | // known to be the object in protoReg |
18897 | |
18898 | Label done; |
18899 | Register output = ToRegister(ins->getDef(0)); |
18900 | |
18901 | // If the lhs is a primitive, the result is false. |
18902 | Register objReg; |
18903 | if (ins->isInstanceOfV()) { |
18904 | Label isObject; |
18905 | ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex); |
18906 | masm.branchTestObject(Assembler::Equal, lhsValue, &isObject); |
18907 | masm.mov(ImmWord(0), output); |
18908 | masm.jump(&done); |
18909 | masm.bind(&isObject); |
18910 | objReg = masm.extractObject(lhsValue, output); |
18911 | } else { |
18912 | objReg = ToRegister(ins->toInstanceOfO()->lhs()); |
18913 | } |
18914 | |
18915 | // Crawl the lhs's prototype chain in a loop to search for prototypeObject. |
18916 | // This follows the main loop of js::IsPrototypeOf, though additionally breaks |
18917 | // out of the loop on Proxy::LazyProto. |
18918 | |
18919 | // Load the lhs's prototype. |
18920 | masm.loadObjProto(objReg, output); |
18921 | |
18922 | Label testLazy; |
18923 | { |
18924 | Label loopPrototypeChain; |
18925 | masm.bind(&loopPrototypeChain); |
18926 | |
18927 | // Test for the target prototype object. |
18928 | Label notPrototypeObject; |
18929 | masm.branchPtr(Assembler::NotEqual, output, protoReg, ¬PrototypeObject); |
18930 | masm.mov(ImmWord(1), output); |
18931 | masm.jump(&done); |
18932 | masm.bind(¬PrototypeObject); |
18933 | |
18934 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 18934; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
18935 | |
18936 | // Test for nullptr or Proxy::LazyProto |
18937 | masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy); |
18938 | |
18939 | // Load the current object's prototype. |
18940 | masm.loadObjProto(output, output); |
18941 | |
18942 | masm.jump(&loopPrototypeChain); |
18943 | } |
18944 | |
18945 | // Make a VM call if an object with a lazy proto was found on the prototype |
18946 | // chain. This currently occurs only for cross compartment wrappers, which |
18947 | // we do not expect to be compared with non-wrapper functions from this |
18948 | // compartment. Otherwise, we stopped on a nullptr prototype and the output |
18949 | // register is already correct. |
18950 | |
18951 | using Fn = bool (*)(JSContext*, HandleObject, JSObject*, bool*); |
18952 | auto* ool = oolCallVM<Fn, IsPrototypeOf>(ins, ArgList(protoReg, objReg), |
18953 | StoreRegisterTo(output)); |
18954 | |
18955 | // Regenerate the original lhs object for the VM call. |
18956 | Label regenerate, *lazyEntry; |
18957 | if (objReg != output) { |
18958 | lazyEntry = ool->entry(); |
18959 | } else { |
18960 | masm.bind(®enerate); |
18961 | lazyEntry = ®enerate; |
18962 | if (ins->isInstanceOfV()) { |
18963 | ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex); |
18964 | objReg = masm.extractObject(lhsValue, output); |
18965 | } else { |
18966 | objReg = ToRegister(ins->toInstanceOfO()->lhs()); |
18967 | } |
18968 | MOZ_ASSERT(objReg == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(objReg == output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(objReg == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("objReg == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18968); AnnotateMozCrashReason("MOZ_ASSERT" "(" "objReg == output" ")"); do { *((volatile int*)__null) = 18968; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
18969 | masm.jump(ool->entry()); |
18970 | } |
18971 | |
18972 | masm.bind(&testLazy); |
18973 | masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry); |
18974 | |
18975 | masm.bind(&done); |
18976 | masm.bind(ool->rejoin()); |
18977 | } |
18978 | |
18979 | void CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins) { |
18980 | // The Lowering ensures that RHS is an object, and that LHS is a value. |
18981 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
18982 | TypedOrValueRegister lhs = |
18983 | TypedOrValueRegister(ToValue(ins, LInstanceOfCache::LHS)); |
18984 | Register rhs = ToRegister(ins->rhs()); |
18985 | Register output = ToRegister(ins->output()); |
18986 | |
18987 | IonInstanceOfIC ic(liveRegs, lhs, rhs, output); |
18988 | addIC(ins, allocateIC(ic)); |
18989 | } |
18990 | |
18991 | void CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) { |
18992 | const Register JSContextReg = ToRegister(ins->getJSContextReg()); |
18993 | const Register ObjectReg = ToRegister(ins->getObjectReg()); |
18994 | const Register PrivateReg = ToRegister(ins->getPrivReg()); |
18995 | const Register ValueReg = ToRegister(ins->getValueReg()); |
18996 | |
18997 | Label haveValue; |
18998 | if (ins->mir()->valueMayBeInSlot()) { |
18999 | size_t slot = ins->mir()->domMemberSlotIndex(); |
19000 | // It's a bit annoying to redo these slot calculations, which duplcate |
19001 | // LSlots and a few other things like that, but I'm not sure there's a |
19002 | // way to reuse those here. |
19003 | // |
19004 | // If this ever gets fixed to work with proxies (by not assuming that |
19005 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
19006 | // match fixed slot indices), we can reenable MGetDOMProperty for |
19007 | // proxies in IonBuilder. |
19008 | if (slot < NativeObject::MAX_FIXED_SLOTS) { |
19009 | masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)), |
19010 | JSReturnOperand); |
19011 | } else { |
19012 | // It's a dynamic slot. |
19013 | slot -= NativeObject::MAX_FIXED_SLOTS; |
19014 | // Use PrivateReg as a scratch register for the slots pointer. |
19015 | masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()), |
19016 | PrivateReg); |
19017 | masm.loadValue(Address(PrivateReg, slot * sizeof(js::Value)), |
19018 | JSReturnOperand); |
19019 | } |
19020 | masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue); |
19021 | } |
19022 | |
19023 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
19024 | |
19025 | masm.checkStackAlignment(); |
19026 | |
19027 | // Make space for the outparam. Pre-initialize it to UndefinedValue so we |
19028 | // can trace it at GC time. |
19029 | masm.Push(UndefinedValue()); |
19030 | // We pass the pointer to our out param as an instance of |
19031 | // JSJitGetterCallArgs, since on the binary level it's the same thing. |
19032 | static_assert(sizeof(JSJitGetterCallArgs) == sizeof(Value*)); |
19033 | masm.moveStackPtrTo(ValueReg); |
19034 | |
19035 | masm.Push(ObjectReg); |
19036 | |
19037 | LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind()); |
19038 | |
19039 | // Rooting will happen at GC time. |
19040 | masm.moveStackPtrTo(ObjectReg); |
19041 | |
19042 | Realm* getterRealm = ins->mir()->getterRealm(); |
19043 | if (gen->realm->realmPtr() != getterRealm) { |
19044 | // We use JSContextReg as scratch register here. |
19045 | masm.switchToRealm(getterRealm, JSContextReg); |
19046 | } |
19047 | |
19048 | uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); |
19049 | masm.loadJSContext(JSContextReg); |
19050 | masm.enterFakeExitFrame(JSContextReg, JSContextReg, |
19051 | ExitFrameType::IonDOMGetter); |
19052 | |
19053 | markSafepointAt(safepointOffset, ins); |
19054 | |
19055 | masm.setupAlignedABICall(); |
19056 | masm.loadJSContext(JSContextReg); |
19057 | masm.passABIArg(JSContextReg); |
19058 | masm.passABIArg(ObjectReg); |
19059 | masm.passABIArg(PrivateReg); |
19060 | masm.passABIArg(ValueReg); |
19061 | ensureOsiSpace(); |
19062 | masm.callWithABI(DynamicFunction<JSJitGetterOp>(ins->mir()->fun()), |
19063 | ABIType::General, |
19064 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
19065 | |
19066 | if (ins->mir()->isInfallible()) { |
19067 | masm.loadValue(Address(masm.getStackPointer(), |
19068 | IonDOMExitFrameLayout::offsetOfResult()), |
19069 | JSReturnOperand); |
19070 | } else { |
19071 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
19072 | |
19073 | masm.loadValue(Address(masm.getStackPointer(), |
19074 | IonDOMExitFrameLayout::offsetOfResult()), |
19075 | JSReturnOperand); |
19076 | } |
19077 | |
19078 | // Switch back to the current realm if needed. Note: if the getter threw an |
19079 | // exception, the exception handler will do this. |
19080 | if (gen->realm->realmPtr() != getterRealm) { |
19081 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
19082 | "Clobbering ReturnReg should not affect the return value"); |
19083 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
19084 | } |
19085 | |
19086 | // Until C++ code is instrumented against Spectre, prevent speculative |
19087 | // execution from returning any private data. |
19088 | if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) { |
19089 | masm.speculationBarrier(); |
19090 | } |
19091 | |
19092 | masm.adjustStack(IonDOMExitFrameLayout::Size()); |
19093 | |
19094 | masm.bind(&haveValue); |
19095 | |
19096 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 19096; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19097 | } |
19098 | |
19099 | void CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins) { |
19100 | // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to |
19101 | // use an LLoadFixedSlotV or some subclass of it for this case: that would |
19102 | // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then |
19103 | // we'd have to duplicate a bunch of stuff we now get for free from |
19104 | // MGetDOMProperty. |
19105 | // |
19106 | // If this ever gets fixed to work with proxies (by not assuming that |
19107 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
19108 | // match fixed slot indices), we can reenable MGetDOMMember for |
19109 | // proxies in IonBuilder. |
19110 | Register object = ToRegister(ins->object()); |
19111 | size_t slot = ins->mir()->domMemberSlotIndex(); |
19112 | ValueOperand result = ToOutValue(ins); |
19113 | |
19114 | masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)), |
19115 | result); |
19116 | } |
19117 | |
19118 | void CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT* ins) { |
19119 | // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to |
19120 | // use an LLoadFixedSlotT or some subclass of it for this case: that would |
19121 | // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then |
19122 | // we'd have to duplicate a bunch of stuff we now get for free from |
19123 | // MGetDOMProperty. |
19124 | // |
19125 | // If this ever gets fixed to work with proxies (by not assuming that |
19126 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
19127 | // match fixed slot indices), we can reenable MGetDOMMember for |
19128 | // proxies in IonBuilder. |
19129 | Register object = ToRegister(ins->object()); |
19130 | size_t slot = ins->mir()->domMemberSlotIndex(); |
19131 | AnyRegister result = ToAnyRegister(ins->getDef(0)); |
19132 | MIRType type = ins->mir()->type(); |
19133 | |
19134 | masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)), |
19135 | type, result); |
19136 | } |
19137 | |
19138 | void CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) { |
19139 | const Register JSContextReg = ToRegister(ins->getJSContextReg()); |
19140 | const Register ObjectReg = ToRegister(ins->getObjectReg()); |
19141 | const Register PrivateReg = ToRegister(ins->getPrivReg()); |
19142 | const Register ValueReg = ToRegister(ins->getValueReg()); |
19143 | |
19144 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
19145 | |
19146 | masm.checkStackAlignment(); |
19147 | |
19148 | // Push the argument. Rooting will happen at GC time. |
19149 | ValueOperand argVal = ToValue(ins, LSetDOMProperty::Value); |
19150 | masm.Push(argVal); |
19151 | // We pass the pointer to our out param as an instance of |
19152 | // JSJitGetterCallArgs, since on the binary level it's the same thing. |
19153 | static_assert(sizeof(JSJitSetterCallArgs) == sizeof(Value*)); |
19154 | masm.moveStackPtrTo(ValueReg); |
19155 | |
19156 | masm.Push(ObjectReg); |
19157 | |
19158 | LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind()); |
19159 | |
19160 | // Rooting will happen at GC time. |
19161 | masm.moveStackPtrTo(ObjectReg); |
19162 | |
19163 | Realm* setterRealm = ins->mir()->setterRealm(); |
19164 | if (gen->realm->realmPtr() != setterRealm) { |
19165 | // We use JSContextReg as scratch register here. |
19166 | masm.switchToRealm(setterRealm, JSContextReg); |
19167 | } |
19168 | |
19169 | uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); |
19170 | masm.loadJSContext(JSContextReg); |
19171 | masm.enterFakeExitFrame(JSContextReg, JSContextReg, |
19172 | ExitFrameType::IonDOMSetter); |
19173 | |
19174 | markSafepointAt(safepointOffset, ins); |
19175 | |
19176 | masm.setupAlignedABICall(); |
19177 | masm.loadJSContext(JSContextReg); |
19178 | masm.passABIArg(JSContextReg); |
19179 | masm.passABIArg(ObjectReg); |
19180 | masm.passABIArg(PrivateReg); |
19181 | masm.passABIArg(ValueReg); |
19182 | ensureOsiSpace(); |
19183 | masm.callWithABI(DynamicFunction<JSJitSetterOp>(ins->mir()->fun()), |
19184 | ABIType::General, |
19185 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
19186 | |
19187 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
19188 | |
19189 | // Switch back to the current realm if needed. Note: if the setter threw an |
19190 | // exception, the exception handler will do this. |
19191 | if (gen->realm->realmPtr() != setterRealm) { |
19192 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
19193 | } |
19194 | |
19195 | masm.adjustStack(IonDOMExitFrameLayout::Size()); |
19196 | |
19197 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19197); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 19197; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19198 | } |
19199 | |
19200 | void CodeGenerator::visitLoadDOMExpandoValue(LLoadDOMExpandoValue* ins) { |
19201 | Register proxy = ToRegister(ins->proxy()); |
19202 | ValueOperand out = ToOutValue(ins); |
19203 | |
19204 | masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()), |
19205 | out.scratchReg()); |
19206 | masm.loadValue(Address(out.scratchReg(), |
19207 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()), |
19208 | out); |
19209 | } |
19210 | |
19211 | void CodeGenerator::visitLoadDOMExpandoValueGuardGeneration( |
19212 | LLoadDOMExpandoValueGuardGeneration* ins) { |
19213 | Register proxy = ToRegister(ins->proxy()); |
19214 | ValueOperand out = ToOutValue(ins); |
19215 | |
19216 | Label bail; |
19217 | masm.loadDOMExpandoValueGuardGeneration(proxy, out, |
19218 | ins->mir()->expandoAndGeneration(), |
19219 | ins->mir()->generation(), &bail); |
19220 | bailoutFrom(&bail, ins->snapshot()); |
19221 | } |
19222 | |
19223 | void CodeGenerator::visitLoadDOMExpandoValueIgnoreGeneration( |
19224 | LLoadDOMExpandoValueIgnoreGeneration* ins) { |
19225 | Register proxy = ToRegister(ins->proxy()); |
19226 | ValueOperand out = ToOutValue(ins); |
19227 | |
19228 | masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()), |
19229 | out.scratchReg()); |
19230 | |
19231 | // Load the ExpandoAndGeneration* from the PrivateValue. |
19232 | masm.loadPrivate( |
19233 | Address(out.scratchReg(), |
19234 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()), |
19235 | out.scratchReg()); |
19236 | |
19237 | // Load expandoAndGeneration->expando into the output Value register. |
19238 | masm.loadValue( |
19239 | Address(out.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), out); |
19240 | } |
19241 | |
19242 | void CodeGenerator::visitGuardDOMExpandoMissingOrGuardShape( |
19243 | LGuardDOMExpandoMissingOrGuardShape* ins) { |
19244 | Register temp = ToRegister(ins->temp0()); |
19245 | ValueOperand input = |
19246 | ToValue(ins, LGuardDOMExpandoMissingOrGuardShape::InputIndex); |
19247 | |
19248 | Label done; |
19249 | masm.branchTestUndefined(Assembler::Equal, input, &done); |
19250 | |
19251 | masm.debugAssertIsObject(input); |
19252 | masm.unboxObject(input, temp); |
19253 | // The expando object is not used in this case, so we don't need Spectre |
19254 | // mitigations. |
19255 | Label bail; |
19256 | masm.branchTestObjShapeNoSpectreMitigations(Assembler::NotEqual, temp, |
19257 | ins->mir()->shape(), &bail); |
19258 | bailoutFrom(&bail, ins->snapshot()); |
19259 | |
19260 | masm.bind(&done); |
19261 | } |
19262 | |
19263 | class OutOfLineIsCallable : public OutOfLineCodeBase<CodeGenerator> { |
19264 | Register object_; |
19265 | Register output_; |
19266 | |
19267 | public: |
19268 | OutOfLineIsCallable(Register object, Register output) |
19269 | : object_(object), output_(output) {} |
19270 | |
19271 | void accept(CodeGenerator* codegen) override { |
19272 | codegen->visitOutOfLineIsCallable(this); |
19273 | } |
19274 | Register object() const { return object_; } |
19275 | Register output() const { return output_; } |
19276 | }; |
19277 | |
19278 | void CodeGenerator::visitIsCallableO(LIsCallableO* ins) { |
19279 | Register object = ToRegister(ins->object()); |
19280 | Register output = ToRegister(ins->output()); |
19281 | |
19282 | OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(object, output); |
19283 | addOutOfLineCode(ool, ins->mir()); |
19284 | |
19285 | masm.isCallable(object, output, ool->entry()); |
19286 | |
19287 | masm.bind(ool->rejoin()); |
19288 | } |
19289 | |
19290 | void CodeGenerator::visitIsCallableV(LIsCallableV* ins) { |
19291 | ValueOperand val = ToValue(ins, LIsCallableV::ObjectIndex); |
19292 | Register output = ToRegister(ins->output()); |
19293 | Register temp = ToRegister(ins->temp0()); |
19294 | |
19295 | Label notObject; |
19296 | masm.fallibleUnboxObject(val, temp, ¬Object); |
19297 | |
19298 | OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(temp, output); |
19299 | addOutOfLineCode(ool, ins->mir()); |
19300 | |
19301 | masm.isCallable(temp, output, ool->entry()); |
19302 | masm.jump(ool->rejoin()); |
19303 | |
19304 | masm.bind(¬Object); |
19305 | masm.move32(Imm32(0), output); |
19306 | |
19307 | masm.bind(ool->rejoin()); |
19308 | } |
19309 | |
19310 | void CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool) { |
19311 | Register object = ool->object(); |
19312 | Register output = ool->output(); |
19313 | |
19314 | saveVolatile(output); |
19315 | using Fn = bool (*)(JSObject* obj); |
19316 | masm.setupAlignedABICall(); |
19317 | masm.passABIArg(object); |
19318 | masm.callWithABI<Fn, ObjectIsCallable>(); |
19319 | masm.storeCallBoolResult(output); |
19320 | restoreVolatile(output); |
19321 | masm.jump(ool->rejoin()); |
19322 | } |
19323 | |
19324 | class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator> { |
19325 | LIsConstructor* ins_; |
19326 | |
19327 | public: |
19328 | explicit OutOfLineIsConstructor(LIsConstructor* ins) : ins_(ins) {} |
19329 | |
19330 | void accept(CodeGenerator* codegen) override { |
19331 | codegen->visitOutOfLineIsConstructor(this); |
19332 | } |
19333 | LIsConstructor* ins() const { return ins_; } |
19334 | }; |
19335 | |
19336 | void CodeGenerator::visitIsConstructor(LIsConstructor* ins) { |
19337 | Register object = ToRegister(ins->object()); |
19338 | Register output = ToRegister(ins->output()); |
19339 | |
19340 | OutOfLineIsConstructor* ool = new (alloc()) OutOfLineIsConstructor(ins); |
19341 | addOutOfLineCode(ool, ins->mir()); |
19342 | |
19343 | masm.isConstructor(object, output, ool->entry()); |
19344 | |
19345 | masm.bind(ool->rejoin()); |
19346 | } |
19347 | |
19348 | void CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool) { |
19349 | LIsConstructor* ins = ool->ins(); |
19350 | Register object = ToRegister(ins->object()); |
19351 | Register output = ToRegister(ins->output()); |
19352 | |
19353 | saveVolatile(output); |
19354 | using Fn = bool (*)(JSObject* obj); |
19355 | masm.setupAlignedABICall(); |
19356 | masm.passABIArg(object); |
19357 | masm.callWithABI<Fn, ObjectIsConstructor>(); |
19358 | masm.storeCallBoolResult(output); |
19359 | restoreVolatile(output); |
19360 | masm.jump(ool->rejoin()); |
19361 | } |
19362 | |
19363 | void CodeGenerator::visitIsCrossRealmArrayConstructor( |
19364 | LIsCrossRealmArrayConstructor* ins) { |
19365 | Register object = ToRegister(ins->object()); |
19366 | Register output = ToRegister(ins->output()); |
19367 | |
19368 | masm.setIsCrossRealmArrayConstructor(object, output); |
19369 | } |
19370 | |
19371 | static void EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool, |
19372 | Register obj, Register output, |
19373 | Label* notArray = nullptr) { |
19374 | masm.loadObjClassUnsafe(obj, output); |
19375 | |
19376 | Label isArray; |
19377 | masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_), |
19378 | &isArray); |
19379 | |
19380 | // Branch to OOL path if it's a proxy. |
19381 | masm.branchTestClassIsProxy(true, output, ool->entry()); |
19382 | |
19383 | if (notArray) { |
19384 | masm.bind(notArray); |
19385 | } |
19386 | masm.move32(Imm32(0), output); |
19387 | masm.jump(ool->rejoin()); |
19388 | |
19389 | masm.bind(&isArray); |
19390 | masm.move32(Imm32(1), output); |
19391 | |
19392 | masm.bind(ool->rejoin()); |
19393 | } |
19394 | |
19395 | void CodeGenerator::visitIsArrayO(LIsArrayO* lir) { |
19396 | Register object = ToRegister(lir->object()); |
19397 | Register output = ToRegister(lir->output()); |
19398 | |
19399 | using Fn = bool (*)(JSContext*, HandleObject, bool*); |
19400 | OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>( |
19401 | lir, ArgList(object), StoreRegisterTo(output)); |
19402 | EmitObjectIsArray(masm, ool, object, output); |
19403 | } |
19404 | |
19405 | void CodeGenerator::visitIsArrayV(LIsArrayV* lir) { |
19406 | ValueOperand val = ToValue(lir, LIsArrayV::ValueIndex); |
19407 | Register output = ToRegister(lir->output()); |
19408 | Register temp = ToRegister(lir->temp0()); |
19409 | |
19410 | Label notArray; |
19411 | masm.fallibleUnboxObject(val, temp, ¬Array); |
19412 | |
19413 | using Fn = bool (*)(JSContext*, HandleObject, bool*); |
19414 | OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>( |
19415 | lir, ArgList(temp), StoreRegisterTo(output)); |
19416 | EmitObjectIsArray(masm, ool, temp, output, ¬Array); |
19417 | } |
19418 | |
19419 | void CodeGenerator::visitIsTypedArray(LIsTypedArray* lir) { |
19420 | Register object = ToRegister(lir->object()); |
19421 | Register output = ToRegister(lir->output()); |
19422 | |
19423 | OutOfLineCode* ool = nullptr; |
19424 | if (lir->mir()->isPossiblyWrapped()) { |
19425 | using Fn = bool (*)(JSContext*, JSObject*, bool*); |
19426 | ool = oolCallVM<Fn, jit::IsPossiblyWrappedTypedArray>( |
19427 | lir, ArgList(object), StoreRegisterTo(output)); |
19428 | } |
19429 | |
19430 | Label notTypedArray; |
19431 | Label done; |
19432 | |
19433 | masm.loadObjClassUnsafe(object, output); |
19434 | masm.branchIfClassIsNotTypedArray(output, ¬TypedArray); |
19435 | |
19436 | masm.move32(Imm32(1), output); |
19437 | masm.jump(&done); |
19438 | masm.bind(¬TypedArray); |
19439 | if (ool) { |
19440 | masm.branchTestClassIsProxy(true, output, ool->entry()); |
19441 | } |
19442 | masm.move32(Imm32(0), output); |
19443 | masm.bind(&done); |
19444 | if (ool) { |
19445 | masm.bind(ool->rejoin()); |
19446 | } |
19447 | } |
19448 | |
19449 | void CodeGenerator::visitIsObject(LIsObject* ins) { |
19450 | Register output = ToRegister(ins->output()); |
19451 | ValueOperand value = ToValue(ins, LIsObject::ObjectIndex); |
19452 | masm.testObjectSet(Assembler::Equal, value, output); |
19453 | } |
19454 | |
19455 | void CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins) { |
19456 | ValueOperand value = ToValue(ins, LIsObjectAndBranch::Input); |
19457 | testObjectEmitBranch(Assembler::Equal, value, ins->ifTrue(), ins->ifFalse()); |
19458 | } |
19459 | |
19460 | void CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins) { |
19461 | Register output = ToRegister(ins->output()); |
19462 | ValueOperand value = ToValue(ins, LIsNullOrUndefined::InputIndex); |
19463 | |
19464 | Label isNotNull, done; |
19465 | masm.branchTestNull(Assembler::NotEqual, value, &isNotNull); |
19466 | |
19467 | masm.move32(Imm32(1), output); |
19468 | masm.jump(&done); |
19469 | |
19470 | masm.bind(&isNotNull); |
19471 | masm.testUndefinedSet(Assembler::Equal, value, output); |
19472 | |
19473 | masm.bind(&done); |
19474 | } |
19475 | |
19476 | void CodeGenerator::visitIsNullOrUndefinedAndBranch( |
19477 | LIsNullOrUndefinedAndBranch* ins) { |
19478 | Label* ifTrue = getJumpLabelForBranch(ins->ifTrue()); |
19479 | Label* ifFalse = getJumpLabelForBranch(ins->ifFalse()); |
19480 | ValueOperand value = ToValue(ins, LIsNullOrUndefinedAndBranch::Input); |
19481 | |
19482 | ScratchTagScope tag(masm, value); |
19483 | masm.splitTagForTest(value, tag); |
19484 | |
19485 | masm.branchTestNull(Assembler::Equal, tag, ifTrue); |
19486 | masm.branchTestUndefined(Assembler::Equal, tag, ifTrue); |
19487 | |
19488 | if (!isNextBlock(ins->ifFalse()->lir())) { |
19489 | masm.jump(ifFalse); |
19490 | } |
19491 | } |
19492 | |
19493 | void CodeGenerator::loadOutermostJSScript(Register reg) { |
19494 | // The "outermost" JSScript means the script that we are compiling |
19495 | // basically; this is not always the script associated with the |
19496 | // current basic block, which might be an inlined script. |
19497 | |
19498 | MIRGraph& graph = current->mir()->graph(); |
19499 | MBasicBlock* entryBlock = graph.entryBlock(); |
19500 | masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg); |
19501 | } |
19502 | |
19503 | void CodeGenerator::loadJSScriptForBlock(MBasicBlock* block, Register reg) { |
19504 | // The current JSScript means the script for the current |
19505 | // basic block. This may be an inlined script. |
19506 | |
19507 | JSScript* script = block->info().script(); |
19508 | masm.movePtr(ImmGCPtr(script), reg); |
19509 | } |
19510 | |
19511 | void CodeGenerator::visitHasClass(LHasClass* ins) { |
19512 | Register lhs = ToRegister(ins->lhs()); |
19513 | Register output = ToRegister(ins->output()); |
19514 | |
19515 | masm.loadObjClassUnsafe(lhs, output); |
19516 | masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), |
19517 | output); |
19518 | } |
19519 | |
19520 | void CodeGenerator::visitGuardToClass(LGuardToClass* ins) { |
19521 | Register lhs = ToRegister(ins->lhs()); |
19522 | Register temp = ToRegister(ins->temp0()); |
19523 | |
19524 | // branchTestObjClass may zero the object register on speculative paths |
19525 | // (we should have a defineReuseInput allocation in this case). |
19526 | Register spectreRegToZero = lhs; |
19527 | |
19528 | Label notEqual; |
19529 | |
19530 | masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(), |
19531 | temp, spectreRegToZero, ¬Equal); |
19532 | |
19533 | // Can't return null-return here, so bail. |
19534 | bailoutFrom(¬Equal, ins->snapshot()); |
19535 | } |
19536 | |
19537 | void CodeGenerator::visitGuardToEitherClass(LGuardToEitherClass* ins) { |
19538 | Register lhs = ToRegister(ins->lhs()); |
19539 | Register temp = ToRegister(ins->temp0()); |
19540 | |
19541 | // branchTestObjClass may zero the object register on speculative paths |
19542 | // (we should have a defineReuseInput allocation in this case). |
19543 | Register spectreRegToZero = lhs; |
19544 | |
19545 | Label notEqual; |
19546 | |
19547 | masm.branchTestObjClass(Assembler::NotEqual, lhs, |
19548 | {ins->mir()->getClass1(), ins->mir()->getClass2()}, |
19549 | temp, spectreRegToZero, ¬Equal); |
19550 | |
19551 | // Can't return null-return here, so bail. |
19552 | bailoutFrom(¬Equal, ins->snapshot()); |
19553 | } |
19554 | |
19555 | void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) { |
19556 | Register lhs = ToRegister(ins->lhs()); |
19557 | Register temp = ToRegister(ins->temp0()); |
19558 | |
19559 | // branchTestObjClass may zero the object register on speculative paths |
19560 | // (we should have a defineReuseInput allocation in this case). |
19561 | Register spectreRegToZero = lhs; |
19562 | |
19563 | Label notEqual; |
19564 | |
19565 | masm.branchTestObjIsFunction(Assembler::NotEqual, lhs, temp, spectreRegToZero, |
19566 | ¬Equal); |
19567 | |
19568 | // Can't return null-return here, so bail. |
19569 | bailoutFrom(¬Equal, ins->snapshot()); |
19570 | } |
19571 | |
19572 | void CodeGenerator::visitObjectClassToString(LObjectClassToString* lir) { |
19573 | Register obj = ToRegister(lir->lhs()); |
19574 | Register temp = ToRegister(lir->temp0()); |
19575 | |
19576 | using Fn = JSString* (*)(JSContext*, JSObject*); |
19577 | masm.setupAlignedABICall(); |
19578 | masm.loadJSContext(temp); |
19579 | masm.passABIArg(temp); |
19580 | masm.passABIArg(obj); |
19581 | masm.callWithABI<Fn, js::ObjectClassToString>(); |
19582 | |
19583 | bailoutCmpPtr(Assembler::Equal, ReturnReg, ImmWord(0), lir->snapshot()); |
19584 | } |
19585 | |
19586 | void CodeGenerator::visitWasmParameter(LWasmParameter* lir) {} |
19587 | |
19588 | void CodeGenerator::visitWasmParameterI64(LWasmParameterI64* lir) {} |
19589 | |
19590 | void CodeGenerator::visitWasmReturn(LWasmReturn* lir) { |
19591 | // Don't emit a jump to the return label if this is the last block. |
19592 | if (current->mir() != *gen->graph().poBegin()) { |
19593 | masm.jump(&returnLabel_); |
19594 | } |
19595 | } |
19596 | |
19597 | void CodeGenerator::visitWasmReturnI64(LWasmReturnI64* lir) { |
19598 | // Don't emit a jump to the return label if this is the last block. |
19599 | if (current->mir() != *gen->graph().poBegin()) { |
19600 | masm.jump(&returnLabel_); |
19601 | } |
19602 | } |
19603 | |
19604 | void CodeGenerator::visitWasmReturnVoid(LWasmReturnVoid* lir) { |
19605 | // Don't emit a jump to the return label if this is the last block. |
19606 | if (current->mir() != *gen->graph().poBegin()) { |
19607 | masm.jump(&returnLabel_); |
19608 | } |
19609 | } |
19610 | |
19611 | void CodeGenerator::emitAssertRangeI(MIRType type, const Range* r, |
19612 | Register input) { |
19613 | // Check the lower bound. |
19614 | if (r->hasInt32LowerBound() && r->lower() > INT32_MIN(-2147483647-1)) { |
19615 | Label success; |
19616 | if (type == MIRType::Int32 || type == MIRType::Boolean) { |
19617 | masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), |
19618 | &success); |
19619 | } else { |
19620 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19620); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 19620; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19621 | masm.branchPtr(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), |
19622 | &success); |
19623 | } |
19624 | masm.assumeUnreachable( |
19625 | "Integer input should be equal or higher than Lowerbound."); |
19626 | masm.bind(&success); |
19627 | } |
19628 | |
19629 | // Check the upper bound. |
19630 | if (r->hasInt32UpperBound() && r->upper() < INT32_MAX(2147483647)) { |
19631 | Label success; |
19632 | if (type == MIRType::Int32 || type == MIRType::Boolean) { |
19633 | masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), |
19634 | &success); |
19635 | } else { |
19636 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 19636; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19637 | masm.branchPtr(Assembler::LessThanOrEqual, input, Imm32(r->upper()), |
19638 | &success); |
19639 | } |
19640 | masm.assumeUnreachable( |
19641 | "Integer input should be lower or equal than Upperbound."); |
19642 | masm.bind(&success); |
19643 | } |
19644 | |
19645 | // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and |
19646 | // r->exponent(), there's nothing to check, because if we ended up in the |
19647 | // integer range checking code, the value is already in an integer register |
19648 | // in the integer range. |
19649 | } |
19650 | |
19651 | void CodeGenerator::emitAssertRangeD(const Range* r, FloatRegister input, |
19652 | FloatRegister temp) { |
19653 | // Check the lower bound. |
19654 | if (r->hasInt32LowerBound()) { |
19655 | Label success; |
19656 | masm.loadConstantDouble(r->lower(), temp); |
19657 | if (r->canBeNaN()) { |
19658 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &success); |
19659 | } |
19660 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, |
19661 | &success); |
19662 | masm.assumeUnreachable( |
19663 | "Double input should be equal or higher than Lowerbound."); |
19664 | masm.bind(&success); |
19665 | } |
19666 | // Check the upper bound. |
19667 | if (r->hasInt32UpperBound()) { |
19668 | Label success; |
19669 | masm.loadConstantDouble(r->upper(), temp); |
19670 | if (r->canBeNaN()) { |
19671 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &success); |
19672 | } |
19673 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success); |
19674 | masm.assumeUnreachable( |
19675 | "Double input should be lower or equal than Upperbound."); |
19676 | masm.bind(&success); |
19677 | } |
19678 | |
19679 | // This code does not yet check r->canHaveFractionalPart(). This would require |
19680 | // new assembler interfaces to make rounding instructions available. |
19681 | |
19682 | if (!r->canBeNegativeZero()) { |
19683 | Label success; |
19684 | |
19685 | // First, test for being equal to 0.0, which also includes -0.0. |
19686 | masm.loadConstantDouble(0.0, temp); |
19687 | masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp, |
19688 | &success); |
19689 | |
19690 | // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is |
19691 | // -Infinity instead of Infinity. |
19692 | masm.loadConstantDouble(1.0, temp); |
19693 | masm.divDouble(input, temp); |
19694 | masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success); |
19695 | |
19696 | masm.assumeUnreachable("Input shouldn't be negative zero."); |
19697 | |
19698 | masm.bind(&success); |
19699 | } |
19700 | |
19701 | if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() && |
19702 | r->exponent() < FloatingPoint<double>::kExponentBias) { |
19703 | // Check the bounds implied by the maximum exponent. |
19704 | Label exponentLoOk; |
19705 | masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp); |
19706 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk); |
19707 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, |
19708 | &exponentLoOk); |
19709 | masm.assumeUnreachable("Check for exponent failed."); |
19710 | masm.bind(&exponentLoOk); |
19711 | |
19712 | Label exponentHiOk; |
19713 | masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp); |
19714 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk); |
19715 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, |
19716 | &exponentHiOk); |
19717 | masm.assumeUnreachable("Check for exponent failed."); |
19718 | masm.bind(&exponentHiOk); |
19719 | } else if (!r->hasInt32Bounds() && !r->canBeNaN()) { |
19720 | // If we think the value can't be NaN, check that it isn't. |
19721 | Label notnan; |
19722 | masm.branchDouble(Assembler::DoubleOrdered, input, input, ¬nan); |
19723 | masm.assumeUnreachable("Input shouldn't be NaN."); |
19724 | masm.bind(¬nan); |
19725 | |
19726 | // If we think the value also can't be an infinity, check that it isn't. |
19727 | if (!r->canBeInfiniteOrNaN()) { |
19728 | Label notposinf; |
19729 | masm.loadConstantDouble(PositiveInfinity<double>(), temp); |
19730 | masm.branchDouble(Assembler::DoubleLessThan, input, temp, ¬posinf); |
19731 | masm.assumeUnreachable("Input shouldn't be +Inf."); |
19732 | masm.bind(¬posinf); |
19733 | |
19734 | Label notneginf; |
19735 | masm.loadConstantDouble(NegativeInfinity<double>(), temp); |
19736 | masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, ¬neginf); |
19737 | masm.assumeUnreachable("Input shouldn't be -Inf."); |
19738 | masm.bind(¬neginf); |
19739 | } |
19740 | } |
19741 | } |
19742 | |
19743 | void CodeGenerator::visitAssertClass(LAssertClass* ins) { |
19744 | Register obj = ToRegister(ins->input()); |
19745 | Register temp = ToRegister(ins->getTemp(0)); |
19746 | |
19747 | Label success; |
19748 | if (ins->mir()->getClass() == &FunctionClass) { |
19749 | // Allow both possible function classes here. |
19750 | masm.branchTestObjIsFunctionNoSpectreMitigations(Assembler::Equal, obj, |
19751 | temp, &success); |
19752 | } else { |
19753 | masm.branchTestObjClassNoSpectreMitigations( |
19754 | Assembler::Equal, obj, ins->mir()->getClass(), temp, &success); |
19755 | } |
19756 | masm.assumeUnreachable("Wrong KnownClass during run-time"); |
19757 | masm.bind(&success); |
19758 | } |
19759 | |
19760 | void CodeGenerator::visitAssertShape(LAssertShape* ins) { |
19761 | Register obj = ToRegister(ins->input()); |
19762 | |
19763 | Label success; |
19764 | masm.branchTestObjShapeNoSpectreMitigations(Assembler::Equal, obj, |
19765 | ins->mir()->shape(), &success); |
19766 | masm.assumeUnreachable("Wrong Shape during run-time"); |
19767 | masm.bind(&success); |
19768 | } |
19769 | |
19770 | void CodeGenerator::visitAssertRangeI(LAssertRangeI* ins) { |
19771 | Register input = ToRegister(ins->input()); |
19772 | const Range* r = ins->range(); |
19773 | |
19774 | emitAssertRangeI(ins->mir()->input()->type(), r, input); |
19775 | } |
19776 | |
19777 | void CodeGenerator::visitAssertRangeD(LAssertRangeD* ins) { |
19778 | FloatRegister input = ToFloatRegister(ins->input()); |
19779 | FloatRegister temp = ToFloatRegister(ins->temp()); |
19780 | const Range* r = ins->range(); |
19781 | |
19782 | emitAssertRangeD(r, input, temp); |
19783 | } |
19784 | |
19785 | void CodeGenerator::visitAssertRangeF(LAssertRangeF* ins) { |
19786 | FloatRegister input = ToFloatRegister(ins->input()); |
19787 | FloatRegister temp = ToFloatRegister(ins->temp()); |
19788 | FloatRegister temp2 = ToFloatRegister(ins->temp2()); |
19789 | |
19790 | const Range* r = ins->range(); |
19791 | |
19792 | masm.convertFloat32ToDouble(input, temp); |
19793 | emitAssertRangeD(r, temp, temp2); |
19794 | } |
19795 | |
19796 | void CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) { |
19797 | const Range* r = ins->range(); |
19798 | const ValueOperand value = ToValue(ins, LAssertRangeV::Input); |
19799 | Label done; |
19800 | |
19801 | { |
19802 | ScratchTagScope tag(masm, value); |
19803 | masm.splitTagForTest(value, tag); |
19804 | |
19805 | { |
19806 | Label isNotInt32; |
19807 | masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32); |
19808 | { |
19809 | ScratchTagScopeRelease _(&tag); |
19810 | Register unboxInt32 = ToTempUnboxRegister(ins->temp()); |
19811 | Register input = masm.extractInt32(value, unboxInt32); |
19812 | emitAssertRangeI(MIRType::Int32, r, input); |
19813 | masm.jump(&done); |
19814 | } |
19815 | masm.bind(&isNotInt32); |
19816 | } |
19817 | |
19818 | { |
19819 | Label isNotDouble; |
19820 | masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble); |
19821 | { |
19822 | ScratchTagScopeRelease _(&tag); |
19823 | FloatRegister input = ToFloatRegister(ins->floatTemp1()); |
19824 | FloatRegister temp = ToFloatRegister(ins->floatTemp2()); |
19825 | masm.unboxDouble(value, input); |
19826 | emitAssertRangeD(r, input, temp); |
19827 | masm.jump(&done); |
19828 | } |
19829 | masm.bind(&isNotDouble); |
19830 | } |
19831 | } |
19832 | |
19833 | masm.assumeUnreachable("Incorrect range for Value."); |
19834 | masm.bind(&done); |
19835 | } |
19836 | |
19837 | void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) { |
19838 | using Fn = bool (*)(JSContext*); |
19839 | OutOfLineCode* ool = |
19840 | oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing()); |
19841 | |
19842 | const void* interruptAddr = gen->runtime->addressOfInterruptBits(); |
19843 | masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0), |
19844 | ool->entry()); |
19845 | masm.bind(ool->rejoin()); |
19846 | } |
19847 | |
19848 | void CodeGenerator::visitOutOfLineResumableWasmTrap( |
19849 | OutOfLineResumableWasmTrap* ool) { |
19850 | LInstruction* lir = ool->lir(); |
19851 | masm.wasmTrap(ool->trap(), ool->bytecodeOffset()); |
19852 | |
19853 | markSafepointAt(masm.currentOffset(), lir); |
19854 | |
19855 | // Note that masm.framePushed() doesn't include the register dump area. |
19856 | // That will be taken into account when the StackMap is created from the |
19857 | // LSafepoint. |
19858 | lir->safepoint()->setFramePushedAtStackMapBase(ool->framePushed()); |
19859 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::Trap); |
19860 | |
19861 | masm.jump(ool->rejoin()); |
19862 | } |
19863 | |
19864 | void CodeGenerator::visitOutOfLineAbortingWasmTrap( |
19865 | OutOfLineAbortingWasmTrap* ool) { |
19866 | masm.wasmTrap(ool->trap(), ool->bytecodeOffset()); |
19867 | } |
19868 | |
19869 | void CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir) { |
19870 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19870); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 19870; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19871 | |
19872 | OutOfLineResumableWasmTrap* ool = new (alloc()) OutOfLineResumableWasmTrap( |
19873 | lir, masm.framePushed(), lir->mir()->bytecodeOffset(), |
19874 | wasm::Trap::CheckInterrupt); |
19875 | addOutOfLineCode(ool, lir->mir()); |
19876 | masm.branch32( |
19877 | Assembler::NotEqual, |
19878 | Address(ToRegister(lir->instance()), wasm::Instance::offsetOfInterrupt()), |
19879 | Imm32(0), ool->entry()); |
19880 | masm.bind(ool->rejoin()); |
19881 | } |
19882 | |
19883 | void CodeGenerator::visitWasmTrap(LWasmTrap* lir) { |
19884 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19884); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 19884; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19885 | const MWasmTrap* mir = lir->mir(); |
19886 | |
19887 | masm.wasmTrap(mir->trap(), mir->bytecodeOffset()); |
19888 | } |
19889 | |
19890 | void CodeGenerator::visitWasmTrapIfNull(LWasmTrapIfNull* lir) { |
19891 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 19891; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19892 | const MWasmTrapIfNull* mir = lir->mir(); |
19893 | Label nonNull; |
19894 | Register ref = ToRegister(lir->ref()); |
19895 | |
19896 | masm.branchWasmAnyRefIsNull(false, ref, &nonNull); |
19897 | masm.wasmTrap(mir->trap(), mir->bytecodeOffset()); |
19898 | masm.bind(&nonNull); |
19899 | } |
19900 | |
19901 | void CodeGenerator::visitWasmRefIsSubtypeOfAbstract( |
19902 | LWasmRefIsSubtypeOfAbstract* ins) { |
19903 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19903); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 19903; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19904 | |
19905 | const MWasmRefIsSubtypeOfAbstract* mir = ins->mir(); |
19906 | MOZ_ASSERT(!mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!mir->destType().isTypeRef())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!mir->destType().isTypeRef ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!mir->destType().isTypeRef()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19906); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()" ")"); do { *((volatile int*)__null) = 19906; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19907 | |
19908 | Register ref = ToRegister(ins->ref()); |
19909 | Register superSTV = Register::Invalid(); |
19910 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
19911 | Register scratch2 = Register::Invalid(); |
19912 | Register result = ToRegister(ins->output()); |
19913 | Label onSuccess; |
19914 | Label onFail; |
19915 | Label join; |
19916 | masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(), |
19917 | &onSuccess, /*onSuccess=*/true, superSTV, |
19918 | scratch1, scratch2); |
19919 | masm.bind(&onFail); |
19920 | masm.xor32(result, result); |
19921 | masm.jump(&join); |
19922 | masm.bind(&onSuccess); |
19923 | masm.move32(Imm32(1), result); |
19924 | masm.bind(&join); |
19925 | } |
19926 | |
19927 | void CodeGenerator::visitWasmRefIsSubtypeOfConcrete( |
19928 | LWasmRefIsSubtypeOfConcrete* ins) { |
19929 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19929); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 19929; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19930 | |
19931 | const MWasmRefIsSubtypeOfConcrete* mir = ins->mir(); |
19932 | MOZ_ASSERT(mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->destType().isTypeRef())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->destType().isTypeRef ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mir->destType().isTypeRef()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19932); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()" ")"); do { *((volatile int*)__null) = 19932; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19933 | |
19934 | Register ref = ToRegister(ins->ref()); |
19935 | Register superSTV = ToRegister(ins->superSTV()); |
19936 | Register scratch1 = ToRegister(ins->temp0()); |
19937 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
19938 | Register result = ToRegister(ins->output()); |
19939 | Label onSuccess; |
19940 | Label join; |
19941 | masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(), |
19942 | &onSuccess, /*onSuccess=*/true, superSTV, |
19943 | scratch1, scratch2); |
19944 | masm.move32(Imm32(0), result); |
19945 | masm.jump(&join); |
19946 | masm.bind(&onSuccess); |
19947 | masm.move32(Imm32(1), result); |
19948 | masm.bind(&join); |
19949 | } |
19950 | |
19951 | void CodeGenerator::visitWasmRefIsSubtypeOfAbstractAndBranch( |
19952 | LWasmRefIsSubtypeOfAbstractAndBranch* ins) { |
19953 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19953); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 19953; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19954 | Register ref = ToRegister(ins->ref()); |
19955 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
19956 | Label* onSuccess = getJumpLabelForBranch(ins->ifTrue()); |
19957 | Label* onFail = getJumpLabelForBranch(ins->ifFalse()); |
19958 | masm.branchWasmRefIsSubtype( |
19959 | ref, ins->sourceType(), ins->destType(), onSuccess, /*onSuccess=*/true, |
19960 | Register::Invalid(), scratch1, Register::Invalid()); |
19961 | masm.jump(onFail); |
19962 | } |
19963 | |
19964 | void CodeGenerator::visitWasmRefIsSubtypeOfConcreteAndBranch( |
19965 | LWasmRefIsSubtypeOfConcreteAndBranch* ins) { |
19966 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 19966; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
19967 | Register ref = ToRegister(ins->ref()); |
19968 | Register superSTV = ToRegister(ins->superSTV()); |
19969 | Register scratch1 = ToRegister(ins->temp0()); |
19970 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
19971 | Label* onSuccess = getJumpLabelForBranch(ins->ifTrue()); |
19972 | Label* onFail = getJumpLabelForBranch(ins->ifFalse()); |
19973 | masm.branchWasmRefIsSubtype(ref, ins->sourceType(), ins->destType(), |
19974 | onSuccess, /*onSuccess=*/true, superSTV, scratch1, |
19975 | scratch2); |
19976 | masm.jump(onFail); |
19977 | } |
19978 | |
19979 | void CodeGenerator::callWasmStructAllocFun(LInstruction* lir, |
19980 | wasm::SymbolicAddress fun, |
19981 | Register typeDefData, |
19982 | Register output) { |
19983 | masm.Push(InstanceReg); |
19984 | int32_t framePushedAfterInstance = masm.framePushed(); |
19985 | saveLive(lir); |
19986 | |
19987 | masm.setupWasmABICall(); |
19988 | masm.passABIArg(InstanceReg); |
19989 | masm.passABIArg(typeDefData); |
19990 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
19991 | CodeOffset offset = |
19992 | masm.callWithABI(wasm::BytecodeOffset(0), fun, |
19993 | mozilla::Some(instanceOffset), ABIType::General); |
19994 | masm.storeCallPointerResult(output); |
19995 | |
19996 | markSafepointAt(offset.offset(), lir); |
19997 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance); |
19998 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall); |
19999 | |
20000 | restoreLive(lir); |
20001 | masm.Pop(InstanceReg); |
20002 | #if JS_CODEGEN_ARM64 |
20003 | masm.syncStackPtr(); |
20004 | #endif |
20005 | } |
20006 | |
20007 | // Out-of-line path to allocate wasm GC structs |
20008 | class OutOfLineWasmNewStruct : public OutOfLineCodeBase<CodeGenerator> { |
20009 | LInstruction* lir_; |
20010 | wasm::SymbolicAddress fun_; |
20011 | Register typeDefData_; |
20012 | Register output_; |
20013 | |
20014 | public: |
20015 | OutOfLineWasmNewStruct(LInstruction* lir, wasm::SymbolicAddress fun, |
20016 | Register typeDefData, Register output) |
20017 | : lir_(lir), fun_(fun), typeDefData_(typeDefData), output_(output) {} |
20018 | |
20019 | void accept(CodeGenerator* codegen) override { |
20020 | codegen->visitOutOfLineWasmNewStruct(this); |
20021 | } |
20022 | |
20023 | LInstruction* lir() const { return lir_; } |
20024 | wasm::SymbolicAddress fun() const { return fun_; } |
20025 | Register typeDefData() const { return typeDefData_; } |
20026 | Register output() const { return output_; } |
20027 | }; |
20028 | |
20029 | void CodeGenerator::visitOutOfLineWasmNewStruct(OutOfLineWasmNewStruct* ool) { |
20030 | callWasmStructAllocFun(ool->lir(), ool->fun(), ool->typeDefData(), |
20031 | ool->output()); |
20032 | masm.jump(ool->rejoin()); |
20033 | } |
20034 | |
20035 | void CodeGenerator::visitWasmNewStructObject(LWasmNewStructObject* lir) { |
20036 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20036); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20036; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20037 | |
20038 | MWasmNewStructObject* mir = lir->mir(); |
20039 | |
20040 | Register typeDefData = ToRegister(lir->typeDefData()); |
20041 | Register output = ToRegister(lir->output()); |
20042 | |
20043 | if (mir->isOutline()) { |
20044 | wasm::SymbolicAddress fun = mir->zeroFields() |
20045 | ? wasm::SymbolicAddress::StructNewOOL_true |
20046 | : wasm::SymbolicAddress::StructNewOOL_false; |
20047 | callWasmStructAllocFun(lir, fun, typeDefData, output); |
20048 | } else { |
20049 | wasm::SymbolicAddress fun = mir->zeroFields() |
20050 | ? wasm::SymbolicAddress::StructNewIL_true |
20051 | : wasm::SymbolicAddress::StructNewIL_false; |
20052 | |
20053 | Register instance = ToRegister(lir->instance()); |
20054 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20054); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20054; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20055 | |
20056 | auto ool = |
20057 | new (alloc()) OutOfLineWasmNewStruct(lir, fun, typeDefData, output); |
20058 | addOutOfLineCode(ool, lir->mir()); |
20059 | |
20060 | Register temp1 = ToRegister(lir->temp0()); |
20061 | Register temp2 = ToRegister(lir->temp1()); |
20062 | masm.wasmNewStructObject(instance, output, typeDefData, temp1, temp2, |
20063 | ool->entry(), mir->allocKind(), mir->zeroFields()); |
20064 | |
20065 | masm.bind(ool->rejoin()); |
20066 | } |
20067 | } |
20068 | |
20069 | void CodeGenerator::callWasmArrayAllocFun(LInstruction* lir, |
20070 | wasm::SymbolicAddress fun, |
20071 | Register numElements, |
20072 | Register typeDefData, Register output, |
20073 | wasm::BytecodeOffset bytecodeOffset) { |
20074 | masm.Push(InstanceReg); |
20075 | int32_t framePushedAfterInstance = masm.framePushed(); |
20076 | saveLive(lir); |
20077 | |
20078 | masm.setupWasmABICall(); |
20079 | masm.passABIArg(InstanceReg); |
20080 | masm.passABIArg(numElements); |
20081 | masm.passABIArg(typeDefData); |
20082 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
20083 | CodeOffset offset = masm.callWithABI( |
20084 | bytecodeOffset, fun, mozilla::Some(instanceOffset), ABIType::General); |
20085 | masm.storeCallPointerResult(output); |
20086 | |
20087 | markSafepointAt(offset.offset(), lir); |
20088 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance); |
20089 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall); |
20090 | |
20091 | restoreLive(lir); |
20092 | masm.Pop(InstanceReg); |
20093 | #if JS_CODEGEN_ARM64 |
20094 | masm.syncStackPtr(); |
20095 | #endif |
20096 | |
20097 | Label ok; |
20098 | masm.branchPtr(Assembler::NonZero, output, ImmWord(0), &ok); |
20099 | masm.wasmTrap(wasm::Trap::ThrowReported, bytecodeOffset); |
20100 | masm.bind(&ok); |
20101 | } |
20102 | |
20103 | // Out-of-line path to allocate wasm GC arrays |
20104 | class OutOfLineWasmNewArray : public OutOfLineCodeBase<CodeGenerator> { |
20105 | LInstruction* lir_; |
20106 | wasm::SymbolicAddress fun_; |
20107 | Register numElementsReg_; |
20108 | mozilla::Maybe<uint32_t> numElements_; |
20109 | Register typeDefData_; |
20110 | Register output_; |
20111 | wasm::BytecodeOffset bytecodeOffset_; |
20112 | |
20113 | public: |
20114 | OutOfLineWasmNewArray(LInstruction* lir, wasm::SymbolicAddress fun, |
20115 | Register numElementsReg, |
20116 | mozilla::Maybe<uint32_t> numElements, |
20117 | Register typeDefData, Register output, |
20118 | wasm::BytecodeOffset bytecodeOffset) |
20119 | : lir_(lir), |
20120 | fun_(fun), |
20121 | numElementsReg_(numElementsReg), |
20122 | numElements_(numElements), |
20123 | typeDefData_(typeDefData), |
20124 | output_(output), |
20125 | bytecodeOffset_(bytecodeOffset) {} |
20126 | |
20127 | void accept(CodeGenerator* codegen) override { |
20128 | codegen->visitOutOfLineWasmNewArray(this); |
20129 | } |
20130 | |
20131 | LInstruction* lir() const { return lir_; } |
20132 | wasm::SymbolicAddress fun() const { return fun_; } |
20133 | Register numElementsReg() const { return numElementsReg_; } |
20134 | mozilla::Maybe<uint32_t> numElements() const { return numElements_; } |
20135 | Register typeDefData() const { return typeDefData_; } |
20136 | Register output() const { return output_; } |
20137 | wasm::BytecodeOffset bytecodeOffset() const { return bytecodeOffset_; } |
20138 | }; |
20139 | |
20140 | void CodeGenerator::visitOutOfLineWasmNewArray(OutOfLineWasmNewArray* ool) { |
20141 | if (ool->numElements().isSome()) { |
20142 | masm.move32(Imm32(ool->numElements().value()), ool->numElementsReg()); |
20143 | } |
20144 | callWasmArrayAllocFun(ool->lir(), ool->fun(), ool->numElementsReg(), |
20145 | ool->typeDefData(), ool->output(), |
20146 | ool->bytecodeOffset()); |
20147 | masm.jump(ool->rejoin()); |
20148 | } |
20149 | |
20150 | void CodeGenerator::visitWasmNewArrayObject(LWasmNewArrayObject* lir) { |
20151 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20151); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20151; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20152 | |
20153 | MWasmNewArrayObject* mir = lir->mir(); |
20154 | |
20155 | Register typeDefData = ToRegister(lir->typeDefData()); |
20156 | Register output = ToRegister(lir->output()); |
20157 | Register temp1 = ToRegister(lir->temp0()); |
20158 | Register temp2 = ToRegister(lir->temp1()); |
20159 | |
20160 | wasm::SymbolicAddress fun = mir->zeroFields() |
20161 | ? wasm::SymbolicAddress::ArrayNew_true |
20162 | : wasm::SymbolicAddress::ArrayNew_false; |
20163 | |
20164 | if (lir->numElements()->isConstant()) { |
20165 | // numElements is constant, so we can do optimized code generation. |
20166 | uint32_t numElements = lir->numElements()->toConstant()->toInt32(); |
20167 | CheckedUint32 storageBytes = |
20168 | WasmArrayObject::calcStorageBytesChecked(mir->elemSize(), numElements); |
20169 | if (!storageBytes.isValid() || |
20170 | storageBytes.value() > WasmArrayObject_MaxInlineBytes) { |
20171 | // Too much array data to store inline. Immediately perform an instance |
20172 | // call to handle the out-of-line storage (or the trap). |
20173 | masm.move32(Imm32(numElements), temp1); |
20174 | callWasmArrayAllocFun(lir, fun, temp1, typeDefData, output, |
20175 | mir->bytecodeOffset()); |
20176 | } else { |
20177 | // storageBytes is small enough to be stored inline in WasmArrayObject. |
20178 | // Attempt a nursery allocation and fall back to an instance call if it |
20179 | // fails. |
20180 | Register instance = ToRegister(lir->instance()); |
20181 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20181; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20182 | |
20183 | auto ool = new (alloc()) |
20184 | OutOfLineWasmNewArray(lir, fun, temp1, mozilla::Some(numElements), |
20185 | typeDefData, output, mir->bytecodeOffset()); |
20186 | addOutOfLineCode(ool, lir->mir()); |
20187 | |
20188 | masm.wasmNewArrayObjectFixed(instance, output, typeDefData, temp1, temp2, |
20189 | ool->entry(), numElements, |
20190 | storageBytes.value(), mir->zeroFields()); |
20191 | |
20192 | masm.bind(ool->rejoin()); |
20193 | } |
20194 | } else { |
20195 | // numElements is dynamic. Attempt a dynamic inline-storage nursery |
20196 | // allocation and fall back to an instance call if it fails. |
20197 | Register instance = ToRegister(lir->instance()); |
20198 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20198; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20199 | Register numElements = ToRegister(lir->numElements()); |
20200 | |
20201 | auto ool = new (alloc()) |
20202 | OutOfLineWasmNewArray(lir, fun, numElements, mozilla::Nothing(), |
20203 | typeDefData, output, mir->bytecodeOffset()); |
20204 | addOutOfLineCode(ool, lir->mir()); |
20205 | |
20206 | masm.wasmNewArrayObject(instance, output, numElements, typeDefData, temp1, |
20207 | ool->entry(), mir->elemSize(), mir->zeroFields()); |
20208 | |
20209 | masm.bind(ool->rejoin()); |
20210 | } |
20211 | } |
20212 | |
20213 | void CodeGenerator::visitWasmHeapReg(LWasmHeapReg* ins) { |
20214 | #ifdef WASM_HAS_HEAPREG1 |
20215 | masm.movePtr(HeapReg, ToRegister(ins->output())); |
20216 | #else |
20217 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20217); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 20217; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
20218 | #endif |
20219 | } |
20220 | |
20221 | void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) { |
20222 | const MWasmBoundsCheck* mir = ins->mir(); |
20223 | Register ptr = ToRegister(ins->ptr()); |
20224 | Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit()); |
20225 | // When there are no spectre mitigations in place, branching out-of-line to |
20226 | // the trap is a big performance win, but with mitigations it's trickier. See |
20227 | // bug 1680243. |
20228 | if (JitOptions.spectreIndexMasking) { |
20229 | Label ok; |
20230 | masm.wasmBoundsCheck32(Assembler::Below, ptr, boundsCheckLimit, &ok); |
20231 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); |
20232 | masm.bind(&ok); |
20233 | } else { |
20234 | OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap( |
20235 | mir->bytecodeOffset(), wasm::Trap::OutOfBounds); |
20236 | addOutOfLineCode(ool, mir); |
20237 | masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptr, boundsCheckLimit, |
20238 | ool->entry()); |
20239 | } |
20240 | } |
20241 | |
20242 | void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) { |
20243 | const MWasmBoundsCheck* mir = ins->mir(); |
20244 | Register64 ptr = ToRegister64(ins->ptr()); |
20245 | Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit()); |
20246 | // See above. |
20247 | if (JitOptions.spectreIndexMasking) { |
20248 | Label ok; |
20249 | masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok); |
20250 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->bytecodeOffset()); |
20251 | masm.bind(&ok); |
20252 | } else { |
20253 | OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap( |
20254 | mir->bytecodeOffset(), wasm::Trap::OutOfBounds); |
20255 | addOutOfLineCode(ool, mir); |
20256 | masm.wasmBoundsCheck64(Assembler::AboveOrEqual, ptr, boundsCheckLimit, |
20257 | ool->entry()); |
20258 | } |
20259 | } |
20260 | |
20261 | void CodeGenerator::visitWasmBoundsCheckRange32(LWasmBoundsCheckRange32* ins) { |
20262 | const MWasmBoundsCheckRange32* mir = ins->mir(); |
20263 | Register index = ToRegister(ins->index()); |
20264 | Register length = ToRegister(ins->length()); |
20265 | Register limit = ToRegister(ins->limit()); |
20266 | Register tmp = ToRegister(ins->temp0()); |
20267 | |
20268 | masm.wasmBoundsCheckRange32(index, length, limit, tmp, mir->bytecodeOffset()); |
20269 | } |
20270 | |
20271 | void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) { |
20272 | const MWasmAlignmentCheck* mir = ins->mir(); |
20273 | Register ptr = ToRegister(ins->ptr()); |
20274 | OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap( |
20275 | mir->bytecodeOffset(), wasm::Trap::UnalignedAccess); |
20276 | addOutOfLineCode(ool, mir); |
20277 | masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1), |
20278 | ool->entry()); |
20279 | } |
20280 | |
20281 | void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) { |
20282 | const MWasmAlignmentCheck* mir = ins->mir(); |
20283 | Register64 ptr = ToRegister64(ins->ptr()); |
20284 | #ifdef JS_64BIT1 |
20285 | Register r = ptr.reg; |
20286 | #else |
20287 | Register r = ptr.low; |
20288 | #endif |
20289 | OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap( |
20290 | mir->bytecodeOffset(), wasm::Trap::UnalignedAccess); |
20291 | addOutOfLineCode(ool, mir); |
20292 | masm.branchTestPtr(Assembler::NonZero, r, Imm32(mir->byteSize() - 1), |
20293 | ool->entry()); |
20294 | } |
20295 | |
20296 | void CodeGenerator::visitWasmLoadInstance(LWasmLoadInstance* ins) { |
20297 | switch (ins->mir()->type()) { |
20298 | case MIRType::WasmAnyRef: |
20299 | case MIRType::Pointer: |
20300 | masm.loadPtr(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
20301 | ToRegister(ins->output())); |
20302 | break; |
20303 | case MIRType::Int32: |
20304 | masm.load32(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
20305 | ToRegister(ins->output())); |
20306 | break; |
20307 | default: |
20308 | MOZ_CRASH("MIRType not supported in WasmLoadInstance")do { do { } while (false); MOZ_ReportCrash("" "MIRType not supported in WasmLoadInstance" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20308); AnnotateMozCrashReason("MOZ_CRASH(" "MIRType not supported in WasmLoadInstance" ")"); do { *((volatile int*)__null) = 20308; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
20309 | } |
20310 | } |
20311 | |
20312 | void CodeGenerator::visitWasmLoadInstance64(LWasmLoadInstance64* ins) { |
20313 | MOZ_ASSERT(ins->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->type() == MIRType::Int64)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ins->mir()->type() == MIRType::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ins->mir()->type() == MIRType::Int64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 20313; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20314 | masm.load64(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
20315 | ToOutRegister64(ins)); |
20316 | } |
20317 | |
20318 | void CodeGenerator::incrementWarmUpCounter(AbsoluteAddress warmUpCount, |
20319 | JSScript* script, Register tmp) { |
20320 | // The code depends on the JitScript* not being discarded without also |
20321 | // invalidating Ion code. Assert this. |
20322 | #ifdef DEBUG1 |
20323 | Label ok; |
20324 | masm.movePtr(ImmGCPtr(script), tmp); |
20325 | masm.loadJitScript(tmp, tmp); |
20326 | masm.branchPtr(Assembler::Equal, tmp, ImmPtr(script->jitScript()), &ok); |
20327 | masm.assumeUnreachable("Didn't find JitScript?"); |
20328 | masm.bind(&ok); |
20329 | #endif |
20330 | |
20331 | masm.load32(warmUpCount, tmp); |
20332 | masm.add32(Imm32(1), tmp); |
20333 | masm.store32(tmp, warmUpCount); |
20334 | } |
20335 | |
20336 | void CodeGenerator::visitIncrementWarmUpCounter(LIncrementWarmUpCounter* ins) { |
20337 | Register tmp = ToRegister(ins->temp0()); |
20338 | |
20339 | AbsoluteAddress warmUpCount = |
20340 | AbsoluteAddress(ins->mir()->script()->jitScript()) |
20341 | .offset(JitScript::offsetOfWarmUpCount()); |
20342 | incrementWarmUpCounter(warmUpCount, ins->mir()->script(), tmp); |
20343 | } |
20344 | |
20345 | void CodeGenerator::visitLexicalCheck(LLexicalCheck* ins) { |
20346 | ValueOperand inputValue = ToValue(ins, LLexicalCheck::InputIndex); |
20347 | Label bail; |
20348 | masm.branchTestMagicValue(Assembler::Equal, inputValue, |
20349 | JS_UNINITIALIZED_LEXICAL, &bail); |
20350 | bailoutFrom(&bail, ins->snapshot()); |
20351 | } |
20352 | |
20353 | void CodeGenerator::visitThrowRuntimeLexicalError( |
20354 | LThrowRuntimeLexicalError* ins) { |
20355 | pushArg(Imm32(ins->mir()->errorNumber())); |
20356 | |
20357 | using Fn = bool (*)(JSContext*, unsigned); |
20358 | callVM<Fn, jit::ThrowRuntimeLexicalError>(ins); |
20359 | } |
20360 | |
20361 | void CodeGenerator::visitThrowMsg(LThrowMsg* ins) { |
20362 | pushArg(Imm32(static_cast<int32_t>(ins->mir()->throwMsgKind()))); |
20363 | |
20364 | using Fn = bool (*)(JSContext*, unsigned); |
20365 | callVM<Fn, js::ThrowMsgOperation>(ins); |
20366 | } |
20367 | |
20368 | void CodeGenerator::visitGlobalDeclInstantiation( |
20369 | LGlobalDeclInstantiation* ins) { |
20370 | pushArg(ImmPtr(ins->mir()->resumePoint()->pc())); |
20371 | pushArg(ImmGCPtr(ins->mir()->block()->info().script())); |
20372 | |
20373 | using Fn = bool (*)(JSContext*, HandleScript, const jsbytecode*); |
20374 | callVM<Fn, GlobalDeclInstantiationFromIon>(ins); |
20375 | } |
20376 | |
20377 | void CodeGenerator::visitDebugger(LDebugger* ins) { |
20378 | Register cx = ToRegister(ins->temp0()); |
20379 | |
20380 | masm.loadJSContext(cx); |
20381 | using Fn = bool (*)(JSContext* cx); |
20382 | masm.setupAlignedABICall(); |
20383 | masm.passABIArg(cx); |
20384 | masm.callWithABI<Fn, GlobalHasLiveOnDebuggerStatement>(); |
20385 | |
20386 | Label bail; |
20387 | masm.branchIfTrueBool(ReturnReg, &bail); |
20388 | bailoutFrom(&bail, ins->snapshot()); |
20389 | } |
20390 | |
20391 | void CodeGenerator::visitNewTarget(LNewTarget* ins) { |
20392 | ValueOperand output = ToOutValue(ins); |
20393 | |
20394 | // if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)] |
20395 | Label notConstructing, done; |
20396 | Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
20397 | masm.branchTestPtr(Assembler::Zero, calleeToken, |
20398 | Imm32(CalleeToken_FunctionConstructing), ¬Constructing); |
20399 | |
20400 | Register argvLen = output.scratchReg(); |
20401 | masm.loadNumActualArgs(FramePointer, argvLen); |
20402 | |
20403 | Label useNFormals; |
20404 | |
20405 | size_t numFormalArgs = ins->mirRaw()->block()->info().nargs(); |
20406 | masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs), &useNFormals); |
20407 | |
20408 | size_t argsOffset = JitFrameLayout::offsetOfActualArgs(); |
20409 | { |
20410 | BaseValueIndex newTarget(FramePointer, argvLen, argsOffset); |
20411 | masm.loadValue(newTarget, output); |
20412 | masm.jump(&done); |
20413 | } |
20414 | |
20415 | masm.bind(&useNFormals); |
20416 | |
20417 | { |
20418 | Address newTarget(FramePointer, |
20419 | argsOffset + (numFormalArgs * sizeof(Value))); |
20420 | masm.loadValue(newTarget, output); |
20421 | masm.jump(&done); |
20422 | } |
20423 | |
20424 | // else output = undefined |
20425 | masm.bind(¬Constructing); |
20426 | masm.moveValue(UndefinedValue(), output); |
20427 | masm.bind(&done); |
20428 | } |
20429 | |
20430 | void CodeGenerator::visitCheckReturn(LCheckReturn* ins) { |
20431 | ValueOperand returnValue = ToValue(ins, LCheckReturn::ReturnValueIndex); |
20432 | ValueOperand thisValue = ToValue(ins, LCheckReturn::ThisValueIndex); |
20433 | ValueOperand output = ToOutValue(ins); |
20434 | |
20435 | using Fn = bool (*)(JSContext*, HandleValue); |
20436 | OutOfLineCode* ool = oolCallVM<Fn, ThrowBadDerivedReturnOrUninitializedThis>( |
20437 | ins, ArgList(returnValue), StoreNothing()); |
20438 | |
20439 | Label noChecks; |
20440 | masm.branchTestObject(Assembler::Equal, returnValue, &noChecks); |
20441 | masm.branchTestUndefined(Assembler::NotEqual, returnValue, ool->entry()); |
20442 | masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry()); |
20443 | masm.moveValue(thisValue, output); |
20444 | masm.jump(ool->rejoin()); |
20445 | masm.bind(&noChecks); |
20446 | masm.moveValue(returnValue, output); |
20447 | masm.bind(ool->rejoin()); |
20448 | } |
20449 | |
20450 | void CodeGenerator::visitCheckIsObj(LCheckIsObj* ins) { |
20451 | ValueOperand value = ToValue(ins, LCheckIsObj::ValueIndex); |
20452 | Register output = ToRegister(ins->output()); |
20453 | |
20454 | using Fn = bool (*)(JSContext*, CheckIsObjectKind); |
20455 | OutOfLineCode* ool = oolCallVM<Fn, ThrowCheckIsObject>( |
20456 | ins, ArgList(Imm32(ins->mir()->checkKind())), StoreNothing()); |
20457 | |
20458 | masm.fallibleUnboxObject(value, output, ool->entry()); |
20459 | masm.bind(ool->rejoin()); |
20460 | } |
20461 | |
20462 | void CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) { |
20463 | ValueOperand checkValue = ToValue(ins, LCheckObjCoercible::ValueIndex); |
20464 | |
20465 | using Fn = bool (*)(JSContext*, HandleValue); |
20466 | OutOfLineCode* ool = oolCallVM<Fn, ThrowObjectCoercible>( |
20467 | ins, ArgList(checkValue), StoreNothing()); |
20468 | masm.branchTestNull(Assembler::Equal, checkValue, ool->entry()); |
20469 | masm.branchTestUndefined(Assembler::Equal, checkValue, ool->entry()); |
20470 | masm.bind(ool->rejoin()); |
20471 | } |
20472 | |
20473 | void CodeGenerator::visitCheckClassHeritage(LCheckClassHeritage* ins) { |
20474 | ValueOperand heritage = ToValue(ins, LCheckClassHeritage::HeritageIndex); |
20475 | Register temp0 = ToRegister(ins->temp0()); |
20476 | Register temp1 = ToRegister(ins->temp1()); |
20477 | |
20478 | using Fn = bool (*)(JSContext*, HandleValue); |
20479 | OutOfLineCode* ool = oolCallVM<Fn, CheckClassHeritageOperation>( |
20480 | ins, ArgList(heritage), StoreNothing()); |
20481 | |
20482 | masm.branchTestNull(Assembler::Equal, heritage, ool->rejoin()); |
20483 | masm.fallibleUnboxObject(heritage, temp0, ool->entry()); |
20484 | |
20485 | masm.isConstructor(temp0, temp1, ool->entry()); |
20486 | masm.branchTest32(Assembler::Zero, temp1, temp1, ool->entry()); |
20487 | |
20488 | masm.bind(ool->rejoin()); |
20489 | } |
20490 | |
20491 | void CodeGenerator::visitCheckThis(LCheckThis* ins) { |
20492 | ValueOperand thisValue = ToValue(ins, LCheckThis::ValueIndex); |
20493 | |
20494 | using Fn = bool (*)(JSContext*); |
20495 | OutOfLineCode* ool = |
20496 | oolCallVM<Fn, ThrowUninitializedThis>(ins, ArgList(), StoreNothing()); |
20497 | masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry()); |
20498 | masm.bind(ool->rejoin()); |
20499 | } |
20500 | |
20501 | void CodeGenerator::visitCheckThisReinit(LCheckThisReinit* ins) { |
20502 | ValueOperand thisValue = ToValue(ins, LCheckThisReinit::ThisValueIndex); |
20503 | |
20504 | using Fn = bool (*)(JSContext*); |
20505 | OutOfLineCode* ool = |
20506 | oolCallVM<Fn, ThrowInitializedThis>(ins, ArgList(), StoreNothing()); |
20507 | masm.branchTestMagic(Assembler::NotEqual, thisValue, ool->entry()); |
20508 | masm.bind(ool->rejoin()); |
20509 | } |
20510 | |
20511 | void CodeGenerator::visitGenerator(LGenerator* lir) { |
20512 | Register callee = ToRegister(lir->callee()); |
20513 | Register environmentChain = ToRegister(lir->environmentChain()); |
20514 | Register argsObject = ToRegister(lir->argsObject()); |
20515 | |
20516 | pushArg(argsObject); |
20517 | pushArg(environmentChain); |
20518 | pushArg(ImmGCPtr(current->mir()->info().script())); |
20519 | pushArg(callee); |
20520 | |
20521 | using Fn = JSObject* (*)(JSContext* cx, HandleFunction, HandleScript, |
20522 | HandleObject, HandleObject); |
20523 | callVM<Fn, CreateGenerator>(lir); |
20524 | } |
20525 | |
20526 | void CodeGenerator::visitAsyncResolve(LAsyncResolve* lir) { |
20527 | Register generator = ToRegister(lir->generator()); |
20528 | ValueOperand value = ToValue(lir, LAsyncResolve::ValueIndex); |
20529 | |
20530 | pushArg(value); |
20531 | pushArg(generator); |
20532 | |
20533 | using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>, |
20534 | HandleValue); |
20535 | callVM<Fn, js::AsyncFunctionResolve>(lir); |
20536 | } |
20537 | |
20538 | void CodeGenerator::visitAsyncReject(LAsyncReject* lir) { |
20539 | Register generator = ToRegister(lir->generator()); |
20540 | ValueOperand reason = ToValue(lir, LAsyncReject::ReasonIndex); |
20541 | ValueOperand stack = ToValue(lir, LAsyncReject::StackIndex); |
20542 | |
20543 | pushArg(stack); |
20544 | pushArg(reason); |
20545 | pushArg(generator); |
20546 | |
20547 | using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>, |
20548 | HandleValue, HandleValue); |
20549 | callVM<Fn, js::AsyncFunctionReject>(lir); |
20550 | } |
20551 | |
20552 | void CodeGenerator::visitAsyncAwait(LAsyncAwait* lir) { |
20553 | ValueOperand value = ToValue(lir, LAsyncAwait::ValueIndex); |
20554 | Register generator = ToRegister(lir->generator()); |
20555 | |
20556 | pushArg(value); |
20557 | pushArg(generator); |
20558 | |
20559 | using Fn = |
20560 | JSObject* (*)(JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj, |
20561 | HandleValue value); |
20562 | callVM<Fn, js::AsyncFunctionAwait>(lir); |
20563 | } |
20564 | |
20565 | void CodeGenerator::visitCanSkipAwait(LCanSkipAwait* lir) { |
20566 | ValueOperand value = ToValue(lir, LCanSkipAwait::ValueIndex); |
20567 | |
20568 | pushArg(value); |
20569 | |
20570 | using Fn = bool (*)(JSContext*, HandleValue, bool* canSkip); |
20571 | callVM<Fn, js::CanSkipAwait>(lir); |
20572 | } |
20573 | |
20574 | void CodeGenerator::visitMaybeExtractAwaitValue(LMaybeExtractAwaitValue* lir) { |
20575 | ValueOperand value = ToValue(lir, LMaybeExtractAwaitValue::ValueIndex); |
20576 | ValueOperand output = ToOutValue(lir); |
20577 | Register canSkip = ToRegister(lir->canSkip()); |
20578 | |
20579 | Label cantExtract, finished; |
20580 | masm.branchIfFalseBool(canSkip, &cantExtract); |
20581 | |
20582 | pushArg(value); |
20583 | |
20584 | using Fn = bool (*)(JSContext*, HandleValue, MutableHandleValue); |
20585 | callVM<Fn, js::ExtractAwaitValue>(lir); |
20586 | masm.jump(&finished); |
20587 | masm.bind(&cantExtract); |
20588 | |
20589 | masm.moveValue(value, output); |
20590 | |
20591 | masm.bind(&finished); |
20592 | } |
20593 | |
20594 | void CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) { |
20595 | ValueOperand checkValue = ToValue(ins, LDebugCheckSelfHosted::ValueIndex); |
20596 | pushArg(checkValue); |
20597 | using Fn = bool (*)(JSContext*, HandleValue); |
20598 | callVM<Fn, js::Debug_CheckSelfHosted>(ins); |
20599 | } |
20600 | |
20601 | void CodeGenerator::visitRandom(LRandom* ins) { |
20602 | using mozilla::non_crypto::XorShift128PlusRNG; |
20603 | |
20604 | FloatRegister output = ToFloatRegister(ins->output()); |
20605 | Register rngReg = ToRegister(ins->temp0()); |
20606 | |
20607 | Register64 temp1 = ToRegister64(ins->temp1()); |
20608 | Register64 temp2 = ToRegister64(ins->temp2()); |
20609 | |
20610 | const XorShift128PlusRNG* rng = gen->realm->addressOfRandomNumberGenerator(); |
20611 | masm.movePtr(ImmPtr(rng), rngReg); |
20612 | |
20613 | masm.randomDouble(rngReg, output, temp1, temp2); |
20614 | if (js::SupportDifferentialTesting()) { |
20615 | masm.loadConstantDouble(0.0, output); |
20616 | } |
20617 | } |
20618 | |
20619 | void CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins) { |
20620 | Register input = ToRegister(ins->input()); |
20621 | Register output = ToRegister(ins->output()); |
20622 | |
20623 | switch (ins->mode()) { |
20624 | case MSignExtendInt32::Byte: |
20625 | masm.move8SignExtend(input, output); |
20626 | break; |
20627 | case MSignExtendInt32::Half: |
20628 | masm.move16SignExtend(input, output); |
20629 | break; |
20630 | } |
20631 | } |
20632 | |
20633 | void CodeGenerator::visitRotate(LRotate* ins) { |
20634 | MRotate* mir = ins->mir(); |
20635 | Register input = ToRegister(ins->input()); |
20636 | Register dest = ToRegister(ins->output()); |
20637 | |
20638 | const LAllocation* count = ins->count(); |
20639 | if (count->isConstant()) { |
20640 | int32_t c = ToInt32(count) & 0x1F; |
20641 | if (mir->isLeftRotate()) { |
20642 | masm.rotateLeft(Imm32(c), input, dest); |
20643 | } else { |
20644 | masm.rotateRight(Imm32(c), input, dest); |
20645 | } |
20646 | } else { |
20647 | Register creg = ToRegister(count); |
20648 | if (mir->isLeftRotate()) { |
20649 | masm.rotateLeft(creg, input, dest); |
20650 | } else { |
20651 | masm.rotateRight(creg, input, dest); |
20652 | } |
20653 | } |
20654 | } |
20655 | |
20656 | class OutOfLineNaNToZero : public OutOfLineCodeBase<CodeGenerator> { |
20657 | LNaNToZero* lir_; |
20658 | |
20659 | public: |
20660 | explicit OutOfLineNaNToZero(LNaNToZero* lir) : lir_(lir) {} |
20661 | |
20662 | void accept(CodeGenerator* codegen) override { |
20663 | codegen->visitOutOfLineNaNToZero(this); |
20664 | } |
20665 | LNaNToZero* lir() const { return lir_; } |
20666 | }; |
20667 | |
20668 | void CodeGenerator::visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool) { |
20669 | FloatRegister output = ToFloatRegister(ool->lir()->output()); |
20670 | masm.loadConstantDouble(0.0, output); |
20671 | masm.jump(ool->rejoin()); |
20672 | } |
20673 | |
20674 | void CodeGenerator::visitNaNToZero(LNaNToZero* lir) { |
20675 | FloatRegister input = ToFloatRegister(lir->input()); |
20676 | |
20677 | OutOfLineNaNToZero* ool = new (alloc()) OutOfLineNaNToZero(lir); |
20678 | addOutOfLineCode(ool, lir->mir()); |
20679 | |
20680 | if (lir->mir()->operandIsNeverNegativeZero()) { |
20681 | masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry()); |
20682 | } else { |
20683 | FloatRegister scratch = ToFloatRegister(lir->temp0()); |
20684 | masm.loadConstantDouble(0.0, scratch); |
20685 | masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, |
20686 | ool->entry()); |
20687 | } |
20688 | masm.bind(ool->rejoin()); |
20689 | } |
20690 | |
20691 | void CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) { |
20692 | Register obj = ToRegister(lir->object()); |
20693 | Register output = ToRegister(lir->output()); |
20694 | Register temp = ToRegister(lir->temp0()); |
20695 | |
20696 | masm.setIsPackedArray(obj, output, temp); |
20697 | } |
20698 | |
20699 | void CodeGenerator::visitGuardArrayIsPacked(LGuardArrayIsPacked* lir) { |
20700 | Register array = ToRegister(lir->array()); |
20701 | Register temp0 = ToRegister(lir->temp0()); |
20702 | Register temp1 = ToRegister(lir->temp1()); |
20703 | |
20704 | Label bail; |
20705 | masm.branchArrayIsNotPacked(array, temp0, temp1, &bail); |
20706 | bailoutFrom(&bail, lir->snapshot()); |
20707 | } |
20708 | |
20709 | void CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) { |
20710 | Register target = ToRegister(lir->target()); |
20711 | ValueOperand out = ToOutValue(lir); |
20712 | Register scratch = out.scratchReg(); |
20713 | |
20714 | using Fn = bool (*)(JSContext*, HandleObject, MutableHandleValue); |
20715 | OutOfLineCode* ool = oolCallVM<Fn, jit::GetPrototypeOf>(lir, ArgList(target), |
20716 | StoreValueTo(out)); |
20717 | |
20718 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20718); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 20718; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20719 | |
20720 | masm.loadObjProto(target, scratch); |
20721 | |
20722 | Label hasProto; |
20723 | masm.branchPtr(Assembler::Above, scratch, ImmWord(1), &hasProto); |
20724 | |
20725 | // Call into the VM for lazy prototypes. |
20726 | masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), ool->entry()); |
20727 | |
20728 | masm.moveValue(NullValue(), out); |
20729 | masm.jump(ool->rejoin()); |
20730 | |
20731 | masm.bind(&hasProto); |
20732 | masm.tagValue(JSVAL_TYPE_OBJECT, scratch, out); |
20733 | |
20734 | masm.bind(ool->rejoin()); |
20735 | } |
20736 | |
20737 | void CodeGenerator::visitObjectWithProto(LObjectWithProto* lir) { |
20738 | pushArg(ToValue(lir, LObjectWithProto::PrototypeIndex)); |
20739 | |
20740 | using Fn = PlainObject* (*)(JSContext*, HandleValue); |
20741 | callVM<Fn, js::ObjectWithProtoOperation>(lir); |
20742 | } |
20743 | |
20744 | void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) { |
20745 | Register obj = ToRegister(lir->input()); |
20746 | Register output = ToRegister(lir->output()); |
20747 | |
20748 | masm.loadObjProto(obj, output); |
20749 | |
20750 | #ifdef DEBUG1 |
20751 | // We shouldn't encounter a null or lazy proto. |
20752 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20752); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 20752; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20753 | |
20754 | Label done; |
20755 | masm.branchPtr(Assembler::Above, output, ImmWord(1), &done); |
20756 | masm.assumeUnreachable("Unexpected null or lazy proto in MObjectStaticProto"); |
20757 | masm.bind(&done); |
20758 | #endif |
20759 | } |
20760 | |
20761 | void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) { |
20762 | pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind()))); |
20763 | |
20764 | using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind); |
20765 | callVM<Fn, js::BuiltinObjectOperation>(lir); |
20766 | } |
20767 | |
20768 | void CodeGenerator::visitSuperFunction(LSuperFunction* lir) { |
20769 | Register callee = ToRegister(lir->callee()); |
20770 | ValueOperand out = ToOutValue(lir); |
20771 | Register temp = ToRegister(lir->temp0()); |
20772 | |
20773 | #ifdef DEBUG1 |
20774 | Label classCheckDone; |
20775 | masm.branchTestObjIsFunction(Assembler::Equal, callee, temp, callee, |
20776 | &classCheckDone); |
20777 | masm.assumeUnreachable("Unexpected non-JSFunction callee in JSOp::SuperFun"); |
20778 | masm.bind(&classCheckDone); |
20779 | #endif |
20780 | |
20781 | // Load prototype of callee |
20782 | masm.loadObjProto(callee, temp); |
20783 | |
20784 | #ifdef DEBUG1 |
20785 | // We won't encounter a lazy proto, because |callee| is guaranteed to be a |
20786 | // JSFunction and only proxy objects can have a lazy proto. |
20787 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20787); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 20787; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
20788 | |
20789 | Label proxyCheckDone; |
20790 | masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone); |
20791 | masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperFun"); |
20792 | masm.bind(&proxyCheckDone); |
20793 | #endif |
20794 | |
20795 | Label nullProto, done; |
20796 | masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto); |
20797 | |
20798 | // Box prototype and return |
20799 | masm.tagValue(JSVAL_TYPE_OBJECT, temp, out); |
20800 | masm.jump(&done); |
20801 | |
20802 | masm.bind(&nullProto); |
20803 | masm.moveValue(NullValue(), out); |
20804 | |
20805 | masm.bind(&done); |
20806 | } |
20807 | |
20808 | void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) { |
20809 | Register func = ToRegister(lir->function()); |
20810 | ValueOperand homeObject = ToValue(lir, LInitHomeObject::HomeObjectIndex); |
20811 | |
20812 | masm.assertFunctionIsExtended(func); |
20813 | |
20814 | Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot()); |
20815 | |
20816 | emitPreBarrier(addr); |
20817 | masm.storeValue(homeObject, addr); |
20818 | } |
20819 | |
20820 | void CodeGenerator::visitIsTypedArrayConstructor( |
20821 | LIsTypedArrayConstructor* lir) { |
20822 | Register object = ToRegister(lir->object()); |
20823 | Register output = ToRegister(lir->output()); |
20824 | |
20825 | masm.setIsDefinitelyTypedArrayConstructor(object, output); |
20826 | } |
20827 | |
20828 | void CodeGenerator::visitLoadValueTag(LLoadValueTag* lir) { |
20829 | ValueOperand value = ToValue(lir, LLoadValueTag::ValueIndex); |
20830 | Register output = ToRegister(lir->output()); |
20831 | |
20832 | Register tag = masm.extractTag(value, output); |
20833 | if (tag != output) { |
20834 | masm.mov(tag, output); |
20835 | } |
20836 | } |
20837 | |
20838 | void CodeGenerator::visitGuardTagNotEqual(LGuardTagNotEqual* lir) { |
20839 | Register lhs = ToRegister(lir->lhs()); |
20840 | Register rhs = ToRegister(lir->rhs()); |
20841 | |
20842 | bailoutCmp32(Assembler::Equal, lhs, rhs, lir->snapshot()); |
20843 | |
20844 | // If both lhs and rhs are numbers, can't use tag comparison to do inequality |
20845 | // comparison |
20846 | Label done; |
20847 | masm.branchTestNumber(Assembler::NotEqual, lhs, &done); |
20848 | masm.branchTestNumber(Assembler::NotEqual, rhs, &done); |
20849 | bailout(lir->snapshot()); |
20850 | |
20851 | masm.bind(&done); |
20852 | } |
20853 | |
20854 | void CodeGenerator::visitLoadWrapperTarget(LLoadWrapperTarget* lir) { |
20855 | Register object = ToRegister(lir->object()); |
20856 | Register output = ToRegister(lir->output()); |
20857 | |
20858 | masm.loadPtr(Address(object, ProxyObject::offsetOfReservedSlots()), output); |
20859 | |
20860 | // Bail for revoked proxies. |
20861 | Label bail; |
20862 | Address targetAddr(output, |
20863 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()); |
20864 | if (lir->mir()->fallible()) { |
20865 | masm.fallibleUnboxObject(targetAddr, output, &bail); |
20866 | bailoutFrom(&bail, lir->snapshot()); |
20867 | } else { |
20868 | masm.unboxObject(targetAddr, output); |
20869 | } |
20870 | } |
20871 | |
20872 | void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) { |
20873 | Register object = ToRegister(lir->object()); |
20874 | Register temp0 = ToRegister(lir->temp0()); |
20875 | Register temp1 = ToRegister(lir->temp1()); |
20876 | Register temp2 = ToRegister(lir->temp2()); |
20877 | |
20878 | masm.movePropertyKey(lir->mir()->propId(), temp1); |
20879 | masm.movePtr(ImmGCPtr(lir->mir()->getterSetter()), temp2); |
20880 | |
20881 | using Fn = bool (*)(JSContext* cx, JSObject* obj, jsid id, |
20882 | GetterSetter* getterSetter); |
20883 | masm.setupAlignedABICall(); |
20884 | masm.loadJSContext(temp0); |
20885 | masm.passABIArg(temp0); |
20886 | masm.passABIArg(object); |
20887 | masm.passABIArg(temp1); |
20888 | masm.passABIArg(temp2); |
20889 | masm.callWithABI<Fn, ObjectHasGetterSetterPure>(); |
20890 | |
20891 | bailoutIfFalseBool(ReturnReg, lir->snapshot()); |
20892 | } |
20893 | |
20894 | void CodeGenerator::visitGuardIsExtensible(LGuardIsExtensible* lir) { |
20895 | Register object = ToRegister(lir->object()); |
20896 | Register temp = ToRegister(lir->temp0()); |
20897 | |
20898 | Label bail; |
20899 | masm.branchIfObjectNotExtensible(object, temp, &bail); |
20900 | bailoutFrom(&bail, lir->snapshot()); |
20901 | } |
20902 | |
20903 | void CodeGenerator::visitGuardInt32IsNonNegative( |
20904 | LGuardInt32IsNonNegative* lir) { |
20905 | Register index = ToRegister(lir->index()); |
20906 | |
20907 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
20908 | } |
20909 | |
20910 | void CodeGenerator::visitGuardInt32Range(LGuardInt32Range* lir) { |
20911 | Register input = ToRegister(lir->input()); |
20912 | |
20913 | bailoutCmp32(Assembler::LessThan, input, Imm32(lir->mir()->minimum()), |
20914 | lir->snapshot()); |
20915 | bailoutCmp32(Assembler::GreaterThan, input, Imm32(lir->mir()->maximum()), |
20916 | lir->snapshot()); |
20917 | } |
20918 | |
20919 | void CodeGenerator::visitGuardIndexIsNotDenseElement( |
20920 | LGuardIndexIsNotDenseElement* lir) { |
20921 | Register object = ToRegister(lir->object()); |
20922 | Register index = ToRegister(lir->index()); |
20923 | Register temp = ToRegister(lir->temp0()); |
20924 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
20925 | |
20926 | // Load obj->elements. |
20927 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
20928 | |
20929 | // Ensure index >= initLength or the element is a hole. |
20930 | Label notDense; |
20931 | Address capacity(temp, ObjectElements::offsetOfInitializedLength()); |
20932 | masm.spectreBoundsCheck32(index, capacity, spectreTemp, ¬Dense); |
20933 | |
20934 | BaseValueIndex element(temp, index); |
20935 | masm.branchTestMagic(Assembler::Equal, element, ¬Dense); |
20936 | |
20937 | bailout(lir->snapshot()); |
20938 | |
20939 | masm.bind(¬Dense); |
20940 | } |
20941 | |
20942 | void CodeGenerator::visitGuardIndexIsValidUpdateOrAdd( |
20943 | LGuardIndexIsValidUpdateOrAdd* lir) { |
20944 | Register object = ToRegister(lir->object()); |
20945 | Register index = ToRegister(lir->index()); |
20946 | Register temp = ToRegister(lir->temp0()); |
20947 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
20948 | |
20949 | // Load obj->elements. |
20950 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
20951 | |
20952 | Label success; |
20953 | |
20954 | // If length is writable, branch to &success. All indices are writable. |
20955 | Address flags(temp, ObjectElements::offsetOfFlags()); |
20956 | masm.branchTest32(Assembler::Zero, flags, |
20957 | Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH), |
20958 | &success); |
20959 | |
20960 | // Otherwise, ensure index is in bounds. |
20961 | Label bail; |
20962 | Address length(temp, ObjectElements::offsetOfLength()); |
20963 | masm.spectreBoundsCheck32(index, length, spectreTemp, &bail); |
20964 | masm.bind(&success); |
20965 | |
20966 | bailoutFrom(&bail, lir->snapshot()); |
20967 | } |
20968 | |
20969 | void CodeGenerator::visitCallAddOrUpdateSparseElement( |
20970 | LCallAddOrUpdateSparseElement* lir) { |
20971 | Register object = ToRegister(lir->object()); |
20972 | Register index = ToRegister(lir->index()); |
20973 | ValueOperand value = ToValue(lir, LCallAddOrUpdateSparseElement::ValueIndex); |
20974 | |
20975 | pushArg(Imm32(lir->mir()->strict())); |
20976 | pushArg(value); |
20977 | pushArg(index); |
20978 | pushArg(object); |
20979 | |
20980 | using Fn = |
20981 | bool (*)(JSContext*, Handle<NativeObject*>, int32_t, HandleValue, bool); |
20982 | callVM<Fn, js::AddOrUpdateSparseElementHelper>(lir); |
20983 | } |
20984 | |
20985 | void CodeGenerator::visitCallGetSparseElement(LCallGetSparseElement* lir) { |
20986 | Register object = ToRegister(lir->object()); |
20987 | Register index = ToRegister(lir->index()); |
20988 | |
20989 | pushArg(index); |
20990 | pushArg(object); |
20991 | |
20992 | using Fn = |
20993 | bool (*)(JSContext*, Handle<NativeObject*>, int32_t, MutableHandleValue); |
20994 | callVM<Fn, js::GetSparseElementHelper>(lir); |
20995 | } |
20996 | |
20997 | void CodeGenerator::visitCallNativeGetElement(LCallNativeGetElement* lir) { |
20998 | Register object = ToRegister(lir->object()); |
20999 | Register index = ToRegister(lir->index()); |
21000 | |
21001 | pushArg(index); |
21002 | pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(object))); |
21003 | pushArg(object); |
21004 | |
21005 | using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t, |
21006 | MutableHandleValue); |
21007 | callVM<Fn, js::NativeGetElement>(lir); |
21008 | } |
21009 | |
21010 | void CodeGenerator::visitCallNativeGetElementSuper( |
21011 | LCallNativeGetElementSuper* lir) { |
21012 | Register object = ToRegister(lir->object()); |
21013 | Register index = ToRegister(lir->index()); |
21014 | ValueOperand receiver = |
21015 | ToValue(lir, LCallNativeGetElementSuper::ReceiverIndex); |
21016 | |
21017 | pushArg(index); |
21018 | pushArg(receiver); |
21019 | pushArg(object); |
21020 | |
21021 | using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t, |
21022 | MutableHandleValue); |
21023 | callVM<Fn, js::NativeGetElement>(lir); |
21024 | } |
21025 | |
21026 | void CodeGenerator::visitCallObjectHasSparseElement( |
21027 | LCallObjectHasSparseElement* lir) { |
21028 | Register object = ToRegister(lir->object()); |
21029 | Register index = ToRegister(lir->index()); |
21030 | Register temp0 = ToRegister(lir->temp0()); |
21031 | Register temp1 = ToRegister(lir->temp1()); |
21032 | Register output = ToRegister(lir->output()); |
21033 | |
21034 | masm.reserveStack(sizeof(Value)); |
21035 | masm.moveStackPtrTo(temp1); |
21036 | |
21037 | using Fn = bool (*)(JSContext*, NativeObject*, int32_t, Value*); |
21038 | masm.setupAlignedABICall(); |
21039 | masm.loadJSContext(temp0); |
21040 | masm.passABIArg(temp0); |
21041 | masm.passABIArg(object); |
21042 | masm.passABIArg(index); |
21043 | masm.passABIArg(temp1); |
21044 | masm.callWithABI<Fn, HasNativeElementPure>(); |
21045 | masm.storeCallPointerResult(temp0); |
21046 | |
21047 | Label bail, ok; |
21048 | uint32_t framePushed = masm.framePushed(); |
21049 | masm.branchIfTrueBool(temp0, &ok); |
21050 | masm.adjustStack(sizeof(Value)); |
21051 | masm.jump(&bail); |
21052 | |
21053 | masm.bind(&ok); |
21054 | masm.setFramePushed(framePushed); |
21055 | masm.unboxBoolean(Address(masm.getStackPointer(), 0), output); |
21056 | masm.adjustStack(sizeof(Value)); |
21057 | |
21058 | bailoutFrom(&bail, lir->snapshot()); |
21059 | } |
21060 | |
21061 | void CodeGenerator::visitBigIntAsIntN(LBigIntAsIntN* ins) { |
21062 | Register bits = ToRegister(ins->bits()); |
21063 | Register input = ToRegister(ins->input()); |
21064 | |
21065 | pushArg(bits); |
21066 | pushArg(input); |
21067 | |
21068 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t); |
21069 | callVM<Fn, jit::BigIntAsIntN>(ins); |
21070 | } |
21071 | |
21072 | void CodeGenerator::visitBigIntAsIntN64(LBigIntAsIntN64* ins) { |
21073 | Register input = ToRegister(ins->input()); |
21074 | Register temp = ToRegister(ins->temp()); |
21075 | Register64 temp64 = ToRegister64(ins->temp64()); |
21076 | Register output = ToRegister(ins->output()); |
21077 | |
21078 | Label done, create; |
21079 | |
21080 | masm.movePtr(input, output); |
21081 | |
21082 | // Load the BigInt value as an int64. |
21083 | masm.loadBigInt64(input, temp64); |
21084 | |
21085 | // Create a new BigInt when the input exceeds the int64 range. |
21086 | masm.branch32(Assembler::Above, Address(input, BigInt::offsetOfLength()), |
21087 | Imm32(64 / BigInt::DigitBits), &create); |
21088 | |
21089 | // And create a new BigInt when the value and the BigInt have different signs. |
21090 | Label nonNegative; |
21091 | masm.branchIfBigIntIsNonNegative(input, &nonNegative); |
21092 | masm.branchTest64(Assembler::NotSigned, temp64, temp64, temp, &create); |
21093 | masm.jump(&done); |
21094 | |
21095 | masm.bind(&nonNegative); |
21096 | masm.branchTest64(Assembler::NotSigned, temp64, temp64, temp, &done); |
21097 | |
21098 | masm.bind(&create); |
21099 | emitCreateBigInt(ins, Scalar::BigInt64, temp64, output, temp); |
21100 | |
21101 | masm.bind(&done); |
21102 | } |
21103 | |
21104 | void CodeGenerator::visitBigIntAsIntN32(LBigIntAsIntN32* ins) { |
21105 | Register input = ToRegister(ins->input()); |
21106 | Register temp = ToRegister(ins->temp()); |
21107 | Register64 temp64 = ToRegister64(ins->temp64()); |
21108 | Register output = ToRegister(ins->output()); |
21109 | |
21110 | Label done, create; |
21111 | |
21112 | masm.movePtr(input, output); |
21113 | |
21114 | // Load the absolute value of the first digit. |
21115 | masm.loadFirstBigIntDigitOrZero(input, temp); |
21116 | |
21117 | // If the absolute value exceeds the int32 range, create a new BigInt. |
21118 | masm.branchPtr(Assembler::Above, temp, Imm32(INT32_MAX(2147483647)), &create); |
21119 | |
21120 | // Also create a new BigInt if we have more than one digit. |
21121 | masm.branch32(Assembler::BelowOrEqual, |
21122 | Address(input, BigInt::offsetOfLength()), Imm32(1), &done); |
21123 | |
21124 | masm.bind(&create); |
21125 | |
21126 | // |temp| stores the absolute value, negate it when the sign flag is set. |
21127 | Label nonNegative; |
21128 | masm.branchIfBigIntIsNonNegative(input, &nonNegative); |
21129 | masm.negPtr(temp); |
21130 | masm.bind(&nonNegative); |
21131 | |
21132 | masm.move32To64SignExtend(temp, temp64); |
21133 | emitCreateBigInt(ins, Scalar::BigInt64, temp64, output, temp); |
21134 | |
21135 | masm.bind(&done); |
21136 | } |
21137 | |
21138 | void CodeGenerator::visitBigIntAsUintN(LBigIntAsUintN* ins) { |
21139 | Register bits = ToRegister(ins->bits()); |
21140 | Register input = ToRegister(ins->input()); |
21141 | |
21142 | pushArg(bits); |
21143 | pushArg(input); |
21144 | |
21145 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t); |
21146 | callVM<Fn, jit::BigIntAsUintN>(ins); |
21147 | } |
21148 | |
21149 | void CodeGenerator::visitBigIntAsUintN64(LBigIntAsUintN64* ins) { |
21150 | Register input = ToRegister(ins->input()); |
21151 | Register temp = ToRegister(ins->temp()); |
21152 | Register64 temp64 = ToRegister64(ins->temp64()); |
21153 | Register output = ToRegister(ins->output()); |
21154 | |
21155 | Label done, create; |
21156 | |
21157 | masm.movePtr(input, output); |
21158 | |
21159 | // Load the BigInt value as an uint64. |
21160 | masm.loadBigInt64(input, temp64); |
21161 | |
21162 | // Create a new BigInt when the input exceeds the uint64 range. |
21163 | masm.branch32(Assembler::Above, Address(input, BigInt::offsetOfLength()), |
21164 | Imm32(64 / BigInt::DigitBits), &create); |
21165 | |
21166 | // And create a new BigInt when the input has the sign flag set. |
21167 | masm.branchIfBigIntIsNonNegative(input, &done); |
21168 | |
21169 | masm.bind(&create); |
21170 | emitCreateBigInt(ins, Scalar::BigUint64, temp64, output, temp); |
21171 | |
21172 | masm.bind(&done); |
21173 | } |
21174 | |
21175 | void CodeGenerator::visitBigIntAsUintN32(LBigIntAsUintN32* ins) { |
21176 | Register input = ToRegister(ins->input()); |
21177 | Register temp = ToRegister(ins->temp()); |
21178 | Register64 temp64 = ToRegister64(ins->temp64()); |
21179 | Register output = ToRegister(ins->output()); |
21180 | |
21181 | Label done, create; |
21182 | |
21183 | masm.movePtr(input, output); |
21184 | |
21185 | // Load the absolute value of the first digit. |
21186 | masm.loadFirstBigIntDigitOrZero(input, temp); |
21187 | |
21188 | // If the absolute value exceeds the uint32 range, create a new BigInt. |
21189 | #if JS_PUNBOX641 |
21190 | masm.branchPtr(Assembler::Above, temp, ImmWord(UINT32_MAX(4294967295U)), &create); |
21191 | #endif |
21192 | |
21193 | // Also create a new BigInt if we have more than one digit. |
21194 | masm.branch32(Assembler::Above, Address(input, BigInt::offsetOfLength()), |
21195 | Imm32(1), &create); |
21196 | |
21197 | // And create a new BigInt when the input has the sign flag set. |
21198 | masm.branchIfBigIntIsNonNegative(input, &done); |
21199 | |
21200 | masm.bind(&create); |
21201 | |
21202 | // |temp| stores the absolute value, negate it when the sign flag is set. |
21203 | Label nonNegative; |
21204 | masm.branchIfBigIntIsNonNegative(input, &nonNegative); |
21205 | masm.negPtr(temp); |
21206 | masm.bind(&nonNegative); |
21207 | |
21208 | masm.move32To64ZeroExtend(temp, temp64); |
21209 | emitCreateBigInt(ins, Scalar::BigUint64, temp64, output, temp); |
21210 | |
21211 | masm.bind(&done); |
21212 | } |
21213 | |
21214 | void CodeGenerator::visitGuardNonGCThing(LGuardNonGCThing* ins) { |
21215 | ValueOperand input = ToValue(ins, LGuardNonGCThing::InputIndex); |
21216 | |
21217 | Label bail; |
21218 | masm.branchTestGCThing(Assembler::Equal, input, &bail); |
21219 | bailoutFrom(&bail, ins->snapshot()); |
21220 | } |
21221 | |
21222 | void CodeGenerator::visitToHashableNonGCThing(LToHashableNonGCThing* ins) { |
21223 | ValueOperand input = ToValue(ins, LToHashableNonGCThing::InputIndex); |
21224 | FloatRegister tempFloat = ToFloatRegister(ins->temp0()); |
21225 | ValueOperand output = ToOutValue(ins); |
21226 | |
21227 | masm.toHashableNonGCThing(input, output, tempFloat); |
21228 | } |
21229 | |
21230 | void CodeGenerator::visitToHashableString(LToHashableString* ins) { |
21231 | Register input = ToRegister(ins->input()); |
21232 | Register output = ToRegister(ins->output()); |
21233 | |
21234 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
21235 | auto* ool = oolCallVM<Fn, js::AtomizeString>(ins, ArgList(input), |
21236 | StoreRegisterTo(output)); |
21237 | |
21238 | Label isAtom; |
21239 | masm.branchTest32(Assembler::NonZero, |
21240 | Address(input, JSString::offsetOfFlags()), |
21241 | Imm32(JSString::ATOM_BIT), &isAtom); |
21242 | |
21243 | masm.tryFastAtomize(input, output, output, ool->entry()); |
21244 | masm.jump(ool->rejoin()); |
21245 | masm.bind(&isAtom); |
21246 | masm.movePtr(input, output); |
21247 | masm.bind(ool->rejoin()); |
21248 | } |
21249 | |
21250 | void CodeGenerator::visitToHashableValue(LToHashableValue* ins) { |
21251 | ValueOperand input = ToValue(ins, LToHashableValue::InputIndex); |
21252 | FloatRegister tempFloat = ToFloatRegister(ins->temp0()); |
21253 | ValueOperand output = ToOutValue(ins); |
21254 | |
21255 | Register str = output.scratchReg(); |
21256 | |
21257 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
21258 | auto* ool = |
21259 | oolCallVM<Fn, js::AtomizeString>(ins, ArgList(str), StoreRegisterTo(str)); |
21260 | |
21261 | masm.toHashableValue(input, output, tempFloat, ool->entry(), ool->rejoin()); |
21262 | } |
21263 | |
21264 | void CodeGenerator::visitHashNonGCThing(LHashNonGCThing* ins) { |
21265 | ValueOperand input = ToValue(ins, LHashNonGCThing::InputIndex); |
21266 | Register temp = ToRegister(ins->temp0()); |
21267 | Register output = ToRegister(ins->output()); |
21268 | |
21269 | masm.prepareHashNonGCThing(input, output, temp); |
21270 | } |
21271 | |
21272 | void CodeGenerator::visitHashString(LHashString* ins) { |
21273 | Register input = ToRegister(ins->input()); |
21274 | Register temp = ToRegister(ins->temp0()); |
21275 | Register output = ToRegister(ins->output()); |
21276 | |
21277 | masm.prepareHashString(input, output, temp); |
21278 | } |
21279 | |
21280 | void CodeGenerator::visitHashSymbol(LHashSymbol* ins) { |
21281 | Register input = ToRegister(ins->input()); |
21282 | Register output = ToRegister(ins->output()); |
21283 | |
21284 | masm.prepareHashSymbol(input, output); |
21285 | } |
21286 | |
21287 | void CodeGenerator::visitHashBigInt(LHashBigInt* ins) { |
21288 | Register input = ToRegister(ins->input()); |
21289 | Register temp0 = ToRegister(ins->temp0()); |
21290 | Register temp1 = ToRegister(ins->temp1()); |
21291 | Register temp2 = ToRegister(ins->temp2()); |
21292 | Register output = ToRegister(ins->output()); |
21293 | |
21294 | masm.prepareHashBigInt(input, output, temp0, temp1, temp2); |
21295 | } |
21296 | |
21297 | void CodeGenerator::visitHashObject(LHashObject* ins) { |
21298 | Register setObj = ToRegister(ins->setObject()); |
21299 | ValueOperand input = ToValue(ins, LHashObject::InputIndex); |
21300 | Register temp0 = ToRegister(ins->temp0()); |
21301 | Register temp1 = ToRegister(ins->temp1()); |
21302 | Register temp2 = ToRegister(ins->temp2()); |
21303 | Register temp3 = ToRegister(ins->temp3()); |
21304 | Register output = ToRegister(ins->output()); |
21305 | |
21306 | masm.prepareHashObject(setObj, input, output, temp0, temp1, temp2, temp3); |
21307 | } |
21308 | |
21309 | void CodeGenerator::visitHashValue(LHashValue* ins) { |
21310 | Register setObj = ToRegister(ins->setObject()); |
21311 | ValueOperand input = ToValue(ins, LHashValue::InputIndex); |
21312 | Register temp0 = ToRegister(ins->temp0()); |
21313 | Register temp1 = ToRegister(ins->temp1()); |
21314 | Register temp2 = ToRegister(ins->temp2()); |
21315 | Register temp3 = ToRegister(ins->temp3()); |
21316 | Register output = ToRegister(ins->output()); |
21317 | |
21318 | masm.prepareHashValue(setObj, input, output, temp0, temp1, temp2, temp3); |
21319 | } |
21320 | |
21321 | void CodeGenerator::visitSetObjectHasNonBigInt(LSetObjectHasNonBigInt* ins) { |
21322 | Register setObj = ToRegister(ins->setObject()); |
21323 | ValueOperand input = ToValue(ins, LSetObjectHasNonBigInt::InputIndex); |
21324 | Register hash = ToRegister(ins->hash()); |
21325 | Register temp0 = ToRegister(ins->temp0()); |
21326 | Register temp1 = ToRegister(ins->temp1()); |
21327 | Register output = ToRegister(ins->output()); |
21328 | |
21329 | masm.setObjectHasNonBigInt(setObj, input, hash, output, temp0, temp1); |
21330 | } |
21331 | |
21332 | void CodeGenerator::visitSetObjectHasBigInt(LSetObjectHasBigInt* ins) { |
21333 | Register setObj = ToRegister(ins->setObject()); |
21334 | ValueOperand input = ToValue(ins, LSetObjectHasBigInt::InputIndex); |
21335 | Register hash = ToRegister(ins->hash()); |
21336 | Register temp0 = ToRegister(ins->temp0()); |
21337 | Register temp1 = ToRegister(ins->temp1()); |
21338 | Register temp2 = ToRegister(ins->temp2()); |
21339 | Register temp3 = ToRegister(ins->temp3()); |
21340 | Register output = ToRegister(ins->output()); |
21341 | |
21342 | masm.setObjectHasBigInt(setObj, input, hash, output, temp0, temp1, temp2, |
21343 | temp3); |
21344 | } |
21345 | |
21346 | void CodeGenerator::visitSetObjectHasValue(LSetObjectHasValue* ins) { |
21347 | Register setObj = ToRegister(ins->setObject()); |
21348 | ValueOperand input = ToValue(ins, LSetObjectHasValue::InputIndex); |
21349 | Register hash = ToRegister(ins->hash()); |
21350 | Register temp0 = ToRegister(ins->temp0()); |
21351 | Register temp1 = ToRegister(ins->temp1()); |
21352 | Register temp2 = ToRegister(ins->temp2()); |
21353 | Register temp3 = ToRegister(ins->temp3()); |
21354 | Register output = ToRegister(ins->output()); |
21355 | |
21356 | masm.setObjectHasValue(setObj, input, hash, output, temp0, temp1, temp2, |
21357 | temp3); |
21358 | } |
21359 | |
21360 | void CodeGenerator::visitSetObjectHasValueVMCall( |
21361 | LSetObjectHasValueVMCall* ins) { |
21362 | pushArg(ToValue(ins, LSetObjectHasValueVMCall::InputIndex)); |
21363 | pushArg(ToRegister(ins->setObject())); |
21364 | |
21365 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*); |
21366 | callVM<Fn, jit::SetObjectHas>(ins); |
21367 | } |
21368 | |
21369 | void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) { |
21370 | Register setObj = ToRegister(ins->setObject()); |
21371 | Register output = ToRegister(ins->output()); |
21372 | |
21373 | masm.loadSetObjectSize(setObj, output); |
21374 | } |
21375 | |
21376 | void CodeGenerator::visitMapObjectHasNonBigInt(LMapObjectHasNonBigInt* ins) { |
21377 | Register mapObj = ToRegister(ins->mapObject()); |
21378 | ValueOperand input = ToValue(ins, LMapObjectHasNonBigInt::InputIndex); |
21379 | Register hash = ToRegister(ins->hash()); |
21380 | Register temp0 = ToRegister(ins->temp0()); |
21381 | Register temp1 = ToRegister(ins->temp1()); |
21382 | Register output = ToRegister(ins->output()); |
21383 | |
21384 | masm.mapObjectHasNonBigInt(mapObj, input, hash, output, temp0, temp1); |
21385 | } |
21386 | |
21387 | void CodeGenerator::visitMapObjectHasBigInt(LMapObjectHasBigInt* ins) { |
21388 | Register mapObj = ToRegister(ins->mapObject()); |
21389 | ValueOperand input = ToValue(ins, LMapObjectHasBigInt::InputIndex); |
21390 | Register hash = ToRegister(ins->hash()); |
21391 | Register temp0 = ToRegister(ins->temp0()); |
21392 | Register temp1 = ToRegister(ins->temp1()); |
21393 | Register temp2 = ToRegister(ins->temp2()); |
21394 | Register temp3 = ToRegister(ins->temp3()); |
21395 | Register output = ToRegister(ins->output()); |
21396 | |
21397 | masm.mapObjectHasBigInt(mapObj, input, hash, output, temp0, temp1, temp2, |
21398 | temp3); |
21399 | } |
21400 | |
21401 | void CodeGenerator::visitMapObjectHasValue(LMapObjectHasValue* ins) { |
21402 | Register mapObj = ToRegister(ins->mapObject()); |
21403 | ValueOperand input = ToValue(ins, LMapObjectHasValue::InputIndex); |
21404 | Register hash = ToRegister(ins->hash()); |
21405 | Register temp0 = ToRegister(ins->temp0()); |
21406 | Register temp1 = ToRegister(ins->temp1()); |
21407 | Register temp2 = ToRegister(ins->temp2()); |
21408 | Register temp3 = ToRegister(ins->temp3()); |
21409 | Register output = ToRegister(ins->output()); |
21410 | |
21411 | masm.mapObjectHasValue(mapObj, input, hash, output, temp0, temp1, temp2, |
21412 | temp3); |
21413 | } |
21414 | |
21415 | void CodeGenerator::visitMapObjectHasValueVMCall( |
21416 | LMapObjectHasValueVMCall* ins) { |
21417 | pushArg(ToValue(ins, LMapObjectHasValueVMCall::InputIndex)); |
21418 | pushArg(ToRegister(ins->mapObject())); |
21419 | |
21420 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*); |
21421 | callVM<Fn, jit::MapObjectHas>(ins); |
21422 | } |
21423 | |
21424 | void CodeGenerator::visitMapObjectGetNonBigInt(LMapObjectGetNonBigInt* ins) { |
21425 | Register mapObj = ToRegister(ins->mapObject()); |
21426 | ValueOperand input = ToValue(ins, LMapObjectGetNonBigInt::InputIndex); |
21427 | Register hash = ToRegister(ins->hash()); |
21428 | Register temp0 = ToRegister(ins->temp0()); |
21429 | Register temp1 = ToRegister(ins->temp1()); |
21430 | ValueOperand output = ToOutValue(ins); |
21431 | |
21432 | masm.mapObjectGetNonBigInt(mapObj, input, hash, output, temp0, temp1, |
21433 | output.scratchReg()); |
21434 | } |
21435 | |
21436 | void CodeGenerator::visitMapObjectGetBigInt(LMapObjectGetBigInt* ins) { |
21437 | Register mapObj = ToRegister(ins->mapObject()); |
21438 | ValueOperand input = ToValue(ins, LMapObjectGetBigInt::InputIndex); |
21439 | Register hash = ToRegister(ins->hash()); |
21440 | Register temp0 = ToRegister(ins->temp0()); |
21441 | Register temp1 = ToRegister(ins->temp1()); |
21442 | Register temp2 = ToRegister(ins->temp2()); |
21443 | Register temp3 = ToRegister(ins->temp3()); |
21444 | ValueOperand output = ToOutValue(ins); |
21445 | |
21446 | masm.mapObjectGetBigInt(mapObj, input, hash, output, temp0, temp1, temp2, |
21447 | temp3, output.scratchReg()); |
21448 | } |
21449 | |
21450 | void CodeGenerator::visitMapObjectGetValue(LMapObjectGetValue* ins) { |
21451 | Register mapObj = ToRegister(ins->mapObject()); |
21452 | ValueOperand input = ToValue(ins, LMapObjectGetValue::InputIndex); |
21453 | Register hash = ToRegister(ins->hash()); |
21454 | Register temp0 = ToRegister(ins->temp0()); |
21455 | Register temp1 = ToRegister(ins->temp1()); |
21456 | Register temp2 = ToRegister(ins->temp2()); |
21457 | Register temp3 = ToRegister(ins->temp3()); |
21458 | ValueOperand output = ToOutValue(ins); |
21459 | |
21460 | masm.mapObjectGetValue(mapObj, input, hash, output, temp0, temp1, temp2, |
21461 | temp3, output.scratchReg()); |
21462 | } |
21463 | |
21464 | void CodeGenerator::visitMapObjectGetValueVMCall( |
21465 | LMapObjectGetValueVMCall* ins) { |
21466 | pushArg(ToValue(ins, LMapObjectGetValueVMCall::InputIndex)); |
21467 | pushArg(ToRegister(ins->mapObject())); |
21468 | |
21469 | using Fn = |
21470 | bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue); |
21471 | callVM<Fn, jit::MapObjectGet>(ins); |
21472 | } |
21473 | |
21474 | void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) { |
21475 | Register mapObj = ToRegister(ins->mapObject()); |
21476 | Register output = ToRegister(ins->output()); |
21477 | |
21478 | masm.loadMapObjectSize(mapObj, output); |
21479 | } |
21480 | |
21481 | template <size_t NumDefs> |
21482 | void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) { |
21483 | wasm::JitCallStackArgVector stackArgs; |
21484 | masm.propagateOOM(stackArgs.reserve(lir->numOperands())); |
21485 | if (masm.oom()) { |
21486 | return; |
21487 | } |
21488 | |
21489 | MIonToWasmCall* mir = lir->mir(); |
21490 | const wasm::FuncExport& funcExport = mir->funcExport(); |
21491 | const wasm::FuncType& sig = |
21492 | mir->instance()->code().codeMeta().getFuncType(funcExport.funcIndex()); |
21493 | |
21494 | WasmABIArgGenerator abi; |
21495 | for (size_t i = 0; i < lir->numOperands(); i++) { |
21496 | MIRType argMir; |
21497 | switch (sig.args()[i].kind()) { |
21498 | case wasm::ValType::I32: |
21499 | case wasm::ValType::I64: |
21500 | case wasm::ValType::F32: |
21501 | case wasm::ValType::F64: |
21502 | argMir = sig.args()[i].toMIRType(); |
21503 | break; |
21504 | case wasm::ValType::V128: |
21505 | MOZ_CRASH("unexpected argument type when calling from ion to wasm")do { do { } while (false); MOZ_ReportCrash("" "unexpected argument type when calling from ion to wasm" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21505); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected argument type when calling from ion to wasm" ")"); do { *((volatile int*)__null) = 21505; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
21506 | case wasm::ValType::Ref: |
21507 | // temporarilyUnsupportedReftypeForEntry() restricts args to externref |
21508 | MOZ_RELEASE_ASSERT(sig.args()[i].refType().isExtern())do { static_assert( mozilla::detail::AssertionConditionType< decltype(sig.args()[i].refType().isExtern())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(sig.args()[i].refType().isExtern ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("sig.args()[i].refType().isExtern()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21508); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "sig.args()[i].refType().isExtern()" ")"); do { *((volatile int*)__null) = 21508; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21509 | // Argument is boxed on the JS side to an anyref, so passed as a |
21510 | // pointer here. |
21511 | argMir = sig.args()[i].toMIRType(); |
21512 | break; |
21513 | } |
21514 | |
21515 | ABIArg arg = abi.next(argMir); |
21516 | switch (arg.kind()) { |
21517 | case ABIArg::GPR: |
21518 | case ABIArg::FPU: { |
21519 | MOZ_ASSERT(ToAnyRegister(lir->getOperand(i)) == arg.reg())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToAnyRegister(lir->getOperand(i)) == arg.reg())> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToAnyRegister(lir->getOperand(i)) == arg.reg()))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToAnyRegister(lir->getOperand(i)) == arg.reg()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21519); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToAnyRegister(lir->getOperand(i)) == arg.reg()" ")"); do { *((volatile int*)__null) = 21519; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21520 | stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg()); |
21521 | break; |
21522 | } |
21523 | case ABIArg::Stack: { |
21524 | const LAllocation* larg = lir->getOperand(i); |
21525 | if (larg->isConstant()) { |
21526 | stackArgs.infallibleEmplaceBack(ToInt32(larg)); |
21527 | } else if (larg->isGeneralReg()) { |
21528 | stackArgs.infallibleEmplaceBack(ToRegister(larg)); |
21529 | } else if (larg->isFloatReg()) { |
21530 | stackArgs.infallibleEmplaceBack(ToFloatRegister(larg)); |
21531 | } else { |
21532 | // Always use the stack pointer here because GenerateDirectCallFromJit |
21533 | // depends on this. |
21534 | Address addr = ToAddress<BaseRegForAddress::SP>(larg); |
21535 | stackArgs.infallibleEmplaceBack(addr); |
21536 | } |
21537 | break; |
21538 | } |
21539 | #ifdef JS_CODEGEN_REGISTER_PAIR |
21540 | case ABIArg::GPR_PAIR: { |
21541 | MOZ_CRASH(do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21542); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls" ")"); do { *((volatile int*)__null) = 21542; __attribute__(( nomerge)) ::abort(); } while (false); } while (false) |
21542 | "no way to pass i64, and wasm uses hardfp for function calls")do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21542); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls" ")"); do { *((volatile int*)__null) = 21542; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
21543 | } |
21544 | #endif |
21545 | case ABIArg::Uninitialized: { |
21546 | MOZ_CRASH("Uninitialized ABIArg kind")do { do { } while (false); MOZ_ReportCrash("" "Uninitialized ABIArg kind" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21546); AnnotateMozCrashReason("MOZ_CRASH(" "Uninitialized ABIArg kind" ")"); do { *((volatile int*)__null) = 21546; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
21547 | } |
21548 | } |
21549 | } |
21550 | |
21551 | const wasm::ValTypeVector& results = sig.results(); |
21552 | if (results.length() == 0) { |
21553 | MOZ_ASSERT(lir->mir()->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Value)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Value))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Value" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21553); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value" ")"); do { *((volatile int*)__null) = 21553; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21554 | } else { |
21555 | MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented")do { static_assert( mozilla::detail::AssertionConditionType< decltype(results.length() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(results.length() == 1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("results.length() == 1" " (" "multi-value return unimplemented" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21555); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 1" ") (" "multi-value return unimplemented" ")"); do { *((volatile int*)__null) = 21555; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
21556 | switch (results[0].kind()) { |
21557 | case wasm::ValType::I32: |
21558 | MOZ_ASSERT(lir->mir()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Int32)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21558); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int32" ")"); do { *((volatile int*)__null) = 21558; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21559 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21559); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 21559; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21560 | break; |
21561 | case wasm::ValType::I64: |
21562 | MOZ_ASSERT(lir->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Int64)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21562); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 21562; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21563 | MOZ_ASSERT(ToOutRegister64(lir) == ReturnReg64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutRegister64(lir) == ReturnReg64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutRegister64(lir) == ReturnReg64 ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutRegister64(lir) == ReturnReg64", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21563); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutRegister64(lir) == ReturnReg64" ")"); do { *((volatile int*)__null) = 21563; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21564 | break; |
21565 | case wasm::ValType::F32: |
21566 | MOZ_ASSERT(lir->mir()->type() == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Float32)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Float32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Float32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32" ")"); do { *((volatile int*)__null) = 21566; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21567 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnFloat32Reg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnFloat32Reg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnFloat32Reg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnFloat32Reg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg" ")"); do { *((volatile int*)__null) = 21567; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21568 | break; |
21569 | case wasm::ValType::F64: |
21570 | MOZ_ASSERT(lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Double)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Double))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Double" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double" ")"); do { *((volatile int*)__null) = 21570; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21571 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 21571; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21572 | break; |
21573 | case wasm::ValType::V128: |
21574 | MOZ_CRASH("unexpected return type when calling from ion to wasm")do { do { } while (false); MOZ_ReportCrash("" "unexpected return type when calling from ion to wasm" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21574); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected return type when calling from ion to wasm" ")"); do { *((volatile int*)__null) = 21574; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
21575 | case wasm::ValType::Ref: |
21576 | // The wasm stubs layer unboxes anything that needs to be unboxed |
21577 | // and leaves it in a Value. A FuncRef/EqRef we could in principle |
21578 | // leave it as a raw object pointer but for now it complicates the |
21579 | // API to do so. |
21580 | MOZ_ASSERT(lir->mir()->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Value)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Value))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Value" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value" ")"); do { *((volatile int*)__null) = 21580; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21581 | break; |
21582 | } |
21583 | } |
21584 | |
21585 | WasmInstanceObject* instObj = lir->mir()->instanceObject(); |
21586 | |
21587 | Register scratch = ToRegister(lir->temp()); |
21588 | |
21589 | uint32_t callOffset; |
21590 | ensureOsiSpace(); |
21591 | GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs, |
21592 | scratch, &callOffset); |
21593 | |
21594 | // Add the instance object to the constant pool, so it is transferred to |
21595 | // the owning IonScript and so that it gets traced as long as the IonScript |
21596 | // lives. |
21597 | |
21598 | uint32_t unused; |
21599 | masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused)); |
21600 | |
21601 | markSafepointAt(callOffset, lir); |
21602 | } |
21603 | |
21604 | void CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) { |
21605 | emitIonToWasmCallBase(lir); |
21606 | } |
21607 | void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) { |
21608 | emitIonToWasmCallBase(lir); |
21609 | } |
21610 | void CodeGenerator::visitIonToWasmCallI64(LIonToWasmCallI64* lir) { |
21611 | emitIonToWasmCallBase(lir); |
21612 | } |
21613 | |
21614 | void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) { |
21615 | masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output())); |
21616 | } |
21617 | |
21618 | void CodeGenerator::visitWasmFence(LWasmFence* lir) { |
21619 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21619); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 21619; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21620 | masm.memoryBarrier(MembarFull); |
21621 | } |
21622 | |
21623 | void CodeGenerator::visitWasmAnyRefFromJSValue(LWasmAnyRefFromJSValue* lir) { |
21624 | ValueOperand input = ToValue(lir, LWasmAnyRefFromJSValue::InputIndex); |
21625 | Register output = ToRegister(lir->output()); |
21626 | FloatRegister tempFloat = ToFloatRegister(lir->temp0()); |
21627 | |
21628 | using Fn = JSObject* (*)(JSContext* cx, HandleValue value); |
21629 | OutOfLineCode* oolBoxValue = oolCallVM<Fn, wasm::AnyRef::boxValue>( |
21630 | lir, ArgList(input), StoreRegisterTo(output)); |
21631 | masm.convertValueToWasmAnyRef(input, output, tempFloat, oolBoxValue->entry()); |
21632 | masm.bind(oolBoxValue->rejoin()); |
21633 | } |
21634 | |
21635 | void CodeGenerator::visitWasmAnyRefFromJSObject(LWasmAnyRefFromJSObject* lir) { |
21636 | Register input = ToRegister(lir->input()); |
21637 | Register output = ToRegister(lir->output()); |
21638 | masm.convertObjectToWasmAnyRef(input, output); |
21639 | } |
21640 | |
21641 | void CodeGenerator::visitWasmAnyRefFromJSString(LWasmAnyRefFromJSString* lir) { |
21642 | Register input = ToRegister(lir->input()); |
21643 | Register output = ToRegister(lir->output()); |
21644 | masm.convertStringToWasmAnyRef(input, output); |
21645 | } |
21646 | |
21647 | void CodeGenerator::visitWasmNewI31Ref(LWasmNewI31Ref* lir) { |
21648 | if (lir->value()->isConstant()) { |
21649 | // i31ref are often created with constants. If that's the case we will |
21650 | // do the operation statically here. This is similar to what is done |
21651 | // in masm.truncate32ToWasmI31Ref. |
21652 | Register output = ToRegister(lir->output()); |
21653 | uint32_t value = |
21654 | static_cast<uint32_t>(lir->value()->toConstant()->toInt32()); |
21655 | uintptr_t ptr = wasm::AnyRef::fromUint32Truncate(value).rawValue(); |
21656 | masm.movePtr(ImmWord(ptr), output); |
21657 | } else { |
21658 | Register value = ToRegister(lir->value()); |
21659 | Register output = ToRegister(lir->output()); |
21660 | masm.truncate32ToWasmI31Ref(value, output); |
21661 | } |
21662 | } |
21663 | |
21664 | void CodeGenerator::visitWasmI31RefGet(LWasmI31RefGet* lir) { |
21665 | Register value = ToRegister(lir->value()); |
21666 | Register output = ToRegister(lir->output()); |
21667 | if (lir->mir()->wideningOp() == wasm::FieldWideningOp::Signed) { |
21668 | masm.convertWasmI31RefTo32Signed(value, output); |
21669 | } else { |
21670 | masm.convertWasmI31RefTo32Unsigned(value, output); |
21671 | } |
21672 | } |
21673 | |
21674 | #ifdef FUZZING_JS_FUZZILLI |
21675 | void CodeGenerator::emitFuzzilliHashObject(LInstruction* lir, Register obj, |
21676 | Register output) { |
21677 | using Fn = void (*)(JSContext* cx, JSObject* obj, uint32_t* out); |
21678 | OutOfLineCode* ool = oolCallVM<Fn, FuzzilliHashObjectInl>( |
21679 | lir, ArgList(obj), StoreRegisterTo(output)); |
21680 | |
21681 | masm.jump(ool->entry()); |
21682 | masm.bind(ool->rejoin()); |
21683 | } |
21684 | |
21685 | void CodeGenerator::emitFuzzilliHashBigInt(LInstruction* lir, Register bigInt, |
21686 | Register output) { |
21687 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
21688 | volatileRegs.takeUnchecked(output); |
21689 | |
21690 | masm.PushRegsInMask(volatileRegs); |
21691 | |
21692 | using Fn = uint32_t (*)(BigInt* bigInt); |
21693 | masm.setupUnalignedABICall(output); |
21694 | masm.passABIArg(bigInt); |
21695 | masm.callWithABI<Fn, js::FuzzilliHashBigInt>(); |
21696 | masm.storeCallInt32Result(output); |
21697 | |
21698 | masm.PopRegsInMask(volatileRegs); |
21699 | } |
21700 | |
21701 | void CodeGenerator::visitFuzzilliHashV(LFuzzilliHashV* ins) { |
21702 | ValueOperand value = ToValue(ins, 0); |
21703 | |
21704 | FloatRegister scratchFloat = ToFloatRegister(ins->getTemp(1)); |
21705 | Register scratch = ToRegister(ins->getTemp(0)); |
21706 | Register output = ToRegister(ins->output()); |
21707 | MOZ_ASSERT(scratch != output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch != output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(scratch != output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch != output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21707); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output" ")"); do { *((volatile int*)__null) = 21707; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21708 | |
21709 | Label hashDouble, done; |
21710 | |
21711 | Label isInt32, isDouble, isNull, isUndefined, isBoolean, isBigInt, isObject; |
21712 | { |
21713 | ScratchTagScope tag(masm, value); |
21714 | masm.splitTagForTest(value, tag); |
21715 | |
21716 | masm.branchTestInt32(Assembler::Equal, tag, &isInt32); |
21717 | masm.branchTestDouble(Assembler::Equal, tag, &isDouble); |
21718 | masm.branchTestNull(Assembler::Equal, tag, &isNull); |
21719 | masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined); |
21720 | masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean); |
21721 | masm.branchTestBigInt(Assembler::Equal, tag, &isBigInt); |
21722 | masm.branchTestObject(Assembler::Equal, tag, &isObject); |
21723 | |
21724 | // Symbol or String. |
21725 | masm.move32(Imm32(0), output); |
21726 | masm.jump(&done); |
21727 | } |
21728 | |
21729 | masm.bind(&isInt32); |
21730 | { |
21731 | masm.unboxInt32(value, scratch); |
21732 | masm.convertInt32ToDouble(scratch, scratchFloat); |
21733 | masm.jump(&hashDouble); |
21734 | } |
21735 | |
21736 | masm.bind(&isDouble); |
21737 | { |
21738 | masm.unboxDouble(value, scratchFloat); |
21739 | masm.jump(&hashDouble); |
21740 | } |
21741 | |
21742 | masm.bind(&isNull); |
21743 | { |
21744 | masm.loadConstantDouble(1.0, scratchFloat); |
21745 | masm.jump(&hashDouble); |
21746 | } |
21747 | |
21748 | masm.bind(&isUndefined); |
21749 | { |
21750 | masm.loadConstantDouble(2.0, scratchFloat); |
21751 | masm.jump(&hashDouble); |
21752 | } |
21753 | |
21754 | masm.bind(&isBoolean); |
21755 | { |
21756 | masm.unboxBoolean(value, scratch); |
21757 | masm.add32(Imm32(3), scratch); |
21758 | masm.convertInt32ToDouble(scratch, scratchFloat); |
21759 | masm.jump(&hashDouble); |
21760 | } |
21761 | |
21762 | masm.bind(&isBigInt); |
21763 | { |
21764 | masm.unboxBigInt(value, scratch); |
21765 | emitFuzzilliHashBigInt(ins, scratch, output); |
21766 | masm.jump(&done); |
21767 | } |
21768 | |
21769 | masm.bind(&isObject); |
21770 | { |
21771 | masm.unboxObject(value, scratch); |
21772 | emitFuzzilliHashObject(ins, scratch, output); |
21773 | masm.jump(&done); |
21774 | } |
21775 | |
21776 | masm.bind(&hashDouble); |
21777 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
21778 | |
21779 | masm.bind(&done); |
21780 | } |
21781 | |
21782 | void CodeGenerator::visitFuzzilliHashT(LFuzzilliHashT* ins) { |
21783 | const LAllocation* value = ins->value(); |
21784 | MIRType mirType = ins->mir()->getOperand(0)->type(); |
21785 | |
21786 | Register scratch = ToTempRegisterOrInvalid(ins->getTemp(0)); |
21787 | FloatRegister scratchFloat = ToTempFloatRegisterOrInvalid(ins->getTemp(1)); |
21788 | |
21789 | Register output = ToRegister(ins->output()); |
21790 | MOZ_ASSERT(scratch != output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch != output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(scratch != output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch != output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21790); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output" ")"); do { *((volatile int*)__null) = 21790; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
21791 | |
21792 | switch (mirType) { |
21793 | case MIRType::Undefined: { |
21794 | masm.loadConstantDouble(2.0, scratchFloat); |
21795 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
21796 | break; |
21797 | } |
21798 | |
21799 | case MIRType::Null: { |
21800 | masm.loadConstantDouble(1.0, scratchFloat); |
21801 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
21802 | break; |
21803 | } |
21804 | |
21805 | case MIRType::Int32: { |
21806 | masm.move32(ToRegister(value), scratch); |
21807 | masm.convertInt32ToDouble(scratch, scratchFloat); |
21808 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
21809 | break; |
21810 | } |
21811 | |
21812 | case MIRType::Double: { |
21813 | masm.moveDouble(ToFloatRegister(value), scratchFloat); |
21814 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
21815 | break; |
21816 | } |
21817 | |
21818 | case MIRType::Float32: { |
21819 | masm.convertFloat32ToDouble(ToFloatRegister(value), scratchFloat); |
21820 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
21821 | break; |
21822 | } |
21823 | |
21824 | case MIRType::Boolean: { |
21825 | masm.move32(ToRegister(value), scratch); |
21826 | masm.add32(Imm32(3), scratch); |
21827 | masm.convertInt32ToDouble(scratch, scratchFloat); |
21828 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
21829 | break; |
21830 | } |
21831 | |
21832 | case MIRType::BigInt: { |
21833 | emitFuzzilliHashBigInt(ins, ToRegister(value), output); |
21834 | break; |
21835 | } |
21836 | |
21837 | case MIRType::Object: { |
21838 | emitFuzzilliHashObject(ins, ToRegister(value), output); |
21839 | break; |
21840 | } |
21841 | |
21842 | default: |
21843 | MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21843); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type" ")"); do { *((volatile int*)__null) = 21843; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
21844 | } |
21845 | } |
21846 | |
21847 | void CodeGenerator::visitFuzzilliHashStore(LFuzzilliHashStore* ins) { |
21848 | Register value = ToRegister(ins->value()); |
21849 | Register temp0 = ToRegister(ins->getTemp(0)); |
21850 | Register temp1 = ToRegister(ins->getTemp(1)); |
21851 | |
21852 | masm.fuzzilliStoreHash(value, temp0, temp1); |
21853 | } |
21854 | #endif |
21855 | |
21856 | static_assert(!std::is_polymorphic_v<CodeGenerator>, |
21857 | "CodeGenerator should not have any virtual methods"); |
21858 | |
21859 | } // namespace jit |
21860 | } // namespace js |