| File: | var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp |
| Warning: | line 12828, column 8 Value stored to 'extractObject' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
| 2 | * vim: set ts=8 sts=2 et sw=2 tw=80: |
| 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 6 | |
| 7 | #include "jit/CodeGenerator.h" |
| 8 | |
| 9 | #include "mozilla/Assertions.h" |
| 10 | #include "mozilla/Casting.h" |
| 11 | #include "mozilla/DebugOnly.h" |
| 12 | #include "mozilla/EndianUtils.h" |
| 13 | #include "mozilla/EnumeratedArray.h" |
| 14 | #include "mozilla/EnumeratedRange.h" |
| 15 | #include "mozilla/EnumSet.h" |
| 16 | #include "mozilla/IntegerTypeTraits.h" |
| 17 | #include "mozilla/Latin1.h" |
| 18 | #include "mozilla/MathAlgorithms.h" |
| 19 | #include "mozilla/ScopeExit.h" |
| 20 | #include "mozilla/SIMD.h" |
| 21 | |
| 22 | #include <limits> |
| 23 | #include <type_traits> |
| 24 | #include <utility> |
| 25 | |
| 26 | #include "jslibmath.h" |
| 27 | #include "jsmath.h" |
| 28 | #include "jsnum.h" |
| 29 | |
| 30 | #include "builtin/MapObject.h" |
| 31 | #include "builtin/RegExp.h" |
| 32 | #include "builtin/String.h" |
| 33 | #include "irregexp/RegExpTypes.h" |
| 34 | #include "jit/ABIArgGenerator.h" |
| 35 | #include "jit/CompileInfo.h" |
| 36 | #include "jit/InlineScriptTree.h" |
| 37 | #include "jit/Invalidation.h" |
| 38 | #include "jit/IonGenericCallStub.h" |
| 39 | #include "jit/IonIC.h" |
| 40 | #include "jit/IonScript.h" |
| 41 | #include "jit/JitcodeMap.h" |
| 42 | #include "jit/JitFrames.h" |
| 43 | #include "jit/JitRuntime.h" |
| 44 | #include "jit/JitSpewer.h" |
| 45 | #include "jit/JitZone.h" |
| 46 | #include "jit/Linker.h" |
| 47 | #include "jit/MIRGenerator.h" |
| 48 | #include "jit/MoveEmitter.h" |
| 49 | #include "jit/RangeAnalysis.h" |
| 50 | #include "jit/RegExpStubConstants.h" |
| 51 | #include "jit/SafepointIndex.h" |
| 52 | #include "jit/SharedICHelpers.h" |
| 53 | #include "jit/SharedICRegisters.h" |
| 54 | #include "jit/VMFunctions.h" |
| 55 | #include "jit/WarpSnapshot.h" |
| 56 | #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin |
| 57 | #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter}CallArgs, JSJitMethodCallArgsTraits, JSJitInfo |
| 58 | #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration |
| 59 | #include "js/RegExpFlags.h" // JS::RegExpFlag |
| 60 | #include "js/ScalarType.h" // js::Scalar::Type |
| 61 | #include "proxy/DOMProxy.h" |
| 62 | #include "proxy/ScriptedProxyHandler.h" |
| 63 | #include "util/CheckedArithmetic.h" |
| 64 | #include "util/Unicode.h" |
| 65 | #include "vm/ArrayBufferViewObject.h" |
| 66 | #include "vm/AsyncFunction.h" |
| 67 | #include "vm/AsyncIteration.h" |
| 68 | #include "vm/BuiltinObjectKind.h" |
| 69 | #include "vm/FunctionFlags.h" // js::FunctionFlags |
| 70 | #include "vm/Interpreter.h" |
| 71 | #include "vm/JSAtomUtils.h" // AtomizeString |
| 72 | #include "vm/MatchPairs.h" |
| 73 | #include "vm/RegExpObject.h" |
| 74 | #include "vm/RegExpStatics.h" |
| 75 | #include "vm/StaticStrings.h" |
| 76 | #include "vm/StringObject.h" |
| 77 | #include "vm/StringType.h" |
| 78 | #include "vm/TypedArrayObject.h" |
| 79 | #include "wasm/WasmCodegenConstants.h" |
| 80 | #include "wasm/WasmPI.h" |
| 81 | #include "wasm/WasmValType.h" |
| 82 | #ifdef MOZ_VTUNE1 |
| 83 | # include "vtune/VTuneWrapper.h" |
| 84 | #endif |
| 85 | #include "wasm/WasmBinary.h" |
| 86 | #include "wasm/WasmGC.h" |
| 87 | #include "wasm/WasmGcObject.h" |
| 88 | #include "wasm/WasmStubs.h" |
| 89 | |
| 90 | #include "builtin/Boolean-inl.h" |
| 91 | #include "jit/MacroAssembler-inl.h" |
| 92 | #include "jit/shared/CodeGenerator-shared-inl.h" |
| 93 | #include "jit/TemplateObject-inl.h" |
| 94 | #include "jit/VMFunctionList-inl.h" |
| 95 | #include "vm/BytecodeUtil-inl.h" |
| 96 | #include "vm/JSScript-inl.h" |
| 97 | #include "wasm/WasmInstance-inl.h" |
| 98 | |
| 99 | using namespace js; |
| 100 | using namespace js::jit; |
| 101 | |
| 102 | using mozilla::CheckedUint32; |
| 103 | using mozilla::DebugOnly; |
| 104 | using mozilla::FloatingPoint; |
| 105 | using mozilla::NegativeInfinity; |
| 106 | using mozilla::PositiveInfinity; |
| 107 | |
| 108 | using JS::ExpandoAndGeneration; |
| 109 | |
| 110 | namespace js { |
| 111 | namespace jit { |
| 112 | |
| 113 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
| 114 | template <class Op> |
| 115 | static void HandleRegisterDump(Op op, MacroAssembler& masm, |
| 116 | LiveRegisterSet liveRegs, Register activation, |
| 117 | Register scratch) { |
| 118 | const size_t baseOffset = JitActivation::offsetOfRegs(); |
| 119 | |
| 120 | // Handle live GPRs. |
| 121 | for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) { |
| 122 | Register reg = *iter; |
| 123 | Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
| 124 | |
| 125 | if (reg == activation) { |
| 126 | // To use the original value of the activation register (that's |
| 127 | // now on top of the stack), we need the scratch register. |
| 128 | masm.push(scratch); |
| 129 | masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch); |
| 130 | op(scratch, dump); |
| 131 | masm.pop(scratch); |
| 132 | } else { |
| 133 | op(reg, dump); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | // Handle live FPRs. |
| 138 | for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) { |
| 139 | FloatRegister reg = *iter; |
| 140 | Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
| 141 | op(reg, dump); |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | class StoreOp { |
| 146 | MacroAssembler& masm; |
| 147 | |
| 148 | public: |
| 149 | explicit StoreOp(MacroAssembler& masm) : masm(masm) {} |
| 150 | |
| 151 | void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); } |
| 152 | void operator()(FloatRegister reg, Address dump) { |
| 153 | if (reg.isDouble()) { |
| 154 | masm.storeDouble(reg, dump); |
| 155 | } else if (reg.isSingle()) { |
| 156 | masm.storeFloat32(reg, dump); |
| 157 | } else if (reg.isSimd128()) { |
| 158 | MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 158); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { *((volatile int*)__null) = 158; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 159 | } else { |
| 160 | MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 160); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type." ")"); do { *((volatile int*)__null) = 160; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 161 | } |
| 162 | } |
| 163 | }; |
| 164 | |
| 165 | class VerifyOp { |
| 166 | MacroAssembler& masm; |
| 167 | Label* failure_; |
| 168 | |
| 169 | public: |
| 170 | VerifyOp(MacroAssembler& masm, Label* failure) |
| 171 | : masm(masm), failure_(failure) {} |
| 172 | |
| 173 | void operator()(Register reg, Address dump) { |
| 174 | masm.branchPtr(Assembler::NotEqual, dump, reg, failure_); |
| 175 | } |
| 176 | void operator()(FloatRegister reg, Address dump) { |
| 177 | if (reg.isDouble()) { |
| 178 | ScratchDoubleScope scratch(masm); |
| 179 | masm.loadDouble(dump, scratch); |
| 180 | masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_); |
| 181 | } else if (reg.isSingle()) { |
| 182 | ScratchFloat32Scope scratch(masm); |
| 183 | masm.loadFloat32(dump, scratch); |
| 184 | masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_); |
| 185 | } else if (reg.isSimd128()) { |
| 186 | MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 186); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { *((volatile int*)__null) = 186; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 187 | } else { |
| 188 | MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 188); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected register type." ")"); do { *((volatile int*)__null) = 188; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 189 | } |
| 190 | } |
| 191 | }; |
| 192 | |
| 193 | void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) { |
| 194 | // Ensure the live registers stored by callVM did not change between |
| 195 | // the call and this OsiPoint. Try-catch relies on this invariant. |
| 196 | |
| 197 | // Load pointer to the JitActivation in a scratch register. |
| 198 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 199 | Register scratch = allRegs.takeAny(); |
| 200 | masm.push(scratch); |
| 201 | masm.loadJitActivation(scratch); |
| 202 | |
| 203 | // If we should not check registers (because the instruction did not call |
| 204 | // into the VM, or a GC happened), we're done. |
| 205 | Label failure, done; |
| 206 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
| 207 | masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done); |
| 208 | |
| 209 | // Having more than one VM function call made in one visit function at |
| 210 | // runtime is a sec-ciritcal error, because if we conservatively assume that |
| 211 | // one of the function call can re-enter Ion, then the invalidation process |
| 212 | // will potentially add a call at a random location, by patching the code |
| 213 | // before the return address. |
| 214 | masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure); |
| 215 | |
| 216 | // Set checkRegs to 0, so that we don't try to verify registers after we |
| 217 | // return from this script to the caller. |
| 218 | masm.store32(Imm32(0), checkRegs); |
| 219 | |
| 220 | // Ignore clobbered registers. Some instructions (like LValueToInt32) modify |
| 221 | // temps after calling into the VM. This is fine because no other |
| 222 | // instructions (including this OsiPoint) will depend on them. Also |
| 223 | // backtracking can also use the same register for an input and an output. |
| 224 | // These are marked as clobbered and shouldn't get checked. |
| 225 | LiveRegisterSet liveRegs; |
| 226 | liveRegs.set() = RegisterSet::Intersect( |
| 227 | safepoint->liveRegs().set(), |
| 228 | RegisterSet::Not(safepoint->clobberedRegs().set())); |
| 229 | |
| 230 | VerifyOp op(masm, &failure); |
| 231 | HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
| 232 | |
| 233 | masm.jump(&done); |
| 234 | |
| 235 | // Do not profile the callWithABI that occurs below. This is to avoid a |
| 236 | // rare corner case that occurs when profiling interacts with itself: |
| 237 | // |
| 238 | // When slow profiling assertions are turned on, FunctionBoundary ops |
| 239 | // (which update the profiler pseudo-stack) may emit a callVM, which |
| 240 | // forces them to have an osi point associated with them. The |
| 241 | // FunctionBoundary for inline function entry is added to the caller's |
| 242 | // graph with a PC from the caller's code, but during codegen it modifies |
| 243 | // Gecko Profiler instrumentation to add the callee as the current top-most |
| 244 | // script. When codegen gets to the OSIPoint, and the callWithABI below is |
| 245 | // emitted, the codegen thinks that the current frame is the callee, but |
| 246 | // the PC it's using from the OSIPoint refers to the caller. This causes |
| 247 | // the profiler instrumentation of the callWithABI below to ASSERT, since |
| 248 | // the script and pc are mismatched. To avoid this, we simply omit |
| 249 | // instrumentation for these callWithABIs. |
| 250 | |
| 251 | // Any live register captured by a safepoint (other than temp registers) |
| 252 | // must remain unchanged between the call and the OsiPoint instruction. |
| 253 | masm.bind(&failure); |
| 254 | masm.assumeUnreachable("Modified registers between VM call and OsiPoint"); |
| 255 | |
| 256 | masm.bind(&done); |
| 257 | masm.pop(scratch); |
| 258 | } |
| 259 | |
| 260 | bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) { |
| 261 | if (!checkOsiPointRegisters) { |
| 262 | return false; |
| 263 | } |
| 264 | |
| 265 | if (safepoint->liveRegs().emptyGeneral() && |
| 266 | safepoint->liveRegs().emptyFloat()) { |
| 267 | return false; // No registers to check. |
| 268 | } |
| 269 | |
| 270 | return true; |
| 271 | } |
| 272 | |
| 273 | void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) { |
| 274 | if (!shouldVerifyOsiPointRegs(safepoint)) { |
| 275 | return; |
| 276 | } |
| 277 | |
| 278 | // Set checkRegs to 0. If we perform a VM call, the instruction |
| 279 | // will set it to 1. |
| 280 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 281 | Register scratch = allRegs.takeAny(); |
| 282 | masm.push(scratch); |
| 283 | masm.loadJitActivation(scratch); |
| 284 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
| 285 | masm.store32(Imm32(0), checkRegs); |
| 286 | masm.pop(scratch); |
| 287 | } |
| 288 | |
| 289 | static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) { |
| 290 | // Store a copy of all live registers before performing the call. |
| 291 | // When we reach the OsiPoint, we can use this to check nothing |
| 292 | // modified them in the meantime. |
| 293 | |
| 294 | // Load pointer to the JitActivation in a scratch register. |
| 295 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 296 | Register scratch = allRegs.takeAny(); |
| 297 | masm.push(scratch); |
| 298 | masm.loadJitActivation(scratch); |
| 299 | |
| 300 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
| 301 | masm.add32(Imm32(1), checkRegs); |
| 302 | |
| 303 | StoreOp op(masm); |
| 304 | HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
| 305 | |
| 306 | masm.pop(scratch); |
| 307 | } |
| 308 | #endif // CHECK_OSIPOINT_REGISTERS |
| 309 | |
| 310 | // Before doing any call to Cpp, you should ensure that volatile |
| 311 | // registers are evicted by the register allocator. |
| 312 | void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) { |
| 313 | TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id); |
| 314 | const VMFunctionData& fun = GetVMFunction(id); |
| 315 | |
| 316 | // Stack is: |
| 317 | // ... frame ... |
| 318 | // [args] |
| 319 | #ifdef DEBUG1 |
| 320 | MOZ_ASSERT(pushedArgs_ == fun.explicitArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(pushedArgs_ == fun.explicitArgs)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pushedArgs_ == fun.explicitArgs ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pushedArgs_ == fun.explicitArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 320); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs" ")"); do { *((volatile int*)__null) = 320; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 321 | pushedArgs_ = 0; |
| 322 | #endif |
| 323 | |
| 324 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
| 325 | if (shouldVerifyOsiPointRegs(ins->safepoint())) { |
| 326 | StoreAllLiveRegs(masm, ins->safepoint()->liveRegs()); |
| 327 | } |
| 328 | #endif |
| 329 | |
| 330 | #ifdef DEBUG1 |
| 331 | if (ins->mirRaw()) { |
| 332 | MOZ_ASSERT(ins->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mirRaw()->isInstruction())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mirRaw()->isInstruction ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ins->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 332); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()" ")"); do { *((volatile int*)__null) = 332; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 333 | MInstruction* mir = ins->mirRaw()->toInstruction(); |
| 334 | MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint())do { if (mir->needsResumePoint()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(mir->resumePoint ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mir->resumePoint()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("mir->resumePoint()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()" ")"); do { *((volatile int*)__null) = 334; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 335 | |
| 336 | // If this MIR instruction has an overridden AliasSet, set the JitRuntime's |
| 337 | // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call |
| 338 | // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because |
| 339 | // interrupt callbacks can call JS (chrome JS or shell testing functions). |
| 340 | bool isWhitelisted = mir->isInterruptCheck() || mir->isCheckOverRecursed(); |
| 341 | if (!mir->hasDefaultAliasSet() && !isWhitelisted) { |
| 342 | const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode(); |
| 343 | masm.move32(Imm32(1), ReturnReg); |
| 344 | masm.store32(ReturnReg, AbsoluteAddress(addr)); |
| 345 | } |
| 346 | } |
| 347 | #endif |
| 348 | |
| 349 | // Push an exit frame descriptor. |
| 350 | masm.PushFrameDescriptor(FrameType::IonJS); |
| 351 | |
| 352 | // Call the wrapper function. The wrapper is in charge to unwind the stack |
| 353 | // when returning from the call. Failures are handled with exceptions based |
| 354 | // on the return value of the C functions. To guard the outcome of the |
| 355 | // returned value, use another LIR instruction. |
| 356 | ensureOsiSpace(); |
| 357 | uint32_t callOffset = masm.callJit(code); |
| 358 | markSafepointAt(callOffset, ins); |
| 359 | |
| 360 | #ifdef DEBUG1 |
| 361 | // Reset the disallowArbitraryCode flag after the call. |
| 362 | { |
| 363 | const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode(); |
| 364 | masm.push(ReturnReg); |
| 365 | masm.move32(Imm32(0), ReturnReg); |
| 366 | masm.store32(ReturnReg, AbsoluteAddress(addr)); |
| 367 | masm.pop(ReturnReg); |
| 368 | } |
| 369 | #endif |
| 370 | |
| 371 | // Pop rest of the exit frame and the arguments left on the stack. |
| 372 | int framePop = |
| 373 | sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall(); |
| 374 | masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop); |
| 375 | |
| 376 | // Stack is: |
| 377 | // ... frame ... |
| 378 | } |
| 379 | |
| 380 | template <typename Fn, Fn fn> |
| 381 | void CodeGenerator::callVM(LInstruction* ins) { |
| 382 | VMFunctionId id = VMFunctionToId<Fn, fn>::id; |
| 383 | callVMInternal(id, ins); |
| 384 | } |
| 385 | |
| 386 | // ArgSeq store arguments for OutOfLineCallVM. |
| 387 | // |
| 388 | // OutOfLineCallVM are created with "oolCallVM" function. The third argument of |
| 389 | // this function is an instance of a class which provides a "generate" in charge |
| 390 | // of pushing the argument, with "pushArg", for a VMFunction. |
| 391 | // |
| 392 | // Such list of arguments can be created by using the "ArgList" function which |
| 393 | // creates one instance of "ArgSeq", where the type of the arguments are |
| 394 | // inferred from the type of the arguments. |
| 395 | // |
| 396 | // The list of arguments must be written in the same order as if you were |
| 397 | // calling the function in C++. |
| 398 | // |
| 399 | // Example: |
| 400 | // ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs())) |
| 401 | |
| 402 | template <typename... ArgTypes> |
| 403 | class ArgSeq { |
| 404 | std::tuple<std::remove_reference_t<ArgTypes>...> args_; |
| 405 | |
| 406 | template <std::size_t... ISeq> |
| 407 | inline void generate(CodeGenerator* codegen, |
| 408 | std::index_sequence<ISeq...>) const { |
| 409 | // Arguments are pushed in reverse order, from last argument to first |
| 410 | // argument. |
| 411 | (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...); |
| 412 | } |
| 413 | |
| 414 | public: |
| 415 | explicit ArgSeq(ArgTypes&&... args) |
| 416 | : args_(std::forward<ArgTypes>(args)...) {} |
| 417 | |
| 418 | inline void generate(CodeGenerator* codegen) const { |
| 419 | generate(codegen, std::index_sequence_for<ArgTypes...>{}); |
| 420 | } |
| 421 | |
| 422 | #ifdef DEBUG1 |
| 423 | static constexpr size_t numArgs = sizeof...(ArgTypes); |
| 424 | #endif |
| 425 | }; |
| 426 | |
| 427 | template <typename... ArgTypes> |
| 428 | inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) { |
| 429 | return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...); |
| 430 | } |
| 431 | |
| 432 | // Store wrappers, to generate the right move of data after the VM call. |
| 433 | |
| 434 | struct StoreNothing { |
| 435 | inline void generate(CodeGenerator* codegen) const {} |
| 436 | inline LiveRegisterSet clobbered() const { |
| 437 | return LiveRegisterSet(); // No register gets clobbered |
| 438 | } |
| 439 | }; |
| 440 | |
| 441 | class StoreRegisterTo { |
| 442 | private: |
| 443 | Register out_; |
| 444 | |
| 445 | public: |
| 446 | explicit StoreRegisterTo(Register out) : out_(out) {} |
| 447 | |
| 448 | inline void generate(CodeGenerator* codegen) const { |
| 449 | // It's okay to use storePointerResultTo here - the VMFunction wrapper |
| 450 | // ensures the upper bytes are zero for bool/int32 return values. |
| 451 | codegen->storePointerResultTo(out_); |
| 452 | } |
| 453 | inline LiveRegisterSet clobbered() const { |
| 454 | LiveRegisterSet set; |
| 455 | set.add(out_); |
| 456 | return set; |
| 457 | } |
| 458 | }; |
| 459 | |
| 460 | class StoreFloatRegisterTo { |
| 461 | private: |
| 462 | FloatRegister out_; |
| 463 | |
| 464 | public: |
| 465 | explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {} |
| 466 | |
| 467 | inline void generate(CodeGenerator* codegen) const { |
| 468 | codegen->storeFloatResultTo(out_); |
| 469 | } |
| 470 | inline LiveRegisterSet clobbered() const { |
| 471 | LiveRegisterSet set; |
| 472 | set.add(out_); |
| 473 | return set; |
| 474 | } |
| 475 | }; |
| 476 | |
| 477 | template <typename Output> |
| 478 | class StoreValueTo_ { |
| 479 | private: |
| 480 | Output out_; |
| 481 | |
| 482 | public: |
| 483 | explicit StoreValueTo_(const Output& out) : out_(out) {} |
| 484 | |
| 485 | inline void generate(CodeGenerator* codegen) const { |
| 486 | codegen->storeResultValueTo(out_); |
| 487 | } |
| 488 | inline LiveRegisterSet clobbered() const { |
| 489 | LiveRegisterSet set; |
| 490 | set.add(out_); |
| 491 | return set; |
| 492 | } |
| 493 | }; |
| 494 | |
| 495 | template <typename Output> |
| 496 | StoreValueTo_<Output> StoreValueTo(const Output& out) { |
| 497 | return StoreValueTo_<Output>(out); |
| 498 | } |
| 499 | |
| 500 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
| 501 | class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> { |
| 502 | private: |
| 503 | LInstruction* lir_; |
| 504 | ArgSeq args_; |
| 505 | StoreOutputTo out_; |
| 506 | |
| 507 | public: |
| 508 | OutOfLineCallVM(LInstruction* lir, const ArgSeq& args, |
| 509 | const StoreOutputTo& out) |
| 510 | : lir_(lir), args_(args), out_(out) {} |
| 511 | |
| 512 | void accept(CodeGenerator* codegen) override { |
| 513 | codegen->visitOutOfLineCallVM(this); |
| 514 | } |
| 515 | |
| 516 | LInstruction* lir() const { return lir_; } |
| 517 | const ArgSeq& args() const { return args_; } |
| 518 | const StoreOutputTo& out() const { return out_; } |
| 519 | }; |
| 520 | |
| 521 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
| 522 | OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args, |
| 523 | const StoreOutputTo& out) { |
| 524 | MOZ_ASSERT(lir->mirRaw())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mirRaw())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mirRaw()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mirRaw()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 524); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()" ")"); do { *((volatile int*)__null) = 524; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 525 | MOZ_ASSERT(lir->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mirRaw()->isInstruction())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mirRaw()->isInstruction ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mirRaw()->isInstruction()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 525); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()" ")"); do { *((volatile int*)__null) = 525; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 526 | |
| 527 | #ifdef DEBUG1 |
| 528 | VMFunctionId id = VMFunctionToId<Fn, fn>::id; |
| 529 | const VMFunctionData& fun = GetVMFunction(id); |
| 530 | MOZ_ASSERT(fun.explicitArgs == args.numArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.explicitArgs == args.numArgs)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.explicitArgs == args.numArgs ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "fun.explicitArgs == args.numArgs", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 530); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs" ")"); do { *((volatile int*)__null) = 530; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 531 | MOZ_ASSERT(fun.returnsData() !=do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo , StoreNothing>))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v <StoreOutputTo, StoreNothing>)))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" ")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 532 | (std::is_same_v<StoreOutputTo, StoreNothing>))do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun.returnsData() != (std::is_same_v<StoreOutputTo , StoreNothing>))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun.returnsData() != (std::is_same_v <StoreOutputTo, StoreNothing>)))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 532); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" ")"); do { *((volatile int*)__null) = 532; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 533 | #endif |
| 534 | |
| 535 | OutOfLineCode* ool = new (alloc()) |
| 536 | OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out); |
| 537 | addOutOfLineCode(ool, lir->mirRaw()->toInstruction()); |
| 538 | return ool; |
| 539 | } |
| 540 | |
| 541 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
| 542 | void CodeGenerator::visitOutOfLineCallVM( |
| 543 | OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) { |
| 544 | LInstruction* lir = ool->lir(); |
| 545 | |
| 546 | #ifdef JS_JITSPEW1 |
| 547 | JitSpewStart(JitSpew_Codegen, " # LIR=%s", |
| 548 | lir->opName()); |
| 549 | if (const char* extra = lir->getExtraName()) { |
| 550 | JitSpewCont(JitSpew_Codegen, ":%s", extra); |
| 551 | } |
| 552 | JitSpewFin(JitSpew_Codegen); |
| 553 | #endif |
| 554 | perfSpewer_.recordInstruction(masm, lir); |
| 555 | saveLive(lir); |
| 556 | ool->args().generate(this); |
| 557 | callVM<Fn, fn>(lir); |
| 558 | ool->out().generate(this); |
| 559 | restoreLiveIgnore(lir, ool->out().clobbered()); |
| 560 | masm.jump(ool->rejoin()); |
| 561 | } |
| 562 | |
| 563 | class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> { |
| 564 | private: |
| 565 | LInstruction* lir_; |
| 566 | size_t cacheIndex_; |
| 567 | size_t cacheInfoIndex_; |
| 568 | |
| 569 | public: |
| 570 | OutOfLineICFallback(LInstruction* lir, size_t cacheIndex, |
| 571 | size_t cacheInfoIndex) |
| 572 | : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {} |
| 573 | |
| 574 | void bind(MacroAssembler* masm) override { |
| 575 | // The binding of the initial jump is done in |
| 576 | // CodeGenerator::visitOutOfLineICFallback. |
| 577 | } |
| 578 | |
| 579 | size_t cacheIndex() const { return cacheIndex_; } |
| 580 | size_t cacheInfoIndex() const { return cacheInfoIndex_; } |
| 581 | LInstruction* lir() const { return lir_; } |
| 582 | |
| 583 | void accept(CodeGenerator* codegen) override { |
| 584 | codegen->visitOutOfLineICFallback(this); |
| 585 | } |
| 586 | }; |
| 587 | |
| 588 | void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) { |
| 589 | if (cacheIndex == SIZE_MAX(18446744073709551615UL)) { |
| 590 | masm.setOOM(); |
| 591 | return; |
| 592 | } |
| 593 | |
| 594 | DataPtr<IonIC> cache(this, cacheIndex); |
| 595 | MInstruction* mir = lir->mirRaw()->toInstruction(); |
| 596 | cache->setScriptedLocation(mir->block()->info().script(), |
| 597 | mir->resumePoint()->pc()); |
| 598 | |
| 599 | Register temp = cache->scratchRegisterForEntryJump(); |
| 600 | icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp); |
| 601 | masm.jump(Address(temp, 0)); |
| 602 | |
| 603 | MOZ_ASSERT(!icInfo_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!icInfo_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!icInfo_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!icInfo_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!icInfo_.empty()" ")"); do { *((volatile int*)__null) = 603; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 604 | |
| 605 | OutOfLineICFallback* ool = |
| 606 | new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1); |
| 607 | addOutOfLineCode(ool, mir); |
| 608 | |
| 609 | masm.bind(ool->rejoin()); |
| 610 | cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset())); |
| 611 | } |
| 612 | |
| 613 | void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) { |
| 614 | LInstruction* lir = ool->lir(); |
| 615 | size_t cacheIndex = ool->cacheIndex(); |
| 616 | size_t cacheInfoIndex = ool->cacheInfoIndex(); |
| 617 | |
| 618 | DataPtr<IonIC> ic(this, cacheIndex); |
| 619 | |
| 620 | // Register the location of the OOL path in the IC. |
| 621 | ic->setFallbackOffset(CodeOffset(masm.currentOffset())); |
| 622 | |
| 623 | switch (ic->kind()) { |
| 624 | case CacheKind::GetProp: |
| 625 | case CacheKind::GetElem: { |
| 626 | IonGetPropertyIC* getPropIC = ic->asGetPropertyIC(); |
| 627 | |
| 628 | saveLive(lir); |
| 629 | |
| 630 | pushArg(getPropIC->id()); |
| 631 | pushArg(getPropIC->value()); |
| 632 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 633 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 634 | |
| 635 | using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*, |
| 636 | HandleValue, HandleValue, MutableHandleValue); |
| 637 | callVM<Fn, IonGetPropertyIC::update>(lir); |
| 638 | |
| 639 | StoreValueTo(getPropIC->output()).generate(this); |
| 640 | restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered()); |
| 641 | |
| 642 | masm.jump(ool->rejoin()); |
| 643 | return; |
| 644 | } |
| 645 | case CacheKind::GetPropSuper: |
| 646 | case CacheKind::GetElemSuper: { |
| 647 | IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC(); |
| 648 | |
| 649 | saveLive(lir); |
| 650 | |
| 651 | pushArg(getPropSuperIC->id()); |
| 652 | pushArg(getPropSuperIC->receiver()); |
| 653 | pushArg(getPropSuperIC->object()); |
| 654 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 655 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 656 | |
| 657 | using Fn = |
| 658 | bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject, |
| 659 | HandleValue, HandleValue, MutableHandleValue); |
| 660 | callVM<Fn, IonGetPropSuperIC::update>(lir); |
| 661 | |
| 662 | StoreValueTo(getPropSuperIC->output()).generate(this); |
| 663 | restoreLiveIgnore(lir, |
| 664 | StoreValueTo(getPropSuperIC->output()).clobbered()); |
| 665 | |
| 666 | masm.jump(ool->rejoin()); |
| 667 | return; |
| 668 | } |
| 669 | case CacheKind::SetProp: |
| 670 | case CacheKind::SetElem: { |
| 671 | IonSetPropertyIC* setPropIC = ic->asSetPropertyIC(); |
| 672 | |
| 673 | saveLive(lir); |
| 674 | |
| 675 | pushArg(setPropIC->rhs()); |
| 676 | pushArg(setPropIC->id()); |
| 677 | pushArg(setPropIC->object()); |
| 678 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 679 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 680 | |
| 681 | using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*, |
| 682 | HandleObject, HandleValue, HandleValue); |
| 683 | callVM<Fn, IonSetPropertyIC::update>(lir); |
| 684 | |
| 685 | restoreLive(lir); |
| 686 | |
| 687 | masm.jump(ool->rejoin()); |
| 688 | return; |
| 689 | } |
| 690 | case CacheKind::GetName: { |
| 691 | IonGetNameIC* getNameIC = ic->asGetNameIC(); |
| 692 | |
| 693 | saveLive(lir); |
| 694 | |
| 695 | pushArg(getNameIC->environment()); |
| 696 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 697 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 698 | |
| 699 | using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject, |
| 700 | MutableHandleValue); |
| 701 | callVM<Fn, IonGetNameIC::update>(lir); |
| 702 | |
| 703 | StoreValueTo(getNameIC->output()).generate(this); |
| 704 | restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered()); |
| 705 | |
| 706 | masm.jump(ool->rejoin()); |
| 707 | return; |
| 708 | } |
| 709 | case CacheKind::BindName: { |
| 710 | IonBindNameIC* bindNameIC = ic->asBindNameIC(); |
| 711 | |
| 712 | saveLive(lir); |
| 713 | |
| 714 | pushArg(bindNameIC->environment()); |
| 715 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 716 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 717 | |
| 718 | using Fn = |
| 719 | JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject); |
| 720 | callVM<Fn, IonBindNameIC::update>(lir); |
| 721 | |
| 722 | StoreRegisterTo(bindNameIC->output()).generate(this); |
| 723 | restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered()); |
| 724 | |
| 725 | masm.jump(ool->rejoin()); |
| 726 | return; |
| 727 | } |
| 728 | case CacheKind::GetIterator: { |
| 729 | IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC(); |
| 730 | |
| 731 | saveLive(lir); |
| 732 | |
| 733 | pushArg(getIteratorIC->value()); |
| 734 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 735 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 736 | |
| 737 | using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*, |
| 738 | HandleValue); |
| 739 | callVM<Fn, IonGetIteratorIC::update>(lir); |
| 740 | |
| 741 | StoreRegisterTo(getIteratorIC->output()).generate(this); |
| 742 | restoreLiveIgnore(lir, |
| 743 | StoreRegisterTo(getIteratorIC->output()).clobbered()); |
| 744 | |
| 745 | masm.jump(ool->rejoin()); |
| 746 | return; |
| 747 | } |
| 748 | case CacheKind::OptimizeSpreadCall: { |
| 749 | auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC(); |
| 750 | |
| 751 | saveLive(lir); |
| 752 | |
| 753 | pushArg(optimizeSpreadCallIC->value()); |
| 754 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 755 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 756 | |
| 757 | using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*, |
| 758 | HandleValue, MutableHandleValue); |
| 759 | callVM<Fn, IonOptimizeSpreadCallIC::update>(lir); |
| 760 | |
| 761 | StoreValueTo(optimizeSpreadCallIC->output()).generate(this); |
| 762 | restoreLiveIgnore( |
| 763 | lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered()); |
| 764 | |
| 765 | masm.jump(ool->rejoin()); |
| 766 | return; |
| 767 | } |
| 768 | case CacheKind::In: { |
| 769 | IonInIC* inIC = ic->asInIC(); |
| 770 | |
| 771 | saveLive(lir); |
| 772 | |
| 773 | pushArg(inIC->object()); |
| 774 | pushArg(inIC->key()); |
| 775 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 776 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 777 | |
| 778 | using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue, |
| 779 | HandleObject, bool*); |
| 780 | callVM<Fn, IonInIC::update>(lir); |
| 781 | |
| 782 | StoreRegisterTo(inIC->output()).generate(this); |
| 783 | restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered()); |
| 784 | |
| 785 | masm.jump(ool->rejoin()); |
| 786 | return; |
| 787 | } |
| 788 | case CacheKind::HasOwn: { |
| 789 | IonHasOwnIC* hasOwnIC = ic->asHasOwnIC(); |
| 790 | |
| 791 | saveLive(lir); |
| 792 | |
| 793 | pushArg(hasOwnIC->id()); |
| 794 | pushArg(hasOwnIC->value()); |
| 795 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 796 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 797 | |
| 798 | using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue, |
| 799 | HandleValue, int32_t*); |
| 800 | callVM<Fn, IonHasOwnIC::update>(lir); |
| 801 | |
| 802 | StoreRegisterTo(hasOwnIC->output()).generate(this); |
| 803 | restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered()); |
| 804 | |
| 805 | masm.jump(ool->rejoin()); |
| 806 | return; |
| 807 | } |
| 808 | case CacheKind::CheckPrivateField: { |
| 809 | IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC(); |
| 810 | |
| 811 | saveLive(lir); |
| 812 | |
| 813 | pushArg(checkPrivateFieldIC->id()); |
| 814 | pushArg(checkPrivateFieldIC->value()); |
| 815 | |
| 816 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 817 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 818 | |
| 819 | using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*, |
| 820 | HandleValue, HandleValue, bool*); |
| 821 | callVM<Fn, IonCheckPrivateFieldIC::update>(lir); |
| 822 | |
| 823 | StoreRegisterTo(checkPrivateFieldIC->output()).generate(this); |
| 824 | restoreLiveIgnore( |
| 825 | lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered()); |
| 826 | |
| 827 | masm.jump(ool->rejoin()); |
| 828 | return; |
| 829 | } |
| 830 | case CacheKind::InstanceOf: { |
| 831 | IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC(); |
| 832 | |
| 833 | saveLive(lir); |
| 834 | |
| 835 | pushArg(hasInstanceOfIC->rhs()); |
| 836 | pushArg(hasInstanceOfIC->lhs()); |
| 837 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 838 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 839 | |
| 840 | using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*, |
| 841 | HandleValue lhs, HandleObject rhs, bool* res); |
| 842 | callVM<Fn, IonInstanceOfIC::update>(lir); |
| 843 | |
| 844 | StoreRegisterTo(hasInstanceOfIC->output()).generate(this); |
| 845 | restoreLiveIgnore(lir, |
| 846 | StoreRegisterTo(hasInstanceOfIC->output()).clobbered()); |
| 847 | |
| 848 | masm.jump(ool->rejoin()); |
| 849 | return; |
| 850 | } |
| 851 | case CacheKind::UnaryArith: { |
| 852 | IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC(); |
| 853 | |
| 854 | saveLive(lir); |
| 855 | |
| 856 | pushArg(unaryArithIC->input()); |
| 857 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 858 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 859 | |
| 860 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
| 861 | IonUnaryArithIC* stub, HandleValue val, |
| 862 | MutableHandleValue res); |
| 863 | callVM<Fn, IonUnaryArithIC::update>(lir); |
| 864 | |
| 865 | StoreValueTo(unaryArithIC->output()).generate(this); |
| 866 | restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered()); |
| 867 | |
| 868 | masm.jump(ool->rejoin()); |
| 869 | return; |
| 870 | } |
| 871 | case CacheKind::ToPropertyKey: { |
| 872 | IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC(); |
| 873 | |
| 874 | saveLive(lir); |
| 875 | |
| 876 | pushArg(toPropertyKeyIC->input()); |
| 877 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 878 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 879 | |
| 880 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
| 881 | IonToPropertyKeyIC* ic, HandleValue val, |
| 882 | MutableHandleValue res); |
| 883 | callVM<Fn, IonToPropertyKeyIC::update>(lir); |
| 884 | |
| 885 | StoreValueTo(toPropertyKeyIC->output()).generate(this); |
| 886 | restoreLiveIgnore(lir, |
| 887 | StoreValueTo(toPropertyKeyIC->output()).clobbered()); |
| 888 | |
| 889 | masm.jump(ool->rejoin()); |
| 890 | return; |
| 891 | } |
| 892 | case CacheKind::BinaryArith: { |
| 893 | IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC(); |
| 894 | |
| 895 | saveLive(lir); |
| 896 | |
| 897 | pushArg(binaryArithIC->rhs()); |
| 898 | pushArg(binaryArithIC->lhs()); |
| 899 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 900 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 901 | |
| 902 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
| 903 | IonBinaryArithIC* stub, HandleValue lhs, |
| 904 | HandleValue rhs, MutableHandleValue res); |
| 905 | callVM<Fn, IonBinaryArithIC::update>(lir); |
| 906 | |
| 907 | StoreValueTo(binaryArithIC->output()).generate(this); |
| 908 | restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered()); |
| 909 | |
| 910 | masm.jump(ool->rejoin()); |
| 911 | return; |
| 912 | } |
| 913 | case CacheKind::Compare: { |
| 914 | IonCompareIC* compareIC = ic->asCompareIC(); |
| 915 | |
| 916 | saveLive(lir); |
| 917 | |
| 918 | pushArg(compareIC->rhs()); |
| 919 | pushArg(compareIC->lhs()); |
| 920 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 921 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 922 | |
| 923 | using Fn = |
| 924 | bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub, |
| 925 | HandleValue lhs, HandleValue rhs, bool* res); |
| 926 | callVM<Fn, IonCompareIC::update>(lir); |
| 927 | |
| 928 | StoreRegisterTo(compareIC->output()).generate(this); |
| 929 | restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered()); |
| 930 | |
| 931 | masm.jump(ool->rejoin()); |
| 932 | return; |
| 933 | } |
| 934 | case CacheKind::CloseIter: { |
| 935 | IonCloseIterIC* closeIterIC = ic->asCloseIterIC(); |
| 936 | |
| 937 | saveLive(lir); |
| 938 | |
| 939 | pushArg(closeIterIC->iter()); |
| 940 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 941 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 942 | |
| 943 | using Fn = |
| 944 | bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject); |
| 945 | callVM<Fn, IonCloseIterIC::update>(lir); |
| 946 | |
| 947 | restoreLive(lir); |
| 948 | |
| 949 | masm.jump(ool->rejoin()); |
| 950 | return; |
| 951 | } |
| 952 | case CacheKind::OptimizeGetIterator: { |
| 953 | auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC(); |
| 954 | |
| 955 | saveLive(lir); |
| 956 | |
| 957 | pushArg(optimizeGetIteratorIC->value()); |
| 958 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 959 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 960 | |
| 961 | using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*, |
| 962 | HandleValue, bool* res); |
| 963 | callVM<Fn, IonOptimizeGetIteratorIC::update>(lir); |
| 964 | |
| 965 | StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this); |
| 966 | restoreLiveIgnore( |
| 967 | lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered()); |
| 968 | |
| 969 | masm.jump(ool->rejoin()); |
| 970 | return; |
| 971 | } |
| 972 | case CacheKind::Call: |
| 973 | case CacheKind::TypeOf: |
| 974 | case CacheKind::TypeOfEq: |
| 975 | case CacheKind::ToBool: |
| 976 | case CacheKind::LazyConstant: |
| 977 | case CacheKind::NewArray: |
| 978 | case CacheKind::NewObject: |
| 979 | case CacheKind::Lambda: |
| 980 | case CacheKind::GetImport: |
| 981 | MOZ_CRASH("Unsupported IC")do { do { } while (false); MOZ_ReportCrash("" "Unsupported IC" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 981); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported IC" ")" ); do { *((volatile int*)__null) = 981; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 982 | } |
| 983 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 983); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 983; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
| 984 | } |
| 985 | |
| 986 | StringObject* MNewStringObject::templateObj() const { |
| 987 | return &templateObj_->as<StringObject>(); |
| 988 | } |
| 989 | |
| 990 | CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph, |
| 991 | MacroAssembler* masm) |
| 992 | : CodeGeneratorSpecific(gen, graph, masm), |
| 993 | ionScriptLabels_(gen->alloc()), |
| 994 | ionNurseryObjectLabels_(gen->alloc()), |
| 995 | scriptCounts_(nullptr) {} |
| 996 | |
| 997 | CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); } |
| 998 | |
| 999 | void CodeGenerator::visitValueToNumberInt32(LValueToNumberInt32* lir) { |
| 1000 | ValueOperand operand = ToValue(lir, LValueToNumberInt32::InputIndex); |
| 1001 | Register output = ToRegister(lir->output()); |
| 1002 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 1003 | |
| 1004 | Label fails; |
| 1005 | masm.convertValueToInt32(operand, temp, output, &fails, |
| 1006 | lir->mir()->needsNegativeZeroCheck(), |
| 1007 | lir->mir()->conversion()); |
| 1008 | |
| 1009 | bailoutFrom(&fails, lir->snapshot()); |
| 1010 | } |
| 1011 | |
| 1012 | void CodeGenerator::visitValueTruncateToInt32(LValueTruncateToInt32* lir) { |
| 1013 | ValueOperand operand = ToValue(lir, LValueTruncateToInt32::InputIndex); |
| 1014 | Register output = ToRegister(lir->output()); |
| 1015 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 1016 | Register stringReg = ToRegister(lir->temp1()); |
| 1017 | |
| 1018 | auto* oolDouble = oolTruncateDouble(temp, output, lir->mir()); |
| 1019 | |
| 1020 | using Fn = bool (*)(JSContext*, JSString*, double*); |
| 1021 | auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg), |
| 1022 | StoreFloatRegisterTo(temp)); |
| 1023 | Label* stringEntry = oolString->entry(); |
| 1024 | Label* stringRejoin = oolString->rejoin(); |
| 1025 | |
| 1026 | Label fails; |
| 1027 | masm.truncateValueToInt32(operand, stringEntry, stringRejoin, |
| 1028 | oolDouble->entry(), stringReg, temp, output, |
| 1029 | &fails); |
| 1030 | masm.bind(oolDouble->rejoin()); |
| 1031 | |
| 1032 | bailoutFrom(&fails, lir->snapshot()); |
| 1033 | } |
| 1034 | |
| 1035 | void CodeGenerator::visitValueToDouble(LValueToDouble* lir) { |
| 1036 | ValueOperand operand = ToValue(lir, LValueToDouble::InputIndex); |
| 1037 | FloatRegister output = ToFloatRegister(lir->output()); |
| 1038 | |
| 1039 | Label fail; |
| 1040 | masm.convertValueToDouble(operand, output, &fail); |
| 1041 | bailoutFrom(&fail, lir->snapshot()); |
| 1042 | } |
| 1043 | |
| 1044 | void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) { |
| 1045 | ValueOperand operand = ToValue(lir, LValueToFloat32::InputIndex); |
| 1046 | FloatRegister output = ToFloatRegister(lir->output()); |
| 1047 | |
| 1048 | Label fail; |
| 1049 | masm.convertValueToFloat32(operand, output, &fail); |
| 1050 | bailoutFrom(&fail, lir->snapshot()); |
| 1051 | } |
| 1052 | |
| 1053 | void CodeGenerator::visitValueToFloat16(LValueToFloat16* lir) { |
| 1054 | ValueOperand operand = ToValue(lir, LValueToFloat16::InputIndex); |
| 1055 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 1056 | FloatRegister output = ToFloatRegister(lir->output()); |
| 1057 | |
| 1058 | LiveRegisterSet volatileRegs; |
| 1059 | if (!MacroAssembler::SupportsFloat64To16()) { |
| 1060 | volatileRegs = liveVolatileRegs(lir); |
| 1061 | } |
| 1062 | |
| 1063 | Label fail; |
| 1064 | masm.convertValueToFloat16(operand, output, temp, volatileRegs, &fail); |
| 1065 | bailoutFrom(&fail, lir->snapshot()); |
| 1066 | } |
| 1067 | |
| 1068 | void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) { |
| 1069 | ValueOperand operand = ToValue(lir, LValueToBigInt::InputIndex); |
| 1070 | Register output = ToRegister(lir->output()); |
| 1071 | |
| 1072 | using Fn = BigInt* (*)(JSContext*, HandleValue); |
| 1073 | auto* ool = |
| 1074 | oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output)); |
| 1075 | |
| 1076 | Register tag = masm.extractTag(operand, output); |
| 1077 | |
| 1078 | Label notBigInt, done; |
| 1079 | masm.branchTestBigInt(Assembler::NotEqual, tag, ¬BigInt); |
| 1080 | masm.unboxBigInt(operand, output); |
| 1081 | masm.jump(&done); |
| 1082 | masm.bind(¬BigInt); |
| 1083 | |
| 1084 | masm.branchTestBoolean(Assembler::Equal, tag, ool->entry()); |
| 1085 | masm.branchTestString(Assembler::Equal, tag, ool->entry()); |
| 1086 | |
| 1087 | // ToBigInt(object) can have side-effects; all other types throw a TypeError. |
| 1088 | bailout(lir->snapshot()); |
| 1089 | |
| 1090 | masm.bind(ool->rejoin()); |
| 1091 | masm.bind(&done); |
| 1092 | } |
| 1093 | |
| 1094 | void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) { |
| 1095 | masm.convertInt32ToDouble(ToRegister(lir->input()), |
| 1096 | ToFloatRegister(lir->output())); |
| 1097 | } |
| 1098 | |
| 1099 | void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) { |
| 1100 | masm.convertFloat32ToDouble(ToFloatRegister(lir->input()), |
| 1101 | ToFloatRegister(lir->output())); |
| 1102 | } |
| 1103 | |
| 1104 | void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) { |
| 1105 | masm.convertDoubleToFloat32(ToFloatRegister(lir->input()), |
| 1106 | ToFloatRegister(lir->output())); |
| 1107 | } |
| 1108 | |
| 1109 | void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) { |
| 1110 | masm.convertInt32ToFloat32(ToRegister(lir->input()), |
| 1111 | ToFloatRegister(lir->output())); |
| 1112 | } |
| 1113 | |
| 1114 | void CodeGenerator::visitDoubleToFloat16(LDoubleToFloat16* lir) { |
| 1115 | LiveRegisterSet volatileRegs; |
| 1116 | if (!MacroAssembler::SupportsFloat64To16()) { |
| 1117 | volatileRegs = liveVolatileRegs(lir); |
| 1118 | } |
| 1119 | masm.convertDoubleToFloat16( |
| 1120 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
| 1121 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
| 1122 | } |
| 1123 | |
| 1124 | void CodeGenerator::visitDoubleToFloat32ToFloat16( |
| 1125 | LDoubleToFloat32ToFloat16* lir) { |
| 1126 | masm.convertDoubleToFloat16( |
| 1127 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
| 1128 | ToRegister(lir->temp0()), ToRegister(lir->temp1())); |
| 1129 | } |
| 1130 | |
| 1131 | void CodeGenerator::visitFloat32ToFloat16(LFloat32ToFloat16* lir) { |
| 1132 | LiveRegisterSet volatileRegs; |
| 1133 | if (!MacroAssembler::SupportsFloat32To16()) { |
| 1134 | volatileRegs = liveVolatileRegs(lir); |
| 1135 | } |
| 1136 | masm.convertFloat32ToFloat16( |
| 1137 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
| 1138 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
| 1139 | } |
| 1140 | |
| 1141 | void CodeGenerator::visitInt32ToFloat16(LInt32ToFloat16* lir) { |
| 1142 | LiveRegisterSet volatileRegs; |
| 1143 | if (!MacroAssembler::SupportsFloat32To16()) { |
| 1144 | volatileRegs = liveVolatileRegs(lir); |
| 1145 | } |
| 1146 | masm.convertInt32ToFloat16( |
| 1147 | ToRegister(lir->input()), ToFloatRegister(lir->output()), |
| 1148 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
| 1149 | } |
| 1150 | |
| 1151 | void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) { |
| 1152 | Label fail; |
| 1153 | FloatRegister input = ToFloatRegister(lir->input()); |
| 1154 | Register output = ToRegister(lir->output()); |
| 1155 | masm.convertDoubleToInt32(input, output, &fail, |
| 1156 | lir->mir()->needsNegativeZeroCheck()); |
| 1157 | bailoutFrom(&fail, lir->snapshot()); |
| 1158 | } |
| 1159 | |
| 1160 | void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) { |
| 1161 | Label fail; |
| 1162 | FloatRegister input = ToFloatRegister(lir->input()); |
| 1163 | Register output = ToRegister(lir->output()); |
| 1164 | masm.convertFloat32ToInt32(input, output, &fail, |
| 1165 | lir->mir()->needsNegativeZeroCheck()); |
| 1166 | bailoutFrom(&fail, lir->snapshot()); |
| 1167 | } |
| 1168 | |
| 1169 | void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) { |
| 1170 | #ifdef JS_64BIT1 |
| 1171 | // This LIR instruction is only used if the input can be negative. |
| 1172 | MOZ_ASSERT(lir->mir()->canBeNegative())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->canBeNegative())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->canBeNegative ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->canBeNegative()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1172); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->canBeNegative()" ")"); do { *((volatile int*)__null) = 1172; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1173 | |
| 1174 | Register output = ToRegister(lir->output()); |
| 1175 | const LAllocation* input = lir->input(); |
| 1176 | if (input->isRegister()) { |
| 1177 | masm.move32SignExtendToPtr(ToRegister(input), output); |
| 1178 | } else { |
| 1179 | masm.load32SignExtendToPtr(ToAddress(input), output); |
| 1180 | } |
| 1181 | #else |
| 1182 | MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1182); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { *((volatile int*)__null) = 1182; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 1183 | #endif |
| 1184 | } |
| 1185 | |
| 1186 | void CodeGenerator::visitNonNegativeIntPtrToInt32( |
| 1187 | LNonNegativeIntPtrToInt32* lir) { |
| 1188 | #ifdef JS_64BIT1 |
| 1189 | Register output = ToRegister(lir->output()); |
| 1190 | MOZ_ASSERT(ToRegister(lir->input()) == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->input()) == output)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(ToRegister(lir->input()) == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->input()) == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1190); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"); do { *((volatile int*)__null) = 1190; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1191 | |
| 1192 | Label bail; |
| 1193 | masm.guardNonNegativeIntPtrToInt32(output, &bail); |
| 1194 | bailoutFrom(&bail, lir->snapshot()); |
| 1195 | #else |
| 1196 | MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1196); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { *((volatile int*)__null) = 1196; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 1197 | #endif |
| 1198 | } |
| 1199 | |
| 1200 | void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) { |
| 1201 | Register input = ToRegister(lir->input()); |
| 1202 | FloatRegister output = ToFloatRegister(lir->output()); |
| 1203 | masm.convertIntPtrToDouble(input, output); |
| 1204 | } |
| 1205 | |
| 1206 | void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) { |
| 1207 | Register output = ToRegister(lir->output()); |
| 1208 | MOZ_ASSERT(ToRegister(lir->input()) == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->input()) == output)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(ToRegister(lir->input()) == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->input()) == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1208); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")"); do { *((volatile int*)__null) = 1208; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1209 | |
| 1210 | uint32_t byteSize = lir->mir()->byteSize(); |
| 1211 | |
| 1212 | #ifdef DEBUG1 |
| 1213 | Label ok; |
| 1214 | masm.branchTestPtr(Assembler::NotSigned, output, output, &ok); |
| 1215 | masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength"); |
| 1216 | masm.bind(&ok); |
| 1217 | #endif |
| 1218 | |
| 1219 | Label bail; |
| 1220 | masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail); |
| 1221 | bailoutFrom(&bail, lir->snapshot()); |
| 1222 | } |
| 1223 | |
| 1224 | void CodeGenerator::emitOOLTestObject(Register objreg, |
| 1225 | Label* ifEmulatesUndefined, |
| 1226 | Label* ifDoesntEmulateUndefined, |
| 1227 | Register scratch) { |
| 1228 | saveVolatile(scratch); |
| 1229 | #if defined(DEBUG1) || defined(FUZZING) |
| 1230 | masm.loadPtr(AbsoluteAddress( |
| 1231 | gen->runtime->addressOfHasSeenObjectEmulateUndefinedFuse()), |
| 1232 | scratch); |
| 1233 | using Fn = bool (*)(JSObject* obj, size_t fuseValue); |
| 1234 | masm.setupAlignedABICall(); |
| 1235 | masm.passABIArg(objreg); |
| 1236 | masm.passABIArg(scratch); |
| 1237 | masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>(); |
| 1238 | #else |
| 1239 | using Fn = bool (*)(JSObject* obj); |
| 1240 | masm.setupAlignedABICall(); |
| 1241 | masm.passABIArg(objreg); |
| 1242 | masm.callWithABI<Fn, js::EmulatesUndefined>(); |
| 1243 | #endif |
| 1244 | masm.storeCallPointerResult(scratch); |
| 1245 | restoreVolatile(scratch); |
| 1246 | |
| 1247 | masm.branchIfTrueBool(scratch, ifEmulatesUndefined); |
| 1248 | masm.jump(ifDoesntEmulateUndefined); |
| 1249 | } |
| 1250 | |
| 1251 | // Base out-of-line code generator for all tests of the truthiness of an |
| 1252 | // object, where the object might not be truthy. (Recall that per spec all |
| 1253 | // objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class |
| 1254 | // flag to permit objects to look like |undefined| in certain contexts, |
| 1255 | // including in object truthiness testing.) We check truthiness inline except |
| 1256 | // when we're testing it on a proxy, in which case out-of-line code will call |
| 1257 | // EmulatesUndefined for a conclusive answer. |
| 1258 | class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> { |
| 1259 | Register objreg_; |
| 1260 | Register scratch_; |
| 1261 | |
| 1262 | Label* ifEmulatesUndefined_; |
| 1263 | Label* ifDoesntEmulateUndefined_; |
| 1264 | |
| 1265 | #ifdef DEBUG1 |
| 1266 | bool initialized() { return ifEmulatesUndefined_ != nullptr; } |
| 1267 | #endif |
| 1268 | |
| 1269 | public: |
| 1270 | OutOfLineTestObject() |
| 1271 | : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {} |
| 1272 | |
| 1273 | void accept(CodeGenerator* codegen) final { |
| 1274 | MOZ_ASSERT(initialized())do { static_assert( mozilla::detail::AssertionConditionType< decltype(initialized())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(initialized()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("initialized()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1274); AnnotateMozCrashReason("MOZ_ASSERT" "(" "initialized()" ")"); do { *((volatile int*)__null) = 1274; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1275 | codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_, |
| 1276 | ifDoesntEmulateUndefined_, scratch_); |
| 1277 | } |
| 1278 | |
| 1279 | // Specify the register where the object to be tested is found, labels to |
| 1280 | // jump to if the object is truthy or falsy, and a scratch register for |
| 1281 | // use in the out-of-line path. |
| 1282 | void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined, |
| 1283 | Label* ifDoesntEmulateUndefined, Register scratch) { |
| 1284 | MOZ_ASSERT(!initialized())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!initialized())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!initialized()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!initialized()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1284); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!initialized()" ")"); do { *((volatile int*)__null) = 1284; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1285 | MOZ_ASSERT(ifEmulatesUndefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ifEmulatesUndefined)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ifEmulatesUndefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ifEmulatesUndefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1285); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ifEmulatesUndefined" ")"); do { *((volatile int*)__null) = 1285; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1286 | objreg_ = objreg; |
| 1287 | scratch_ = scratch; |
| 1288 | ifEmulatesUndefined_ = ifEmulatesUndefined; |
| 1289 | ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined; |
| 1290 | } |
| 1291 | }; |
| 1292 | |
| 1293 | // A subclass of OutOfLineTestObject containing two extra labels, for use when |
| 1294 | // the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line |
| 1295 | // code. The user should bind these labels in inline code, and specify them as |
| 1296 | // targets via setInputAndTargets, as appropriate. |
| 1297 | class OutOfLineTestObjectWithLabels : public OutOfLineTestObject { |
| 1298 | Label label1_; |
| 1299 | Label label2_; |
| 1300 | |
| 1301 | public: |
| 1302 | OutOfLineTestObjectWithLabels() = default; |
| 1303 | |
| 1304 | Label* label1() { return &label1_; } |
| 1305 | Label* label2() { return &label2_; } |
| 1306 | }; |
| 1307 | |
| 1308 | void CodeGenerator::testObjectEmulatesUndefinedKernel( |
| 1309 | Register objreg, Label* ifEmulatesUndefined, |
| 1310 | Label* ifDoesntEmulateUndefined, Register scratch, |
| 1311 | OutOfLineTestObject* ool) { |
| 1312 | ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, |
| 1313 | scratch); |
| 1314 | |
| 1315 | // Perform a fast-path check of the object's class flags if the object's |
| 1316 | // not a proxy. Let out-of-line code handle the slow cases that require |
| 1317 | // saving registers, making a function call, and restoring registers. |
| 1318 | masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(), |
| 1319 | ifEmulatesUndefined); |
| 1320 | } |
| 1321 | |
| 1322 | void CodeGenerator::branchTestObjectEmulatesUndefined( |
| 1323 | Register objreg, Label* ifEmulatesUndefined, |
| 1324 | Label* ifDoesntEmulateUndefined, Register scratch, |
| 1325 | OutOfLineTestObject* ool) { |
| 1326 | MOZ_ASSERT(!ifDoesntEmulateUndefined->bound(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ifDoesntEmulateUndefined->bound())>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(!ifDoesntEmulateUndefined->bound()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!ifDoesntEmulateUndefined->bound()" " (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")"); do { *((volatile int*)__null) = 1327; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1327 | "ifDoesntEmulateUndefined will be bound to the fallthrough path")do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ifDoesntEmulateUndefined->bound())>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(!ifDoesntEmulateUndefined->bound()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!ifDoesntEmulateUndefined->bound()" " (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1327); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")"); do { *((volatile int*)__null) = 1327; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1328 | |
| 1329 | testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, |
| 1330 | ifDoesntEmulateUndefined, scratch, ool); |
| 1331 | masm.bind(ifDoesntEmulateUndefined); |
| 1332 | } |
| 1333 | |
| 1334 | void CodeGenerator::testObjectEmulatesUndefined(Register objreg, |
| 1335 | Label* ifEmulatesUndefined, |
| 1336 | Label* ifDoesntEmulateUndefined, |
| 1337 | Register scratch, |
| 1338 | OutOfLineTestObject* ool) { |
| 1339 | testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, |
| 1340 | ifDoesntEmulateUndefined, scratch, ool); |
| 1341 | masm.jump(ifDoesntEmulateUndefined); |
| 1342 | } |
| 1343 | |
| 1344 | void CodeGenerator::testValueTruthyForType( |
| 1345 | JSValueType type, ScratchTagScope& tag, const ValueOperand& value, |
| 1346 | Register tempToUnbox, Register temp, FloatRegister floatTemp, |
| 1347 | Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool, |
| 1348 | bool skipTypeTest) { |
| 1349 | #ifdef DEBUG1 |
| 1350 | if (skipTypeTest) { |
| 1351 | Label expected; |
| 1352 | masm.branchTestType(Assembler::Equal, tag, type, &expected); |
| 1353 | masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType"); |
| 1354 | masm.bind(&expected); |
| 1355 | } |
| 1356 | #endif |
| 1357 | |
| 1358 | // Handle irregular types first. |
| 1359 | switch (type) { |
| 1360 | case JSVAL_TYPE_UNDEFINED: |
| 1361 | case JSVAL_TYPE_NULL: |
| 1362 | // Undefined and null are falsy. |
| 1363 | if (!skipTypeTest) { |
| 1364 | masm.branchTestType(Assembler::Equal, tag, type, ifFalsy); |
| 1365 | } else { |
| 1366 | masm.jump(ifFalsy); |
| 1367 | } |
| 1368 | return; |
| 1369 | case JSVAL_TYPE_SYMBOL: |
| 1370 | // Symbols are truthy. |
| 1371 | if (!skipTypeTest) { |
| 1372 | masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy); |
| 1373 | } else { |
| 1374 | masm.jump(ifTruthy); |
| 1375 | } |
| 1376 | return; |
| 1377 | case JSVAL_TYPE_OBJECT: { |
| 1378 | Label notObject; |
| 1379 | if (!skipTypeTest) { |
| 1380 | masm.branchTestObject(Assembler::NotEqual, tag, ¬Object); |
| 1381 | } |
| 1382 | ScratchTagScopeRelease _(&tag); |
| 1383 | Register objreg = masm.extractObject(value, tempToUnbox); |
| 1384 | testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool); |
| 1385 | masm.bind(¬Object); |
| 1386 | return; |
| 1387 | } |
| 1388 | default: |
| 1389 | break; |
| 1390 | } |
| 1391 | |
| 1392 | // Check the type of the value (unless this is the last possible type). |
| 1393 | Label differentType; |
| 1394 | if (!skipTypeTest) { |
| 1395 | masm.branchTestType(Assembler::NotEqual, tag, type, &differentType); |
| 1396 | } |
| 1397 | |
| 1398 | // Branch if the value is falsy. |
| 1399 | ScratchTagScopeRelease _(&tag); |
| 1400 | switch (type) { |
| 1401 | case JSVAL_TYPE_BOOLEAN: { |
| 1402 | masm.branchTestBooleanTruthy(false, value, ifFalsy); |
| 1403 | break; |
| 1404 | } |
| 1405 | case JSVAL_TYPE_INT32: { |
| 1406 | masm.branchTestInt32Truthy(false, value, ifFalsy); |
| 1407 | break; |
| 1408 | } |
| 1409 | case JSVAL_TYPE_STRING: { |
| 1410 | masm.branchTestStringTruthy(false, value, ifFalsy); |
| 1411 | break; |
| 1412 | } |
| 1413 | case JSVAL_TYPE_BIGINT: { |
| 1414 | masm.branchTestBigIntTruthy(false, value, ifFalsy); |
| 1415 | break; |
| 1416 | } |
| 1417 | case JSVAL_TYPE_DOUBLE: { |
| 1418 | masm.unboxDouble(value, floatTemp); |
| 1419 | masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy); |
| 1420 | break; |
| 1421 | } |
| 1422 | default: |
| 1423 | MOZ_CRASH("Unexpected value type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected value type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1423); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected value type" ")"); do { *((volatile int*)__null) = 1423; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 1424 | } |
| 1425 | |
| 1426 | // If we reach this point, the value is truthy. We fall through for |
| 1427 | // truthy on the last test; otherwise, branch. |
| 1428 | if (!skipTypeTest) { |
| 1429 | masm.jump(ifTruthy); |
| 1430 | } |
| 1431 | |
| 1432 | masm.bind(&differentType); |
| 1433 | } |
| 1434 | |
| 1435 | void CodeGenerator::testValueTruthy(const ValueOperand& value, |
| 1436 | Register tempToUnbox, Register temp, |
| 1437 | FloatRegister floatTemp, |
| 1438 | const TypeDataList& observedTypes, |
| 1439 | Label* ifTruthy, Label* ifFalsy, |
| 1440 | OutOfLineTestObject* ool) { |
| 1441 | ScratchTagScope tag(masm, value); |
| 1442 | masm.splitTagForTest(value, tag); |
| 1443 | |
| 1444 | const std::initializer_list<JSValueType> defaultOrder = { |
| 1445 | JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, |
| 1446 | JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING, |
| 1447 | JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT}; |
| 1448 | |
| 1449 | mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder); |
| 1450 | |
| 1451 | // Generate tests for previously observed types first. |
| 1452 | // The TypeDataList is sorted by descending frequency. |
| 1453 | for (auto& observed : observedTypes) { |
| 1454 | JSValueType type = observed.type(); |
| 1455 | remaining -= type; |
| 1456 | |
| 1457 | testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp, |
| 1458 | ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false); |
| 1459 | } |
| 1460 | |
| 1461 | // Generate tests for remaining types. |
| 1462 | for (auto type : defaultOrder) { |
| 1463 | if (!remaining.contains(type)) { |
| 1464 | continue; |
| 1465 | } |
| 1466 | remaining -= type; |
| 1467 | |
| 1468 | // We don't need a type test for the last possible type. |
| 1469 | bool skipTypeTest = remaining.isEmpty(); |
| 1470 | testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp, |
| 1471 | ifTruthy, ifFalsy, ool, skipTypeTest); |
| 1472 | } |
| 1473 | MOZ_ASSERT(remaining.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(remaining.isEmpty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(remaining.isEmpty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("remaining.isEmpty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { *((volatile int*)__null) = 1473; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1474 | |
| 1475 | // We fall through if the final test is truthy. |
| 1476 | } |
| 1477 | |
| 1478 | void CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test) { |
| 1479 | Register input = ToRegister(test->input()); |
| 1480 | MBasicBlock* ifTrue = test->ifTrue(); |
| 1481 | MBasicBlock* ifFalse = test->ifFalse(); |
| 1482 | |
| 1483 | if (isNextBlock(ifFalse->lir())) { |
| 1484 | masm.branchTest32(Assembler::NonZero, input, input, |
| 1485 | getJumpLabelForBranch(ifTrue)); |
| 1486 | } else { |
| 1487 | masm.branchTest32(Assembler::Zero, input, input, |
| 1488 | getJumpLabelForBranch(ifFalse)); |
| 1489 | jumpToBlock(ifTrue); |
| 1490 | } |
| 1491 | } |
| 1492 | |
| 1493 | void CodeGenerator::visitTestIPtrAndBranch(LTestIPtrAndBranch* test) { |
| 1494 | Register input = ToRegister(test->input()); |
| 1495 | MBasicBlock* ifTrue = test->ifTrue(); |
| 1496 | MBasicBlock* ifFalse = test->ifFalse(); |
| 1497 | |
| 1498 | if (isNextBlock(ifFalse->lir())) { |
| 1499 | masm.branchTestPtr(Assembler::NonZero, input, input, |
| 1500 | getJumpLabelForBranch(ifTrue)); |
| 1501 | } else { |
| 1502 | masm.branchTestPtr(Assembler::Zero, input, input, |
| 1503 | getJumpLabelForBranch(ifFalse)); |
| 1504 | jumpToBlock(ifTrue); |
| 1505 | } |
| 1506 | } |
| 1507 | |
| 1508 | void CodeGenerator::visitTestI64AndBranch(LTestI64AndBranch* test) { |
| 1509 | Register64 input = ToRegister64(test->input()); |
| 1510 | MBasicBlock* ifTrue = test->ifTrue(); |
| 1511 | MBasicBlock* ifFalse = test->ifFalse(); |
| 1512 | |
| 1513 | if (isNextBlock(ifFalse->lir())) { |
| 1514 | masm.branchTest64(Assembler::NonZero, input, input, |
| 1515 | getJumpLabelForBranch(ifTrue)); |
| 1516 | } else if (isNextBlock(ifTrue->lir())) { |
| 1517 | masm.branchTest64(Assembler::Zero, input, input, |
| 1518 | getJumpLabelForBranch(ifFalse)); |
| 1519 | } else { |
| 1520 | masm.branchTest64(Assembler::NonZero, input, input, |
| 1521 | getJumpLabelForBranch(ifTrue), |
| 1522 | getJumpLabelForBranch(ifFalse)); |
| 1523 | } |
| 1524 | } |
| 1525 | |
| 1526 | void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) { |
| 1527 | Register input = ToRegister(lir->input()); |
| 1528 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 1529 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 1530 | |
| 1531 | if (isNextBlock(ifFalse->lir())) { |
| 1532 | masm.branchIfBigIntIsNonZero(input, getJumpLabelForBranch(ifTrue)); |
| 1533 | } else { |
| 1534 | masm.branchIfBigIntIsZero(input, getJumpLabelForBranch(ifFalse)); |
| 1535 | jumpToBlock(ifTrue); |
| 1536 | } |
| 1537 | } |
| 1538 | |
| 1539 | static Assembler::Condition ReverseCondition(Assembler::Condition condition) { |
| 1540 | switch (condition) { |
| 1541 | case Assembler::Equal: |
| 1542 | case Assembler::NotEqual: |
| 1543 | return condition; |
| 1544 | case Assembler::Above: |
| 1545 | return Assembler::Below; |
| 1546 | case Assembler::AboveOrEqual: |
| 1547 | return Assembler::BelowOrEqual; |
| 1548 | case Assembler::Below: |
| 1549 | return Assembler::Above; |
| 1550 | case Assembler::BelowOrEqual: |
| 1551 | return Assembler::AboveOrEqual; |
| 1552 | case Assembler::GreaterThan: |
| 1553 | return Assembler::LessThan; |
| 1554 | case Assembler::GreaterThanOrEqual: |
| 1555 | return Assembler::LessThanOrEqual; |
| 1556 | case Assembler::LessThan: |
| 1557 | return Assembler::GreaterThan; |
| 1558 | case Assembler::LessThanOrEqual: |
| 1559 | return Assembler::GreaterThanOrEqual; |
| 1560 | default: |
| 1561 | break; |
| 1562 | } |
| 1563 | MOZ_CRASH("unhandled condition")do { do { } while (false); MOZ_ReportCrash("" "unhandled condition" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1563); AnnotateMozCrashReason("MOZ_CRASH(" "unhandled condition" ")"); do { *((volatile int*)__null) = 1563; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 1564 | } |
| 1565 | |
| 1566 | void CodeGenerator::visitCompare(LCompare* comp) { |
| 1567 | MCompare::CompareType compareType = comp->mir()->compareType(); |
| 1568 | Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop()); |
| 1569 | Register left = ToRegister(comp->left()); |
| 1570 | const LAllocation* right = comp->right(); |
| 1571 | Register output = ToRegister(comp->output()); |
| 1572 | |
| 1573 | if (compareType == MCompare::Compare_Object || |
| 1574 | compareType == MCompare::Compare_Symbol || |
| 1575 | compareType == MCompare::Compare_IntPtr || |
| 1576 | compareType == MCompare::Compare_UIntPtr || |
| 1577 | compareType == MCompare::Compare_WasmAnyRef) { |
| 1578 | if (right->isConstant()) { |
| 1579 | MOZ_ASSERT(compareType == MCompare::Compare_IntPtr ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { *((volatile int*)__null) = 1580; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1580 | compareType == MCompare::Compare_UIntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1580); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { *((volatile int*)__null) = 1580; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1581 | masm.cmpPtrSet(cond, left, ImmWord(ToInt32(right)), output); |
| 1582 | } else if (right->isRegister()) { |
| 1583 | masm.cmpPtrSet(cond, left, ToRegister(right), output); |
| 1584 | } else { |
| 1585 | masm.cmpPtrSet(ReverseCondition(cond), ToAddress(right), left, output); |
| 1586 | } |
| 1587 | return; |
| 1588 | } |
| 1589 | |
| 1590 | MOZ_ASSERT(compareType == MCompare::Compare_Int32 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1591); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { *((volatile int*)__null) = 1591; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1591 | compareType == MCompare::Compare_UInt32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1591); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { *((volatile int*)__null) = 1591; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1592 | |
| 1593 | if (right->isConstant()) { |
| 1594 | masm.cmp32Set(cond, left, Imm32(ToInt32(right)), output); |
| 1595 | } else if (right->isRegister()) { |
| 1596 | masm.cmp32Set(cond, left, ToRegister(right), output); |
| 1597 | } else { |
| 1598 | masm.cmp32Set(ReverseCondition(cond), ToAddress(right), left, output); |
| 1599 | } |
| 1600 | } |
| 1601 | |
| 1602 | void CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp) { |
| 1603 | MCompare::CompareType compareType = comp->cmpMir()->compareType(); |
| 1604 | Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop()); |
| 1605 | Register left = ToRegister(comp->left()); |
| 1606 | const LAllocation* right = comp->right(); |
| 1607 | |
| 1608 | MBasicBlock* ifTrue = comp->ifTrue(); |
| 1609 | MBasicBlock* ifFalse = comp->ifFalse(); |
| 1610 | |
| 1611 | // If the next block is the true case, invert the condition to fall through. |
| 1612 | Label* label; |
| 1613 | if (isNextBlock(ifTrue->lir())) { |
| 1614 | cond = Assembler::InvertCondition(cond); |
| 1615 | label = getJumpLabelForBranch(ifFalse); |
| 1616 | } else { |
| 1617 | label = getJumpLabelForBranch(ifTrue); |
| 1618 | } |
| 1619 | |
| 1620 | if (compareType == MCompare::Compare_Object || |
| 1621 | compareType == MCompare::Compare_Symbol || |
| 1622 | compareType == MCompare::Compare_IntPtr || |
| 1623 | compareType == MCompare::Compare_UIntPtr || |
| 1624 | compareType == MCompare::Compare_WasmAnyRef) { |
| 1625 | if (right->isConstant()) { |
| 1626 | MOZ_ASSERT(compareType == MCompare::Compare_IntPtr ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { *((volatile int*)__null) = 1627; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1627 | compareType == MCompare::Compare_UIntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1627); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { *((volatile int*)__null) = 1627; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1628 | masm.branchPtr(cond, left, ImmWord(ToInt32(right)), label); |
| 1629 | } else if (right->isRegister()) { |
| 1630 | masm.branchPtr(cond, left, ToRegister(right), label); |
| 1631 | } else { |
| 1632 | masm.branchPtr(ReverseCondition(cond), ToAddress(right), left, label); |
| 1633 | } |
| 1634 | } else { |
| 1635 | MOZ_ASSERT(compareType == MCompare::Compare_Int32 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { *((volatile int*)__null) = 1636; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1636 | compareType == MCompare::Compare_UInt32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1636); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { *((volatile int*)__null) = 1636; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1637 | |
| 1638 | if (right->isConstant()) { |
| 1639 | masm.branch32(cond, left, Imm32(ToInt32(right)), label); |
| 1640 | } else if (right->isRegister()) { |
| 1641 | masm.branch32(cond, left, ToRegister(right), label); |
| 1642 | } else { |
| 1643 | masm.branch32(ReverseCondition(cond), ToAddress(right), left, label); |
| 1644 | } |
| 1645 | } |
| 1646 | |
| 1647 | if (!isNextBlock(ifTrue->lir())) { |
| 1648 | jumpToBlock(ifFalse); |
| 1649 | } |
| 1650 | } |
| 1651 | |
| 1652 | void CodeGenerator::visitCompareI64(LCompareI64* lir) { |
| 1653 | MCompare::CompareType compareType = lir->mir()->compareType(); |
| 1654 | MOZ_ASSERT(compareType == MCompare::Compare_Int64 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { *((volatile int*)__null) = 1655; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1655 | compareType == MCompare::Compare_UInt64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1655); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { *((volatile int*)__null) = 1655; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1656 | bool isSigned = compareType == MCompare::Compare_Int64; |
| 1657 | Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned); |
| 1658 | Register64 left = ToRegister64(lir->left()); |
| 1659 | LInt64Allocation right = lir->right(); |
| 1660 | Register output = ToRegister(lir->output()); |
| 1661 | |
| 1662 | if (IsConstant(right)) { |
| 1663 | masm.cmp64Set(cond, left, Imm64(ToInt64(right)), output); |
| 1664 | } else if (IsRegister64(right)) { |
| 1665 | masm.cmp64Set(cond, left, ToRegister64(right), output); |
| 1666 | } else { |
| 1667 | masm.cmp64Set(ReverseCondition(cond), ToAddress(right), left, output); |
| 1668 | } |
| 1669 | } |
| 1670 | |
| 1671 | void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) { |
| 1672 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
| 1673 | MOZ_ASSERT(compareType == MCompare::Compare_Int64 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { *((volatile int*)__null) = 1674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1674 | compareType == MCompare::Compare_UInt64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { *((volatile int*)__null) = 1674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1675 | bool isSigned = compareType == MCompare::Compare_Int64; |
| 1676 | Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned); |
| 1677 | Register64 left = ToRegister64(lir->left()); |
| 1678 | LInt64Allocation right = lir->right(); |
| 1679 | |
| 1680 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 1681 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 1682 | |
| 1683 | Label* trueLabel = getJumpLabelForBranch(ifTrue); |
| 1684 | Label* falseLabel = getJumpLabelForBranch(ifFalse); |
| 1685 | |
| 1686 | // If the next block is the true case, invert the condition to fall through. |
| 1687 | if (isNextBlock(ifTrue->lir())) { |
| 1688 | cond = Assembler::InvertCondition(cond); |
| 1689 | trueLabel = falseLabel; |
| 1690 | falseLabel = nullptr; |
| 1691 | } else if (isNextBlock(ifFalse->lir())) { |
| 1692 | falseLabel = nullptr; |
| 1693 | } |
| 1694 | |
| 1695 | if (IsConstant(right)) { |
| 1696 | masm.branch64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel); |
| 1697 | } else if (IsRegister64(right)) { |
| 1698 | masm.branch64(cond, left, ToRegister64(right), trueLabel, falseLabel); |
| 1699 | } else { |
| 1700 | masm.branch64(ReverseCondition(cond), ToAddress(right), left, trueLabel, |
| 1701 | falseLabel); |
| 1702 | } |
| 1703 | } |
| 1704 | |
| 1705 | void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) { |
| 1706 | Assembler::Condition cond = baab->cond(); |
| 1707 | Register left = ToRegister(baab->left()); |
| 1708 | const LAllocation* right = baab->right(); |
| 1709 | |
| 1710 | MBasicBlock* ifTrue = baab->ifTrue(); |
| 1711 | MBasicBlock* ifFalse = baab->ifFalse(); |
| 1712 | |
| 1713 | // If the next block is the true case, invert the condition to fall through. |
| 1714 | Label* label; |
| 1715 | if (isNextBlock(ifTrue->lir())) { |
| 1716 | cond = Assembler::InvertCondition(cond); |
| 1717 | label = getJumpLabelForBranch(ifFalse); |
| 1718 | } else { |
| 1719 | label = getJumpLabelForBranch(ifTrue); |
| 1720 | } |
| 1721 | |
| 1722 | if (right->isConstant()) { |
| 1723 | masm.branchTest32(cond, left, Imm32(ToInt32(right)), label); |
| 1724 | } else { |
| 1725 | masm.branchTest32(cond, left, ToRegister(right), label); |
| 1726 | } |
| 1727 | |
| 1728 | if (!isNextBlock(ifTrue->lir())) { |
| 1729 | jumpToBlock(ifFalse); |
| 1730 | } |
| 1731 | } |
| 1732 | |
| 1733 | void CodeGenerator::visitBitAnd64AndBranch(LBitAnd64AndBranch* baab) { |
| 1734 | Assembler::Condition cond = baab->cond(); |
| 1735 | Register64 left = ToRegister64(baab->left()); |
| 1736 | LInt64Allocation right = baab->right(); |
| 1737 | |
| 1738 | MBasicBlock* ifTrue = baab->ifTrue(); |
| 1739 | MBasicBlock* ifFalse = baab->ifFalse(); |
| 1740 | |
| 1741 | Label* trueLabel = getJumpLabelForBranch(ifTrue); |
| 1742 | Label* falseLabel = getJumpLabelForBranch(ifFalse); |
| 1743 | |
| 1744 | // If the next block is the true case, invert the condition to fall through. |
| 1745 | if (isNextBlock(ifTrue->lir())) { |
| 1746 | cond = Assembler::InvertCondition(cond); |
| 1747 | trueLabel = falseLabel; |
| 1748 | falseLabel = nullptr; |
| 1749 | } else if (isNextBlock(ifFalse->lir())) { |
| 1750 | falseLabel = nullptr; |
| 1751 | } |
| 1752 | |
| 1753 | if (IsConstant(right)) { |
| 1754 | masm.branchTest64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel); |
| 1755 | } else { |
| 1756 | masm.branchTest64(cond, left, ToRegister64(right), trueLabel, falseLabel); |
| 1757 | } |
| 1758 | } |
| 1759 | |
| 1760 | void CodeGenerator::assertObjectDoesNotEmulateUndefined( |
| 1761 | Register input, Register temp, const MInstruction* mir) { |
| 1762 | #if defined(DEBUG1) || defined(FUZZING) |
| 1763 | // Validate that the object indeed doesn't have the emulates undefined flag. |
| 1764 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 1765 | addOutOfLineCode(ool, mir); |
| 1766 | |
| 1767 | Label* doesNotEmulateUndefined = ool->label1(); |
| 1768 | Label* emulatesUndefined = ool->label2(); |
| 1769 | |
| 1770 | testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined, |
| 1771 | temp, ool); |
| 1772 | masm.bind(emulatesUndefined); |
| 1773 | masm.assumeUnreachable( |
| 1774 | "Found an object emulating undefined while the fuse is intact"); |
| 1775 | masm.bind(doesNotEmulateUndefined); |
| 1776 | #endif |
| 1777 | } |
| 1778 | |
| 1779 | void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) { |
| 1780 | Label* truthy = getJumpLabelForBranch(lir->ifTruthy()); |
| 1781 | Label* falsy = getJumpLabelForBranch(lir->ifFalsy()); |
| 1782 | Register input = ToRegister(lir->input()); |
| 1783 | |
| 1784 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 1785 | if (intact) { |
| 1786 | assertObjectDoesNotEmulateUndefined(input, ToRegister(lir->temp()), |
| 1787 | lir->mir()); |
| 1788 | // Bug 1874905: It would be fantastic if this could be optimized out |
| 1789 | masm.jump(truthy); |
| 1790 | } else { |
| 1791 | auto* ool = new (alloc()) OutOfLineTestObject(); |
| 1792 | addOutOfLineCode(ool, lir->mir()); |
| 1793 | |
| 1794 | testObjectEmulatesUndefined(input, falsy, truthy, ToRegister(lir->temp()), |
| 1795 | ool); |
| 1796 | } |
| 1797 | } |
| 1798 | |
| 1799 | void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) { |
| 1800 | auto* ool = new (alloc()) OutOfLineTestObject(); |
| 1801 | addOutOfLineCode(ool, lir->mir()); |
| 1802 | |
| 1803 | Label* truthy = getJumpLabelForBranch(lir->ifTruthy()); |
| 1804 | Label* falsy = getJumpLabelForBranch(lir->ifFalsy()); |
| 1805 | |
| 1806 | ValueOperand input = ToValue(lir, LTestVAndBranch::Input); |
| 1807 | Register tempToUnbox = ToTempUnboxRegister(lir->temp1()); |
| 1808 | Register temp = ToRegister(lir->temp2()); |
| 1809 | FloatRegister floatTemp = ToFloatRegister(lir->tempFloat()); |
| 1810 | const TypeDataList& observedTypes = lir->mir()->observedTypes(); |
| 1811 | |
| 1812 | testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy, |
| 1813 | falsy, ool); |
| 1814 | masm.jump(truthy); |
| 1815 | } |
| 1816 | |
| 1817 | void CodeGenerator::visitBooleanToString(LBooleanToString* lir) { |
| 1818 | Register input = ToRegister(lir->input()); |
| 1819 | Register output = ToRegister(lir->output()); |
| 1820 | const JSAtomState& names = gen->runtime->names(); |
| 1821 | Label true_, done; |
| 1822 | |
| 1823 | masm.branchTest32(Assembler::NonZero, input, input, &true_); |
| 1824 | masm.movePtr(ImmGCPtr(names.false_), output); |
| 1825 | masm.jump(&done); |
| 1826 | |
| 1827 | masm.bind(&true_); |
| 1828 | masm.movePtr(ImmGCPtr(names.true_), output); |
| 1829 | |
| 1830 | masm.bind(&done); |
| 1831 | } |
| 1832 | |
| 1833 | void CodeGenerator::visitIntToString(LIntToString* lir) { |
| 1834 | Register input = ToRegister(lir->input()); |
| 1835 | Register output = ToRegister(lir->output()); |
| 1836 | |
| 1837 | using Fn = JSLinearString* (*)(JSContext*, int); |
| 1838 | OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>( |
| 1839 | lir, ArgList(input), StoreRegisterTo(output)); |
| 1840 | |
| 1841 | masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(), |
| 1842 | ool->entry()); |
| 1843 | |
| 1844 | masm.bind(ool->rejoin()); |
| 1845 | } |
| 1846 | |
| 1847 | void CodeGenerator::visitDoubleToString(LDoubleToString* lir) { |
| 1848 | FloatRegister input = ToFloatRegister(lir->input()); |
| 1849 | Register temp = ToRegister(lir->temp0()); |
| 1850 | Register output = ToRegister(lir->output()); |
| 1851 | |
| 1852 | using Fn = JSString* (*)(JSContext*, double); |
| 1853 | OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>( |
| 1854 | lir, ArgList(input), StoreRegisterTo(output)); |
| 1855 | |
| 1856 | // Try double to integer conversion and run integer to string code. |
| 1857 | masm.convertDoubleToInt32(input, temp, ool->entry(), false); |
| 1858 | masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(), |
| 1859 | ool->entry()); |
| 1860 | |
| 1861 | masm.bind(ool->rejoin()); |
| 1862 | } |
| 1863 | |
| 1864 | void CodeGenerator::visitValueToString(LValueToString* lir) { |
| 1865 | ValueOperand input = ToValue(lir, LValueToString::InputIndex); |
| 1866 | Register output = ToRegister(lir->output()); |
| 1867 | |
| 1868 | using Fn = JSString* (*)(JSContext*, HandleValue); |
| 1869 | OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>( |
| 1870 | lir, ArgList(input), StoreRegisterTo(output)); |
| 1871 | |
| 1872 | Label done; |
| 1873 | Register tag = masm.extractTag(input, output); |
| 1874 | const JSAtomState& names = gen->runtime->names(); |
| 1875 | |
| 1876 | // String |
| 1877 | { |
| 1878 | Label notString; |
| 1879 | masm.branchTestString(Assembler::NotEqual, tag, ¬String); |
| 1880 | masm.unboxString(input, output); |
| 1881 | masm.jump(&done); |
| 1882 | masm.bind(¬String); |
| 1883 | } |
| 1884 | |
| 1885 | // Integer |
| 1886 | { |
| 1887 | Label notInteger; |
| 1888 | masm.branchTestInt32(Assembler::NotEqual, tag, ¬Integer); |
| 1889 | Register unboxed = ToTempUnboxRegister(lir->temp0()); |
| 1890 | unboxed = masm.extractInt32(input, unboxed); |
| 1891 | masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(), |
| 1892 | ool->entry()); |
| 1893 | masm.jump(&done); |
| 1894 | masm.bind(¬Integer); |
| 1895 | } |
| 1896 | |
| 1897 | // Double |
| 1898 | { |
| 1899 | // Note: no fastpath. Need two extra registers and can only convert doubles |
| 1900 | // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT. |
| 1901 | masm.branchTestDouble(Assembler::Equal, tag, ool->entry()); |
| 1902 | } |
| 1903 | |
| 1904 | // Undefined |
| 1905 | { |
| 1906 | Label notUndefined; |
| 1907 | masm.branchTestUndefined(Assembler::NotEqual, tag, ¬Undefined); |
| 1908 | masm.movePtr(ImmGCPtr(names.undefined), output); |
| 1909 | masm.jump(&done); |
| 1910 | masm.bind(¬Undefined); |
| 1911 | } |
| 1912 | |
| 1913 | // Null |
| 1914 | { |
| 1915 | Label notNull; |
| 1916 | masm.branchTestNull(Assembler::NotEqual, tag, ¬Null); |
| 1917 | masm.movePtr(ImmGCPtr(names.null), output); |
| 1918 | masm.jump(&done); |
| 1919 | masm.bind(¬Null); |
| 1920 | } |
| 1921 | |
| 1922 | // Boolean |
| 1923 | { |
| 1924 | Label notBoolean, true_; |
| 1925 | masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean); |
| 1926 | masm.branchTestBooleanTruthy(true, input, &true_); |
| 1927 | masm.movePtr(ImmGCPtr(names.false_), output); |
| 1928 | masm.jump(&done); |
| 1929 | masm.bind(&true_); |
| 1930 | masm.movePtr(ImmGCPtr(names.true_), output); |
| 1931 | masm.jump(&done); |
| 1932 | masm.bind(¬Boolean); |
| 1933 | } |
| 1934 | |
| 1935 | // Objects/symbols are only possible when |mir->mightHaveSideEffects()|. |
| 1936 | if (lir->mir()->mightHaveSideEffects()) { |
| 1937 | // Object |
| 1938 | if (lir->mir()->supportSideEffects()) { |
| 1939 | masm.branchTestObject(Assembler::Equal, tag, ool->entry()); |
| 1940 | } else { |
| 1941 | // Bail. |
| 1942 | MOZ_ASSERT(lir->mir()->needsSnapshot())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->needsSnapshot())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->needsSnapshot ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->needsSnapshot()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1942); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()" ")"); do { *((volatile int*)__null) = 1942; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1943 | Label bail; |
| 1944 | masm.branchTestObject(Assembler::Equal, tag, &bail); |
| 1945 | bailoutFrom(&bail, lir->snapshot()); |
| 1946 | } |
| 1947 | |
| 1948 | // Symbol |
| 1949 | if (lir->mir()->supportSideEffects()) { |
| 1950 | masm.branchTestSymbol(Assembler::Equal, tag, ool->entry()); |
| 1951 | } else { |
| 1952 | // Bail. |
| 1953 | MOZ_ASSERT(lir->mir()->needsSnapshot())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->needsSnapshot())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->needsSnapshot ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->needsSnapshot()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 1953); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()" ")"); do { *((volatile int*)__null) = 1953; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1954 | Label bail; |
| 1955 | masm.branchTestSymbol(Assembler::Equal, tag, &bail); |
| 1956 | bailoutFrom(&bail, lir->snapshot()); |
| 1957 | } |
| 1958 | } |
| 1959 | |
| 1960 | // BigInt |
| 1961 | { |
| 1962 | // No fastpath currently implemented. |
| 1963 | masm.branchTestBigInt(Assembler::Equal, tag, ool->entry()); |
| 1964 | } |
| 1965 | |
| 1966 | masm.assumeUnreachable("Unexpected type for LValueToString."); |
| 1967 | |
| 1968 | masm.bind(&done); |
| 1969 | masm.bind(ool->rejoin()); |
| 1970 | } |
| 1971 | |
| 1972 | using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**); |
| 1973 | |
| 1974 | static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder, |
| 1975 | size_t offset, Register buffer, |
| 1976 | LiveGeneralRegisterSet& liveVolatiles, |
| 1977 | StoreBufferMutationFn fun) { |
| 1978 | Label callVM; |
| 1979 | Label exit; |
| 1980 | |
| 1981 | // Call into the VM to barrier the write. The only registers that need to |
| 1982 | // be preserved are those in liveVolatiles, so once they are saved on the |
| 1983 | // stack all volatile registers are available for use. |
| 1984 | masm.bind(&callVM); |
| 1985 | masm.PushRegsInMask(liveVolatiles); |
| 1986 | |
| 1987 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 1988 | regs.takeUnchecked(buffer); |
| 1989 | regs.takeUnchecked(holder); |
| 1990 | Register addrReg = regs.takeAny(); |
| 1991 | |
| 1992 | masm.computeEffectiveAddress(Address(holder, offset), addrReg); |
| 1993 | |
| 1994 | bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>(); |
| 1995 | if (needExtraReg) { |
| 1996 | masm.push(holder); |
| 1997 | masm.setupUnalignedABICall(holder); |
| 1998 | } else { |
| 1999 | masm.setupUnalignedABICall(regs.takeAny()); |
| 2000 | } |
| 2001 | masm.passABIArg(buffer); |
| 2002 | masm.passABIArg(addrReg); |
| 2003 | masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun), |
| 2004 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 2005 | |
| 2006 | if (needExtraReg) { |
| 2007 | masm.pop(holder); |
| 2008 | } |
| 2009 | masm.PopRegsInMask(liveVolatiles); |
| 2010 | masm.bind(&exit); |
| 2011 | } |
| 2012 | |
| 2013 | // Warning: this function modifies prev and next. |
| 2014 | static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder, |
| 2015 | size_t offset, Register prev, Register next, |
| 2016 | LiveGeneralRegisterSet& liveVolatiles) { |
| 2017 | Label exit; |
| 2018 | Label checkRemove, putCell; |
| 2019 | |
| 2020 | // if (next && (buffer = next->storeBuffer())) |
| 2021 | // but we never pass in nullptr for next. |
| 2022 | Register storebuffer = next; |
| 2023 | masm.loadStoreBuffer(next, storebuffer); |
| 2024 | masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove); |
| 2025 | |
| 2026 | // if (prev && prev->storeBuffer()) |
| 2027 | masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell); |
| 2028 | masm.loadStoreBuffer(prev, prev); |
| 2029 | masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit); |
| 2030 | |
| 2031 | // buffer->putCell(cellp) |
| 2032 | masm.bind(&putCell); |
| 2033 | EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles, |
| 2034 | JSString::addCellAddressToStoreBuffer); |
| 2035 | masm.jump(&exit); |
| 2036 | |
| 2037 | // if (prev && (buffer = prev->storeBuffer())) |
| 2038 | masm.bind(&checkRemove); |
| 2039 | masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit); |
| 2040 | masm.loadStoreBuffer(prev, storebuffer); |
| 2041 | masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit); |
| 2042 | EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles, |
| 2043 | JSString::removeCellAddressFromStoreBuffer); |
| 2044 | |
| 2045 | masm.bind(&exit); |
| 2046 | } |
| 2047 | |
| 2048 | void CodeGenerator::visitRegExp(LRegExp* lir) { |
| 2049 | Register output = ToRegister(lir->output()); |
| 2050 | Register temp = ToRegister(lir->temp0()); |
| 2051 | JSObject* source = lir->mir()->source(); |
| 2052 | |
| 2053 | using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>); |
| 2054 | OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>( |
| 2055 | lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output)); |
| 2056 | if (lir->mir()->hasShared()) { |
| 2057 | TemplateObject templateObject(source); |
| 2058 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 2059 | ool->entry()); |
| 2060 | } else { |
| 2061 | masm.jump(ool->entry()); |
| 2062 | } |
| 2063 | masm.bind(ool->rejoin()); |
| 2064 | } |
| 2065 | |
| 2066 | static constexpr int32_t RegExpPairsVectorStartOffset( |
| 2067 | int32_t inputOutputDataStartOffset) { |
| 2068 | return inputOutputDataStartOffset + int32_t(InputOutputDataSize) + |
| 2069 | int32_t(sizeof(MatchPairs)); |
| 2070 | } |
| 2071 | |
| 2072 | static Address RegExpPairCountAddress(MacroAssembler& masm, |
| 2073 | int32_t inputOutputDataStartOffset) { |
| 2074 | return Address(FramePointer, inputOutputDataStartOffset + |
| 2075 | int32_t(InputOutputDataSize) + |
| 2076 | MatchPairs::offsetOfPairCount()); |
| 2077 | } |
| 2078 | |
| 2079 | static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp, |
| 2080 | Register input, Register lastIndex, |
| 2081 | Register staticsReg, Register temp1, |
| 2082 | Register temp2, gc::Heap initialStringHeap, |
| 2083 | LiveGeneralRegisterSet& volatileRegs) { |
| 2084 | Address pendingInputAddress(staticsReg, |
| 2085 | RegExpStatics::offsetOfPendingInput()); |
| 2086 | Address matchesInputAddress(staticsReg, |
| 2087 | RegExpStatics::offsetOfMatchesInput()); |
| 2088 | Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource()); |
| 2089 | Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex()); |
| 2090 | |
| 2091 | masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String); |
| 2092 | masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String); |
| 2093 | masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String); |
| 2094 | |
| 2095 | if (initialStringHeap == gc::Heap::Default) { |
| 2096 | // Writing into RegExpStatics tenured memory; must post-barrier. |
| 2097 | if (staticsReg.volatile_()) { |
| 2098 | volatileRegs.add(staticsReg); |
| 2099 | } |
| 2100 | |
| 2101 | masm.loadPtr(pendingInputAddress, temp1); |
| 2102 | masm.storePtr(input, pendingInputAddress); |
| 2103 | masm.movePtr(input, temp2); |
| 2104 | EmitPostWriteBarrierS(masm, staticsReg, |
| 2105 | RegExpStatics::offsetOfPendingInput(), |
| 2106 | temp1 /* prev */, temp2 /* next */, volatileRegs); |
| 2107 | |
| 2108 | masm.loadPtr(matchesInputAddress, temp1); |
| 2109 | masm.storePtr(input, matchesInputAddress); |
| 2110 | masm.movePtr(input, temp2); |
| 2111 | EmitPostWriteBarrierS(masm, staticsReg, |
| 2112 | RegExpStatics::offsetOfMatchesInput(), |
| 2113 | temp1 /* prev */, temp2 /* next */, volatileRegs); |
| 2114 | } else { |
| 2115 | masm.debugAssertGCThingIsTenured(input, temp1); |
| 2116 | masm.storePtr(input, pendingInputAddress); |
| 2117 | masm.storePtr(input, matchesInputAddress); |
| 2118 | } |
| 2119 | |
| 2120 | masm.storePtr(lastIndex, |
| 2121 | Address(staticsReg, RegExpStatics::offsetOfLazyIndex())); |
| 2122 | masm.store32( |
| 2123 | Imm32(1), |
| 2124 | Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation())); |
| 2125 | |
| 2126 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset( |
| 2127 | RegExpObject::SHARED_SLOT)), |
| 2128 | temp1, JSVAL_TYPE_PRIVATE_GCTHING); |
| 2129 | masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2); |
| 2130 | masm.storePtr(temp2, lazySourceAddress); |
| 2131 | static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size"); |
| 2132 | masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2); |
| 2133 | masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags())); |
| 2134 | } |
| 2135 | |
| 2136 | // Prepare an InputOutputData and optional MatchPairs which space has been |
| 2137 | // allocated for on the stack, and try to execute a RegExp on a string input. |
| 2138 | // If the RegExp was successfully executed and matched the input, fallthrough. |
| 2139 | // Otherwise, jump to notFound or failure. |
| 2140 | // |
| 2141 | // inputOutputDataStartOffset is the offset relative to the frame pointer |
| 2142 | // register. This offset is negative for the RegExpExecTest stub. |
| 2143 | static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp, |
| 2144 | Register input, Register lastIndex, |
| 2145 | Register temp1, Register temp2, |
| 2146 | Register temp3, |
| 2147 | int32_t inputOutputDataStartOffset, |
| 2148 | gc::Heap initialStringHeap, Label* notFound, |
| 2149 | Label* failure) { |
| 2150 | JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp"); |
| 2151 | |
| 2152 | using irregexp::InputOutputData; |
| 2153 | |
| 2154 | /* |
| 2155 | * [SMDOC] Stack layout for PrepareAndExecuteRegExp |
| 2156 | * |
| 2157 | * Before this function is called, the caller is responsible for |
| 2158 | * allocating enough stack space for the following data: |
| 2159 | * |
| 2160 | * inputOutputDataStartOffset +-----> +---------------+ |
| 2161 | * |InputOutputData| |
| 2162 | * inputStartAddress +----------> inputStart| |
| 2163 | * inputEndAddress +----------> inputEnd| |
| 2164 | * startIndexAddress +----------> startIndex| |
| 2165 | * matchesAddress +----------> matches|-----+ |
| 2166 | * +---------------+ | |
| 2167 | * matchPairs(Address|Offset) +-----> +---------------+ <--+ |
| 2168 | * | MatchPairs | |
| 2169 | * pairCountAddress +----------> count | |
| 2170 | * pairsPointerAddress +----------> pairs |-----+ |
| 2171 | * +---------------+ | |
| 2172 | * pairsArray(Address|Offset) +-----> +---------------+ <--+ |
| 2173 | * | MatchPair | |
| 2174 | * firstMatchStartAddress +----------> start | <--+ |
| 2175 | * | limit | | |
| 2176 | * +---------------+ | |
| 2177 | * . | |
| 2178 | * . Reserved space for |
| 2179 | * . RegExpObject::MaxPairCount |
| 2180 | * . MatchPair objects |
| 2181 | * . | |
| 2182 | * +---------------+ | |
| 2183 | * | MatchPair | | |
| 2184 | * | start | | |
| 2185 | * | limit | <--+ |
| 2186 | * +---------------+ |
| 2187 | */ |
| 2188 | |
| 2189 | int32_t ioOffset = inputOutputDataStartOffset; |
| 2190 | int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData)); |
| 2191 | int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs)); |
| 2192 | |
| 2193 | Address inputStartAddress(FramePointer, |
| 2194 | ioOffset + InputOutputData::offsetOfInputStart()); |
| 2195 | Address inputEndAddress(FramePointer, |
| 2196 | ioOffset + InputOutputData::offsetOfInputEnd()); |
| 2197 | Address startIndexAddress(FramePointer, |
| 2198 | ioOffset + InputOutputData::offsetOfStartIndex()); |
| 2199 | Address matchesAddress(FramePointer, |
| 2200 | ioOffset + InputOutputData::offsetOfMatches()); |
| 2201 | |
| 2202 | Address matchPairsAddress(FramePointer, matchPairsOffset); |
| 2203 | Address pairCountAddress(FramePointer, |
| 2204 | matchPairsOffset + MatchPairs::offsetOfPairCount()); |
| 2205 | Address pairsPointerAddress(FramePointer, |
| 2206 | matchPairsOffset + MatchPairs::offsetOfPairs()); |
| 2207 | |
| 2208 | Address pairsArrayAddress(FramePointer, pairsArrayOffset); |
| 2209 | Address firstMatchStartAddress(FramePointer, |
| 2210 | pairsArrayOffset + MatchPair::offsetOfStart()); |
| 2211 | |
| 2212 | // First, fill in a skeletal MatchPairs instance on the stack. This will be |
| 2213 | // passed to the OOL stub in the caller if we aren't able to execute the |
| 2214 | // RegExp inline, and that stub needs to be able to determine whether the |
| 2215 | // execution finished successfully. |
| 2216 | |
| 2217 | // Initialize MatchPairs::pairCount to 1. The correct value can only |
| 2218 | // be determined after loading the RegExpShared. If the RegExpShared |
| 2219 | // has Kind::Atom, this is the correct pairCount. |
| 2220 | masm.store32(Imm32(1), pairCountAddress); |
| 2221 | |
| 2222 | // Initialize MatchPairs::pairs pointer |
| 2223 | masm.computeEffectiveAddress(pairsArrayAddress, temp1); |
| 2224 | masm.storePtr(temp1, pairsPointerAddress); |
| 2225 | |
| 2226 | // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch |
| 2227 | masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress); |
| 2228 | |
| 2229 | // Determine the set of volatile inputs to save when calling into C++ or |
| 2230 | // regexp code. |
| 2231 | LiveGeneralRegisterSet volatileRegs; |
| 2232 | if (lastIndex.volatile_()) { |
| 2233 | volatileRegs.add(lastIndex); |
| 2234 | } |
| 2235 | if (input.volatile_()) { |
| 2236 | volatileRegs.add(input); |
| 2237 | } |
| 2238 | if (regexp.volatile_()) { |
| 2239 | volatileRegs.add(regexp); |
| 2240 | } |
| 2241 | |
| 2242 | // Ensure the input string is not a rope. |
| 2243 | Label isLinear; |
| 2244 | masm.branchIfNotRope(input, &isLinear); |
| 2245 | { |
| 2246 | masm.PushRegsInMask(volatileRegs); |
| 2247 | |
| 2248 | using Fn = JSLinearString* (*)(JSString*); |
| 2249 | masm.setupUnalignedABICall(temp1); |
| 2250 | masm.passABIArg(input); |
| 2251 | masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>(); |
| 2252 | |
| 2253 | MOZ_ASSERT(!volatileRegs.has(temp1))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!volatileRegs.has(temp1))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!volatileRegs.has(temp1)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!volatileRegs.has(temp1)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2253); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { *((volatile int*)__null) = 2253; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 2254 | masm.storeCallPointerResult(temp1); |
| 2255 | masm.PopRegsInMask(volatileRegs); |
| 2256 | |
| 2257 | masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure); |
| 2258 | } |
| 2259 | masm.bind(&isLinear); |
| 2260 | |
| 2261 | // Load the RegExpShared. |
| 2262 | Register regexpReg = temp1; |
| 2263 | Address sharedSlot = Address( |
| 2264 | regexp, NativeObject::getFixedSlotOffset(RegExpObject::SHARED_SLOT)); |
| 2265 | masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure); |
| 2266 | masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING); |
| 2267 | |
| 2268 | // Handle Atom matches |
| 2269 | Label notAtom, checkSuccess; |
| 2270 | masm.branchPtr(Assembler::Equal, |
| 2271 | Address(regexpReg, RegExpShared::offsetOfPatternAtom()), |
| 2272 | ImmWord(0), ¬Atom); |
| 2273 | { |
| 2274 | masm.computeEffectiveAddress(matchPairsAddress, temp3); |
| 2275 | |
| 2276 | masm.PushRegsInMask(volatileRegs); |
| 2277 | using Fn = |
| 2278 | RegExpRunStatus (*)(RegExpShared* re, const JSLinearString* input, |
| 2279 | size_t start, MatchPairs* matchPairs); |
| 2280 | masm.setupUnalignedABICall(temp2); |
| 2281 | masm.passABIArg(regexpReg); |
| 2282 | masm.passABIArg(input); |
| 2283 | masm.passABIArg(lastIndex); |
| 2284 | masm.passABIArg(temp3); |
| 2285 | masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>(); |
| 2286 | |
| 2287 | MOZ_ASSERT(!volatileRegs.has(temp1))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!volatileRegs.has(temp1))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!volatileRegs.has(temp1)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!volatileRegs.has(temp1)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2287); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { *((volatile int*)__null) = 2287; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 2288 | masm.storeCallInt32Result(temp1); |
| 2289 | masm.PopRegsInMask(volatileRegs); |
| 2290 | |
| 2291 | masm.jump(&checkSuccess); |
| 2292 | } |
| 2293 | masm.bind(¬Atom); |
| 2294 | |
| 2295 | // Don't handle regexps with too many capture pairs. |
| 2296 | masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2); |
| 2297 | masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount), |
| 2298 | failure); |
| 2299 | |
| 2300 | // Fill in the pair count in the MatchPairs on the stack. |
| 2301 | masm.store32(temp2, pairCountAddress); |
| 2302 | |
| 2303 | // Load code pointer and length of input (in bytes). |
| 2304 | // Store the input start in the InputOutputData. |
| 2305 | Register codePointer = temp1; // Note: temp1 was previously regexpReg. |
| 2306 | Register byteLength = temp3; |
| 2307 | { |
| 2308 | Label isLatin1, done; |
| 2309 | masm.loadStringLength(input, byteLength); |
| 2310 | |
| 2311 | masm.branchLatin1String(input, &isLatin1); |
| 2312 | |
| 2313 | // Two-byte input |
| 2314 | masm.loadStringChars(input, temp2, CharEncoding::TwoByte); |
| 2315 | masm.storePtr(temp2, inputStartAddress); |
| 2316 | masm.loadPtr( |
| 2317 | Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)), |
| 2318 | codePointer); |
| 2319 | masm.lshiftPtr(Imm32(1), byteLength); |
| 2320 | masm.jump(&done); |
| 2321 | |
| 2322 | // Latin1 input |
| 2323 | masm.bind(&isLatin1); |
| 2324 | masm.loadStringChars(input, temp2, CharEncoding::Latin1); |
| 2325 | masm.storePtr(temp2, inputStartAddress); |
| 2326 | masm.loadPtr( |
| 2327 | Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)), |
| 2328 | codePointer); |
| 2329 | |
| 2330 | masm.bind(&done); |
| 2331 | |
| 2332 | // Store end pointer |
| 2333 | masm.addPtr(byteLength, temp2); |
| 2334 | masm.storePtr(temp2, inputEndAddress); |
| 2335 | } |
| 2336 | |
| 2337 | // Guard that the RegExpShared has been compiled for this type of input. |
| 2338 | // If it has not been compiled, we fall back to the OOL case, which will |
| 2339 | // do a VM call into the interpreter. |
| 2340 | // TODO: add an interpreter trampoline? |
| 2341 | masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure); |
| 2342 | masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer); |
| 2343 | |
| 2344 | // Finish filling in the InputOutputData instance on the stack |
| 2345 | masm.computeEffectiveAddress(matchPairsAddress, temp2); |
| 2346 | masm.storePtr(temp2, matchesAddress); |
| 2347 | masm.storePtr(lastIndex, startIndexAddress); |
| 2348 | |
| 2349 | // Execute the RegExp. |
| 2350 | masm.computeEffectiveAddress( |
| 2351 | Address(FramePointer, inputOutputDataStartOffset), temp2); |
| 2352 | masm.PushRegsInMask(volatileRegs); |
| 2353 | masm.setupUnalignedABICall(temp3); |
| 2354 | masm.passABIArg(temp2); |
| 2355 | masm.callWithABI(codePointer); |
| 2356 | masm.storeCallInt32Result(temp1); |
| 2357 | masm.PopRegsInMask(volatileRegs); |
| 2358 | |
| 2359 | masm.bind(&checkSuccess); |
| 2360 | masm.branch32(Assembler::Equal, temp1, |
| 2361 | Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound); |
| 2362 | masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)), |
| 2363 | failure); |
| 2364 | |
| 2365 | // Lazily update the RegExpStatics. |
| 2366 | size_t offset = GlobalObjectData::offsetOfRegExpRealm() + |
| 2367 | RegExpRealm::offsetOfRegExpStatics(); |
| 2368 | masm.loadGlobalObjectData(temp1); |
| 2369 | masm.loadPtr(Address(temp1, offset), temp1); |
| 2370 | UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3, |
| 2371 | initialStringHeap, volatileRegs); |
| 2372 | |
| 2373 | return true; |
| 2374 | } |
| 2375 | |
| 2376 | static void EmitInitDependentStringBase(MacroAssembler& masm, |
| 2377 | Register dependent, Register base, |
| 2378 | Register temp1, Register temp2, |
| 2379 | bool needsPostBarrier) { |
| 2380 | // Determine the base string to use and store it in temp2. |
| 2381 | Label notDependent, markedDependedOn; |
| 2382 | masm.load32(Address(base, JSString::offsetOfFlags()), temp1); |
| 2383 | masm.branchTest32(Assembler::Zero, temp1, Imm32(JSString::DEPENDENT_BIT), |
| 2384 | ¬Dependent); |
| 2385 | { |
| 2386 | // The base is also a dependent string. Load its base to prevent chains of |
| 2387 | // dependent strings in most cases. This must either be an atom or already |
| 2388 | // have the DEPENDED_ON_BIT set. |
| 2389 | masm.loadDependentStringBase(base, temp2); |
| 2390 | masm.jump(&markedDependedOn); |
| 2391 | } |
| 2392 | masm.bind(¬Dependent); |
| 2393 | { |
| 2394 | // The base is not a dependent string. Set the DEPENDED_ON_BIT if it's not |
| 2395 | // an atom. |
| 2396 | masm.movePtr(base, temp2); |
| 2397 | masm.branchTest32(Assembler::NonZero, temp1, Imm32(JSString::ATOM_BIT), |
| 2398 | &markedDependedOn); |
| 2399 | masm.or32(Imm32(JSString::DEPENDED_ON_BIT), temp1); |
| 2400 | masm.store32(temp1, Address(temp2, JSString::offsetOfFlags())); |
| 2401 | } |
| 2402 | masm.bind(&markedDependedOn); |
| 2403 | |
| 2404 | #ifdef DEBUG1 |
| 2405 | // Assert the base has the DEPENDED_ON_BIT set or is an atom. |
| 2406 | Label isAppropriatelyMarked; |
| 2407 | masm.branchTest32(Assembler::NonZero, |
| 2408 | Address(temp2, JSString::offsetOfFlags()), |
| 2409 | Imm32(JSString::ATOM_BIT | JSString::DEPENDED_ON_BIT), |
| 2410 | &isAppropriatelyMarked); |
| 2411 | masm.assumeUnreachable("Base string is missing DEPENDED_ON_BIT"); |
| 2412 | masm.bind(&isAppropriatelyMarked); |
| 2413 | #endif |
| 2414 | masm.storeDependentStringBase(temp2, dependent); |
| 2415 | |
| 2416 | // Post-barrier the base store. The base is still in temp2. |
| 2417 | if (needsPostBarrier) { |
| 2418 | Label done; |
| 2419 | masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done); |
| 2420 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done); |
| 2421 | |
| 2422 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
| 2423 | regsToSave.takeUnchecked(temp1); |
| 2424 | regsToSave.takeUnchecked(temp2); |
| 2425 | |
| 2426 | masm.PushRegsInMask(regsToSave); |
| 2427 | |
| 2428 | masm.mov(ImmPtr(masm.runtime()), temp1); |
| 2429 | |
| 2430 | using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell); |
| 2431 | masm.setupUnalignedABICall(temp2); |
| 2432 | masm.passABIArg(temp1); |
| 2433 | masm.passABIArg(dependent); |
| 2434 | masm.callWithABI<Fn, PostWriteBarrier>(); |
| 2435 | |
| 2436 | masm.PopRegsInMask(regsToSave); |
| 2437 | |
| 2438 | masm.bind(&done); |
| 2439 | } else { |
| 2440 | #ifdef DEBUG1 |
| 2441 | Label done; |
| 2442 | masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done); |
| 2443 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done); |
| 2444 | masm.assumeUnreachable("Missing post barrier for dependent string base"); |
| 2445 | masm.bind(&done); |
| 2446 | #endif |
| 2447 | } |
| 2448 | } |
| 2449 | |
| 2450 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
| 2451 | Register len, Register byteOpScratch, |
| 2452 | CharEncoding encoding, |
| 2453 | size_t maximumLength = SIZE_MAX(18446744073709551615UL)); |
| 2454 | |
| 2455 | class CreateDependentString { |
| 2456 | CharEncoding encoding_; |
| 2457 | Register string_; |
| 2458 | Register temp1_; |
| 2459 | Register temp2_; |
| 2460 | Label* failure_; |
| 2461 | |
| 2462 | enum class FallbackKind : uint8_t { |
| 2463 | InlineString, |
| 2464 | FatInlineString, |
| 2465 | NotInlineString, |
| 2466 | Count |
| 2467 | }; |
| 2468 | mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)> |
| 2469 | fallbacks_, joins_; |
| 2470 | |
| 2471 | public: |
| 2472 | CreateDependentString(CharEncoding encoding, Register string, Register temp1, |
| 2473 | Register temp2, Label* failure) |
| 2474 | : encoding_(encoding), |
| 2475 | string_(string), |
| 2476 | temp1_(temp1), |
| 2477 | temp2_(temp2), |
| 2478 | failure_(failure) {} |
| 2479 | |
| 2480 | Register string() const { return string_; } |
| 2481 | CharEncoding encoding() const { return encoding_; } |
| 2482 | |
| 2483 | // Generate code that creates DependentString. |
| 2484 | // Caller should call generateFallback after masm.ret(), to generate |
| 2485 | // fallback path. |
| 2486 | void generate(MacroAssembler& masm, const JSAtomState& names, |
| 2487 | CompileRuntime* runtime, Register base, |
| 2488 | BaseIndex startIndexAddress, BaseIndex limitIndexAddress, |
| 2489 | gc::Heap initialStringHeap); |
| 2490 | |
| 2491 | // Generate fallback path for creating DependentString. |
| 2492 | void generateFallback(MacroAssembler& masm); |
| 2493 | }; |
| 2494 | |
| 2495 | void CreateDependentString::generate(MacroAssembler& masm, |
| 2496 | const JSAtomState& names, |
| 2497 | CompileRuntime* runtime, Register base, |
| 2498 | BaseIndex startIndexAddress, |
| 2499 | BaseIndex limitIndexAddress, |
| 2500 | gc::Heap initialStringHeap) { |
| 2501 | JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)", |
| 2502 | (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
| 2503 | |
| 2504 | auto newGCString = [&](FallbackKind kind) { |
| 2505 | uint32_t flags = kind == FallbackKind::InlineString |
| 2506 | ? JSString::INIT_THIN_INLINE_FLAGS |
| 2507 | : kind == FallbackKind::FatInlineString |
| 2508 | ? JSString::INIT_FAT_INLINE_FLAGS |
| 2509 | : JSString::INIT_DEPENDENT_FLAGS; |
| 2510 | if (encoding_ == CharEncoding::Latin1) { |
| 2511 | flags |= JSString::LATIN1_CHARS_BIT; |
| 2512 | } |
| 2513 | |
| 2514 | if (kind != FallbackKind::FatInlineString) { |
| 2515 | masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]); |
| 2516 | } else { |
| 2517 | masm.newGCFatInlineString(string_, temp2_, initialStringHeap, |
| 2518 | &fallbacks_[kind]); |
| 2519 | } |
| 2520 | masm.bind(&joins_[kind]); |
| 2521 | masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags())); |
| 2522 | }; |
| 2523 | |
| 2524 | // Compute the string length. |
| 2525 | masm.load32(startIndexAddress, temp2_); |
| 2526 | masm.load32(limitIndexAddress, temp1_); |
| 2527 | masm.sub32(temp2_, temp1_); |
| 2528 | |
| 2529 | Label done, nonEmpty; |
| 2530 | |
| 2531 | // Zero length matches use the empty string. |
| 2532 | masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty); |
| 2533 | masm.movePtr(ImmGCPtr(names.empty_), string_); |
| 2534 | masm.jump(&done); |
| 2535 | |
| 2536 | masm.bind(&nonEmpty); |
| 2537 | |
| 2538 | // Complete matches use the base string. |
| 2539 | Label nonBaseStringMatch; |
| 2540 | masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch); |
| 2541 | masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()), |
| 2542 | temp1_, &nonBaseStringMatch); |
| 2543 | masm.movePtr(base, string_); |
| 2544 | masm.jump(&done); |
| 2545 | |
| 2546 | masm.bind(&nonBaseStringMatch); |
| 2547 | |
| 2548 | Label notInline; |
| 2549 | |
| 2550 | int32_t maxInlineLength = encoding_ == CharEncoding::Latin1 |
| 2551 | ? JSFatInlineString::MAX_LENGTH_LATIN1 |
| 2552 | : JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
| 2553 | masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), ¬Inline); |
| 2554 | { |
| 2555 | // Make a thin or fat inline string. |
| 2556 | Label stringAllocated, fatInline; |
| 2557 | |
| 2558 | int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1 |
| 2559 | ? JSThinInlineString::MAX_LENGTH_LATIN1 |
| 2560 | : JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
| 2561 | masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength), |
| 2562 | &fatInline); |
| 2563 | if (encoding_ == CharEncoding::Latin1) { |
| 2564 | // One character Latin-1 strings can be loaded directly from the |
| 2565 | // static strings table. |
| 2566 | Label thinInline; |
| 2567 | masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline); |
| 2568 | { |
| 2569 | static_assert( |
| 2570 | StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR, |
| 2571 | "Latin-1 strings can be loaded from static strings"); |
| 2572 | |
| 2573 | masm.loadStringChars(base, temp1_, encoding_); |
| 2574 | masm.loadChar(temp1_, temp2_, temp1_, encoding_); |
| 2575 | |
| 2576 | masm.lookupStaticString(temp1_, string_, runtime->staticStrings()); |
| 2577 | |
| 2578 | masm.jump(&done); |
| 2579 | } |
| 2580 | masm.bind(&thinInline); |
| 2581 | } |
| 2582 | { |
| 2583 | newGCString(FallbackKind::InlineString); |
| 2584 | masm.jump(&stringAllocated); |
| 2585 | } |
| 2586 | masm.bind(&fatInline); |
| 2587 | { |
| 2588 | newGCString(FallbackKind::FatInlineString); |
| 2589 | } |
| 2590 | masm.bind(&stringAllocated); |
| 2591 | |
| 2592 | masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); |
| 2593 | |
| 2594 | masm.push(string_); |
| 2595 | masm.push(base); |
| 2596 | |
| 2597 | MOZ_ASSERT(startIndexAddress.base == FramePointer,do { static_assert( mozilla::detail::AssertionConditionType< decltype(startIndexAddress.base == FramePointer)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(startIndexAddress.base == FramePointer))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("startIndexAddress.base == FramePointer" " (" "startIndexAddress is still valid after stack pushes" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2598); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") (" "startIndexAddress is still valid after stack pushes" ")" ); do { *((volatile int*)__null) = 2598; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 2598 | "startIndexAddress is still valid after stack pushes")do { static_assert( mozilla::detail::AssertionConditionType< decltype(startIndexAddress.base == FramePointer)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(startIndexAddress.base == FramePointer))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("startIndexAddress.base == FramePointer" " (" "startIndexAddress is still valid after stack pushes" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2598); AnnotateMozCrashReason("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") (" "startIndexAddress is still valid after stack pushes" ")" ); do { *((volatile int*)__null) = 2598; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 2599 | |
| 2600 | // Load chars pointer for the new string. |
| 2601 | masm.loadInlineStringCharsForStore(string_, string_); |
| 2602 | |
| 2603 | // Load the source characters pointer. |
| 2604 | masm.loadStringChars(base, temp2_, encoding_); |
| 2605 | masm.load32(startIndexAddress, base); |
| 2606 | masm.addToCharPtr(temp2_, base, encoding_); |
| 2607 | |
| 2608 | CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_); |
| 2609 | |
| 2610 | masm.pop(base); |
| 2611 | masm.pop(string_); |
| 2612 | |
| 2613 | masm.jump(&done); |
| 2614 | } |
| 2615 | |
| 2616 | masm.bind(¬Inline); |
| 2617 | |
| 2618 | { |
| 2619 | // Make a dependent string. |
| 2620 | // Warning: string may be tenured (if the fallback case is hit), so |
| 2621 | // stores into it must be post barriered. |
| 2622 | newGCString(FallbackKind::NotInlineString); |
| 2623 | |
| 2624 | masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); |
| 2625 | |
| 2626 | masm.loadNonInlineStringChars(base, temp1_, encoding_); |
| 2627 | masm.load32(startIndexAddress, temp2_); |
| 2628 | masm.addToCharPtr(temp1_, temp2_, encoding_); |
| 2629 | masm.storeNonInlineStringChars(temp1_, string_); |
| 2630 | |
| 2631 | EmitInitDependentStringBase(masm, string_, base, temp1_, temp2_, |
| 2632 | /* needsPostBarrier = */ true); |
| 2633 | } |
| 2634 | |
| 2635 | masm.bind(&done); |
| 2636 | } |
| 2637 | |
| 2638 | void CreateDependentString::generateFallback(MacroAssembler& masm) { |
| 2639 | JitSpew(JitSpew_Codegen, |
| 2640 | "# Emitting CreateDependentString fallback (encoding=%s)", |
| 2641 | (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
| 2642 | |
| 2643 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
| 2644 | regsToSave.takeUnchecked(string_); |
| 2645 | regsToSave.takeUnchecked(temp2_); |
| 2646 | |
| 2647 | for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) { |
| 2648 | masm.bind(&fallbacks_[kind]); |
| 2649 | |
| 2650 | masm.PushRegsInMask(regsToSave); |
| 2651 | |
| 2652 | using Fn = void* (*)(JSContext* cx); |
| 2653 | masm.setupUnalignedABICall(string_); |
| 2654 | masm.loadJSContext(string_); |
| 2655 | masm.passABIArg(string_); |
| 2656 | if (kind == FallbackKind::FatInlineString) { |
| 2657 | masm.callWithABI<Fn, AllocateFatInlineString>(); |
| 2658 | } else { |
| 2659 | masm.callWithABI<Fn, AllocateDependentString>(); |
| 2660 | } |
| 2661 | masm.storeCallPointerResult(string_); |
| 2662 | |
| 2663 | masm.PopRegsInMask(regsToSave); |
| 2664 | |
| 2665 | masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_); |
| 2666 | |
| 2667 | masm.jump(&joins_[kind]); |
| 2668 | } |
| 2669 | } |
| 2670 | |
| 2671 | // Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar, |
| 2672 | // but RegExpExecMatch also has to load and update .lastIndex for global/sticky |
| 2673 | // regular expressions. |
| 2674 | static JitCode* GenerateRegExpMatchStubShared(JSContext* cx, |
| 2675 | gc::Heap initialStringHeap, |
| 2676 | bool isExecMatch) { |
| 2677 | if (isExecMatch) { |
| 2678 | JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub"); |
| 2679 | } else { |
| 2680 | JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub"); |
| 2681 | } |
| 2682 | |
| 2683 | // |initialStringHeap| could be stale after a GC. |
| 2684 | JS::AutoCheckCannotGC nogc(cx); |
| 2685 | |
| 2686 | Register regexp = RegExpMatcherRegExpReg; |
| 2687 | Register input = RegExpMatcherStringReg; |
| 2688 | Register lastIndex = RegExpMatcherLastIndexReg; |
| 2689 | ValueOperand result = JSReturnOperand; |
| 2690 | |
| 2691 | // We are free to clobber all registers, as LRegExpMatcher is a call |
| 2692 | // instruction. |
| 2693 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 2694 | regs.take(input); |
| 2695 | regs.take(regexp); |
| 2696 | regs.take(lastIndex); |
| 2697 | |
| 2698 | Register temp1 = regs.takeAny(); |
| 2699 | Register temp2 = regs.takeAny(); |
| 2700 | Register temp3 = regs.takeAny(); |
| 2701 | Register maybeTemp4 = InvalidReg; |
| 2702 | if (!regs.empty()) { |
| 2703 | // There are not enough registers on x86. |
| 2704 | maybeTemp4 = regs.takeAny(); |
| 2705 | } |
| 2706 | Register maybeTemp5 = InvalidReg; |
| 2707 | if (!regs.empty()) { |
| 2708 | // There are not enough registers on x86. |
| 2709 | maybeTemp5 = regs.takeAny(); |
| 2710 | } |
| 2711 | |
| 2712 | Address flagsSlot(regexp, RegExpObject::offsetOfFlags()); |
| 2713 | Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex()); |
| 2714 | |
| 2715 | TempAllocator temp(&cx->tempLifoAlloc()); |
| 2716 | JitContext jcx(cx); |
| 2717 | StackMacroAssembler masm(cx, temp); |
| 2718 | AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared"); |
| 2719 | |
| 2720 | #ifdef JS_USE_LINK_REGISTER |
| 2721 | masm.pushReturnAddress(); |
| 2722 | #endif |
| 2723 | masm.push(FramePointer); |
| 2724 | masm.moveStackPtrTo(FramePointer); |
| 2725 | |
| 2726 | Label notFoundZeroLastIndex; |
| 2727 | if (isExecMatch) { |
| 2728 | masm.loadRegExpLastIndex(regexp, input, lastIndex, ¬FoundZeroLastIndex); |
| 2729 | } |
| 2730 | |
| 2731 | // The InputOutputData is placed above the frame pointer and return address on |
| 2732 | // the stack. |
| 2733 | int32_t inputOutputDataStartOffset = 2 * sizeof(void*); |
| 2734 | |
| 2735 | Label notFound, oolEntry; |
| 2736 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
| 2737 | temp3, inputOutputDataStartOffset, |
| 2738 | initialStringHeap, ¬Found, &oolEntry)) { |
| 2739 | return nullptr; |
| 2740 | } |
| 2741 | |
| 2742 | // If a regexp has named captures, fall back to the OOL stub, which |
| 2743 | // will end up calling CreateRegExpMatchResults. |
| 2744 | Register shared = temp2; |
| 2745 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset( |
| 2746 | RegExpObject::SHARED_SLOT)), |
| 2747 | shared, JSVAL_TYPE_PRIVATE_GCTHING); |
| 2748 | masm.branchPtr(Assembler::NotEqual, |
| 2749 | Address(shared, RegExpShared::offsetOfGroupsTemplate()), |
| 2750 | ImmWord(0), &oolEntry); |
| 2751 | |
| 2752 | // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub. |
| 2753 | masm.branchTest32(Assembler::NonZero, |
| 2754 | Address(shared, RegExpShared::offsetOfFlags()), |
| 2755 | Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry); |
| 2756 | |
| 2757 | Address pairCountAddress = |
| 2758 | RegExpPairCountAddress(masm, inputOutputDataStartOffset); |
| 2759 | |
| 2760 | // Construct the result. |
| 2761 | Register object = temp1; |
| 2762 | { |
| 2763 | // In most cases, the array will have just 1-2 elements, so we optimize for |
| 2764 | // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots |
| 2765 | // because two slots are used for the elements header). |
| 2766 | |
| 2767 | // Load the array length in temp2 and the shape in temp3. |
| 2768 | Label allocated; |
| 2769 | masm.load32(pairCountAddress, temp2); |
| 2770 | size_t offset = GlobalObjectData::offsetOfRegExpRealm() + |
| 2771 | RegExpRealm::offsetOfNormalMatchResultShape(); |
| 2772 | masm.loadGlobalObjectData(temp3); |
| 2773 | masm.loadPtr(Address(temp3, offset), temp3); |
| 2774 | |
| 2775 | auto emitAllocObject = [&](size_t elementCapacity) { |
| 2776 | gc::AllocKind kind = GuessArrayGCKind(elementCapacity); |
| 2777 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(kind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(kind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2777); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(kind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 2777; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 2778 | kind = ForegroundToBackgroundAllocKind(kind); |
| 2779 | |
| 2780 | #ifdef DEBUG1 |
| 2781 | // Assert all of the available slots are used for |elementCapacity| |
| 2782 | // elements. |
| 2783 | size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity; |
| 2784 | MOZ_ASSERT(usedSlots == GetGCKindSlots(kind))do { static_assert( mozilla::detail::AssertionConditionType< decltype(usedSlots == GetGCKindSlots(kind))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(usedSlots == GetGCKindSlots( kind)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("usedSlots == GetGCKindSlots(kind)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2784); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)" ")"); do { *((volatile int*)__null) = 2784; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 2785 | #endif |
| 2786 | |
| 2787 | constexpr size_t numUsedDynamicSlots = |
| 2788 | RegExpRealm::MatchResultObjectSlotSpan; |
| 2789 | constexpr size_t numDynamicSlots = |
| 2790 | RegExpRealm::MatchResultObjectNumDynamicSlots; |
| 2791 | constexpr size_t arrayLength = 1; |
| 2792 | masm.createArrayWithFixedElements(object, temp3, temp2, temp3, |
| 2793 | arrayLength, elementCapacity, |
| 2794 | numUsedDynamicSlots, numDynamicSlots, |
| 2795 | kind, gc::Heap::Default, &oolEntry); |
| 2796 | }; |
| 2797 | |
| 2798 | Label moreThan2; |
| 2799 | masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2); |
| 2800 | emitAllocObject(2); |
| 2801 | masm.jump(&allocated); |
| 2802 | |
| 2803 | Label moreThan6; |
| 2804 | masm.bind(&moreThan2); |
| 2805 | masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6); |
| 2806 | emitAllocObject(6); |
| 2807 | masm.jump(&allocated); |
| 2808 | |
| 2809 | masm.bind(&moreThan6); |
| 2810 | static_assert(RegExpObject::MaxPairCount == 14); |
| 2811 | emitAllocObject(RegExpObject::MaxPairCount); |
| 2812 | |
| 2813 | masm.bind(&allocated); |
| 2814 | } |
| 2815 | |
| 2816 | // clang-format off |
| 2817 | /* |
| 2818 | * [SMDOC] Stack layout for the RegExpMatcher stub |
| 2819 | * |
| 2820 | * +---------------+ |
| 2821 | * FramePointer +-----> |Caller-FramePtr| |
| 2822 | * +---------------+ |
| 2823 | * |Return-Address | |
| 2824 | * +---------------+ |
| 2825 | * inputOutputDataStartOffset +-----> +---------------+ |
| 2826 | * |InputOutputData| |
| 2827 | * +---------------+ |
| 2828 | * +---------------+ |
| 2829 | * | MatchPairs | |
| 2830 | * pairsCountAddress +-----------> count | |
| 2831 | * | pairs | |
| 2832 | * | | |
| 2833 | * +---------------+ |
| 2834 | * pairsVectorStartOffset +-----> +---------------+ |
| 2835 | * | MatchPair | |
| 2836 | * matchPairStart +------------> start | <-------+ |
| 2837 | * matchPairLimit +------------> limit | | Reserved space for |
| 2838 | * +---------------+ | `RegExpObject::MaxPairCount` |
| 2839 | * . | MatchPair objects. |
| 2840 | * . | |
| 2841 | * . | `count` objects will be |
| 2842 | * +---------------+ | initialized and can be |
| 2843 | * | MatchPair | | accessed below. |
| 2844 | * | start | <-------+ |
| 2845 | * | limit | |
| 2846 | * +---------------+ |
| 2847 | */ |
| 2848 | // clang-format on |
| 2849 | |
| 2850 | static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t), |
| 2851 | "MatchPair consists of two int32 values representing the start" |
| 2852 | "and the end offset of the match"); |
| 2853 | |
| 2854 | int32_t pairsVectorStartOffset = |
| 2855 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
| 2856 | |
| 2857 | // Incremented by one below for each match pair. |
| 2858 | Register matchIndex = temp2; |
| 2859 | masm.move32(Imm32(0), matchIndex); |
| 2860 | |
| 2861 | // The element in which to store the result of the current match. |
| 2862 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
| 2863 | BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset); |
| 2864 | |
| 2865 | // The current match pair's "start" and "limit" member. |
| 2866 | BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight, |
| 2867 | pairsVectorStartOffset + MatchPair::offsetOfStart()); |
| 2868 | BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight, |
| 2869 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
| 2870 | |
| 2871 | Label* depStrFailure = &oolEntry; |
| 2872 | Label restoreRegExpAndLastIndex; |
| 2873 | |
| 2874 | Register temp4; |
| 2875 | if (maybeTemp4 == InvalidReg) { |
| 2876 | depStrFailure = &restoreRegExpAndLastIndex; |
| 2877 | |
| 2878 | // We don't have enough registers for a fourth temporary. Reuse |regexp| |
| 2879 | // as a temporary. We restore its value at |restoreRegExpAndLastIndex|. |
| 2880 | masm.push(regexp); |
| 2881 | temp4 = regexp; |
| 2882 | } else { |
| 2883 | temp4 = maybeTemp4; |
| 2884 | } |
| 2885 | |
| 2886 | Register temp5; |
| 2887 | if (maybeTemp5 == InvalidReg) { |
| 2888 | depStrFailure = &restoreRegExpAndLastIndex; |
| 2889 | |
| 2890 | // We don't have enough registers for a fifth temporary. Reuse |lastIndex| |
| 2891 | // as a temporary. We restore its value at |restoreRegExpAndLastIndex|. |
| 2892 | masm.push(lastIndex); |
| 2893 | temp5 = lastIndex; |
| 2894 | } else { |
| 2895 | temp5 = maybeTemp5; |
| 2896 | } |
| 2897 | |
| 2898 | auto maybeRestoreRegExpAndLastIndex = [&]() { |
| 2899 | if (maybeTemp5 == InvalidReg) { |
| 2900 | masm.pop(lastIndex); |
| 2901 | } |
| 2902 | if (maybeTemp4 == InvalidReg) { |
| 2903 | masm.pop(regexp); |
| 2904 | } |
| 2905 | }; |
| 2906 | |
| 2907 | // Loop to construct the match strings. There are two different loops, |
| 2908 | // depending on whether the input is a Two-Byte or a Latin-1 string. |
| 2909 | CreateDependentString depStrs[]{ |
| 2910 | {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure}, |
| 2911 | {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure}, |
| 2912 | }; |
| 2913 | |
| 2914 | { |
| 2915 | Label isLatin1, done; |
| 2916 | masm.branchLatin1String(input, &isLatin1); |
| 2917 | |
| 2918 | for (auto& depStr : depStrs) { |
| 2919 | if (depStr.encoding() == CharEncoding::Latin1) { |
| 2920 | masm.bind(&isLatin1); |
| 2921 | } |
| 2922 | |
| 2923 | Label matchLoop; |
| 2924 | masm.bind(&matchLoop); |
| 2925 | |
| 2926 | static_assert(MatchPair::NoMatch == -1, |
| 2927 | "MatchPair::start is negative if no match was found"); |
| 2928 | |
| 2929 | Label isUndefined, storeDone; |
| 2930 | masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0), |
| 2931 | &isUndefined); |
| 2932 | { |
| 2933 | depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()), |
| 2934 | input, matchPairStart, matchPairLimit, |
| 2935 | initialStringHeap); |
| 2936 | |
| 2937 | // Storing into nursery-allocated results object's elements; no post |
| 2938 | // barrier. |
| 2939 | masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement); |
| 2940 | masm.jump(&storeDone); |
| 2941 | } |
| 2942 | masm.bind(&isUndefined); |
| 2943 | { |
| 2944 | masm.storeValue(UndefinedValue(), objectMatchElement); |
| 2945 | } |
| 2946 | masm.bind(&storeDone); |
| 2947 | |
| 2948 | masm.add32(Imm32(1), matchIndex); |
| 2949 | masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, |
| 2950 | &done); |
| 2951 | masm.jump(&matchLoop); |
| 2952 | } |
| 2953 | |
| 2954 | #ifdef DEBUG1 |
| 2955 | masm.assumeUnreachable("The match string loop doesn't fall through."); |
| 2956 | #endif |
| 2957 | |
| 2958 | masm.bind(&done); |
| 2959 | } |
| 2960 | |
| 2961 | maybeRestoreRegExpAndLastIndex(); |
| 2962 | |
| 2963 | // Fill in the rest of the output object. |
| 2964 | masm.store32( |
| 2965 | matchIndex, |
| 2966 | Address(object, |
| 2967 | elementsOffset + ObjectElements::offsetOfInitializedLength())); |
| 2968 | masm.store32( |
| 2969 | matchIndex, |
| 2970 | Address(object, elementsOffset + ObjectElements::offsetOfLength())); |
| 2971 | |
| 2972 | Address firstMatchPairStartAddress( |
| 2973 | FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart()); |
| 2974 | Address firstMatchPairLimitAddress( |
| 2975 | FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
| 2976 | |
| 2977 | static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0, |
| 2978 | "First slot holds the 'index' property"); |
| 2979 | static_assert(RegExpRealm::MatchResultObjectInputSlot == 1, |
| 2980 | "Second slot holds the 'input' property"); |
| 2981 | |
| 2982 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
| 2983 | |
| 2984 | masm.load32(firstMatchPairStartAddress, temp3); |
| 2985 | masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0)); |
| 2986 | |
| 2987 | // No post barrier needed (address is within nursery object.) |
| 2988 | masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value))); |
| 2989 | |
| 2990 | // For the ExecMatch stub, if the regular expression is global or sticky, we |
| 2991 | // have to update its .lastIndex slot. |
| 2992 | if (isExecMatch) { |
| 2993 | MOZ_ASSERT(object != lastIndex)do { static_assert( mozilla::detail::AssertionConditionType< decltype(object != lastIndex)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(object != lastIndex))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("object != lastIndex" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 2993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "object != lastIndex" ")"); do { *((volatile int*)__null) = 2993; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 2994 | Label notGlobalOrSticky; |
| 2995 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 2996 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
| 2997 | ¬GlobalOrSticky); |
| 2998 | masm.load32(firstMatchPairLimitAddress, lastIndex); |
| 2999 | masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot); |
| 3000 | masm.bind(¬GlobalOrSticky); |
| 3001 | } |
| 3002 | |
| 3003 | // All done! |
| 3004 | masm.tagValue(JSVAL_TYPE_OBJECT, object, result); |
| 3005 | masm.pop(FramePointer); |
| 3006 | masm.ret(); |
| 3007 | |
| 3008 | masm.bind(¬Found); |
| 3009 | if (isExecMatch) { |
| 3010 | Label notGlobalOrSticky; |
| 3011 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 3012 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
| 3013 | ¬GlobalOrSticky); |
| 3014 | masm.bind(¬FoundZeroLastIndex); |
| 3015 | masm.storeValue(Int32Value(0), lastIndexSlot); |
| 3016 | masm.bind(¬GlobalOrSticky); |
| 3017 | } |
| 3018 | masm.moveValue(NullValue(), result); |
| 3019 | masm.pop(FramePointer); |
| 3020 | masm.ret(); |
| 3021 | |
| 3022 | // Fallback paths for CreateDependentString. |
| 3023 | for (auto& depStr : depStrs) { |
| 3024 | depStr.generateFallback(masm); |
| 3025 | } |
| 3026 | |
| 3027 | // Fall-through to the ool entry after restoring the registers. |
| 3028 | masm.bind(&restoreRegExpAndLastIndex); |
| 3029 | maybeRestoreRegExpAndLastIndex(); |
| 3030 | |
| 3031 | // Use an undefined value to signal to the caller that the OOL stub needs to |
| 3032 | // be called. |
| 3033 | masm.bind(&oolEntry); |
| 3034 | masm.moveValue(UndefinedValue(), result); |
| 3035 | masm.pop(FramePointer); |
| 3036 | masm.ret(); |
| 3037 | |
| 3038 | Linker linker(masm); |
| 3039 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
| 3040 | if (!code) { |
| 3041 | return nullptr; |
| 3042 | } |
| 3043 | |
| 3044 | const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub"; |
| 3045 | CollectPerfSpewerJitCodeProfile(code, name); |
| 3046 | #ifdef MOZ_VTUNE1 |
| 3047 | vtune::MarkStub(code, name); |
| 3048 | #endif |
| 3049 | |
| 3050 | return code; |
| 3051 | } |
| 3052 | |
| 3053 | JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) { |
| 3054 | return GenerateRegExpMatchStubShared(cx, initialStringHeap, |
| 3055 | /* isExecMatch = */ false); |
| 3056 | } |
| 3057 | |
| 3058 | JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) { |
| 3059 | return GenerateRegExpMatchStubShared(cx, initialStringHeap, |
| 3060 | /* isExecMatch = */ true); |
| 3061 | } |
| 3062 | |
| 3063 | class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator> { |
| 3064 | LRegExpMatcher* lir_; |
| 3065 | |
| 3066 | public: |
| 3067 | explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir) : lir_(lir) {} |
| 3068 | |
| 3069 | void accept(CodeGenerator* codegen) override { |
| 3070 | codegen->visitOutOfLineRegExpMatcher(this); |
| 3071 | } |
| 3072 | |
| 3073 | LRegExpMatcher* lir() const { return lir_; } |
| 3074 | }; |
| 3075 | |
| 3076 | void CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool) { |
| 3077 | LRegExpMatcher* lir = ool->lir(); |
| 3078 | Register lastIndex = ToRegister(lir->lastIndex()); |
| 3079 | Register input = ToRegister(lir->string()); |
| 3080 | Register regexp = ToRegister(lir->regexp()); |
| 3081 | |
| 3082 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3083 | regs.take(lastIndex); |
| 3084 | regs.take(input); |
| 3085 | regs.take(regexp); |
| 3086 | Register temp = regs.takeAny(); |
| 3087 | |
| 3088 | masm.computeEffectiveAddress( |
| 3089 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
| 3090 | |
| 3091 | pushArg(temp); |
| 3092 | pushArg(lastIndex); |
| 3093 | pushArg(input); |
| 3094 | pushArg(regexp); |
| 3095 | |
| 3096 | // We are not using oolCallVM because we are in a Call, and that live |
| 3097 | // registers are already saved by the the register allocator. |
| 3098 | using Fn = |
| 3099 | bool (*)(JSContext*, HandleObject regexp, HandleString input, |
| 3100 | int32_t lastIndex, MatchPairs* pairs, MutableHandleValue output); |
| 3101 | callVM<Fn, RegExpMatcherRaw>(lir); |
| 3102 | |
| 3103 | masm.jump(ool->rejoin()); |
| 3104 | } |
| 3105 | |
| 3106 | void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) { |
| 3107 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3107); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg" ")"); do { *((volatile int*)__null) = 3107; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3108 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpMatcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpMatcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpMatcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg" ")"); do { *((volatile int*)__null) = 3108; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3109 | MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg" ")"); do { *((volatile int*)__null) = 3109; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3110 | MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir) == JSReturnOperand)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutValue(lir) == JSReturnOperand ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutValue(lir) == JSReturnOperand", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3110); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand" ")"); do { *((volatile int*)__null) = 3110; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3111 | |
| 3112 | #if defined(JS_NUNBOX32) |
| 3113 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type); |
| 3114 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data); |
| 3115 | static_assert(RegExpMatcherStringReg != JSReturnReg_Type); |
| 3116 | static_assert(RegExpMatcherStringReg != JSReturnReg_Data); |
| 3117 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type); |
| 3118 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data); |
| 3119 | #elif defined(JS_PUNBOX641) |
| 3120 | static_assert(RegExpMatcherRegExpReg != JSReturnReg); |
| 3121 | static_assert(RegExpMatcherStringReg != JSReturnReg); |
| 3122 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg); |
| 3123 | #endif |
| 3124 | |
| 3125 | masm.reserveStack(RegExpReservedStack); |
| 3126 | |
| 3127 | OutOfLineRegExpMatcher* ool = new (alloc()) OutOfLineRegExpMatcher(lir); |
| 3128 | addOutOfLineCode(ool, lir->mir()); |
| 3129 | |
| 3130 | JitCode* regExpMatcherStub = |
| 3131 | snapshot_->getZoneStub(JitZone::StubKind::RegExpMatcher); |
| 3132 | masm.call(regExpMatcherStub); |
| 3133 | masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry()); |
| 3134 | masm.bind(ool->rejoin()); |
| 3135 | |
| 3136 | masm.freeStack(RegExpReservedStack); |
| 3137 | } |
| 3138 | |
| 3139 | class OutOfLineRegExpExecMatch : public OutOfLineCodeBase<CodeGenerator> { |
| 3140 | LRegExpExecMatch* lir_; |
| 3141 | |
| 3142 | public: |
| 3143 | explicit OutOfLineRegExpExecMatch(LRegExpExecMatch* lir) : lir_(lir) {} |
| 3144 | |
| 3145 | void accept(CodeGenerator* codegen) override { |
| 3146 | codegen->visitOutOfLineRegExpExecMatch(this); |
| 3147 | } |
| 3148 | |
| 3149 | LRegExpExecMatch* lir() const { return lir_; } |
| 3150 | }; |
| 3151 | |
| 3152 | void CodeGenerator::visitOutOfLineRegExpExecMatch( |
| 3153 | OutOfLineRegExpExecMatch* ool) { |
| 3154 | LRegExpExecMatch* lir = ool->lir(); |
| 3155 | Register input = ToRegister(lir->string()); |
| 3156 | Register regexp = ToRegister(lir->regexp()); |
| 3157 | |
| 3158 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3159 | regs.take(input); |
| 3160 | regs.take(regexp); |
| 3161 | Register temp = regs.takeAny(); |
| 3162 | |
| 3163 | masm.computeEffectiveAddress( |
| 3164 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
| 3165 | |
| 3166 | pushArg(temp); |
| 3167 | pushArg(input); |
| 3168 | pushArg(regexp); |
| 3169 | |
| 3170 | // We are not using oolCallVM because we are in a Call and live registers have |
| 3171 | // already been saved by the register allocator. |
| 3172 | using Fn = |
| 3173 | bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input, |
| 3174 | MatchPairs* pairs, MutableHandleValue output); |
| 3175 | callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir); |
| 3176 | masm.jump(ool->rejoin()); |
| 3177 | } |
| 3178 | |
| 3179 | void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) { |
| 3180 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3180); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg" ")"); do { *((volatile int*)__null) = 3180; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3181 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpMatcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpMatcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpMatcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg" ")"); do { *((volatile int*)__null) = 3181; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3182 | MOZ_ASSERT(ToOutValue(lir) == JSReturnOperand)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir) == JSReturnOperand)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutValue(lir) == JSReturnOperand ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutValue(lir) == JSReturnOperand", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand" ")"); do { *((volatile int*)__null) = 3182; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3183 | |
| 3184 | #if defined(JS_NUNBOX32) |
| 3185 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type); |
| 3186 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data); |
| 3187 | static_assert(RegExpMatcherStringReg != JSReturnReg_Type); |
| 3188 | static_assert(RegExpMatcherStringReg != JSReturnReg_Data); |
| 3189 | #elif defined(JS_PUNBOX641) |
| 3190 | static_assert(RegExpMatcherRegExpReg != JSReturnReg); |
| 3191 | static_assert(RegExpMatcherStringReg != JSReturnReg); |
| 3192 | #endif |
| 3193 | |
| 3194 | masm.reserveStack(RegExpReservedStack); |
| 3195 | |
| 3196 | auto* ool = new (alloc()) OutOfLineRegExpExecMatch(lir); |
| 3197 | addOutOfLineCode(ool, lir->mir()); |
| 3198 | |
| 3199 | JitCode* regExpExecMatchStub = |
| 3200 | snapshot_->getZoneStub(JitZone::StubKind::RegExpExecMatch); |
| 3201 | masm.call(regExpExecMatchStub); |
| 3202 | masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry()); |
| 3203 | |
| 3204 | masm.bind(ool->rejoin()); |
| 3205 | masm.freeStack(RegExpReservedStack); |
| 3206 | } |
| 3207 | |
| 3208 | JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) { |
| 3209 | JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub"); |
| 3210 | |
| 3211 | Register regexp = RegExpSearcherRegExpReg; |
| 3212 | Register input = RegExpSearcherStringReg; |
| 3213 | Register lastIndex = RegExpSearcherLastIndexReg; |
| 3214 | Register result = ReturnReg; |
| 3215 | |
| 3216 | // We are free to clobber all registers, as LRegExpSearcher is a call |
| 3217 | // instruction. |
| 3218 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3219 | regs.take(input); |
| 3220 | regs.take(regexp); |
| 3221 | regs.take(lastIndex); |
| 3222 | |
| 3223 | Register temp1 = regs.takeAny(); |
| 3224 | Register temp2 = regs.takeAny(); |
| 3225 | Register temp3 = regs.takeAny(); |
| 3226 | |
| 3227 | TempAllocator temp(&cx->tempLifoAlloc()); |
| 3228 | JitContext jcx(cx); |
| 3229 | StackMacroAssembler masm(cx, temp); |
| 3230 | AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub"); |
| 3231 | |
| 3232 | #ifdef JS_USE_LINK_REGISTER |
| 3233 | masm.pushReturnAddress(); |
| 3234 | #endif |
| 3235 | masm.push(FramePointer); |
| 3236 | masm.moveStackPtrTo(FramePointer); |
| 3237 | |
| 3238 | #ifdef DEBUG1 |
| 3239 | // Store sentinel value to cx->regExpSearcherLastLimit. |
| 3240 | // See comment in RegExpSearcherImpl. |
| 3241 | masm.loadJSContext(temp1); |
| 3242 | masm.store32(Imm32(RegExpSearcherLastLimitSentinel), |
| 3243 | Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit())); |
| 3244 | #endif |
| 3245 | |
| 3246 | // The InputOutputData is placed above the frame pointer and return address on |
| 3247 | // the stack. |
| 3248 | int32_t inputOutputDataStartOffset = 2 * sizeof(void*); |
| 3249 | |
| 3250 | Label notFound, oolEntry; |
| 3251 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
| 3252 | temp3, inputOutputDataStartOffset, |
| 3253 | initialStringHeap, ¬Found, &oolEntry)) { |
| 3254 | return nullptr; |
| 3255 | } |
| 3256 | |
| 3257 | // clang-format off |
| 3258 | /* |
| 3259 | * [SMDOC] Stack layout for the RegExpSearcher stub |
| 3260 | * |
| 3261 | * +---------------+ |
| 3262 | * FramePointer +-----> |Caller-FramePtr| |
| 3263 | * +---------------+ |
| 3264 | * |Return-Address | |
| 3265 | * +---------------+ |
| 3266 | * inputOutputDataStartOffset +-----> +---------------+ |
| 3267 | * |InputOutputData| |
| 3268 | * +---------------+ |
| 3269 | * +---------------+ |
| 3270 | * | MatchPairs | |
| 3271 | * | count | |
| 3272 | * | pairs | |
| 3273 | * | | |
| 3274 | * +---------------+ |
| 3275 | * pairsVectorStartOffset +-----> +---------------+ |
| 3276 | * | MatchPair | |
| 3277 | * matchPairStart +------------> start | <-------+ |
| 3278 | * matchPairLimit +------------> limit | | Reserved space for |
| 3279 | * +---------------+ | `RegExpObject::MaxPairCount` |
| 3280 | * . | MatchPair objects. |
| 3281 | * . | |
| 3282 | * . | Only a single object will |
| 3283 | * +---------------+ | be initialized and can be |
| 3284 | * | MatchPair | | accessed below. |
| 3285 | * | start | <-------+ |
| 3286 | * | limit | |
| 3287 | * +---------------+ |
| 3288 | */ |
| 3289 | // clang-format on |
| 3290 | |
| 3291 | int32_t pairsVectorStartOffset = |
| 3292 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
| 3293 | Address matchPairStart(FramePointer, |
| 3294 | pairsVectorStartOffset + MatchPair::offsetOfStart()); |
| 3295 | Address matchPairLimit(FramePointer, |
| 3296 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
| 3297 | |
| 3298 | // Store match limit to cx->regExpSearcherLastLimit and return the index. |
| 3299 | masm.load32(matchPairLimit, result); |
| 3300 | masm.loadJSContext(input); |
| 3301 | masm.store32(result, |
| 3302 | Address(input, JSContext::offsetOfRegExpSearcherLastLimit())); |
| 3303 | masm.load32(matchPairStart, result); |
| 3304 | masm.pop(FramePointer); |
| 3305 | masm.ret(); |
| 3306 | |
| 3307 | masm.bind(¬Found); |
| 3308 | masm.move32(Imm32(RegExpSearcherResultNotFound), result); |
| 3309 | masm.pop(FramePointer); |
| 3310 | masm.ret(); |
| 3311 | |
| 3312 | masm.bind(&oolEntry); |
| 3313 | masm.move32(Imm32(RegExpSearcherResultFailed), result); |
| 3314 | masm.pop(FramePointer); |
| 3315 | masm.ret(); |
| 3316 | |
| 3317 | Linker linker(masm); |
| 3318 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
| 3319 | if (!code) { |
| 3320 | return nullptr; |
| 3321 | } |
| 3322 | |
| 3323 | CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub"); |
| 3324 | #ifdef MOZ_VTUNE1 |
| 3325 | vtune::MarkStub(code, "RegExpSearcherStub"); |
| 3326 | #endif |
| 3327 | |
| 3328 | return code; |
| 3329 | } |
| 3330 | |
| 3331 | class OutOfLineRegExpSearcher : public OutOfLineCodeBase<CodeGenerator> { |
| 3332 | LRegExpSearcher* lir_; |
| 3333 | |
| 3334 | public: |
| 3335 | explicit OutOfLineRegExpSearcher(LRegExpSearcher* lir) : lir_(lir) {} |
| 3336 | |
| 3337 | void accept(CodeGenerator* codegen) override { |
| 3338 | codegen->visitOutOfLineRegExpSearcher(this); |
| 3339 | } |
| 3340 | |
| 3341 | LRegExpSearcher* lir() const { return lir_; } |
| 3342 | }; |
| 3343 | |
| 3344 | void CodeGenerator::visitOutOfLineRegExpSearcher(OutOfLineRegExpSearcher* ool) { |
| 3345 | LRegExpSearcher* lir = ool->lir(); |
| 3346 | Register lastIndex = ToRegister(lir->lastIndex()); |
| 3347 | Register input = ToRegister(lir->string()); |
| 3348 | Register regexp = ToRegister(lir->regexp()); |
| 3349 | |
| 3350 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3351 | regs.take(lastIndex); |
| 3352 | regs.take(input); |
| 3353 | regs.take(regexp); |
| 3354 | Register temp = regs.takeAny(); |
| 3355 | |
| 3356 | masm.computeEffectiveAddress( |
| 3357 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
| 3358 | |
| 3359 | pushArg(temp); |
| 3360 | pushArg(lastIndex); |
| 3361 | pushArg(input); |
| 3362 | pushArg(regexp); |
| 3363 | |
| 3364 | // We are not using oolCallVM because we are in a Call, and that live |
| 3365 | // registers are already saved by the the register allocator. |
| 3366 | using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input, |
| 3367 | int32_t lastIndex, MatchPairs* pairs, int32_t* result); |
| 3368 | callVM<Fn, RegExpSearcherRaw>(lir); |
| 3369 | |
| 3370 | masm.jump(ool->rejoin()); |
| 3371 | } |
| 3372 | |
| 3373 | void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) { |
| 3374 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpSearcherRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3374); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg" ")"); do { *((volatile int*)__null) = 3374; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3375 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpSearcherStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpSearcherStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpSearcherStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpSearcherStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3375); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg" ")"); do { *((volatile int*)__null) = 3375; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3376 | MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg" ")"); do { *((volatile int*)__null) = 3376; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3377 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3377); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 3377; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3378 | |
| 3379 | static_assert(RegExpSearcherRegExpReg != ReturnReg); |
| 3380 | static_assert(RegExpSearcherStringReg != ReturnReg); |
| 3381 | static_assert(RegExpSearcherLastIndexReg != ReturnReg); |
| 3382 | |
| 3383 | masm.reserveStack(RegExpReservedStack); |
| 3384 | |
| 3385 | OutOfLineRegExpSearcher* ool = new (alloc()) OutOfLineRegExpSearcher(lir); |
| 3386 | addOutOfLineCode(ool, lir->mir()); |
| 3387 | |
| 3388 | JitCode* regExpSearcherStub = |
| 3389 | snapshot_->getZoneStub(JitZone::StubKind::RegExpSearcher); |
| 3390 | masm.call(regExpSearcherStub); |
| 3391 | masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed), |
| 3392 | ool->entry()); |
| 3393 | masm.bind(ool->rejoin()); |
| 3394 | |
| 3395 | masm.freeStack(RegExpReservedStack); |
| 3396 | } |
| 3397 | |
| 3398 | void CodeGenerator::visitRegExpSearcherLastLimit( |
| 3399 | LRegExpSearcherLastLimit* lir) { |
| 3400 | Register result = ToRegister(lir->output()); |
| 3401 | Register scratch = ToRegister(lir->temp0()); |
| 3402 | |
| 3403 | masm.loadAndClearRegExpSearcherLastLimit(result, scratch); |
| 3404 | } |
| 3405 | |
| 3406 | JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) { |
| 3407 | JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub"); |
| 3408 | |
| 3409 | Register regexp = RegExpExecTestRegExpReg; |
| 3410 | Register input = RegExpExecTestStringReg; |
| 3411 | Register result = ReturnReg; |
| 3412 | |
| 3413 | TempAllocator temp(&cx->tempLifoAlloc()); |
| 3414 | JitContext jcx(cx); |
| 3415 | StackMacroAssembler masm(cx, temp); |
| 3416 | AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub"); |
| 3417 | |
| 3418 | #ifdef JS_USE_LINK_REGISTER |
| 3419 | masm.pushReturnAddress(); |
| 3420 | #endif |
| 3421 | masm.push(FramePointer); |
| 3422 | masm.moveStackPtrTo(FramePointer); |
| 3423 | |
| 3424 | // We are free to clobber all registers, as LRegExpExecTest is a call |
| 3425 | // instruction. |
| 3426 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3427 | regs.take(input); |
| 3428 | regs.take(regexp); |
| 3429 | |
| 3430 | // Ensure lastIndex != result. |
| 3431 | regs.take(result); |
| 3432 | Register lastIndex = regs.takeAny(); |
| 3433 | regs.add(result); |
| 3434 | Register temp1 = regs.takeAny(); |
| 3435 | Register temp2 = regs.takeAny(); |
| 3436 | Register temp3 = regs.takeAny(); |
| 3437 | |
| 3438 | Address flagsSlot(regexp, RegExpObject::offsetOfFlags()); |
| 3439 | Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex()); |
| 3440 | |
| 3441 | masm.reserveStack(RegExpReservedStack); |
| 3442 | |
| 3443 | // Load lastIndex and skip RegExp execution if needed. |
| 3444 | Label notFoundZeroLastIndex; |
| 3445 | masm.loadRegExpLastIndex(regexp, input, lastIndex, ¬FoundZeroLastIndex); |
| 3446 | |
| 3447 | // In visitRegExpMatcher and visitRegExpSearcher, we reserve stack space |
| 3448 | // before calling the stub. For RegExpExecTest we call the stub before |
| 3449 | // reserving stack space, so the offset of the InputOutputData relative to the |
| 3450 | // frame pointer is negative. |
| 3451 | constexpr int32_t inputOutputDataStartOffset = -int32_t(RegExpReservedStack); |
| 3452 | |
| 3453 | // On ARM64, load/store instructions can encode an immediate offset in the |
| 3454 | // range [-256, 4095]. If we ever fail this assertion, it would be more |
| 3455 | // efficient to store the data above the frame pointer similar to |
| 3456 | // RegExpMatcher and RegExpSearcher. |
| 3457 | static_assert(inputOutputDataStartOffset >= -256); |
| 3458 | |
| 3459 | Label notFound, oolEntry; |
| 3460 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
| 3461 | temp3, inputOutputDataStartOffset, |
| 3462 | initialStringHeap, ¬Found, &oolEntry)) { |
| 3463 | return nullptr; |
| 3464 | } |
| 3465 | |
| 3466 | // Set `result` to true/false to indicate found/not-found, or to |
| 3467 | // RegExpExecTestResultFailed if we have to retry in C++. If the regular |
| 3468 | // expression is global or sticky, we also have to update its .lastIndex slot. |
| 3469 | |
| 3470 | Label done; |
| 3471 | int32_t pairsVectorStartOffset = |
| 3472 | RegExpPairsVectorStartOffset(inputOutputDataStartOffset); |
| 3473 | Address matchPairLimit(FramePointer, |
| 3474 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
| 3475 | |
| 3476 | masm.move32(Imm32(1), result); |
| 3477 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 3478 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
| 3479 | &done); |
| 3480 | masm.load32(matchPairLimit, lastIndex); |
| 3481 | masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot); |
| 3482 | masm.jump(&done); |
| 3483 | |
| 3484 | masm.bind(¬Found); |
| 3485 | masm.move32(Imm32(0), result); |
| 3486 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 3487 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
| 3488 | &done); |
| 3489 | masm.storeValue(Int32Value(0), lastIndexSlot); |
| 3490 | masm.jump(&done); |
| 3491 | |
| 3492 | masm.bind(¬FoundZeroLastIndex); |
| 3493 | masm.move32(Imm32(0), result); |
| 3494 | masm.storeValue(Int32Value(0), lastIndexSlot); |
| 3495 | masm.jump(&done); |
| 3496 | |
| 3497 | masm.bind(&oolEntry); |
| 3498 | masm.move32(Imm32(RegExpExecTestResultFailed), result); |
| 3499 | |
| 3500 | masm.bind(&done); |
| 3501 | masm.freeStack(RegExpReservedStack); |
| 3502 | masm.pop(FramePointer); |
| 3503 | masm.ret(); |
| 3504 | |
| 3505 | Linker linker(masm); |
| 3506 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
| 3507 | if (!code) { |
| 3508 | return nullptr; |
| 3509 | } |
| 3510 | |
| 3511 | CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub"); |
| 3512 | #ifdef MOZ_VTUNE1 |
| 3513 | vtune::MarkStub(code, "RegExpExecTestStub"); |
| 3514 | #endif |
| 3515 | |
| 3516 | return code; |
| 3517 | } |
| 3518 | |
| 3519 | class OutOfLineRegExpExecTest : public OutOfLineCodeBase<CodeGenerator> { |
| 3520 | LRegExpExecTest* lir_; |
| 3521 | |
| 3522 | public: |
| 3523 | explicit OutOfLineRegExpExecTest(LRegExpExecTest* lir) : lir_(lir) {} |
| 3524 | |
| 3525 | void accept(CodeGenerator* codegen) override { |
| 3526 | codegen->visitOutOfLineRegExpExecTest(this); |
| 3527 | } |
| 3528 | |
| 3529 | LRegExpExecTest* lir() const { return lir_; } |
| 3530 | }; |
| 3531 | |
| 3532 | void CodeGenerator::visitOutOfLineRegExpExecTest(OutOfLineRegExpExecTest* ool) { |
| 3533 | LRegExpExecTest* lir = ool->lir(); |
| 3534 | Register input = ToRegister(lir->string()); |
| 3535 | Register regexp = ToRegister(lir->regexp()); |
| 3536 | |
| 3537 | pushArg(input); |
| 3538 | pushArg(regexp); |
| 3539 | |
| 3540 | // We are not using oolCallVM because we are in a Call and live registers have |
| 3541 | // already been saved by the register allocator. |
| 3542 | using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp, |
| 3543 | HandleString input, bool* result); |
| 3544 | callVM<Fn, RegExpBuiltinExecTestFromJit>(lir); |
| 3545 | |
| 3546 | masm.jump(ool->rejoin()); |
| 3547 | } |
| 3548 | |
| 3549 | void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) { |
| 3550 | MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->regexp()) == RegExpExecTestRegExpReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3550); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg" ")"); do { *((volatile int*)__null) = 3550; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3551 | MOZ_ASSERT(ToRegister(lir->string()) == RegExpExecTestStringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->string()) == RegExpExecTestStringReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->string()) == RegExpExecTestStringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(lir->string()) == RegExpExecTestStringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3551); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg" ")"); do { *((volatile int*)__null) = 3551; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3552 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3552); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 3552; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3553 | |
| 3554 | static_assert(RegExpExecTestRegExpReg != ReturnReg); |
| 3555 | static_assert(RegExpExecTestStringReg != ReturnReg); |
| 3556 | |
| 3557 | auto* ool = new (alloc()) OutOfLineRegExpExecTest(lir); |
| 3558 | addOutOfLineCode(ool, lir->mir()); |
| 3559 | |
| 3560 | JitCode* regExpExecTestStub = |
| 3561 | snapshot_->getZoneStub(JitZone::StubKind::RegExpExecTest); |
| 3562 | masm.call(regExpExecTestStub); |
| 3563 | |
| 3564 | masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed), |
| 3565 | ool->entry()); |
| 3566 | |
| 3567 | masm.bind(ool->rejoin()); |
| 3568 | } |
| 3569 | |
| 3570 | void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) { |
| 3571 | Register regexp = ToRegister(ins->regexp()); |
| 3572 | Register input = ToRegister(ins->input()); |
| 3573 | Register output = ToRegister(ins->output()); |
| 3574 | |
| 3575 | using Fn = |
| 3576 | bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*); |
| 3577 | auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>( |
| 3578 | ins, ArgList(regexp, input), StoreRegisterTo(output)); |
| 3579 | |
| 3580 | // Load RegExpShared in |output|. |
| 3581 | Label vmCall; |
| 3582 | masm.loadParsedRegExpShared(regexp, output, ool->entry()); |
| 3583 | |
| 3584 | // Return true iff pairCount > 1. |
| 3585 | Label returnTrue; |
| 3586 | masm.branch32(Assembler::Above, |
| 3587 | Address(output, RegExpShared::offsetOfPairCount()), Imm32(1), |
| 3588 | &returnTrue); |
| 3589 | masm.move32(Imm32(0), output); |
| 3590 | masm.jump(ool->rejoin()); |
| 3591 | |
| 3592 | masm.bind(&returnTrue); |
| 3593 | masm.move32(Imm32(1), output); |
| 3594 | |
| 3595 | masm.bind(ool->rejoin()); |
| 3596 | } |
| 3597 | |
| 3598 | class OutOfLineRegExpPrototypeOptimizable |
| 3599 | : public OutOfLineCodeBase<CodeGenerator> { |
| 3600 | LRegExpPrototypeOptimizable* ins_; |
| 3601 | |
| 3602 | public: |
| 3603 | explicit OutOfLineRegExpPrototypeOptimizable(LRegExpPrototypeOptimizable* ins) |
| 3604 | : ins_(ins) {} |
| 3605 | |
| 3606 | void accept(CodeGenerator* codegen) override { |
| 3607 | codegen->visitOutOfLineRegExpPrototypeOptimizable(this); |
| 3608 | } |
| 3609 | LRegExpPrototypeOptimizable* ins() const { return ins_; } |
| 3610 | }; |
| 3611 | |
| 3612 | void CodeGenerator::visitRegExpPrototypeOptimizable( |
| 3613 | LRegExpPrototypeOptimizable* ins) { |
| 3614 | Register object = ToRegister(ins->object()); |
| 3615 | Register output = ToRegister(ins->output()); |
| 3616 | Register temp = ToRegister(ins->temp0()); |
| 3617 | |
| 3618 | OutOfLineRegExpPrototypeOptimizable* ool = |
| 3619 | new (alloc()) OutOfLineRegExpPrototypeOptimizable(ins); |
| 3620 | addOutOfLineCode(ool, ins->mir()); |
| 3621 | |
| 3622 | const GlobalObject* global = gen->realm->maybeGlobal(); |
| 3623 | MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType< decltype(global)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("global", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3623); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")" ); do { *((volatile int*)__null) = 3623; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3624 | masm.branchIfNotRegExpPrototypeOptimizable(object, temp, global, |
| 3625 | ool->entry()); |
| 3626 | masm.move32(Imm32(0x1), output); |
| 3627 | |
| 3628 | masm.bind(ool->rejoin()); |
| 3629 | } |
| 3630 | |
| 3631 | void CodeGenerator::visitOutOfLineRegExpPrototypeOptimizable( |
| 3632 | OutOfLineRegExpPrototypeOptimizable* ool) { |
| 3633 | LRegExpPrototypeOptimizable* ins = ool->ins(); |
| 3634 | Register object = ToRegister(ins->object()); |
| 3635 | Register output = ToRegister(ins->output()); |
| 3636 | |
| 3637 | saveVolatile(output); |
| 3638 | |
| 3639 | using Fn = bool (*)(JSContext* cx, JSObject* proto); |
| 3640 | masm.setupAlignedABICall(); |
| 3641 | masm.loadJSContext(output); |
| 3642 | masm.passABIArg(output); |
| 3643 | masm.passABIArg(object); |
| 3644 | masm.callWithABI<Fn, RegExpPrototypeOptimizableRaw>(); |
| 3645 | masm.storeCallBoolResult(output); |
| 3646 | |
| 3647 | restoreVolatile(output); |
| 3648 | |
| 3649 | masm.jump(ool->rejoin()); |
| 3650 | } |
| 3651 | |
| 3652 | class OutOfLineRegExpInstanceOptimizable |
| 3653 | : public OutOfLineCodeBase<CodeGenerator> { |
| 3654 | LRegExpInstanceOptimizable* ins_; |
| 3655 | |
| 3656 | public: |
| 3657 | explicit OutOfLineRegExpInstanceOptimizable(LRegExpInstanceOptimizable* ins) |
| 3658 | : ins_(ins) {} |
| 3659 | |
| 3660 | void accept(CodeGenerator* codegen) override { |
| 3661 | codegen->visitOutOfLineRegExpInstanceOptimizable(this); |
| 3662 | } |
| 3663 | LRegExpInstanceOptimizable* ins() const { return ins_; } |
| 3664 | }; |
| 3665 | |
| 3666 | void CodeGenerator::visitRegExpInstanceOptimizable( |
| 3667 | LRegExpInstanceOptimizable* ins) { |
| 3668 | Register object = ToRegister(ins->object()); |
| 3669 | Register output = ToRegister(ins->output()); |
| 3670 | Register temp = ToRegister(ins->temp0()); |
| 3671 | |
| 3672 | OutOfLineRegExpInstanceOptimizable* ool = |
| 3673 | new (alloc()) OutOfLineRegExpInstanceOptimizable(ins); |
| 3674 | addOutOfLineCode(ool, ins->mir()); |
| 3675 | |
| 3676 | const GlobalObject* global = gen->realm->maybeGlobal(); |
| 3677 | MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType< decltype(global)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("global", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")" ); do { *((volatile int*)__null) = 3677; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3678 | masm.branchIfNotRegExpInstanceOptimizable(object, temp, global, ool->entry()); |
| 3679 | masm.move32(Imm32(0x1), output); |
| 3680 | |
| 3681 | masm.bind(ool->rejoin()); |
| 3682 | } |
| 3683 | |
| 3684 | void CodeGenerator::visitOutOfLineRegExpInstanceOptimizable( |
| 3685 | OutOfLineRegExpInstanceOptimizable* ool) { |
| 3686 | LRegExpInstanceOptimizable* ins = ool->ins(); |
| 3687 | Register object = ToRegister(ins->object()); |
| 3688 | Register proto = ToRegister(ins->proto()); |
| 3689 | Register output = ToRegister(ins->output()); |
| 3690 | |
| 3691 | saveVolatile(output); |
| 3692 | |
| 3693 | using Fn = bool (*)(JSContext* cx, JSObject* obj, JSObject* proto); |
| 3694 | masm.setupAlignedABICall(); |
| 3695 | masm.loadJSContext(output); |
| 3696 | masm.passABIArg(output); |
| 3697 | masm.passABIArg(object); |
| 3698 | masm.passABIArg(proto); |
| 3699 | masm.callWithABI<Fn, RegExpInstanceOptimizableRaw>(); |
| 3700 | masm.storeCallBoolResult(output); |
| 3701 | |
| 3702 | restoreVolatile(output); |
| 3703 | |
| 3704 | masm.jump(ool->rejoin()); |
| 3705 | } |
| 3706 | |
| 3707 | static void FindFirstDollarIndex(MacroAssembler& masm, Register str, |
| 3708 | Register len, Register temp0, Register temp1, |
| 3709 | Register output, CharEncoding encoding) { |
| 3710 | #ifdef DEBUG1 |
| 3711 | Label ok; |
| 3712 | masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); |
| 3713 | masm.assumeUnreachable("Length should be greater than 0."); |
| 3714 | masm.bind(&ok); |
| 3715 | #endif |
| 3716 | |
| 3717 | Register chars = temp0; |
| 3718 | masm.loadStringChars(str, chars, encoding); |
| 3719 | |
| 3720 | masm.move32(Imm32(0), output); |
| 3721 | |
| 3722 | Label start, done; |
| 3723 | masm.bind(&start); |
| 3724 | |
| 3725 | Register currentChar = temp1; |
| 3726 | masm.loadChar(chars, output, currentChar, encoding); |
| 3727 | masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done); |
| 3728 | |
| 3729 | masm.add32(Imm32(1), output); |
| 3730 | masm.branch32(Assembler::NotEqual, output, len, &start); |
| 3731 | |
| 3732 | masm.move32(Imm32(-1), output); |
| 3733 | |
| 3734 | masm.bind(&done); |
| 3735 | } |
| 3736 | |
| 3737 | void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) { |
| 3738 | Register str = ToRegister(ins->str()); |
| 3739 | Register output = ToRegister(ins->output()); |
| 3740 | Register temp0 = ToRegister(ins->temp0()); |
| 3741 | Register temp1 = ToRegister(ins->temp1()); |
| 3742 | Register len = ToRegister(ins->temp2()); |
| 3743 | |
| 3744 | using Fn = bool (*)(JSContext*, JSString*, int32_t*); |
| 3745 | OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>( |
| 3746 | ins, ArgList(str), StoreRegisterTo(output)); |
| 3747 | |
| 3748 | masm.branchIfRope(str, ool->entry()); |
| 3749 | masm.loadStringLength(str, len); |
| 3750 | |
| 3751 | Label isLatin1, done; |
| 3752 | masm.branchLatin1String(str, &isLatin1); |
| 3753 | { |
| 3754 | FindFirstDollarIndex(masm, str, len, temp0, temp1, output, |
| 3755 | CharEncoding::TwoByte); |
| 3756 | masm.jump(&done); |
| 3757 | } |
| 3758 | masm.bind(&isLatin1); |
| 3759 | { |
| 3760 | FindFirstDollarIndex(masm, str, len, temp0, temp1, output, |
| 3761 | CharEncoding::Latin1); |
| 3762 | } |
| 3763 | masm.bind(&done); |
| 3764 | masm.bind(ool->rejoin()); |
| 3765 | } |
| 3766 | |
| 3767 | void CodeGenerator::visitStringReplace(LStringReplace* lir) { |
| 3768 | if (lir->replacement()->isConstant()) { |
| 3769 | pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString())); |
| 3770 | } else { |
| 3771 | pushArg(ToRegister(lir->replacement())); |
| 3772 | } |
| 3773 | |
| 3774 | if (lir->pattern()->isConstant()) { |
| 3775 | pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString())); |
| 3776 | } else { |
| 3777 | pushArg(ToRegister(lir->pattern())); |
| 3778 | } |
| 3779 | |
| 3780 | if (lir->string()->isConstant()) { |
| 3781 | pushArg(ImmGCPtr(lir->string()->toConstant()->toString())); |
| 3782 | } else { |
| 3783 | pushArg(ToRegister(lir->string())); |
| 3784 | } |
| 3785 | |
| 3786 | using Fn = |
| 3787 | JSString* (*)(JSContext*, HandleString, HandleString, HandleString); |
| 3788 | if (lir->mir()->isFlatReplacement()) { |
| 3789 | callVM<Fn, StringFlatReplaceString>(lir); |
| 3790 | } else { |
| 3791 | callVM<Fn, StringReplace>(lir); |
| 3792 | } |
| 3793 | } |
| 3794 | |
| 3795 | void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) { |
| 3796 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 3797 | TypedOrValueRegister lhs = |
| 3798 | TypedOrValueRegister(ToValue(lir, LBinaryValueCache::LhsIndex)); |
| 3799 | TypedOrValueRegister rhs = |
| 3800 | TypedOrValueRegister(ToValue(lir, LBinaryValueCache::RhsIndex)); |
| 3801 | ValueOperand output = ToOutValue(lir); |
| 3802 | |
| 3803 | JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc()); |
| 3804 | |
| 3805 | switch (jsop) { |
| 3806 | case JSOp::Add: |
| 3807 | case JSOp::Sub: |
| 3808 | case JSOp::Mul: |
| 3809 | case JSOp::Div: |
| 3810 | case JSOp::Mod: |
| 3811 | case JSOp::Pow: |
| 3812 | case JSOp::BitAnd: |
| 3813 | case JSOp::BitOr: |
| 3814 | case JSOp::BitXor: |
| 3815 | case JSOp::Lsh: |
| 3816 | case JSOp::Rsh: |
| 3817 | case JSOp::Ursh: { |
| 3818 | IonBinaryArithIC ic(liveRegs, lhs, rhs, output); |
| 3819 | addIC(lir, allocateIC(ic)); |
| 3820 | return; |
| 3821 | } |
| 3822 | default: |
| 3823 | MOZ_CRASH("Unsupported jsop in MBinaryValueCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryValueCache" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3823); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache" ")"); do { *((volatile int*)__null) = 3823; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 3824 | } |
| 3825 | } |
| 3826 | |
| 3827 | void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) { |
| 3828 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 3829 | TypedOrValueRegister lhs = |
| 3830 | TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::LhsIndex)); |
| 3831 | TypedOrValueRegister rhs = |
| 3832 | TypedOrValueRegister(ToValue(lir, LBinaryBoolCache::RhsIndex)); |
| 3833 | Register output = ToRegister(lir->output()); |
| 3834 | |
| 3835 | JSOp jsop = JSOp(*lir->mirRaw()->toInstruction()->resumePoint()->pc()); |
| 3836 | |
| 3837 | switch (jsop) { |
| 3838 | case JSOp::Lt: |
| 3839 | case JSOp::Le: |
| 3840 | case JSOp::Gt: |
| 3841 | case JSOp::Ge: |
| 3842 | case JSOp::Eq: |
| 3843 | case JSOp::Ne: |
| 3844 | case JSOp::StrictEq: |
| 3845 | case JSOp::StrictNe: { |
| 3846 | IonCompareIC ic(liveRegs, lhs, rhs, output); |
| 3847 | addIC(lir, allocateIC(ic)); |
| 3848 | return; |
| 3849 | } |
| 3850 | default: |
| 3851 | MOZ_CRASH("Unsupported jsop in MBinaryBoolCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryBoolCache" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3851); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache" ")"); do { *((volatile int*)__null) = 3851; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 3852 | } |
| 3853 | } |
| 3854 | |
| 3855 | void CodeGenerator::visitUnaryCache(LUnaryCache* lir) { |
| 3856 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 3857 | TypedOrValueRegister input = |
| 3858 | TypedOrValueRegister(ToValue(lir, LUnaryCache::InputIndex)); |
| 3859 | ValueOperand output = ToOutValue(lir); |
| 3860 | |
| 3861 | IonUnaryArithIC ic(liveRegs, input, output); |
| 3862 | addIC(lir, allocateIC(ic)); |
| 3863 | } |
| 3864 | |
| 3865 | void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) { |
| 3866 | pushArg(ImmPtr(lir->mir()->module())); |
| 3867 | |
| 3868 | using Fn = JSObject* (*)(JSContext*, HandleObject); |
| 3869 | callVM<Fn, js::GetOrCreateModuleMetaObject>(lir); |
| 3870 | } |
| 3871 | |
| 3872 | void CodeGenerator::visitDynamicImport(LDynamicImport* lir) { |
| 3873 | pushArg(ToValue(lir, LDynamicImport::OptionsIndex)); |
| 3874 | pushArg(ToValue(lir, LDynamicImport::SpecifierIndex)); |
| 3875 | pushArg(ImmGCPtr(current->mir()->info().script())); |
| 3876 | |
| 3877 | using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue); |
| 3878 | callVM<Fn, js::StartDynamicModuleImport>(lir); |
| 3879 | } |
| 3880 | |
| 3881 | void CodeGenerator::visitLambda(LLambda* lir) { |
| 3882 | Register envChain = ToRegister(lir->environmentChain()); |
| 3883 | Register output = ToRegister(lir->output()); |
| 3884 | Register tempReg = ToRegister(lir->temp0()); |
| 3885 | |
| 3886 | JSFunction* fun = lir->mir()->templateFunction(); |
| 3887 | |
| 3888 | using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject); |
| 3889 | OutOfLineCode* ool = oolCallVM<Fn, js::Lambda>( |
| 3890 | lir, ArgList(ImmGCPtr(fun), envChain), StoreRegisterTo(output)); |
| 3891 | |
| 3892 | TemplateObject templateObject(fun); |
| 3893 | masm.createGCObject(output, tempReg, templateObject, gc::Heap::Default, |
| 3894 | ool->entry()); |
| 3895 | |
| 3896 | masm.storeValue(JSVAL_TYPE_OBJECT, envChain, |
| 3897 | Address(output, JSFunction::offsetOfEnvironment())); |
| 3898 | // No post barrier needed because output is guaranteed to be allocated in |
| 3899 | // the nursery. |
| 3900 | |
| 3901 | masm.bind(ool->rejoin()); |
| 3902 | } |
| 3903 | |
| 3904 | void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) { |
| 3905 | Register envChain = ToRegister(lir->envChain()); |
| 3906 | Register prototype = ToRegister(lir->prototype()); |
| 3907 | |
| 3908 | pushArg(prototype); |
| 3909 | pushArg(envChain); |
| 3910 | pushArg(ImmGCPtr(lir->mir()->function())); |
| 3911 | |
| 3912 | using Fn = |
| 3913 | JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject); |
| 3914 | callVM<Fn, js::FunWithProtoOperation>(lir); |
| 3915 | } |
| 3916 | |
| 3917 | void CodeGenerator::visitSetFunName(LSetFunName* lir) { |
| 3918 | pushArg(Imm32(lir->mir()->prefixKind())); |
| 3919 | pushArg(ToValue(lir, LSetFunName::NameIndex)); |
| 3920 | pushArg(ToRegister(lir->fun())); |
| 3921 | |
| 3922 | using Fn = |
| 3923 | bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind); |
| 3924 | callVM<Fn, js::SetFunctionName>(lir); |
| 3925 | } |
| 3926 | |
| 3927 | void CodeGenerator::visitOsiPoint(LOsiPoint* lir) { |
| 3928 | // Note: markOsiPoint ensures enough space exists between the last |
| 3929 | // LOsiPoint and this one to patch adjacent call instructions. |
| 3930 | |
| 3931 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3931); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 3931; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3932 | |
| 3933 | uint32_t osiCallPointOffset = markOsiPoint(lir); |
| 3934 | |
| 3935 | LSafepoint* safepoint = lir->associatedSafepoint(); |
| 3936 | MOZ_ASSERT(!safepoint->osiCallPointOffset())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!safepoint->osiCallPointOffset())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!safepoint->osiCallPointOffset ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!safepoint->osiCallPointOffset()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3936); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()" ")"); do { *((volatile int*)__null) = 3936; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3937 | safepoint->setOsiCallPointOffset(osiCallPointOffset); |
| 3938 | |
| 3939 | #ifdef DEBUG1 |
| 3940 | // There should be no movegroups or other instructions between |
| 3941 | // an instruction and its OsiPoint. This is necessary because |
| 3942 | // we use the OsiPoint's snapshot from within VM calls. |
| 3943 | for (LInstructionReverseIterator iter(current->rbegin(lir)); |
| 3944 | iter != current->rend(); iter++) { |
| 3945 | if (*iter == lir) { |
| 3946 | continue; |
| 3947 | } |
| 3948 | MOZ_ASSERT(!iter->isMoveGroup())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!iter->isMoveGroup())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!iter->isMoveGroup()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!iter->isMoveGroup()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3948); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!iter->isMoveGroup()" ")"); do { *((volatile int*)__null) = 3948; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3949 | MOZ_ASSERT(iter->safepoint() == safepoint)do { static_assert( mozilla::detail::AssertionConditionType< decltype(iter->safepoint() == safepoint)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(iter->safepoint() == safepoint ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "iter->safepoint() == safepoint", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3949); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint" ")"); do { *((volatile int*)__null) = 3949; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3950 | break; |
| 3951 | } |
| 3952 | #endif |
| 3953 | |
| 3954 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
| 3955 | if (shouldVerifyOsiPointRegs(safepoint)) { |
| 3956 | verifyOsiPointRegs(safepoint); |
| 3957 | } |
| 3958 | #endif |
| 3959 | } |
| 3960 | |
| 3961 | void CodeGenerator::visitPhi(LPhi* lir) { |
| 3962 | MOZ_CRASH("Unexpected LPhi in CodeGenerator")do { do { } while (false); MOZ_ReportCrash("" "Unexpected LPhi in CodeGenerator" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 3962); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator" ")"); do { *((volatile int*)__null) = 3962; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 3963 | } |
| 3964 | |
| 3965 | void CodeGenerator::visitGoto(LGoto* lir) { jumpToBlock(lir->target()); } |
| 3966 | |
| 3967 | void CodeGenerator::visitTableSwitch(LTableSwitch* ins) { |
| 3968 | MTableSwitch* mir = ins->mir(); |
| 3969 | Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); |
| 3970 | const LAllocation* temp; |
| 3971 | |
| 3972 | if (mir->getOperand(0)->type() != MIRType::Int32) { |
| 3973 | temp = ins->tempInt()->output(); |
| 3974 | |
| 3975 | // The input is a double, so try and convert it to an integer. |
| 3976 | // If it does not fit in an integer, take the default case. |
| 3977 | masm.convertDoubleToInt32(ToFloatRegister(ins->index()), ToRegister(temp), |
| 3978 | defaultcase, false); |
| 3979 | } else { |
| 3980 | temp = ins->index(); |
| 3981 | } |
| 3982 | |
| 3983 | emitTableSwitchDispatch(mir, ToRegister(temp), |
| 3984 | ToRegisterOrInvalid(ins->tempPointer())); |
| 3985 | } |
| 3986 | |
| 3987 | void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) { |
| 3988 | MTableSwitch* mir = ins->mir(); |
| 3989 | Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); |
| 3990 | |
| 3991 | Register index = ToRegister(ins->tempInt()); |
| 3992 | ValueOperand value = ToValue(ins, LTableSwitchV::InputValue); |
| 3993 | Register tag = masm.extractTag(value, index); |
| 3994 | masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase); |
| 3995 | |
| 3996 | Label unboxInt, isInt; |
| 3997 | masm.branchTestInt32(Assembler::Equal, tag, &unboxInt); |
| 3998 | { |
| 3999 | FloatRegister floatIndex = ToFloatRegister(ins->tempFloat()); |
| 4000 | masm.unboxDouble(value, floatIndex); |
| 4001 | masm.convertDoubleToInt32(floatIndex, index, defaultcase, false); |
| 4002 | masm.jump(&isInt); |
| 4003 | } |
| 4004 | |
| 4005 | masm.bind(&unboxInt); |
| 4006 | masm.unboxInt32(value, index); |
| 4007 | |
| 4008 | masm.bind(&isInt); |
| 4009 | |
| 4010 | emitTableSwitchDispatch(mir, index, ToRegisterOrInvalid(ins->tempPointer())); |
| 4011 | } |
| 4012 | |
| 4013 | void CodeGenerator::visitParameter(LParameter* lir) {} |
| 4014 | |
| 4015 | void CodeGenerator::visitCallee(LCallee* lir) { |
| 4016 | Register callee = ToRegister(lir->output()); |
| 4017 | Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
| 4018 | |
| 4019 | masm.loadFunctionFromCalleeToken(ptr, callee); |
| 4020 | } |
| 4021 | |
| 4022 | void CodeGenerator::visitIsConstructing(LIsConstructing* lir) { |
| 4023 | Register output = ToRegister(lir->output()); |
| 4024 | Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
| 4025 | masm.loadPtr(calleeToken, output); |
| 4026 | |
| 4027 | // We must be inside a function. |
| 4028 | MOZ_ASSERT(current->mir()->info().script()->function())do { static_assert( mozilla::detail::AssertionConditionType< decltype(current->mir()->info().script()->function() )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(current->mir()->info().script()->function() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "current->mir()->info().script()->function()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4028); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()" ")"); do { *((volatile int*)__null) = 4028; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4029 | |
| 4030 | // The low bit indicates whether this call is constructing, just clear the |
| 4031 | // other bits. |
| 4032 | static_assert(CalleeToken_Function == 0x0, |
| 4033 | "CalleeTokenTag value should match"); |
| 4034 | static_assert(CalleeToken_FunctionConstructing == 0x1, |
| 4035 | "CalleeTokenTag value should match"); |
| 4036 | masm.andPtr(Imm32(0x1), output); |
| 4037 | } |
| 4038 | |
| 4039 | void CodeGenerator::visitReturn(LReturn* lir) { |
| 4040 | #if defined(JS_NUNBOX32) |
| 4041 | DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX); |
| 4042 | DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX); |
| 4043 | MOZ_ASSERT(ToRegister(type) == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(type) == JSReturnReg_Type)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToRegister(type) == JSReturnReg_Type ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(type) == JSReturnReg_Type", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4043); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type" ")"); do { *((volatile int*)__null) = 4043; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4044 | MOZ_ASSERT(ToRegister(payload) == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(payload) == JSReturnReg_Data)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(payload) == JSReturnReg_Data))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(payload) == JSReturnReg_Data" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data" ")"); do { *((volatile int*)__null) = 4044; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4045 | #elif defined(JS_PUNBOX641) |
| 4046 | DebugOnly<LAllocation*> result = lir->getOperand(0); |
| 4047 | MOZ_ASSERT(ToRegister(result) == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(result) == JSReturnReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToRegister(result) == JSReturnReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(result) == JSReturnReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg" ")"); do { *((volatile int*)__null) = 4047; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4048 | #endif |
| 4049 | // Don't emit a jump to the return label if this is the last block, as |
| 4050 | // it'll fall through to the epilogue. |
| 4051 | // |
| 4052 | // This is -not- true however for a Generator-return, which may appear in the |
| 4053 | // middle of the last block, so we should always emit the jump there. |
| 4054 | if (current->mir() != *gen->graph().poBegin() || lir->isGenerator()) { |
| 4055 | masm.jump(&returnLabel_); |
| 4056 | } |
| 4057 | } |
| 4058 | |
| 4059 | void CodeGenerator::visitOsrEntry(LOsrEntry* lir) { |
| 4060 | Register temp = ToRegister(lir->temp()); |
| 4061 | |
| 4062 | // Remember the OSR entry offset into the code buffer. |
| 4063 | masm.flushBuffer(); |
| 4064 | setOsrEntryOffset(masm.size()); |
| 4065 | |
| 4066 | // Allocate the full frame for this function |
| 4067 | // Note we have a new entry here. So we reset MacroAssembler::framePushed() |
| 4068 | // to 0, before reserving the stack. |
| 4069 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 4069; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4070 | masm.setFramePushed(0); |
| 4071 | |
| 4072 | // The Baseline code ensured both the frame pointer and stack pointer point to |
| 4073 | // the JitFrameLayout on the stack. |
| 4074 | |
| 4075 | // If profiling, save the current frame pointer to a per-thread global field. |
| 4076 | if (isProfilerInstrumentationEnabled()) { |
| 4077 | masm.profilerEnterFrame(FramePointer, temp); |
| 4078 | } |
| 4079 | |
| 4080 | masm.reserveStack(frameSize()); |
| 4081 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4081); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 4081; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4082 | |
| 4083 | // Ensure that the Ion frames is properly aligned. |
| 4084 | masm.assertStackAlignment(JitStackAlignment, 0); |
| 4085 | } |
| 4086 | |
| 4087 | void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) { |
| 4088 | const LAllocation* frame = lir->getOperand(0); |
| 4089 | const LDefinition* object = lir->getDef(0); |
| 4090 | |
| 4091 | const ptrdiff_t frameOffset = |
| 4092 | BaselineFrame::reverseOffsetOfEnvironmentChain(); |
| 4093 | |
| 4094 | masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
| 4095 | } |
| 4096 | |
| 4097 | void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) { |
| 4098 | const LAllocation* frame = lir->getOperand(0); |
| 4099 | const LDefinition* object = lir->getDef(0); |
| 4100 | |
| 4101 | const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj(); |
| 4102 | |
| 4103 | masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
| 4104 | } |
| 4105 | |
| 4106 | void CodeGenerator::visitOsrValue(LOsrValue* value) { |
| 4107 | const LAllocation* frame = value->getOperand(0); |
| 4108 | const ValueOperand out = ToOutValue(value); |
| 4109 | |
| 4110 | const ptrdiff_t frameOffset = value->mir()->frameOffset(); |
| 4111 | |
| 4112 | masm.loadValue(Address(ToRegister(frame), frameOffset), out); |
| 4113 | } |
| 4114 | |
| 4115 | void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) { |
| 4116 | const LAllocation* frame = lir->getOperand(0); |
| 4117 | const ValueOperand out = ToOutValue(lir); |
| 4118 | |
| 4119 | Address flags = |
| 4120 | Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags()); |
| 4121 | Address retval = |
| 4122 | Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue()); |
| 4123 | |
| 4124 | masm.moveValue(UndefinedValue(), out); |
| 4125 | |
| 4126 | Label done; |
| 4127 | masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), |
| 4128 | &done); |
| 4129 | masm.loadValue(retval, out); |
| 4130 | masm.bind(&done); |
| 4131 | } |
| 4132 | |
| 4133 | void CodeGenerator::visitStackArgT(LStackArgT* lir) { |
| 4134 | const LAllocation* arg = lir->arg(); |
| 4135 | MIRType argType = lir->type(); |
| 4136 | uint32_t argslot = lir->argslot(); |
| 4137 | MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount())do { static_assert( mozilla::detail::AssertionConditionType< decltype(argslot - 1u < graph.argumentSlotCount())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argslot - 1u < graph.argumentSlotCount()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argslot - 1u < graph.argumentSlotCount()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4137); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()" ")"); do { *((volatile int*)__null) = 4137; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4138 | |
| 4139 | Address dest = AddressOfPassedArg(argslot); |
| 4140 | |
| 4141 | if (arg->isFloatReg()) { |
| 4142 | masm.boxDouble(ToFloatRegister(arg), dest); |
| 4143 | } else if (arg->isRegister()) { |
| 4144 | masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest); |
| 4145 | } else { |
| 4146 | masm.storeValue(arg->toConstant()->toJSValue(), dest); |
| 4147 | } |
| 4148 | } |
| 4149 | |
| 4150 | void CodeGenerator::visitStackArgV(LStackArgV* lir) { |
| 4151 | ValueOperand val = ToValue(lir, 0); |
| 4152 | uint32_t argslot = lir->argslot(); |
| 4153 | MOZ_ASSERT(argslot - 1u < graph.argumentSlotCount())do { static_assert( mozilla::detail::AssertionConditionType< decltype(argslot - 1u < graph.argumentSlotCount())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argslot - 1u < graph.argumentSlotCount()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argslot - 1u < graph.argumentSlotCount()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4153); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()" ")"); do { *((volatile int*)__null) = 4153; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4154 | |
| 4155 | masm.storeValue(val, AddressOfPassedArg(argslot)); |
| 4156 | } |
| 4157 | |
| 4158 | void CodeGenerator::visitMoveGroup(LMoveGroup* group) { |
| 4159 | if (!group->numMoves()) { |
| 4160 | return; |
| 4161 | } |
| 4162 | |
| 4163 | MoveResolver& resolver = masm.moveResolver(); |
| 4164 | |
| 4165 | for (size_t i = 0; i < group->numMoves(); i++) { |
| 4166 | const LMove& move = group->getMove(i); |
| 4167 | |
| 4168 | LAllocation from = move.from(); |
| 4169 | LAllocation to = move.to(); |
| 4170 | LDefinition::Type type = move.type(); |
| 4171 | |
| 4172 | // No bogus moves. |
| 4173 | MOZ_ASSERT(from != to)do { static_assert( mozilla::detail::AssertionConditionType< decltype(from != to)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(from != to))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("from != to", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4173); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to" ")"); do { *((volatile int*)__null) = 4173; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4174 | MOZ_ASSERT(!from.isConstant())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!from.isConstant())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!from.isConstant()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!from.isConstant()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!from.isConstant()" ")"); do { *((volatile int*)__null) = 4174; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4175 | MoveOp::Type moveType; |
| 4176 | switch (type) { |
| 4177 | case LDefinition::OBJECT: |
| 4178 | case LDefinition::SLOTS: |
| 4179 | case LDefinition::WASM_ANYREF: |
| 4180 | #ifdef JS_NUNBOX32 |
| 4181 | case LDefinition::TYPE: |
| 4182 | case LDefinition::PAYLOAD: |
| 4183 | #else |
| 4184 | case LDefinition::BOX: |
| 4185 | #endif |
| 4186 | case LDefinition::GENERAL: |
| 4187 | case LDefinition::STACKRESULTS: |
| 4188 | moveType = MoveOp::GENERAL; |
| 4189 | break; |
| 4190 | case LDefinition::INT32: |
| 4191 | moveType = MoveOp::INT32; |
| 4192 | break; |
| 4193 | case LDefinition::FLOAT32: |
| 4194 | moveType = MoveOp::FLOAT32; |
| 4195 | break; |
| 4196 | case LDefinition::DOUBLE: |
| 4197 | moveType = MoveOp::DOUBLE; |
| 4198 | break; |
| 4199 | case LDefinition::SIMD128: |
| 4200 | moveType = MoveOp::SIMD128; |
| 4201 | break; |
| 4202 | default: |
| 4203 | MOZ_CRASH("Unexpected move type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected move type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4203); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected move type" ")"); do { *((volatile int*)__null) = 4203; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 4204 | } |
| 4205 | |
| 4206 | masm.propagateOOM( |
| 4207 | resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType)); |
| 4208 | } |
| 4209 | |
| 4210 | masm.propagateOOM(resolver.resolve()); |
| 4211 | if (masm.oom()) { |
| 4212 | return; |
| 4213 | } |
| 4214 | |
| 4215 | MoveEmitter emitter(masm); |
| 4216 | |
| 4217 | #ifdef JS_CODEGEN_X86 |
| 4218 | if (group->maybeScratchRegister().isGeneralReg()) { |
| 4219 | emitter.setScratchRegister( |
| 4220 | group->maybeScratchRegister().toGeneralReg()->reg()); |
| 4221 | } else { |
| 4222 | resolver.sortMemoryToMemoryMoves(); |
| 4223 | } |
| 4224 | #endif |
| 4225 | |
| 4226 | emitter.emit(resolver); |
| 4227 | emitter.finish(); |
| 4228 | } |
| 4229 | |
| 4230 | void CodeGenerator::visitInteger(LInteger* lir) { |
| 4231 | masm.move32(Imm32(lir->i32()), ToRegister(lir->output())); |
| 4232 | } |
| 4233 | |
| 4234 | void CodeGenerator::visitInteger64(LInteger64* lir) { |
| 4235 | masm.move64(Imm64(lir->i64()), ToOutRegister64(lir)); |
| 4236 | } |
| 4237 | |
| 4238 | void CodeGenerator::visitPointer(LPointer* lir) { |
| 4239 | masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output())); |
| 4240 | } |
| 4241 | |
| 4242 | void CodeGenerator::visitDouble(LDouble* ins) { |
| 4243 | masm.loadConstantDouble(ins->value(), ToFloatRegister(ins->output())); |
| 4244 | } |
| 4245 | |
| 4246 | void CodeGenerator::visitFloat32(LFloat32* ins) { |
| 4247 | masm.loadConstantFloat32(ins->value(), ToFloatRegister(ins->output())); |
| 4248 | } |
| 4249 | |
| 4250 | void CodeGenerator::visitValue(LValue* value) { |
| 4251 | ValueOperand result = ToOutValue(value); |
| 4252 | masm.moveValue(value->value(), result); |
| 4253 | } |
| 4254 | |
| 4255 | void CodeGenerator::visitNurseryObject(LNurseryObject* lir) { |
| 4256 | Register output = ToRegister(lir->output()); |
| 4257 | uint32_t nurseryIndex = lir->mir()->nurseryIndex(); |
| 4258 | |
| 4259 | // Load a pointer to the entry in IonScript's nursery objects list. |
| 4260 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output); |
| 4261 | masm.propagateOOM(ionNurseryObjectLabels_.emplaceBack(label, nurseryIndex)); |
| 4262 | |
| 4263 | // Load the JSObject*. |
| 4264 | masm.loadPtr(Address(output, 0), output); |
| 4265 | } |
| 4266 | |
| 4267 | void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) { |
| 4268 | // No-op. |
| 4269 | } |
| 4270 | |
| 4271 | void CodeGenerator::visitDebugEnterGCUnsafeRegion( |
| 4272 | LDebugEnterGCUnsafeRegion* lir) { |
| 4273 | Register temp = ToRegister(lir->temp0()); |
| 4274 | |
| 4275 | masm.loadJSContext(temp); |
| 4276 | |
| 4277 | Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion()); |
| 4278 | masm.add32(Imm32(1), inUnsafeRegion); |
| 4279 | |
| 4280 | Label ok; |
| 4281 | masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok); |
| 4282 | masm.assumeUnreachable("unbalanced enter/leave GC unsafe region"); |
| 4283 | masm.bind(&ok); |
| 4284 | } |
| 4285 | |
| 4286 | void CodeGenerator::visitDebugLeaveGCUnsafeRegion( |
| 4287 | LDebugLeaveGCUnsafeRegion* lir) { |
| 4288 | Register temp = ToRegister(lir->temp0()); |
| 4289 | |
| 4290 | masm.loadJSContext(temp); |
| 4291 | |
| 4292 | Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion()); |
| 4293 | masm.add32(Imm32(-1), inUnsafeRegion); |
| 4294 | |
| 4295 | Label ok; |
| 4296 | masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok); |
| 4297 | masm.assumeUnreachable("unbalanced enter/leave GC unsafe region"); |
| 4298 | masm.bind(&ok); |
| 4299 | } |
| 4300 | |
| 4301 | void CodeGenerator::visitSlots(LSlots* lir) { |
| 4302 | Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots()); |
| 4303 | masm.loadPtr(slots, ToRegister(lir->output())); |
| 4304 | } |
| 4305 | |
| 4306 | void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) { |
| 4307 | ValueOperand dest = ToOutValue(lir); |
| 4308 | Register base = ToRegister(lir->input()); |
| 4309 | int32_t offset = lir->mir()->slot() * sizeof(js::Value); |
| 4310 | |
| 4311 | masm.loadValue(Address(base, offset), dest); |
| 4312 | } |
| 4313 | |
| 4314 | static ConstantOrRegister ToConstantOrRegister(const LAllocation* value, |
| 4315 | MIRType valueType) { |
| 4316 | if (value->isConstant()) { |
| 4317 | return ConstantOrRegister(value->toConstant()->toJSValue()); |
| 4318 | } |
| 4319 | return TypedOrValueRegister(valueType, ToAnyRegister(value)); |
| 4320 | } |
| 4321 | |
| 4322 | void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) { |
| 4323 | Register base = ToRegister(lir->slots()); |
| 4324 | int32_t offset = lir->mir()->slot() * sizeof(js::Value); |
| 4325 | Address dest(base, offset); |
| 4326 | |
| 4327 | if (lir->mir()->needsBarrier()) { |
| 4328 | emitPreBarrier(dest); |
| 4329 | } |
| 4330 | |
| 4331 | MIRType valueType = lir->mir()->value()->type(); |
| 4332 | ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType); |
| 4333 | masm.storeUnboxedValue(value, valueType, dest); |
| 4334 | } |
| 4335 | |
| 4336 | void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) { |
| 4337 | Register base = ToRegister(lir->slots()); |
| 4338 | int32_t offset = lir->mir()->slot() * sizeof(Value); |
| 4339 | |
| 4340 | const ValueOperand value = ToValue(lir, LStoreDynamicSlotV::ValueIndex); |
| 4341 | |
| 4342 | if (lir->mir()->needsBarrier()) { |
| 4343 | emitPreBarrier(Address(base, offset)); |
| 4344 | } |
| 4345 | |
| 4346 | masm.storeValue(value, Address(base, offset)); |
| 4347 | } |
| 4348 | |
| 4349 | void CodeGenerator::visitElements(LElements* lir) { |
| 4350 | Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); |
| 4351 | masm.loadPtr(elements, ToRegister(lir->output())); |
| 4352 | } |
| 4353 | |
| 4354 | void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) { |
| 4355 | Address environment(ToRegister(lir->function()), |
| 4356 | JSFunction::offsetOfEnvironment()); |
| 4357 | masm.unboxObject(environment, ToRegister(lir->output())); |
| 4358 | } |
| 4359 | |
| 4360 | void CodeGenerator::visitHomeObject(LHomeObject* lir) { |
| 4361 | Register func = ToRegister(lir->function()); |
| 4362 | Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot()); |
| 4363 | |
| 4364 | masm.assertFunctionIsExtended(func); |
| 4365 | #ifdef DEBUG1 |
| 4366 | Label isObject; |
| 4367 | masm.branchTestObject(Assembler::Equal, homeObject, &isObject); |
| 4368 | masm.assumeUnreachable("[[HomeObject]] must be Object"); |
| 4369 | masm.bind(&isObject); |
| 4370 | #endif |
| 4371 | |
| 4372 | masm.unboxObject(homeObject, ToRegister(lir->output())); |
| 4373 | } |
| 4374 | |
| 4375 | void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) { |
| 4376 | Register homeObject = ToRegister(lir->homeObject()); |
| 4377 | ValueOperand output = ToOutValue(lir); |
| 4378 | Register temp = output.scratchReg(); |
| 4379 | |
| 4380 | masm.loadObjProto(homeObject, temp); |
| 4381 | |
| 4382 | #ifdef DEBUG1 |
| 4383 | // We won't encounter a lazy proto, because the prototype is guaranteed to |
| 4384 | // either be a JSFunction or a PlainObject, and only proxy objects can have a |
| 4385 | // lazy proto. |
| 4386 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 4386; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4387 | |
| 4388 | Label proxyCheckDone; |
| 4389 | masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone); |
| 4390 | masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase"); |
| 4391 | masm.bind(&proxyCheckDone); |
| 4392 | #endif |
| 4393 | |
| 4394 | Label nullProto, done; |
| 4395 | masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto); |
| 4396 | |
| 4397 | // Box prototype and return |
| 4398 | masm.tagValue(JSVAL_TYPE_OBJECT, temp, output); |
| 4399 | masm.jump(&done); |
| 4400 | |
| 4401 | masm.bind(&nullProto); |
| 4402 | masm.moveValue(NullValue(), output); |
| 4403 | |
| 4404 | masm.bind(&done); |
| 4405 | } |
| 4406 | |
| 4407 | template <class T> |
| 4408 | static T* ToConstantObject(MDefinition* def) { |
| 4409 | MOZ_ASSERT(def->isConstant())do { static_assert( mozilla::detail::AssertionConditionType< decltype(def->isConstant())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(def->isConstant()))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("def->isConstant()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4409); AnnotateMozCrashReason("MOZ_ASSERT" "(" "def->isConstant()" ")"); do { *((volatile int*)__null) = 4409; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4410 | return &def->toConstant()->toObject().as<T>(); |
| 4411 | } |
| 4412 | |
| 4413 | void CodeGenerator::visitNewLexicalEnvironmentObject( |
| 4414 | LNewLexicalEnvironmentObject* lir) { |
| 4415 | Register output = ToRegister(lir->output()); |
| 4416 | Register temp = ToRegister(lir->temp0()); |
| 4417 | |
| 4418 | auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>( |
| 4419 | lir->mir()->templateObj()); |
| 4420 | auto* scope = &templateObj->scope(); |
| 4421 | gc::Heap initialHeap = gc::Heap::Default; |
| 4422 | |
| 4423 | using Fn = |
| 4424 | BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>); |
| 4425 | auto* ool = |
| 4426 | oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>( |
| 4427 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
| 4428 | |
| 4429 | TemplateObject templateObject(templateObj); |
| 4430 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
| 4431 | |
| 4432 | masm.bind(ool->rejoin()); |
| 4433 | } |
| 4434 | |
| 4435 | void CodeGenerator::visitNewClassBodyEnvironmentObject( |
| 4436 | LNewClassBodyEnvironmentObject* lir) { |
| 4437 | Register output = ToRegister(lir->output()); |
| 4438 | Register temp = ToRegister(lir->temp0()); |
| 4439 | |
| 4440 | auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>( |
| 4441 | lir->mir()->templateObj()); |
| 4442 | auto* scope = &templateObj->scope(); |
| 4443 | gc::Heap initialHeap = gc::Heap::Default; |
| 4444 | |
| 4445 | using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*, |
| 4446 | Handle<ClassBodyScope*>); |
| 4447 | auto* ool = |
| 4448 | oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>( |
| 4449 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
| 4450 | |
| 4451 | TemplateObject templateObject(templateObj); |
| 4452 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
| 4453 | |
| 4454 | masm.bind(ool->rejoin()); |
| 4455 | } |
| 4456 | |
| 4457 | void CodeGenerator::visitNewVarEnvironmentObject( |
| 4458 | LNewVarEnvironmentObject* lir) { |
| 4459 | Register output = ToRegister(lir->output()); |
| 4460 | Register temp = ToRegister(lir->temp0()); |
| 4461 | |
| 4462 | auto* templateObj = |
| 4463 | ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj()); |
| 4464 | auto* scope = &templateObj->scope().as<VarScope>(); |
| 4465 | gc::Heap initialHeap = gc::Heap::Default; |
| 4466 | |
| 4467 | using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>); |
| 4468 | auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>( |
| 4469 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
| 4470 | |
| 4471 | TemplateObject templateObject(templateObj); |
| 4472 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
| 4473 | |
| 4474 | masm.bind(ool->rejoin()); |
| 4475 | } |
| 4476 | |
| 4477 | void CodeGenerator::visitGuardShape(LGuardShape* guard) { |
| 4478 | Register obj = ToRegister(guard->input()); |
| 4479 | Register temp = ToTempRegisterOrInvalid(guard->temp0()); |
| 4480 | Label bail; |
| 4481 | masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp, |
| 4482 | obj, &bail); |
| 4483 | bailoutFrom(&bail, guard->snapshot()); |
| 4484 | } |
| 4485 | |
| 4486 | void CodeGenerator::visitGuardFuse(LGuardFuse* guard) { |
| 4487 | auto fuseIndex = guard->mir()->fuseIndex(); |
| 4488 | |
| 4489 | Register temp = ToRegister(guard->temp0()); |
| 4490 | Label bail; |
| 4491 | |
| 4492 | // Bake specific fuse address for Ion code, because we won't share this code |
| 4493 | // across realms. |
| 4494 | GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex); |
| 4495 | masm.loadPtr(AbsoluteAddress(fuse->fuseRef()), temp); |
| 4496 | masm.branchPtr(Assembler::NotEqual, temp, ImmPtr(nullptr), &bail); |
| 4497 | |
| 4498 | bailoutFrom(&bail, guard->snapshot()); |
| 4499 | } |
| 4500 | |
| 4501 | void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) { |
| 4502 | Register obj = ToRegister(guard->object()); |
| 4503 | Register shapeList = ToRegister(guard->shapeList()); |
| 4504 | Register temp = ToRegister(guard->temp0()); |
| 4505 | Register temp2 = ToRegister(guard->temp1()); |
| 4506 | Register temp3 = ToRegister(guard->temp2()); |
| 4507 | Register spectre = ToTempRegisterOrInvalid(guard->temp3()); |
| 4508 | |
| 4509 | Label bail; |
| 4510 | masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp); |
| 4511 | masm.branchTestObjShapeList(Assembler::NotEqual, obj, temp, temp2, temp3, |
| 4512 | spectre, &bail); |
| 4513 | bailoutFrom(&bail, guard->snapshot()); |
| 4514 | } |
| 4515 | |
| 4516 | void CodeGenerator::visitGuardProto(LGuardProto* guard) { |
| 4517 | Register obj = ToRegister(guard->object()); |
| 4518 | Register expected = ToRegister(guard->expected()); |
| 4519 | Register temp = ToRegister(guard->temp0()); |
| 4520 | |
| 4521 | masm.loadObjProto(obj, temp); |
| 4522 | |
| 4523 | Label bail; |
| 4524 | masm.branchPtr(Assembler::NotEqual, temp, expected, &bail); |
| 4525 | bailoutFrom(&bail, guard->snapshot()); |
| 4526 | } |
| 4527 | |
| 4528 | void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) { |
| 4529 | Register obj = ToRegister(guard->input()); |
| 4530 | Register temp = ToRegister(guard->temp0()); |
| 4531 | |
| 4532 | masm.loadObjProto(obj, temp); |
| 4533 | |
| 4534 | Label bail; |
| 4535 | masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail); |
| 4536 | bailoutFrom(&bail, guard->snapshot()); |
| 4537 | } |
| 4538 | |
| 4539 | void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) { |
| 4540 | Register obj = ToRegister(guard->input()); |
| 4541 | Register temp = ToRegister(guard->temp0()); |
| 4542 | |
| 4543 | Label bail; |
| 4544 | masm.branchIfNonNativeObj(obj, temp, &bail); |
| 4545 | bailoutFrom(&bail, guard->snapshot()); |
| 4546 | } |
| 4547 | |
| 4548 | void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) { |
| 4549 | Register temp = ToRegister(guard->temp0()); |
| 4550 | Label bail; |
| 4551 | |
| 4552 | masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp); |
| 4553 | masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()), |
| 4554 | &bail); |
| 4555 | bailoutFrom(&bail, guard->snapshot()); |
| 4556 | } |
| 4557 | |
| 4558 | void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) { |
| 4559 | Register obj = ToRegister(guard->input()); |
| 4560 | Register temp = ToRegister(guard->temp0()); |
| 4561 | |
| 4562 | Label bail; |
| 4563 | masm.branchTestObjectIsProxy(false, obj, temp, &bail); |
| 4564 | bailoutFrom(&bail, guard->snapshot()); |
| 4565 | } |
| 4566 | |
| 4567 | void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) { |
| 4568 | Register obj = ToRegister(guard->input()); |
| 4569 | Register temp = ToRegister(guard->temp0()); |
| 4570 | |
| 4571 | Label bail; |
| 4572 | masm.branchTestObjectIsProxy(true, obj, temp, &bail); |
| 4573 | bailoutFrom(&bail, guard->snapshot()); |
| 4574 | } |
| 4575 | |
| 4576 | void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) { |
| 4577 | Register proxy = ToRegister(guard->proxy()); |
| 4578 | Register temp = ToRegister(guard->temp0()); |
| 4579 | |
| 4580 | Label bail; |
| 4581 | masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp, |
| 4582 | GetDOMProxyHandlerFamily(), &bail); |
| 4583 | bailoutFrom(&bail, guard->snapshot()); |
| 4584 | } |
| 4585 | |
| 4586 | void CodeGenerator::visitProxyGet(LProxyGet* lir) { |
| 4587 | Register proxy = ToRegister(lir->proxy()); |
| 4588 | Register temp = ToRegister(lir->temp0()); |
| 4589 | |
| 4590 | pushArg(lir->mir()->id(), temp); |
| 4591 | pushArg(proxy); |
| 4592 | |
| 4593 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue); |
| 4594 | callVM<Fn, ProxyGetProperty>(lir); |
| 4595 | } |
| 4596 | |
| 4597 | void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) { |
| 4598 | Register proxy = ToRegister(lir->proxy()); |
| 4599 | ValueOperand idVal = ToValue(lir, LProxyGetByValue::IdIndex); |
| 4600 | |
| 4601 | pushArg(idVal); |
| 4602 | pushArg(proxy); |
| 4603 | |
| 4604 | using Fn = |
| 4605 | bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue); |
| 4606 | callVM<Fn, ProxyGetPropertyByValue>(lir); |
| 4607 | } |
| 4608 | |
| 4609 | void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) { |
| 4610 | Register proxy = ToRegister(lir->proxy()); |
| 4611 | ValueOperand idVal = ToValue(lir, LProxyHasProp::IdIndex); |
| 4612 | |
| 4613 | pushArg(idVal); |
| 4614 | pushArg(proxy); |
| 4615 | |
| 4616 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*); |
| 4617 | if (lir->mir()->hasOwn()) { |
| 4618 | callVM<Fn, ProxyHasOwn>(lir); |
| 4619 | } else { |
| 4620 | callVM<Fn, ProxyHas>(lir); |
| 4621 | } |
| 4622 | } |
| 4623 | |
| 4624 | void CodeGenerator::visitProxySet(LProxySet* lir) { |
| 4625 | Register proxy = ToRegister(lir->proxy()); |
| 4626 | ValueOperand rhs = ToValue(lir, LProxySet::RhsIndex); |
| 4627 | Register temp = ToRegister(lir->temp0()); |
| 4628 | |
| 4629 | pushArg(Imm32(lir->mir()->strict())); |
| 4630 | pushArg(rhs); |
| 4631 | pushArg(lir->mir()->id(), temp); |
| 4632 | pushArg(proxy); |
| 4633 | |
| 4634 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool); |
| 4635 | callVM<Fn, ProxySetProperty>(lir); |
| 4636 | } |
| 4637 | |
| 4638 | void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) { |
| 4639 | Register proxy = ToRegister(lir->proxy()); |
| 4640 | ValueOperand idVal = ToValue(lir, LProxySetByValue::IdIndex); |
| 4641 | ValueOperand rhs = ToValue(lir, LProxySetByValue::RhsIndex); |
| 4642 | |
| 4643 | pushArg(Imm32(lir->mir()->strict())); |
| 4644 | pushArg(rhs); |
| 4645 | pushArg(idVal); |
| 4646 | pushArg(proxy); |
| 4647 | |
| 4648 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool); |
| 4649 | callVM<Fn, ProxySetPropertyByValue>(lir); |
| 4650 | } |
| 4651 | |
| 4652 | void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) { |
| 4653 | Register obj = ToRegister(lir->obj()); |
| 4654 | ValueOperand rhs = ToValue(lir, LCallSetArrayLength::RhsIndex); |
| 4655 | |
| 4656 | pushArg(Imm32(lir->mir()->strict())); |
| 4657 | pushArg(rhs); |
| 4658 | pushArg(obj); |
| 4659 | |
| 4660 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool); |
| 4661 | callVM<Fn, jit::SetArrayLength>(lir); |
| 4662 | } |
| 4663 | |
| 4664 | void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) { |
| 4665 | Register obj = ToRegister(lir->object()); |
| 4666 | Register temp0 = ToRegister(lir->temp0()); |
| 4667 | Register temp1 = ToRegister(lir->temp1()); |
| 4668 | Register temp2 = ToRegister(lir->temp2()); |
| 4669 | Register temp3 = ToRegister(lir->temp3()); |
| 4670 | ValueOperand output = ToOutValue(lir); |
| 4671 | |
| 4672 | Label cacheHit; |
| 4673 | masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2, |
| 4674 | output, &cacheHit); |
| 4675 | |
| 4676 | Label bail; |
| 4677 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
| 4678 | |
| 4679 | masm.Push(UndefinedValue()); |
| 4680 | masm.moveStackPtrTo(temp3); |
| 4681 | |
| 4682 | using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id, |
| 4683 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
| 4684 | masm.setupAlignedABICall(); |
| 4685 | masm.loadJSContext(temp0); |
| 4686 | masm.passABIArg(temp0); |
| 4687 | masm.passABIArg(obj); |
| 4688 | masm.movePropertyKey(lir->mir()->name(), temp1); |
| 4689 | masm.passABIArg(temp1); |
| 4690 | masm.passABIArg(temp2); |
| 4691 | masm.passABIArg(temp3); |
| 4692 | |
| 4693 | masm.callWithABI<Fn, GetNativeDataPropertyPure>(); |
| 4694 | |
| 4695 | MOZ_ASSERT(!output.aliases(ReturnReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!output.aliases(ReturnReg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!output.aliases(ReturnReg))) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!output.aliases(ReturnReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4695); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)" ")"); do { *((volatile int*)__null) = 4695; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4696 | masm.Pop(output); |
| 4697 | |
| 4698 | masm.branchIfFalseBool(ReturnReg, &bail); |
| 4699 | masm.bind(&cacheHit); |
| 4700 | |
| 4701 | bailoutFrom(&bail, lir->snapshot()); |
| 4702 | } |
| 4703 | |
| 4704 | void CodeGenerator::visitMegamorphicLoadSlotPermissive( |
| 4705 | LMegamorphicLoadSlotPermissive* lir) { |
| 4706 | Register obj = ToRegister(lir->object()); |
| 4707 | Register temp0 = ToRegister(lir->temp0()); |
| 4708 | Register temp1 = ToRegister(lir->temp1()); |
| 4709 | Register temp2 = ToRegister(lir->temp2()); |
| 4710 | ValueOperand output = ToOutValue(lir); |
| 4711 | |
| 4712 | Label cacheHit; |
| 4713 | masm.emitMegamorphicCacheLookup(lir->mir()->name(), obj, temp0, temp1, temp2, |
| 4714 | output, &cacheHit); |
| 4715 | |
| 4716 | masm.movePropertyKey(lir->mir()->name(), temp1); |
| 4717 | pushArg(temp2); |
| 4718 | pushArg(temp1); |
| 4719 | pushArg(obj); |
| 4720 | |
| 4721 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, |
| 4722 | MegamorphicCacheEntry*, MutableHandleValue); |
| 4723 | callVM<Fn, GetPropMaybeCached>(lir); |
| 4724 | |
| 4725 | masm.bind(&cacheHit); |
| 4726 | } |
| 4727 | |
| 4728 | void CodeGenerator::visitMegamorphicLoadSlotByValue( |
| 4729 | LMegamorphicLoadSlotByValue* lir) { |
| 4730 | Register obj = ToRegister(lir->object()); |
| 4731 | ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex); |
| 4732 | Register temp0 = ToRegister(lir->temp0()); |
| 4733 | Register temp1 = ToRegister(lir->temp1()); |
| 4734 | Register temp2 = ToRegister(lir->temp2()); |
| 4735 | ValueOperand output = ToOutValue(lir); |
| 4736 | |
| 4737 | Label cacheHit, bail; |
| 4738 | masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2, |
| 4739 | output, &cacheHit); |
| 4740 | |
| 4741 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
| 4742 | |
| 4743 | // idVal will be in vp[0], result will be stored in vp[1]. |
| 4744 | masm.reserveStack(sizeof(Value)); |
| 4745 | masm.Push(idVal); |
| 4746 | masm.moveStackPtrTo(temp0); |
| 4747 | |
| 4748 | using Fn = bool (*)(JSContext* cx, JSObject* obj, |
| 4749 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
| 4750 | masm.setupAlignedABICall(); |
| 4751 | masm.loadJSContext(temp1); |
| 4752 | masm.passABIArg(temp1); |
| 4753 | masm.passABIArg(obj); |
| 4754 | masm.passABIArg(temp2); |
| 4755 | masm.passABIArg(temp0); |
| 4756 | masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>(); |
| 4757 | |
| 4758 | MOZ_ASSERT(!idVal.aliases(temp0))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!idVal.aliases(temp0))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!idVal.aliases(temp0)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("!idVal.aliases(temp0)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4758); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { *((volatile int*)__null) = 4758; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4759 | masm.storeCallPointerResult(temp0); |
| 4760 | masm.Pop(idVal); |
| 4761 | |
| 4762 | uint32_t framePushed = masm.framePushed(); |
| 4763 | Label ok; |
| 4764 | masm.branchIfTrueBool(temp0, &ok); |
| 4765 | masm.freeStack(sizeof(Value)); // Discard result Value. |
| 4766 | masm.jump(&bail); |
| 4767 | |
| 4768 | masm.bind(&ok); |
| 4769 | masm.setFramePushed(framePushed); |
| 4770 | masm.Pop(output); |
| 4771 | |
| 4772 | masm.bind(&cacheHit); |
| 4773 | |
| 4774 | bailoutFrom(&bail, lir->snapshot()); |
| 4775 | } |
| 4776 | |
| 4777 | void CodeGenerator::visitMegamorphicLoadSlotByValuePermissive( |
| 4778 | LMegamorphicLoadSlotByValuePermissive* lir) { |
| 4779 | Register obj = ToRegister(lir->object()); |
| 4780 | ValueOperand idVal = ToValue(lir, LMegamorphicLoadSlotByValue::IdIndex); |
| 4781 | Register temp0 = ToRegister(lir->temp0()); |
| 4782 | Register temp1 = ToRegister(lir->temp1()); |
| 4783 | Register temp2 = ToRegister(lir->temp2()); |
| 4784 | ValueOperand output = ToOutValue(lir); |
| 4785 | |
| 4786 | Label cacheHit; |
| 4787 | masm.emitMegamorphicCacheLookupByValue(idVal, obj, temp0, temp1, temp2, |
| 4788 | output, &cacheHit); |
| 4789 | |
| 4790 | pushArg(temp2); |
| 4791 | pushArg(idVal); |
| 4792 | pushArg(obj); |
| 4793 | |
| 4794 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, |
| 4795 | MegamorphicCacheEntry*, MutableHandleValue); |
| 4796 | callVM<Fn, GetElemMaybeCached>(lir); |
| 4797 | |
| 4798 | masm.bind(&cacheHit); |
| 4799 | } |
| 4800 | |
| 4801 | void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) { |
| 4802 | Register obj = ToRegister(lir->object()); |
| 4803 | ValueOperand value = ToValue(lir, LMegamorphicStoreSlot::RhsIndex); |
| 4804 | |
| 4805 | Register temp0 = ToRegister(lir->temp0()); |
| 4806 | #ifndef JS_CODEGEN_X86 |
| 4807 | Register temp1 = ToRegister(lir->temp1()); |
| 4808 | Register temp2 = ToRegister(lir->temp2()); |
| 4809 | #endif |
| 4810 | |
| 4811 | // The instruction is marked as call-instruction so only these registers are |
| 4812 | // live. |
| 4813 | LiveRegisterSet liveRegs; |
| 4814 | liveRegs.addUnchecked(obj); |
| 4815 | liveRegs.addUnchecked(value); |
| 4816 | liveRegs.addUnchecked(temp0); |
| 4817 | #ifndef JS_CODEGEN_X86 |
| 4818 | liveRegs.addUnchecked(temp1); |
| 4819 | liveRegs.addUnchecked(temp2); |
| 4820 | #endif |
| 4821 | |
| 4822 | Label cacheHit, done; |
| 4823 | #ifdef JS_CODEGEN_X86 |
| 4824 | masm.emitMegamorphicCachedSetSlot( |
| 4825 | lir->mir()->name(), obj, temp0, value, liveRegs, &cacheHit, |
| 4826 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
| 4827 | EmitPreBarrier(masm, addr, mirType); |
| 4828 | }); |
| 4829 | #else |
| 4830 | masm.emitMegamorphicCachedSetSlot( |
| 4831 | lir->mir()->name(), obj, temp0, temp1, temp2, value, liveRegs, &cacheHit, |
| 4832 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
| 4833 | EmitPreBarrier(masm, addr, mirType); |
| 4834 | }); |
| 4835 | #endif |
| 4836 | |
| 4837 | pushArg(Imm32(lir->mir()->strict())); |
| 4838 | pushArg(value); |
| 4839 | pushArg(lir->mir()->name(), temp0); |
| 4840 | pushArg(obj); |
| 4841 | |
| 4842 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool); |
| 4843 | callVM<Fn, SetPropertyMegamorphic<true>>(lir); |
| 4844 | |
| 4845 | masm.jump(&done); |
| 4846 | masm.bind(&cacheHit); |
| 4847 | |
| 4848 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done); |
| 4849 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done); |
| 4850 | |
| 4851 | // Note: because this is a call-instruction, no registers need to be saved. |
| 4852 | MOZ_ASSERT(lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4852); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { *((volatile int*)__null) = 4852; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4853 | emitPostWriteBarrier(obj); |
| 4854 | |
| 4855 | masm.bind(&done); |
| 4856 | } |
| 4857 | |
| 4858 | void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) { |
| 4859 | Register obj = ToRegister(lir->object()); |
| 4860 | ValueOperand idVal = ToValue(lir, LMegamorphicHasProp::IdIndex); |
| 4861 | Register temp0 = ToRegister(lir->temp0()); |
| 4862 | Register temp1 = ToRegister(lir->temp1()); |
| 4863 | Register temp2 = ToRegister(lir->temp2()); |
| 4864 | Register output = ToRegister(lir->output()); |
| 4865 | |
| 4866 | Label bail, cacheHit; |
| 4867 | masm.emitMegamorphicCacheLookupExists(idVal, obj, temp0, temp1, temp2, output, |
| 4868 | &cacheHit, lir->mir()->hasOwn()); |
| 4869 | |
| 4870 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
| 4871 | |
| 4872 | // idVal will be in vp[0], result will be stored in vp[1]. |
| 4873 | masm.reserveStack(sizeof(Value)); |
| 4874 | masm.Push(idVal); |
| 4875 | masm.moveStackPtrTo(temp0); |
| 4876 | |
| 4877 | using Fn = bool (*)(JSContext* cx, JSObject* obj, |
| 4878 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
| 4879 | masm.setupAlignedABICall(); |
| 4880 | masm.loadJSContext(temp1); |
| 4881 | masm.passABIArg(temp1); |
| 4882 | masm.passABIArg(obj); |
| 4883 | masm.passABIArg(temp2); |
| 4884 | masm.passABIArg(temp0); |
| 4885 | if (lir->mir()->hasOwn()) { |
| 4886 | masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>(); |
| 4887 | } else { |
| 4888 | masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>(); |
| 4889 | } |
| 4890 | |
| 4891 | MOZ_ASSERT(!idVal.aliases(temp0))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!idVal.aliases(temp0))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!idVal.aliases(temp0)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("!idVal.aliases(temp0)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 4891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { *((volatile int*)__null) = 4891; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4892 | masm.storeCallPointerResult(temp0); |
| 4893 | masm.Pop(idVal); |
| 4894 | |
| 4895 | uint32_t framePushed = masm.framePushed(); |
| 4896 | Label ok; |
| 4897 | masm.branchIfTrueBool(temp0, &ok); |
| 4898 | masm.freeStack(sizeof(Value)); // Discard result Value. |
| 4899 | masm.jump(&bail); |
| 4900 | |
| 4901 | masm.bind(&ok); |
| 4902 | masm.setFramePushed(framePushed); |
| 4903 | masm.unboxBoolean(Address(masm.getStackPointer(), 0), output); |
| 4904 | masm.freeStack(sizeof(Value)); |
| 4905 | masm.bind(&cacheHit); |
| 4906 | |
| 4907 | bailoutFrom(&bail, lir->snapshot()); |
| 4908 | } |
| 4909 | |
| 4910 | void CodeGenerator::visitSmallObjectVariableKeyHasProp( |
| 4911 | LSmallObjectVariableKeyHasProp* lir) { |
| 4912 | Register id = ToRegister(lir->id()); |
| 4913 | Register output = ToRegister(lir->output()); |
| 4914 | |
| 4915 | #ifdef DEBUG1 |
| 4916 | Label isAtom; |
| 4917 | masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()), |
| 4918 | Imm32(JSString::ATOM_BIT), &isAtom); |
| 4919 | masm.assumeUnreachable("Expected atom input"); |
| 4920 | masm.bind(&isAtom); |
| 4921 | #endif |
| 4922 | |
| 4923 | SharedShape* shape = &lir->mir()->shape()->asShared(); |
| 4924 | |
| 4925 | Label done, success; |
| 4926 | for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) { |
| 4927 | masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()), |
| 4928 | &success); |
| 4929 | } |
| 4930 | masm.move32(Imm32(0), output); |
| 4931 | masm.jump(&done); |
| 4932 | masm.bind(&success); |
| 4933 | masm.move32(Imm32(1), output); |
| 4934 | masm.bind(&done); |
| 4935 | } |
| 4936 | |
| 4937 | void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared( |
| 4938 | LGuardIsNotArrayBufferMaybeShared* guard) { |
| 4939 | Register obj = ToRegister(guard->input()); |
| 4940 | Register temp = ToRegister(guard->temp0()); |
| 4941 | |
| 4942 | Label bail; |
| 4943 | masm.loadObjClassUnsafe(obj, temp); |
| 4944 | masm.branchPtr(Assembler::Equal, temp, |
| 4945 | ImmPtr(&FixedLengthArrayBufferObject::class_), &bail); |
| 4946 | masm.branchPtr(Assembler::Equal, temp, |
| 4947 | ImmPtr(&FixedLengthSharedArrayBufferObject::class_), &bail); |
| 4948 | masm.branchPtr(Assembler::Equal, temp, |
| 4949 | ImmPtr(&ResizableArrayBufferObject::class_), &bail); |
| 4950 | masm.branchPtr(Assembler::Equal, temp, |
| 4951 | ImmPtr(&GrowableSharedArrayBufferObject::class_), &bail); |
| 4952 | bailoutFrom(&bail, guard->snapshot()); |
| 4953 | } |
| 4954 | |
| 4955 | void CodeGenerator::visitGuardIsTypedArray(LGuardIsTypedArray* guard) { |
| 4956 | Register obj = ToRegister(guard->input()); |
| 4957 | Register temp = ToRegister(guard->temp0()); |
| 4958 | |
| 4959 | Label bail; |
| 4960 | masm.loadObjClassUnsafe(obj, temp); |
| 4961 | masm.branchIfClassIsNotTypedArray(temp, &bail); |
| 4962 | bailoutFrom(&bail, guard->snapshot()); |
| 4963 | } |
| 4964 | |
| 4965 | void CodeGenerator::visitGuardIsFixedLengthTypedArray( |
| 4966 | LGuardIsFixedLengthTypedArray* guard) { |
| 4967 | Register obj = ToRegister(guard->input()); |
| 4968 | Register temp = ToRegister(guard->temp0()); |
| 4969 | |
| 4970 | Label bail; |
| 4971 | masm.loadObjClassUnsafe(obj, temp); |
| 4972 | masm.branchIfClassIsNotFixedLengthTypedArray(temp, &bail); |
| 4973 | bailoutFrom(&bail, guard->snapshot()); |
| 4974 | } |
| 4975 | |
| 4976 | void CodeGenerator::visitGuardIsResizableTypedArray( |
| 4977 | LGuardIsResizableTypedArray* guard) { |
| 4978 | Register obj = ToRegister(guard->input()); |
| 4979 | Register temp = ToRegister(guard->temp0()); |
| 4980 | |
| 4981 | Label bail; |
| 4982 | masm.loadObjClassUnsafe(obj, temp); |
| 4983 | masm.branchIfClassIsNotResizableTypedArray(temp, &bail); |
| 4984 | bailoutFrom(&bail, guard->snapshot()); |
| 4985 | } |
| 4986 | |
| 4987 | void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) { |
| 4988 | Register obj = ToRegister(guard->input()); |
| 4989 | |
| 4990 | Label bail; |
| 4991 | |
| 4992 | Address handlerAddr(obj, ProxyObject::offsetOfHandler()); |
| 4993 | masm.branchPtr(Assembler::NotEqual, handlerAddr, |
| 4994 | ImmPtr(guard->mir()->handler()), &bail); |
| 4995 | |
| 4996 | bailoutFrom(&bail, guard->snapshot()); |
| 4997 | } |
| 4998 | |
| 4999 | void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) { |
| 5000 | Register input = ToRegister(guard->input()); |
| 5001 | Register expected = ToRegister(guard->expected()); |
| 5002 | |
| 5003 | Assembler::Condition cond = |
| 5004 | guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
| 5005 | bailoutCmpPtr(cond, input, expected, guard->snapshot()); |
| 5006 | } |
| 5007 | |
| 5008 | void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) { |
| 5009 | Register input = ToRegister(guard->input()); |
| 5010 | Register expected = ToRegister(guard->expected()); |
| 5011 | |
| 5012 | bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot()); |
| 5013 | } |
| 5014 | |
| 5015 | void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) { |
| 5016 | Register str = ToRegister(guard->str()); |
| 5017 | Register scratch = ToRegister(guard->temp0()); |
| 5018 | |
| 5019 | LiveRegisterSet volatileRegs = liveVolatileRegs(guard); |
| 5020 | volatileRegs.takeUnchecked(scratch); |
| 5021 | |
| 5022 | Label bail; |
| 5023 | masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs, |
| 5024 | &bail); |
| 5025 | bailoutFrom(&bail, guard->snapshot()); |
| 5026 | } |
| 5027 | |
| 5028 | void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) { |
| 5029 | Register symbol = ToRegister(guard->symbol()); |
| 5030 | |
| 5031 | bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()), |
| 5032 | guard->snapshot()); |
| 5033 | } |
| 5034 | |
| 5035 | void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) { |
| 5036 | Register num = ToRegister(guard->num()); |
| 5037 | |
| 5038 | bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()), |
| 5039 | guard->snapshot()); |
| 5040 | } |
| 5041 | |
| 5042 | void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) { |
| 5043 | Register str = ToRegister(lir->string()); |
| 5044 | Register output = ToRegister(lir->output()); |
| 5045 | |
| 5046 | Label vmCall, done; |
| 5047 | masm.loadStringIndexValue(str, output, &vmCall); |
| 5048 | masm.jump(&done); |
| 5049 | |
| 5050 | { |
| 5051 | masm.bind(&vmCall); |
| 5052 | |
| 5053 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 5054 | volatileRegs.takeUnchecked(output); |
| 5055 | masm.PushRegsInMask(volatileRegs); |
| 5056 | |
| 5057 | using Fn = int32_t (*)(JSString* str); |
| 5058 | masm.setupAlignedABICall(); |
| 5059 | masm.passABIArg(str); |
| 5060 | masm.callWithABI<Fn, GetIndexFromString>(); |
| 5061 | masm.storeCallInt32Result(output); |
| 5062 | |
| 5063 | masm.PopRegsInMask(volatileRegs); |
| 5064 | |
| 5065 | // GetIndexFromString returns a negative value on failure. |
| 5066 | bailoutTest32(Assembler::Signed, output, output, lir->snapshot()); |
| 5067 | } |
| 5068 | |
| 5069 | masm.bind(&done); |
| 5070 | } |
| 5071 | |
| 5072 | void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) { |
| 5073 | Register str = ToRegister(lir->string()); |
| 5074 | Register output = ToRegister(lir->output()); |
| 5075 | Register temp = ToRegister(lir->temp0()); |
| 5076 | |
| 5077 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 5078 | |
| 5079 | Label bail; |
| 5080 | masm.guardStringToInt32(str, output, temp, volatileRegs, &bail); |
| 5081 | bailoutFrom(&bail, lir->snapshot()); |
| 5082 | } |
| 5083 | |
| 5084 | void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) { |
| 5085 | Register str = ToRegister(lir->string()); |
| 5086 | FloatRegister output = ToFloatRegister(lir->output()); |
| 5087 | Register temp0 = ToRegister(lir->temp0()); |
| 5088 | Register temp1 = ToRegister(lir->temp1()); |
| 5089 | |
| 5090 | Label vmCall, done; |
| 5091 | // Use indexed value as fast path if possible. |
| 5092 | masm.loadStringIndexValue(str, temp0, &vmCall); |
| 5093 | masm.convertInt32ToDouble(temp0, output); |
| 5094 | masm.jump(&done); |
| 5095 | { |
| 5096 | masm.bind(&vmCall); |
| 5097 | |
| 5098 | // Reserve stack for holding the result value of the call. |
| 5099 | masm.reserveStack(sizeof(double)); |
| 5100 | masm.moveStackPtrTo(temp0); |
| 5101 | |
| 5102 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 5103 | volatileRegs.takeUnchecked(temp0); |
| 5104 | volatileRegs.takeUnchecked(temp1); |
| 5105 | masm.PushRegsInMask(volatileRegs); |
| 5106 | |
| 5107 | using Fn = bool (*)(JSContext* cx, JSString* str, double* result); |
| 5108 | masm.setupAlignedABICall(); |
| 5109 | masm.loadJSContext(temp1); |
| 5110 | masm.passABIArg(temp1); |
| 5111 | masm.passABIArg(str); |
| 5112 | masm.passABIArg(temp0); |
| 5113 | masm.callWithABI<Fn, StringToNumberPure>(); |
| 5114 | masm.storeCallPointerResult(temp0); |
| 5115 | |
| 5116 | masm.PopRegsInMask(volatileRegs); |
| 5117 | |
| 5118 | Label ok; |
| 5119 | masm.branchIfTrueBool(temp0, &ok); |
| 5120 | { |
| 5121 | // OOM path, recovered by StringToNumberPure. |
| 5122 | // |
| 5123 | // Use addToStackPtr instead of freeStack as freeStack tracks stack height |
| 5124 | // flow-insensitively, and using it here would confuse the stack height |
| 5125 | // tracking. |
| 5126 | masm.addToStackPtr(Imm32(sizeof(double))); |
| 5127 | bailout(lir->snapshot()); |
| 5128 | } |
| 5129 | masm.bind(&ok); |
| 5130 | masm.Pop(output); |
| 5131 | } |
| 5132 | masm.bind(&done); |
| 5133 | } |
| 5134 | |
| 5135 | void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) { |
| 5136 | Register obj = ToRegister(guard->input()); |
| 5137 | Register temp = ToRegister(guard->temp0()); |
| 5138 | |
| 5139 | // Load obj->elements. |
| 5140 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp); |
| 5141 | |
| 5142 | // Make sure there are no dense elements. |
| 5143 | Address initLength(temp, ObjectElements::offsetOfInitializedLength()); |
| 5144 | bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot()); |
| 5145 | } |
| 5146 | |
| 5147 | void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) { |
| 5148 | Register input = ToRegister(lir->input()); |
| 5149 | Register64 output = ToOutRegister64(lir); |
| 5150 | |
| 5151 | masm.move32To64ZeroExtend(input, output); |
| 5152 | } |
| 5153 | |
| 5154 | void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input, |
| 5155 | Register64 output) { |
| 5156 | Register temp = output.scratchReg(); |
| 5157 | |
| 5158 | saveLive(lir); |
| 5159 | |
| 5160 | masm.reserveStack(sizeof(uint64_t)); |
| 5161 | masm.moveStackPtrTo(temp); |
| 5162 | pushArg(temp); |
| 5163 | pushArg(input); |
| 5164 | |
| 5165 | using Fn = bool (*)(JSContext*, HandleString, uint64_t*); |
| 5166 | callVM<Fn, DoStringToInt64>(lir); |
| 5167 | |
| 5168 | masm.load64(Address(masm.getStackPointer(), 0), output); |
| 5169 | masm.freeStack(sizeof(uint64_t)); |
| 5170 | |
| 5171 | restoreLiveIgnore(lir, StoreValueTo(output).clobbered()); |
| 5172 | } |
| 5173 | |
| 5174 | void CodeGenerator::visitStringToInt64(LStringToInt64* lir) { |
| 5175 | Register input = ToRegister(lir->input()); |
| 5176 | Register64 output = ToOutRegister64(lir); |
| 5177 | |
| 5178 | emitStringToInt64(lir, input, output); |
| 5179 | } |
| 5180 | |
| 5181 | void CodeGenerator::visitValueToInt64(LValueToInt64* lir) { |
| 5182 | ValueOperand input = ToValue(lir, LValueToInt64::InputIndex); |
| 5183 | Register temp = ToRegister(lir->temp0()); |
| 5184 | Register64 output = ToOutRegister64(lir); |
| 5185 | |
| 5186 | int checks = 3; |
| 5187 | |
| 5188 | Label fail, done; |
| 5189 | // Jump to fail if this is the last check and we fail it, |
| 5190 | // otherwise to the next test. |
| 5191 | auto emitTestAndUnbox = [&](auto testAndUnbox) { |
| 5192 | MOZ_ASSERT(checks > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(checks > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(checks > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("checks > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks > 0" ")"); do { *((volatile int*)__null) = 5192; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5193 | |
| 5194 | checks--; |
| 5195 | Label notType; |
| 5196 | Label* target = checks ? ¬Type : &fail; |
| 5197 | |
| 5198 | testAndUnbox(target); |
| 5199 | |
| 5200 | if (checks) { |
| 5201 | masm.jump(&done); |
| 5202 | masm.bind(¬Type); |
| 5203 | } |
| 5204 | }; |
| 5205 | |
| 5206 | Register tag = masm.extractTag(input, temp); |
| 5207 | |
| 5208 | // BigInt. |
| 5209 | emitTestAndUnbox([&](Label* target) { |
| 5210 | masm.branchTestBigInt(Assembler::NotEqual, tag, target); |
| 5211 | masm.unboxBigInt(input, temp); |
| 5212 | masm.loadBigInt64(temp, output); |
| 5213 | }); |
| 5214 | |
| 5215 | // Boolean |
| 5216 | emitTestAndUnbox([&](Label* target) { |
| 5217 | masm.branchTestBoolean(Assembler::NotEqual, tag, target); |
| 5218 | masm.unboxBoolean(input, temp); |
| 5219 | masm.move32To64ZeroExtend(temp, output); |
| 5220 | }); |
| 5221 | |
| 5222 | // String |
| 5223 | emitTestAndUnbox([&](Label* target) { |
| 5224 | masm.branchTestString(Assembler::NotEqual, tag, target); |
| 5225 | masm.unboxString(input, temp); |
| 5226 | emitStringToInt64(lir, temp, output); |
| 5227 | }); |
| 5228 | |
| 5229 | MOZ_ASSERT(checks == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(checks == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(checks == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("checks == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5229); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0" ")"); do { *((volatile int*)__null) = 5229; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5230 | |
| 5231 | bailoutFrom(&fail, lir->snapshot()); |
| 5232 | masm.bind(&done); |
| 5233 | } |
| 5234 | |
| 5235 | void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) { |
| 5236 | Register operand = ToRegister(lir->input()); |
| 5237 | Register64 output = ToOutRegister64(lir); |
| 5238 | |
| 5239 | masm.loadBigInt64(operand, output); |
| 5240 | } |
| 5241 | |
| 5242 | OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir, |
| 5243 | Scalar::Type type, |
| 5244 | Register64 input, |
| 5245 | Register output) { |
| 5246 | #if JS_BITS_PER_WORD64 == 32 |
| 5247 | using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t); |
| 5248 | auto args = ArgList(input.low, input.high); |
| 5249 | #else |
| 5250 | using Fn = BigInt* (*)(JSContext*, uint64_t); |
| 5251 | auto args = ArgList(input); |
| 5252 | #endif |
| 5253 | |
| 5254 | if (type == Scalar::BigInt64) { |
| 5255 | return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args, |
| 5256 | StoreRegisterTo(output)); |
| 5257 | } |
| 5258 | MOZ_ASSERT(type == Scalar::BigUint64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == Scalar::BigUint64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == Scalar::BigUint64))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == Scalar::BigUint64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5258); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == Scalar::BigUint64" ")"); do { *((volatile int*)__null) = 5258; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5259 | return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args, |
| 5260 | StoreRegisterTo(output)); |
| 5261 | } |
| 5262 | |
| 5263 | void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type, |
| 5264 | Register64 input, Register output, |
| 5265 | Register maybeTemp, |
| 5266 | Register64 maybeTemp64) { |
| 5267 | OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output); |
| 5268 | |
| 5269 | if (maybeTemp != InvalidReg) { |
| 5270 | masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry()); |
| 5271 | } else { |
| 5272 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 5273 | regs.take(input); |
| 5274 | regs.take(output); |
| 5275 | |
| 5276 | Register temp = regs.takeAny(); |
| 5277 | |
| 5278 | masm.push(temp); |
| 5279 | |
| 5280 | Label fail, ok; |
| 5281 | masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail); |
| 5282 | masm.pop(temp); |
| 5283 | masm.jump(&ok); |
| 5284 | masm.bind(&fail); |
| 5285 | masm.pop(temp); |
| 5286 | masm.jump(ool->entry()); |
| 5287 | masm.bind(&ok); |
| 5288 | } |
| 5289 | masm.initializeBigInt64(type, output, input, maybeTemp64); |
| 5290 | masm.bind(ool->rejoin()); |
| 5291 | } |
| 5292 | |
| 5293 | void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) { |
| 5294 | Register64 input = ToRegister64(lir->input()); |
| 5295 | Register64 temp = ToRegister64(lir->temp()); |
| 5296 | Register output = ToRegister(lir->output()); |
| 5297 | |
| 5298 | emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp.scratchReg(), |
| 5299 | temp); |
| 5300 | } |
| 5301 | |
| 5302 | void CodeGenerator::visitUint64ToBigInt(LUint64ToBigInt* lir) { |
| 5303 | Register64 input = ToRegister64(lir->input()); |
| 5304 | Register temp = ToRegister(lir->temp0()); |
| 5305 | Register output = ToRegister(lir->output()); |
| 5306 | |
| 5307 | emitCreateBigInt(lir, Scalar::BigUint64, input, output, temp); |
| 5308 | } |
| 5309 | |
| 5310 | void CodeGenerator::visitInt64ToIntPtr(LInt64ToIntPtr* lir) { |
| 5311 | Register64 input = ToRegister64(lir->input()); |
| 5312 | #ifdef JS_64BIT1 |
| 5313 | MOZ_ASSERT(input.reg == ToRegister(lir->output()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(input.reg == ToRegister(lir->output()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(input.reg == ToRegister(lir->output())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("input.reg == ToRegister(lir->output())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "input.reg == ToRegister(lir->output())" ")"); do { *((volatile int*)__null) = 5313; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5314 | #else |
| 5315 | Register output = ToRegister(lir->output()); |
| 5316 | #endif |
| 5317 | |
| 5318 | Label bail; |
| 5319 | if (lir->mir()->isSigned()) { |
| 5320 | masm.branchInt64NotInPtrRange(input, &bail); |
| 5321 | } else { |
| 5322 | masm.branchUInt64NotInPtrRange(input, &bail); |
| 5323 | } |
| 5324 | bailoutFrom(&bail, lir->snapshot()); |
| 5325 | |
| 5326 | #ifndef JS_64BIT1 |
| 5327 | masm.move64To32(input, output); |
| 5328 | #endif |
| 5329 | } |
| 5330 | |
| 5331 | void CodeGenerator::visitIntPtrToInt64(LIntPtrToInt64* lir) { |
| 5332 | #ifdef JS_64BIT1 |
| 5333 | MOZ_CRASH("Not used on 64-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 64-bit platforms" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5333); AnnotateMozCrashReason("MOZ_CRASH(" "Not used on 64-bit platforms" ")"); do { *((volatile int*)__null) = 5333; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 5334 | #else |
| 5335 | Register input = ToRegister(lir->input()); |
| 5336 | Register64 output = ToOutRegister64(lir); |
| 5337 | |
| 5338 | masm.move32To64SignExtend(input, output); |
| 5339 | #endif |
| 5340 | } |
| 5341 | |
| 5342 | void CodeGenerator::visitGuardValue(LGuardValue* lir) { |
| 5343 | ValueOperand input = ToValue(lir, LGuardValue::InputIndex); |
| 5344 | Value expected = lir->mir()->expected(); |
| 5345 | Label bail; |
| 5346 | masm.branchTestValue(Assembler::NotEqual, input, expected, &bail); |
| 5347 | bailoutFrom(&bail, lir->snapshot()); |
| 5348 | } |
| 5349 | |
| 5350 | void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) { |
| 5351 | ValueOperand input = ToValue(lir, LGuardNullOrUndefined::InputIndex); |
| 5352 | |
| 5353 | ScratchTagScope tag(masm, input); |
| 5354 | masm.splitTagForTest(input, tag); |
| 5355 | |
| 5356 | Label done; |
| 5357 | masm.branchTestNull(Assembler::Equal, tag, &done); |
| 5358 | |
| 5359 | Label bail; |
| 5360 | masm.branchTestUndefined(Assembler::NotEqual, tag, &bail); |
| 5361 | bailoutFrom(&bail, lir->snapshot()); |
| 5362 | |
| 5363 | masm.bind(&done); |
| 5364 | } |
| 5365 | |
| 5366 | void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) { |
| 5367 | ValueOperand input = ToValue(lir, LGuardIsNotObject::InputIndex); |
| 5368 | |
| 5369 | Label bail; |
| 5370 | masm.branchTestObject(Assembler::Equal, input, &bail); |
| 5371 | bailoutFrom(&bail, lir->snapshot()); |
| 5372 | } |
| 5373 | |
| 5374 | void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) { |
| 5375 | Register function = ToRegister(lir->function()); |
| 5376 | |
| 5377 | Label bail; |
| 5378 | if (uint16_t flags = lir->mir()->expectedFlags()) { |
| 5379 | masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail); |
| 5380 | } |
| 5381 | if (uint16_t flags = lir->mir()->unexpectedFlags()) { |
| 5382 | masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail); |
| 5383 | } |
| 5384 | bailoutFrom(&bail, lir->snapshot()); |
| 5385 | } |
| 5386 | |
| 5387 | void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor( |
| 5388 | LGuardFunctionIsNonBuiltinCtor* lir) { |
| 5389 | Register function = ToRegister(lir->function()); |
| 5390 | Register temp = ToRegister(lir->temp0()); |
| 5391 | |
| 5392 | Label bail; |
| 5393 | masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail); |
| 5394 | bailoutFrom(&bail, lir->snapshot()); |
| 5395 | } |
| 5396 | |
| 5397 | void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) { |
| 5398 | Register function = ToRegister(lir->function()); |
| 5399 | Register temp = ToRegister(lir->temp0()); |
| 5400 | |
| 5401 | Assembler::Condition cond = |
| 5402 | lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
| 5403 | |
| 5404 | Label bail; |
| 5405 | masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail); |
| 5406 | bailoutFrom(&bail, lir->snapshot()); |
| 5407 | } |
| 5408 | |
| 5409 | void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) { |
| 5410 | Register function = ToRegister(lir->function()); |
| 5411 | |
| 5412 | Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript()); |
| 5413 | bailoutCmpPtr(Assembler::NotEqual, scriptAddr, |
| 5414 | ImmGCPtr(lir->mir()->expected()), lir->snapshot()); |
| 5415 | } |
| 5416 | |
| 5417 | // Out-of-line path to update the store buffer. |
| 5418 | class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> { |
| 5419 | LInstruction* lir_; |
| 5420 | const LAllocation* object_; |
| 5421 | |
| 5422 | public: |
| 5423 | OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object) |
| 5424 | : lir_(lir), object_(object) {} |
| 5425 | |
| 5426 | void accept(CodeGenerator* codegen) override { |
| 5427 | codegen->visitOutOfLineCallPostWriteBarrier(this); |
| 5428 | } |
| 5429 | |
| 5430 | LInstruction* lir() const { return lir_; } |
| 5431 | const LAllocation* object() const { return object_; } |
| 5432 | }; |
| 5433 | |
| 5434 | static void EmitStoreBufferCheckForConstant(MacroAssembler& masm, |
| 5435 | const gc::TenuredCell* cell, |
| 5436 | AllocatableGeneralRegisterSet& regs, |
| 5437 | Label* exit, Label* callVM) { |
| 5438 | Register temp = regs.takeAny(); |
| 5439 | |
| 5440 | gc::Arena* arena = cell->arena(); |
| 5441 | |
| 5442 | Register cells = temp; |
| 5443 | masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells); |
| 5444 | |
| 5445 | size_t index = gc::ArenaCellSet::getCellIndex(cell); |
| 5446 | size_t word; |
| 5447 | uint32_t mask; |
| 5448 | gc::ArenaCellSet::getWordIndexAndMask(index, &word, &mask); |
| 5449 | size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t); |
| 5450 | |
| 5451 | masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask), |
| 5452 | exit); |
| 5453 | |
| 5454 | // Check whether this is the sentinel set and if so call the VM to allocate |
| 5455 | // one for this arena. |
| 5456 | masm.branchPtr(Assembler::Equal, |
| 5457 | Address(cells, gc::ArenaCellSet::offsetOfArena()), |
| 5458 | ImmPtr(nullptr), callVM); |
| 5459 | |
| 5460 | // Add the cell to the set. |
| 5461 | masm.or32(Imm32(mask), Address(cells, offset)); |
| 5462 | masm.jump(exit); |
| 5463 | |
| 5464 | regs.add(temp); |
| 5465 | } |
| 5466 | |
| 5467 | static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime, |
| 5468 | Register objreg, JSObject* maybeConstant, |
| 5469 | bool isGlobal, |
| 5470 | AllocatableGeneralRegisterSet& regs) { |
| 5471 | MOZ_ASSERT_IF(isGlobal, maybeConstant)do { if (isGlobal) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(maybeConstant)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(maybeConstant))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("maybeConstant", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5471); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeConstant" ")"); do { *((volatile int*)__null) = 5471; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 5472 | |
| 5473 | Label callVM; |
| 5474 | Label exit; |
| 5475 | |
| 5476 | Register temp = regs.takeAny(); |
| 5477 | |
| 5478 | // We already have a fast path to check whether a global is in the store |
| 5479 | // buffer. |
| 5480 | if (!isGlobal) { |
| 5481 | if (maybeConstant) { |
| 5482 | // Check store buffer bitmap directly for known object. |
| 5483 | EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs, |
| 5484 | &exit, &callVM); |
| 5485 | } else { |
| 5486 | // Check one element cache to avoid VM call. |
| 5487 | masm.branchPtr(Assembler::Equal, |
| 5488 | AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()), |
| 5489 | objreg, &exit); |
| 5490 | } |
| 5491 | } |
| 5492 | |
| 5493 | // Call into the VM to barrier the write. |
| 5494 | masm.bind(&callVM); |
| 5495 | |
| 5496 | Register runtimereg = temp; |
| 5497 | masm.mov(ImmPtr(runtime), runtimereg); |
| 5498 | |
| 5499 | masm.setupAlignedABICall(); |
| 5500 | masm.passABIArg(runtimereg); |
| 5501 | masm.passABIArg(objreg); |
| 5502 | if (isGlobal) { |
| 5503 | using Fn = void (*)(JSRuntime* rt, GlobalObject* obj); |
| 5504 | masm.callWithABI<Fn, PostGlobalWriteBarrier>(); |
| 5505 | } else { |
| 5506 | using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj); |
| 5507 | masm.callWithABI<Fn, PostWriteBarrier>(); |
| 5508 | } |
| 5509 | |
| 5510 | masm.bind(&exit); |
| 5511 | } |
| 5512 | |
| 5513 | void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) { |
| 5514 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 5515 | |
| 5516 | Register objreg; |
| 5517 | JSObject* object = nullptr; |
| 5518 | bool isGlobal = false; |
| 5519 | if (obj->isConstant()) { |
| 5520 | object = &obj->toConstant()->toObject(); |
| 5521 | isGlobal = isGlobalObject(object); |
| 5522 | objreg = regs.takeAny(); |
| 5523 | masm.movePtr(ImmGCPtr(object), objreg); |
| 5524 | } else { |
| 5525 | objreg = ToRegister(obj); |
| 5526 | regs.takeUnchecked(objreg); |
| 5527 | } |
| 5528 | |
| 5529 | EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs); |
| 5530 | } |
| 5531 | |
| 5532 | // Returns true if `def` might be allocated in the nursery. |
| 5533 | static bool ValueNeedsPostBarrier(MDefinition* def) { |
| 5534 | if (def->isBox()) { |
| 5535 | def = def->toBox()->input(); |
| 5536 | } |
| 5537 | if (def->type() == MIRType::Value) { |
| 5538 | return true; |
| 5539 | } |
| 5540 | return NeedsPostBarrier(def->type()); |
| 5541 | } |
| 5542 | |
| 5543 | class OutOfLineElementPostWriteBarrier |
| 5544 | : public OutOfLineCodeBase<CodeGenerator> { |
| 5545 | LiveRegisterSet liveVolatileRegs_; |
| 5546 | const LAllocation* index_; |
| 5547 | int32_t indexDiff_; |
| 5548 | Register obj_; |
| 5549 | Register scratch_; |
| 5550 | |
| 5551 | public: |
| 5552 | OutOfLineElementPostWriteBarrier(const LiveRegisterSet& liveVolatileRegs, |
| 5553 | Register obj, const LAllocation* index, |
| 5554 | Register scratch, int32_t indexDiff) |
| 5555 | : liveVolatileRegs_(liveVolatileRegs), |
| 5556 | index_(index), |
| 5557 | indexDiff_(indexDiff), |
| 5558 | obj_(obj), |
| 5559 | scratch_(scratch) {} |
| 5560 | |
| 5561 | void accept(CodeGenerator* codegen) override { |
| 5562 | codegen->visitOutOfLineElementPostWriteBarrier(this); |
| 5563 | } |
| 5564 | |
| 5565 | const LiveRegisterSet& liveVolatileRegs() const { return liveVolatileRegs_; } |
| 5566 | const LAllocation* index() const { return index_; } |
| 5567 | int32_t indexDiff() const { return indexDiff_; } |
| 5568 | |
| 5569 | Register object() const { return obj_; } |
| 5570 | Register scratch() const { return scratch_; } |
| 5571 | }; |
| 5572 | |
| 5573 | void CodeGenerator::emitElementPostWriteBarrier( |
| 5574 | MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj, |
| 5575 | const LAllocation* index, Register scratch, const ConstantOrRegister& val, |
| 5576 | int32_t indexDiff) { |
| 5577 | if (val.constant()) { |
| 5578 | MOZ_ASSERT_IF(val.value().isGCThing(),do { if (val.value().isGCThing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!IsInsideNursery (val.value().toGCThing()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(val.value() .toGCThing())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!IsInsideNursery(val.value().toGCThing())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())" ")"); do { *((volatile int*)__null) = 5579; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
| 5579 | !IsInsideNursery(val.value().toGCThing()))do { if (val.value().isGCThing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!IsInsideNursery (val.value().toGCThing()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(val.value() .toGCThing())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!IsInsideNursery(val.value().toGCThing())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())" ")"); do { *((volatile int*)__null) = 5579; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 5580 | return; |
| 5581 | } |
| 5582 | |
| 5583 | TypedOrValueRegister reg = val.reg(); |
| 5584 | if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) { |
| 5585 | return; |
| 5586 | } |
| 5587 | |
| 5588 | auto* ool = new (alloc()) OutOfLineElementPostWriteBarrier( |
| 5589 | liveVolatileRegs, obj, index, scratch, indexDiff); |
| 5590 | addOutOfLineCode(ool, mir); |
| 5591 | |
| 5592 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, scratch, ool->rejoin()); |
| 5593 | |
| 5594 | if (reg.hasValue()) { |
| 5595 | masm.branchValueIsNurseryCell(Assembler::Equal, reg.valueReg(), scratch, |
| 5596 | ool->entry()); |
| 5597 | } else { |
| 5598 | masm.branchPtrInNurseryChunk(Assembler::Equal, reg.typedReg().gpr(), |
| 5599 | scratch, ool->entry()); |
| 5600 | } |
| 5601 | |
| 5602 | masm.bind(ool->rejoin()); |
| 5603 | } |
| 5604 | |
| 5605 | void CodeGenerator::visitOutOfLineElementPostWriteBarrier( |
| 5606 | OutOfLineElementPostWriteBarrier* ool) { |
| 5607 | Register obj = ool->object(); |
| 5608 | Register scratch = ool->scratch(); |
| 5609 | const LAllocation* index = ool->index(); |
| 5610 | int32_t indexDiff = ool->indexDiff(); |
| 5611 | |
| 5612 | masm.PushRegsInMask(ool->liveVolatileRegs()); |
| 5613 | |
| 5614 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 5615 | regs.takeUnchecked(obj); |
| 5616 | regs.takeUnchecked(scratch); |
| 5617 | |
| 5618 | Register indexReg; |
| 5619 | if (index->isConstant()) { |
| 5620 | indexReg = regs.takeAny(); |
| 5621 | masm.move32(Imm32(ToInt32(index) + indexDiff), indexReg); |
| 5622 | } else { |
| 5623 | indexReg = ToRegister(index); |
| 5624 | regs.takeUnchecked(indexReg); |
| 5625 | if (indexDiff != 0) { |
| 5626 | masm.add32(Imm32(indexDiff), indexReg); |
| 5627 | } |
| 5628 | } |
| 5629 | |
| 5630 | masm.setupUnalignedABICall(scratch); |
| 5631 | masm.movePtr(ImmPtr(gen->runtime), scratch); |
| 5632 | masm.passABIArg(scratch); |
| 5633 | masm.passABIArg(obj); |
| 5634 | masm.passABIArg(indexReg); |
| 5635 | using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index); |
| 5636 | masm.callWithABI<Fn, PostWriteElementBarrier>(); |
| 5637 | |
| 5638 | // We don't need a sub32 here because indexReg must be in liveVolatileRegs |
| 5639 | // if indexDiff is not zero, so it will be restored below. |
| 5640 | MOZ_ASSERT_IF(indexDiff != 0, ool->liveVolatileRegs().has(indexReg))do { if (indexDiff != 0) { do { static_assert( mozilla::detail ::AssertionConditionType<decltype(ool->liveVolatileRegs ().has(indexReg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ool->liveVolatileRegs().has (indexReg)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ool->liveVolatileRegs().has(indexReg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5640); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ool->liveVolatileRegs().has(indexReg)" ")"); do { *((volatile int*)__null) = 5640; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 5641 | |
| 5642 | masm.PopRegsInMask(ool->liveVolatileRegs()); |
| 5643 | |
| 5644 | masm.jump(ool->rejoin()); |
| 5645 | } |
| 5646 | |
| 5647 | void CodeGenerator::emitPostWriteBarrier(Register objreg) { |
| 5648 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 5649 | regs.takeUnchecked(objreg); |
| 5650 | EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs); |
| 5651 | } |
| 5652 | |
| 5653 | void CodeGenerator::visitOutOfLineCallPostWriteBarrier( |
| 5654 | OutOfLineCallPostWriteBarrier* ool) { |
| 5655 | saveLiveVolatile(ool->lir()); |
| 5656 | const LAllocation* obj = ool->object(); |
| 5657 | emitPostWriteBarrier(obj); |
| 5658 | restoreLiveVolatile(ool->lir()); |
| 5659 | |
| 5660 | masm.jump(ool->rejoin()); |
| 5661 | } |
| 5662 | |
| 5663 | void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal, |
| 5664 | OutOfLineCode* ool) { |
| 5665 | // Check whether an object is a global that we have already barriered before |
| 5666 | // calling into the VM. |
| 5667 | // |
| 5668 | // We only check for the script's global, not other globals within the same |
| 5669 | // compartment, because we bake in a pointer to realm->globalWriteBarriered |
| 5670 | // and doing that would be invalid for other realms because they could be |
| 5671 | // collected before the Ion code is discarded. |
| 5672 | |
| 5673 | if (!maybeGlobal->isConstant()) { |
| 5674 | return; |
| 5675 | } |
| 5676 | |
| 5677 | JSObject* obj = &maybeGlobal->toConstant()->toObject(); |
| 5678 | if (gen->realm->maybeGlobal() != obj) { |
| 5679 | return; |
| 5680 | } |
| 5681 | |
| 5682 | const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered(); |
| 5683 | masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0), |
| 5684 | ool->rejoin()); |
| 5685 | } |
| 5686 | |
| 5687 | template <class LPostBarrierType, MIRType nurseryType> |
| 5688 | void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir, |
| 5689 | OutOfLineCode* ool) { |
| 5690 | static_assert(NeedsPostBarrier(nurseryType)); |
| 5691 | |
| 5692 | addOutOfLineCode(ool, lir->mir()); |
| 5693 | |
| 5694 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 5695 | |
| 5696 | if (lir->object()->isConstant()) { |
| 5697 | // Constant nursery objects cannot appear here, see |
| 5698 | // LIRGenerator::visitPostWriteElementBarrier. |
| 5699 | MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsInsideNursery(&lir->object()->toConstant ()->toObject()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&lir-> object()->toConstant()->toObject())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsInsideNursery(&lir->object()->toConstant()->toObject())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5699); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())" ")"); do { *((volatile int*)__null) = 5699; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5700 | } else { |
| 5701 | masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()), |
| 5702 | temp, ool->rejoin()); |
| 5703 | } |
| 5704 | |
| 5705 | maybeEmitGlobalBarrierCheck(lir->object(), ool); |
| 5706 | |
| 5707 | Register value = ToRegister(lir->value()); |
| 5708 | if constexpr (nurseryType == MIRType::Object) { |
| 5709 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5709); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 5709; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5710 | } else if constexpr (nurseryType == MIRType::String) { |
| 5711 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::String )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 5711; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5712 | } else { |
| 5713 | static_assert(nurseryType == MIRType::BigInt); |
| 5714 | MOZ_ASSERT(lir->mir()->value()->type() == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->value()->type() == MIRType::BigInt )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->value()->type() == MIRType::BigInt ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->value()->type() == MIRType::BigInt", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5714); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 5714; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5715 | } |
| 5716 | masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry()); |
| 5717 | |
| 5718 | masm.bind(ool->rejoin()); |
| 5719 | } |
| 5720 | |
| 5721 | template <class LPostBarrierType> |
| 5722 | void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir, |
| 5723 | OutOfLineCode* ool) { |
| 5724 | addOutOfLineCode(ool, lir->mir()); |
| 5725 | |
| 5726 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 5727 | |
| 5728 | if (lir->object()->isConstant()) { |
| 5729 | // Constant nursery objects cannot appear here, see |
| 5730 | // LIRGenerator::visitPostWriteElementBarrier. |
| 5731 | MOZ_ASSERT(!IsInsideNursery(&lir->object()->toConstant()->toObject()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsInsideNursery(&lir->object()->toConstant ()->toObject()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsInsideNursery(&lir-> object()->toConstant()->toObject())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsInsideNursery(&lir->object()->toConstant()->toObject())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 5731); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())" ")"); do { *((volatile int*)__null) = 5731; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5732 | } else { |
| 5733 | masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()), |
| 5734 | temp, ool->rejoin()); |
| 5735 | } |
| 5736 | |
| 5737 | maybeEmitGlobalBarrierCheck(lir->object(), ool); |
| 5738 | |
| 5739 | ValueOperand value = ToValue(lir, LPostBarrierType::ValueIndex); |
| 5740 | masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry()); |
| 5741 | |
| 5742 | masm.bind(ool->rejoin()); |
| 5743 | } |
| 5744 | |
| 5745 | void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) { |
| 5746 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
| 5747 | visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool); |
| 5748 | } |
| 5749 | |
| 5750 | void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) { |
| 5751 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
| 5752 | visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool); |
| 5753 | } |
| 5754 | |
| 5755 | void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) { |
| 5756 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
| 5757 | visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool); |
| 5758 | } |
| 5759 | |
| 5760 | void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) { |
| 5761 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
| 5762 | visitPostWriteBarrierCommonV(lir, ool); |
| 5763 | } |
| 5764 | |
| 5765 | // Out-of-line path to update the store buffer. |
| 5766 | class OutOfLineCallPostWriteElementBarrier |
| 5767 | : public OutOfLineCodeBase<CodeGenerator> { |
| 5768 | LInstruction* lir_; |
| 5769 | const LAllocation* object_; |
| 5770 | const LAllocation* index_; |
| 5771 | |
| 5772 | public: |
| 5773 | OutOfLineCallPostWriteElementBarrier(LInstruction* lir, |
| 5774 | const LAllocation* object, |
| 5775 | const LAllocation* index) |
| 5776 | : lir_(lir), object_(object), index_(index) {} |
| 5777 | |
| 5778 | void accept(CodeGenerator* codegen) override { |
| 5779 | codegen->visitOutOfLineCallPostWriteElementBarrier(this); |
| 5780 | } |
| 5781 | |
| 5782 | LInstruction* lir() const { return lir_; } |
| 5783 | |
| 5784 | const LAllocation* object() const { return object_; } |
| 5785 | |
| 5786 | const LAllocation* index() const { return index_; } |
| 5787 | }; |
| 5788 | |
| 5789 | void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier( |
| 5790 | OutOfLineCallPostWriteElementBarrier* ool) { |
| 5791 | saveLiveVolatile(ool->lir()); |
| 5792 | |
| 5793 | const LAllocation* obj = ool->object(); |
| 5794 | const LAllocation* index = ool->index(); |
| 5795 | |
| 5796 | Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj); |
| 5797 | Register indexreg = ToRegister(index); |
| 5798 | |
| 5799 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 5800 | regs.takeUnchecked(indexreg); |
| 5801 | |
| 5802 | if (obj->isConstant()) { |
| 5803 | objreg = regs.takeAny(); |
| 5804 | masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg); |
| 5805 | } else { |
| 5806 | regs.takeUnchecked(objreg); |
| 5807 | } |
| 5808 | |
| 5809 | Register runtimereg = regs.takeAny(); |
| 5810 | using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index); |
| 5811 | masm.setupAlignedABICall(); |
| 5812 | masm.mov(ImmPtr(gen->runtime), runtimereg); |
| 5813 | masm.passABIArg(runtimereg); |
| 5814 | masm.passABIArg(objreg); |
| 5815 | masm.passABIArg(indexreg); |
| 5816 | masm.callWithABI<Fn, PostWriteElementBarrier>(); |
| 5817 | |
| 5818 | restoreLiveVolatile(ool->lir()); |
| 5819 | |
| 5820 | masm.jump(ool->rejoin()); |
| 5821 | } |
| 5822 | |
| 5823 | void CodeGenerator::visitPostWriteElementBarrierO( |
| 5824 | LPostWriteElementBarrierO* lir) { |
| 5825 | auto ool = new (alloc()) |
| 5826 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
| 5827 | visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir, |
| 5828 | ool); |
| 5829 | } |
| 5830 | |
| 5831 | void CodeGenerator::visitPostWriteElementBarrierS( |
| 5832 | LPostWriteElementBarrierS* lir) { |
| 5833 | auto ool = new (alloc()) |
| 5834 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
| 5835 | visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir, |
| 5836 | ool); |
| 5837 | } |
| 5838 | |
| 5839 | void CodeGenerator::visitPostWriteElementBarrierBI( |
| 5840 | LPostWriteElementBarrierBI* lir) { |
| 5841 | auto ool = new (alloc()) |
| 5842 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
| 5843 | visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir, |
| 5844 | ool); |
| 5845 | } |
| 5846 | |
| 5847 | void CodeGenerator::visitPostWriteElementBarrierV( |
| 5848 | LPostWriteElementBarrierV* lir) { |
| 5849 | auto ool = new (alloc()) |
| 5850 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
| 5851 | visitPostWriteBarrierCommonV(lir, ool); |
| 5852 | } |
| 5853 | |
| 5854 | void CodeGenerator::visitAssertCanElidePostWriteBarrier( |
| 5855 | LAssertCanElidePostWriteBarrier* lir) { |
| 5856 | Register object = ToRegister(lir->object()); |
| 5857 | ValueOperand value = |
| 5858 | ToValue(lir, LAssertCanElidePostWriteBarrier::ValueIndex); |
| 5859 | Register temp = ToRegister(lir->temp0()); |
| 5860 | |
| 5861 | Label ok; |
| 5862 | masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok); |
| 5863 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok); |
| 5864 | |
| 5865 | masm.assumeUnreachable("Unexpected missing post write barrier"); |
| 5866 | |
| 5867 | masm.bind(&ok); |
| 5868 | } |
| 5869 | |
| 5870 | template <typename LCallIns> |
| 5871 | void CodeGenerator::emitCallNative(LCallIns* call, JSNative native, |
| 5872 | Register argContextReg, Register argUintNReg, |
| 5873 | Register argVpReg, Register tempReg, |
| 5874 | uint32_t unusedStack) { |
| 5875 | masm.checkStackAlignment(); |
| 5876 | |
| 5877 | // Native functions have the signature: |
| 5878 | // bool (*)(JSContext*, unsigned, Value* vp) |
| 5879 | // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward |
| 5880 | // are the function arguments. |
| 5881 | |
| 5882 | // Allocate space for the outparam, moving the StackPointer to what will be |
| 5883 | // &vp[1]. |
| 5884 | masm.adjustStack(unusedStack); |
| 5885 | |
| 5886 | // Push a Value containing the callee object: natives are allowed to access |
| 5887 | // their callee before setting the return value. The StackPointer is moved |
| 5888 | // to &vp[0]. |
| 5889 | // |
| 5890 | // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|. |
| 5891 | if constexpr (std::is_same_v<LCallIns, LCallClassHook>) { |
| 5892 | Register calleeReg = ToRegister(call->getCallee()); |
| 5893 | masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg))); |
| 5894 | |
| 5895 | // Enter the callee realm. |
| 5896 | if (call->mir()->maybeCrossRealm()) { |
| 5897 | masm.switchToObjectRealm(calleeReg, tempReg); |
| 5898 | } |
| 5899 | } else { |
| 5900 | WrappedFunction* target = call->mir()->getSingleTarget(); |
| 5901 | masm.Push(ObjectValue(*target->rawNativeJSFunction())); |
| 5902 | |
| 5903 | // Enter the callee realm. |
| 5904 | if (call->mir()->maybeCrossRealm()) { |
| 5905 | masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg); |
| 5906 | masm.switchToObjectRealm(tempReg, tempReg); |
| 5907 | } |
| 5908 | } |
| 5909 | |
| 5910 | // Preload arguments into registers. |
| 5911 | masm.loadJSContext(argContextReg); |
| 5912 | masm.moveStackPtrTo(argVpReg); |
| 5913 | |
| 5914 | // Initialize |NativeExitFrameLayout::argc_|. |
| 5915 | masm.Push(argUintNReg); |
| 5916 | |
| 5917 | // Construct native exit frame. |
| 5918 | // |
| 5919 | // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and |
| 5920 | // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|. |
| 5921 | // |
| 5922 | // The NativeExitFrameLayout is now fully initialized. |
| 5923 | uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg); |
| 5924 | masm.enterFakeExitFrameForNative(argContextReg, tempReg, |
| 5925 | call->mir()->isConstructing()); |
| 5926 | |
| 5927 | markSafepointAt(safepointOffset, call); |
| 5928 | |
| 5929 | // Construct and execute call. |
| 5930 | masm.setupAlignedABICall(); |
| 5931 | masm.passABIArg(argContextReg); |
| 5932 | masm.passABIArg(argUintNReg); |
| 5933 | masm.passABIArg(argVpReg); |
| 5934 | |
| 5935 | ensureOsiSpace(); |
| 5936 | // If we're using a simulator build, `native` will already point to the |
| 5937 | // simulator's call-redirection code for LCallClassHook. Load the address in |
| 5938 | // a register first so that we don't try to redirect it a second time. |
| 5939 | bool emittedCall = false; |
| 5940 | #ifdef JS_SIMULATOR |
| 5941 | if constexpr (std::is_same_v<LCallIns, LCallClassHook>) { |
| 5942 | masm.movePtr(ImmPtr(native), tempReg); |
| 5943 | masm.callWithABI(tempReg); |
| 5944 | emittedCall = true; |
| 5945 | } |
| 5946 | #endif |
| 5947 | if (!emittedCall) { |
| 5948 | masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General, |
| 5949 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 5950 | } |
| 5951 | |
| 5952 | // Test for failure. |
| 5953 | masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); |
| 5954 | |
| 5955 | // Exit the callee realm. |
| 5956 | if (call->mir()->maybeCrossRealm()) { |
| 5957 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 5958 | } |
| 5959 | |
| 5960 | // Load the outparam vp[0] into output register(s). |
| 5961 | masm.loadValue( |
| 5962 | Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), |
| 5963 | JSReturnOperand); |
| 5964 | |
| 5965 | // Until C++ code is instrumented against Spectre, prevent speculative |
| 5966 | // execution from returning any private data. |
| 5967 | if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() && |
| 5968 | call->mir()->hasLiveDefUses()) { |
| 5969 | masm.speculationBarrier(); |
| 5970 | } |
| 5971 | |
| 5972 | #ifdef DEBUG1 |
| 5973 | // Native constructors are guaranteed to return an Object value. |
| 5974 | if (call->mir()->isConstructing()) { |
| 5975 | Label notPrimitive; |
| 5976 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 5977 | ¬Primitive); |
| 5978 | masm.assumeUnreachable("native constructors don't return primitives"); |
| 5979 | masm.bind(¬Primitive); |
| 5980 | } |
| 5981 | #endif |
| 5982 | } |
| 5983 | |
| 5984 | template <typename LCallIns> |
| 5985 | void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) { |
| 5986 | uint32_t unusedStack = |
| 5987 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
| 5988 | |
| 5989 | // Registers used for callWithABI() argument-passing. |
| 5990 | const Register argContextReg = ToRegister(call->getArgContextReg()); |
| 5991 | const Register argUintNReg = ToRegister(call->getArgUintNReg()); |
| 5992 | const Register argVpReg = ToRegister(call->getArgVpReg()); |
| 5993 | |
| 5994 | // Misc. temporary registers. |
| 5995 | const Register tempReg = ToRegister(call->getTempReg()); |
| 5996 | |
| 5997 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
| 5998 | |
| 5999 | // Initialize the argc register. |
| 6000 | masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg); |
| 6001 | |
| 6002 | // Create the exit frame and call the native. |
| 6003 | emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg, |
| 6004 | unusedStack); |
| 6005 | |
| 6006 | // The next instruction is removing the footer of the exit frame, so there |
| 6007 | // is no need for leaveFakeExitFrame. |
| 6008 | |
| 6009 | // Move the StackPointer back to its original location, unwinding the native |
| 6010 | // exit frame. |
| 6011 | masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack); |
| 6012 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6012); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 6012; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6013 | } |
| 6014 | |
| 6015 | void CodeGenerator::visitCallNative(LCallNative* call) { |
| 6016 | WrappedFunction* target = call->getSingleTarget(); |
| 6017 | MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6017); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")" ); do { *((volatile int*)__null) = 6017; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6018 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6018); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 6018; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6019 | |
| 6020 | JSNative native = target->native(); |
| 6021 | if (call->ignoresReturnValue() && target->hasJitInfo()) { |
| 6022 | const JSJitInfo* jitInfo = target->jitInfo(); |
| 6023 | if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) { |
| 6024 | native = jitInfo->ignoresReturnValueMethod; |
| 6025 | } |
| 6026 | } |
| 6027 | emitCallNative(call, native); |
| 6028 | } |
| 6029 | |
| 6030 | void CodeGenerator::visitCallClassHook(LCallClassHook* call) { |
| 6031 | emitCallNative(call, call->mir()->target()); |
| 6032 | } |
| 6033 | |
| 6034 | static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv, |
| 6035 | DOMObjectKind kind) { |
| 6036 | // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This |
| 6037 | // will be in the first slot but may be fixed or non-fixed. |
| 6038 | MOZ_ASSERT(obj != priv)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj != priv)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj != priv))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj != priv", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6038); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv" ")"); do { *((volatile int*)__null) = 6038; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6039 | |
| 6040 | switch (kind) { |
| 6041 | case DOMObjectKind::Native: |
| 6042 | // If it's a native object, the value must be in a fixed slot. |
| 6043 | // See CanAttachDOMCall in CacheIR.cpp. |
| 6044 | masm.debugAssertObjHasFixedSlots(obj, priv); |
| 6045 | masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv); |
| 6046 | break; |
| 6047 | case DOMObjectKind::Proxy: { |
| 6048 | #ifdef DEBUG1 |
| 6049 | // Sanity check: it must be a DOM proxy. |
| 6050 | Label isDOMProxy; |
| 6051 | masm.branchTestProxyHandlerFamily( |
| 6052 | Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy); |
| 6053 | masm.assumeUnreachable("Expected a DOM proxy"); |
| 6054 | masm.bind(&isDOMProxy); |
| 6055 | #endif |
| 6056 | masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), priv); |
| 6057 | masm.loadPrivate( |
| 6058 | Address(priv, js::detail::ProxyReservedSlots::offsetOfSlot(0)), priv); |
| 6059 | break; |
| 6060 | } |
| 6061 | } |
| 6062 | } |
| 6063 | |
| 6064 | void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) { |
| 6065 | WrappedFunction* target = call->getSingleTarget(); |
| 6066 | MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6066); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")" ); do { *((volatile int*)__null) = 6066; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6067 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6067); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 6067; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6068 | MOZ_ASSERT(target->hasJitInfo())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->hasJitInfo())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->hasJitInfo()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("target->hasJitInfo()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6068); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitInfo()" ")"); do { *((volatile int*)__null) = 6068; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6069 | MOZ_ASSERT(call->mir()->isCallDOMNative())do { static_assert( mozilla::detail::AssertionConditionType< decltype(call->mir()->isCallDOMNative())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(call->mir()->isCallDOMNative ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("call->mir()->isCallDOMNative()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()" ")"); do { *((volatile int*)__null) = 6069; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6070 | |
| 6071 | int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
| 6072 | |
| 6073 | // Registers used for callWithABI() argument-passing. |
| 6074 | const Register argJSContext = ToRegister(call->getArgJSContext()); |
| 6075 | const Register argObj = ToRegister(call->getArgObj()); |
| 6076 | const Register argPrivate = ToRegister(call->getArgPrivate()); |
| 6077 | const Register argArgs = ToRegister(call->getArgArgs()); |
| 6078 | |
| 6079 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
| 6080 | |
| 6081 | masm.checkStackAlignment(); |
| 6082 | |
| 6083 | // DOM methods have the signature: |
| 6084 | // bool (*)(JSContext*, HandleObject, void* private, const |
| 6085 | // JSJitMethodCallArgs& args) |
| 6086 | // Where args is initialized from an argc and a vp, vp[0] is space for an |
| 6087 | // outparam and the callee, vp[1] is |this|, and vp[2] onward are the |
| 6088 | // function arguments. Note that args stores the argv, not the vp, and |
| 6089 | // argv == vp + 2. |
| 6090 | |
| 6091 | // Nestle the stack up against the pushed arguments, leaving StackPointer at |
| 6092 | // &vp[1] |
| 6093 | masm.adjustStack(unusedStack); |
| 6094 | // argObj is filled with the extracted object, then returned. |
| 6095 | Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj); |
| 6096 | MOZ_ASSERT(obj == argObj)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj == argObj)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj == argObj))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj == argObj", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj == argObj" ")"); do { *((volatile int*)__null) = 6096; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6097 | |
| 6098 | // Push a Value containing the callee object: natives are allowed to access |
| 6099 | // their callee before setting the return value. After this the StackPointer |
| 6100 | // points to &vp[0]. |
| 6101 | masm.Push(ObjectValue(*target->rawNativeJSFunction())); |
| 6102 | |
| 6103 | // Now compute the argv value. Since StackPointer is pointing to &vp[0] and |
| 6104 | // argv is &vp[2] we just need to add 2*sizeof(Value) to the current |
| 6105 | // StackPointer. |
| 6106 | static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0); |
| 6107 | static_assert(JSJitMethodCallArgsTraits::offsetOfArgc == |
| 6108 | IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv); |
| 6109 | masm.computeEffectiveAddress( |
| 6110 | Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs); |
| 6111 | |
| 6112 | LoadDOMPrivate(masm, obj, argPrivate, |
| 6113 | static_cast<MCallDOMNative*>(call->mir())->objectKind()); |
| 6114 | |
| 6115 | // Push argc from the call instruction into what will become the IonExitFrame |
| 6116 | masm.Push(Imm32(call->numActualArgs())); |
| 6117 | |
| 6118 | // Push our argv onto the stack |
| 6119 | masm.Push(argArgs); |
| 6120 | // And store our JSJitMethodCallArgs* in argArgs. |
| 6121 | masm.moveStackPtrTo(argArgs); |
| 6122 | |
| 6123 | // Push |this| object for passing HandleObject. We push after argc to |
| 6124 | // maintain the same sp-relative location of the object pointer with other |
| 6125 | // DOMExitFrames. |
| 6126 | masm.Push(argObj); |
| 6127 | masm.moveStackPtrTo(argObj); |
| 6128 | |
| 6129 | if (call->mir()->maybeCrossRealm()) { |
| 6130 | // We use argJSContext as scratch register here. |
| 6131 | masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext); |
| 6132 | masm.switchToObjectRealm(argJSContext, argJSContext); |
| 6133 | } |
| 6134 | |
| 6135 | bool preTenureWrapperAllocation = |
| 6136 | call->mir()->to<MCallDOMNative>()->initialHeap() == gc::Heap::Tenured; |
| 6137 | if (preTenureWrapperAllocation) { |
| 6138 | auto ptr = ImmPtr(mirGen().realm->zone()->tenuringAllocSite()); |
| 6139 | masm.storeLocalAllocSite(ptr, argJSContext); |
| 6140 | } |
| 6141 | |
| 6142 | // Construct native exit frame. |
| 6143 | uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext); |
| 6144 | |
| 6145 | masm.loadJSContext(argJSContext); |
| 6146 | masm.enterFakeExitFrame(argJSContext, argJSContext, |
| 6147 | ExitFrameType::IonDOMMethod); |
| 6148 | |
| 6149 | markSafepointAt(safepointOffset, call); |
| 6150 | |
| 6151 | // Construct and execute call. |
| 6152 | masm.setupAlignedABICall(); |
| 6153 | masm.loadJSContext(argJSContext); |
| 6154 | masm.passABIArg(argJSContext); |
| 6155 | masm.passABIArg(argObj); |
| 6156 | masm.passABIArg(argPrivate); |
| 6157 | masm.passABIArg(argArgs); |
| 6158 | ensureOsiSpace(); |
| 6159 | masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method), |
| 6160 | ABIType::General, |
| 6161 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 6162 | |
| 6163 | if (target->jitInfo()->isInfallible) { |
| 6164 | masm.loadValue(Address(masm.getStackPointer(), |
| 6165 | IonDOMMethodExitFrameLayout::offsetOfResult()), |
| 6166 | JSReturnOperand); |
| 6167 | } else { |
| 6168 | // Test for failure. |
| 6169 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
| 6170 | |
| 6171 | // Load the outparam vp[0] into output register(s). |
| 6172 | masm.loadValue(Address(masm.getStackPointer(), |
| 6173 | IonDOMMethodExitFrameLayout::offsetOfResult()), |
| 6174 | JSReturnOperand); |
| 6175 | } |
| 6176 | |
| 6177 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 6178 | "Clobbering ReturnReg should not affect the return value"); |
| 6179 | |
| 6180 | // Switch back to the current realm if needed. Note: if the DOM method threw |
| 6181 | // an exception, the exception handler will do this. |
| 6182 | if (call->mir()->maybeCrossRealm()) { |
| 6183 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 6184 | } |
| 6185 | |
| 6186 | // Wipe out the preTenuring bit from the local alloc site |
| 6187 | // On exception we handle this in C++ |
| 6188 | if (preTenureWrapperAllocation) { |
| 6189 | masm.storeLocalAllocSite(ImmPtr(nullptr), ReturnReg); |
| 6190 | } |
| 6191 | |
| 6192 | // Until C++ code is instrumented against Spectre, prevent speculative |
| 6193 | // execution from returning any private data. |
| 6194 | if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) { |
| 6195 | masm.speculationBarrier(); |
| 6196 | } |
| 6197 | |
| 6198 | // The next instruction is removing the footer of the exit frame, so there |
| 6199 | // is no need for leaveFakeExitFrame. |
| 6200 | |
| 6201 | // Move the StackPointer back to its original location, unwinding the native |
| 6202 | // exit frame. |
| 6203 | masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack); |
| 6204 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6204); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 6204; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6205 | } |
| 6206 | |
| 6207 | void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) { |
| 6208 | pushArg(ImmGCPtr(lir->mir()->name())); |
| 6209 | |
| 6210 | using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue); |
| 6211 | callVM<Fn, GetIntrinsicValue>(lir); |
| 6212 | } |
| 6213 | |
| 6214 | void CodeGenerator::emitCallInvokeFunction( |
| 6215 | LInstruction* call, Register calleereg, bool constructing, |
| 6216 | bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) { |
| 6217 | // Nestle %esp up to the argument vector. |
| 6218 | // Each path must account for framePushed_ separately, for callVM to be valid. |
| 6219 | masm.freeStack(unusedStack); |
| 6220 | |
| 6221 | pushArg(masm.getStackPointer()); // argv. |
| 6222 | pushArg(Imm32(argc)); // argc. |
| 6223 | pushArg(Imm32(ignoresReturnValue)); |
| 6224 | pushArg(Imm32(constructing)); // constructing. |
| 6225 | pushArg(calleereg); // JSFunction*. |
| 6226 | |
| 6227 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
| 6228 | MutableHandleValue); |
| 6229 | callVM<Fn, jit::InvokeFunction>(call); |
| 6230 | |
| 6231 | // Un-nestle %esp from the argument vector. No prefix was pushed. |
| 6232 | masm.reserveStack(unusedStack); |
| 6233 | } |
| 6234 | |
| 6235 | void CodeGenerator::visitCallGeneric(LCallGeneric* call) { |
| 6236 | // The callee is passed straight through to the trampoline. |
| 6237 | MOZ_ASSERT(ToRegister(call->getCallee()) == IonGenericCallCalleeReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(call->getCallee()) == IonGenericCallCalleeReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(call->getCallee()) == IonGenericCallCalleeReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToRegister(call->getCallee()) == IonGenericCallCalleeReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg" ")"); do { *((volatile int*)__null) = 6237; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6238 | |
| 6239 | Register argcReg = ToRegister(call->getArgc()); |
| 6240 | uint32_t unusedStack = |
| 6241 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
| 6242 | |
| 6243 | // Known-target case is handled by LCallKnown. |
| 6244 | MOZ_ASSERT(!call->hasSingleTarget())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!call->hasSingleTarget())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!call->hasSingleTarget()) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!call->hasSingleTarget()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->hasSingleTarget()" ")"); do { *((volatile int*)__null) = 6244; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6245 | |
| 6246 | masm.checkStackAlignment(); |
| 6247 | |
| 6248 | masm.move32(Imm32(call->numActualArgs()), argcReg); |
| 6249 | |
| 6250 | // Nestle the StackPointer up to the argument vector. |
| 6251 | masm.freeStack(unusedStack); |
| 6252 | ensureOsiSpace(); |
| 6253 | |
| 6254 | auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct |
| 6255 | : IonGenericCallKind::Call; |
| 6256 | |
| 6257 | TrampolinePtr genericCallStub = |
| 6258 | gen->jitRuntime()->getIonGenericCallStub(kind); |
| 6259 | uint32_t callOffset = masm.callJit(genericCallStub); |
| 6260 | markSafepointAt(callOffset, call); |
| 6261 | |
| 6262 | if (call->mir()->maybeCrossRealm()) { |
| 6263 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 6264 | "ReturnReg available as scratch after scripted calls"); |
| 6265 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 6266 | } |
| 6267 | |
| 6268 | // Restore stack pointer. |
| 6269 | masm.setFramePushed(frameSize()); |
| 6270 | emitRestoreStackPointerFromFP(); |
| 6271 | |
| 6272 | // If the return value of the constructing function is Primitive, |
| 6273 | // replace the return value with the Object from CreateThis. |
| 6274 | if (call->mir()->isConstructing()) { |
| 6275 | Label notPrimitive; |
| 6276 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 6277 | ¬Primitive); |
| 6278 | masm.loadValue(Address(masm.getStackPointer(), unusedStack), |
| 6279 | JSReturnOperand); |
| 6280 | #ifdef DEBUG1 |
| 6281 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 6282 | ¬Primitive); |
| 6283 | masm.assumeUnreachable("CreateThis creates an object"); |
| 6284 | #endif |
| 6285 | masm.bind(¬Primitive); |
| 6286 | } |
| 6287 | } |
| 6288 | |
| 6289 | void JitRuntime::generateIonGenericCallArgumentsShift( |
| 6290 | MacroAssembler& masm, Register argc, Register curr, Register end, |
| 6291 | Register scratch, Label* done) { |
| 6292 | static_assert(sizeof(Value) == 8); |
| 6293 | // There are |argc| Values on the stack. Shift them all down by 8 bytes, |
| 6294 | // overwriting the first value. |
| 6295 | |
| 6296 | // Initialize `curr` to the destination of the first copy, and `end` to the |
| 6297 | // final value of curr. |
| 6298 | masm.moveStackPtrTo(curr); |
| 6299 | masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end); |
| 6300 | |
| 6301 | Label loop; |
| 6302 | masm.bind(&loop); |
| 6303 | masm.branchPtr(Assembler::Equal, curr, end, done); |
| 6304 | masm.loadPtr(Address(curr, 8), scratch); |
| 6305 | masm.storePtr(scratch, Address(curr, 0)); |
| 6306 | masm.addPtr(Imm32(sizeof(uintptr_t)), curr); |
| 6307 | masm.jump(&loop); |
| 6308 | } |
| 6309 | |
| 6310 | void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm, |
| 6311 | IonGenericCallKind kind) { |
| 6312 | AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub"); |
| 6313 | ionGenericCallStubOffset_[kind] = startTrampolineCode(masm); |
| 6314 | |
| 6315 | // This code is tightly coupled with visitCallGeneric. |
| 6316 | // |
| 6317 | // Upon entry: |
| 6318 | // IonGenericCallCalleeReg contains a pointer to the callee object. |
| 6319 | // IonGenericCallArgcReg contains the number of actual args. |
| 6320 | // The arguments have been pushed onto the stack: |
| 6321 | // [newTarget] (iff isConstructing) |
| 6322 | // [argN] |
| 6323 | // ... |
| 6324 | // [arg1] |
| 6325 | // [arg0] |
| 6326 | // [this] |
| 6327 | // <return address> (if not JS_USE_LINK_REGISTER) |
| 6328 | // |
| 6329 | // This trampoline is responsible for entering the callee's realm, |
| 6330 | // massaging the stack into the right shape, and then performing a |
| 6331 | // tail call. We will return directly to the Ion code from the |
| 6332 | // callee. |
| 6333 | // |
| 6334 | // To do a tail call, we keep the return address in a register, even |
| 6335 | // on platforms that don't normally use a link register, and push it |
| 6336 | // just before jumping to the callee, after we are done setting up |
| 6337 | // the stack. |
| 6338 | // |
| 6339 | // The caller is responsible for switching back to the caller's |
| 6340 | // realm and cleaning up the stack. |
| 6341 | |
| 6342 | Register calleeReg = IonGenericCallCalleeReg; |
| 6343 | Register argcReg = IonGenericCallArgcReg; |
| 6344 | Register scratch = IonGenericCallScratch; |
| 6345 | Register scratch2 = IonGenericCallScratch2; |
| 6346 | |
| 6347 | #ifndef JS_USE_LINK_REGISTER |
| 6348 | Register returnAddrReg = IonGenericCallReturnAddrReg; |
| 6349 | masm.pop(returnAddrReg); |
| 6350 | #endif |
| 6351 | |
| 6352 | #ifdef JS_CODEGEN_ARM |
| 6353 | // The default second scratch register on arm is lr, which we need |
| 6354 | // preserved for tail calls. |
| 6355 | AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg); |
| 6356 | #endif |
| 6357 | |
| 6358 | bool isConstructing = kind == IonGenericCallKind::Construct; |
| 6359 | |
| 6360 | Label entry, notFunction, noJitEntry, vmCall; |
| 6361 | masm.bind(&entry); |
| 6362 | |
| 6363 | // Guard that the callee is actually a function. |
| 6364 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch, |
| 6365 | calleeReg, ¬Function); |
| 6366 | |
| 6367 | // Guard that the callee supports the [[Call]] or [[Construct]] operation. |
| 6368 | // If these tests fail, we will call into the VM to throw an exception. |
| 6369 | if (isConstructing) { |
| 6370 | masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR, |
| 6371 | Assembler::Zero, &vmCall); |
| 6372 | } else { |
| 6373 | masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor, |
| 6374 | calleeReg, scratch, &vmCall); |
| 6375 | } |
| 6376 | |
| 6377 | if (isConstructing) { |
| 6378 | // Use the slow path if CreateThis was unable to create the |this| object. |
| 6379 | Address thisAddr(masm.getStackPointer(), 0); |
| 6380 | masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall); |
| 6381 | } |
| 6382 | |
| 6383 | masm.switchToObjectRealm(calleeReg, scratch); |
| 6384 | |
| 6385 | // Load jitCodeRaw for callee if it exists. |
| 6386 | masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry); |
| 6387 | |
| 6388 | // **************************** |
| 6389 | // * Functions with jit entry * |
| 6390 | // **************************** |
| 6391 | masm.loadJitCodeRaw(calleeReg, scratch2); |
| 6392 | |
| 6393 | // Construct the JitFrameLayout. |
| 6394 | masm.PushCalleeToken(calleeReg, isConstructing); |
| 6395 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch); |
| 6396 | #ifndef JS_USE_LINK_REGISTER |
| 6397 | masm.push(returnAddrReg); |
| 6398 | #endif |
| 6399 | |
| 6400 | // Check whether we need a rectifier frame. |
| 6401 | Label noRectifier; |
| 6402 | masm.loadFunctionArgCount(calleeReg, scratch); |
| 6403 | masm.branch32(Assembler::BelowOrEqual, scratch, argcReg, &noRectifier); |
| 6404 | { |
| 6405 | // Tail-call the arguments rectifier. |
| 6406 | // Because all trampolines are created at the same time, |
| 6407 | // we can't create a TrampolinePtr for the arguments rectifier, |
| 6408 | // because it hasn't been linked yet. We can, however, directly |
| 6409 | // encode its offset. |
| 6410 | Label rectifier; |
| 6411 | bindLabelToOffset(&rectifier, argumentsRectifierOffset_); |
| 6412 | |
| 6413 | masm.jump(&rectifier); |
| 6414 | } |
| 6415 | |
| 6416 | // Tail call the jit entry. |
| 6417 | masm.bind(&noRectifier); |
| 6418 | masm.jump(scratch2); |
| 6419 | |
| 6420 | // ******************** |
| 6421 | // * Native functions * |
| 6422 | // ******************** |
| 6423 | masm.bind(&noJitEntry); |
| 6424 | if (!isConstructing) { |
| 6425 | generateIonGenericCallFunCall(masm, &entry, &vmCall); |
| 6426 | } |
| 6427 | generateIonGenericCallNativeFunction(masm, isConstructing); |
| 6428 | |
| 6429 | // ******************* |
| 6430 | // * Bound functions * |
| 6431 | // ******************* |
| 6432 | // TODO: support class hooks? |
| 6433 | masm.bind(¬Function); |
| 6434 | if (!isConstructing) { |
| 6435 | // TODO: support generic bound constructors? |
| 6436 | generateIonGenericCallBoundFunction(masm, &entry, &vmCall); |
| 6437 | } |
| 6438 | |
| 6439 | // ******************** |
| 6440 | // * Fallback VM call * |
| 6441 | // ******************** |
| 6442 | masm.bind(&vmCall); |
| 6443 | |
| 6444 | masm.push(masm.getStackPointer()); // argv |
| 6445 | masm.push(argcReg); // argc |
| 6446 | masm.push(Imm32(false)); // ignores return value |
| 6447 | masm.push(Imm32(isConstructing)); // constructing |
| 6448 | masm.push(calleeReg); // callee |
| 6449 | |
| 6450 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
| 6451 | MutableHandleValue); |
| 6452 | VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id; |
| 6453 | uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)]; |
| 6454 | Label invokeFunctionVMEntry; |
| 6455 | bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset); |
| 6456 | |
| 6457 | masm.pushFrameDescriptor(FrameType::IonJS); |
| 6458 | #ifndef JS_USE_LINK_REGISTER |
| 6459 | masm.push(returnAddrReg); |
| 6460 | #endif |
| 6461 | masm.jump(&invokeFunctionVMEntry); |
| 6462 | } |
| 6463 | |
| 6464 | void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm, |
| 6465 | bool isConstructing) { |
| 6466 | Register calleeReg = IonGenericCallCalleeReg; |
| 6467 | Register argcReg = IonGenericCallArgcReg; |
| 6468 | Register scratch = IonGenericCallScratch; |
| 6469 | Register scratch2 = IonGenericCallScratch2; |
| 6470 | Register contextReg = IonGenericCallScratch3; |
| 6471 | #ifndef JS_USE_LINK_REGISTER |
| 6472 | Register returnAddrReg = IonGenericCallReturnAddrReg; |
| 6473 | #endif |
| 6474 | |
| 6475 | // Push a value containing the callee, which will become argv[0]. |
| 6476 | masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg); |
| 6477 | |
| 6478 | // Load the callee address into calleeReg. |
| 6479 | #ifdef JS_SIMULATOR |
| 6480 | masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg); |
| 6481 | #else |
| 6482 | masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()), |
| 6483 | calleeReg); |
| 6484 | #endif |
| 6485 | |
| 6486 | // Load argv into scratch2. |
| 6487 | masm.moveStackPtrTo(scratch2); |
| 6488 | |
| 6489 | // Push argc. |
| 6490 | masm.push(argcReg); |
| 6491 | |
| 6492 | masm.loadJSContext(contextReg); |
| 6493 | |
| 6494 | // Construct native exit frame. Note that unlike other cases in this |
| 6495 | // trampoline, this code does not use a tail call. |
| 6496 | masm.pushFrameDescriptor(FrameType::IonJS); |
| 6497 | #ifdef JS_USE_LINK_REGISTER |
| 6498 | masm.pushReturnAddress(); |
| 6499 | #else |
| 6500 | masm.push(returnAddrReg); |
| 6501 | #endif |
| 6502 | |
| 6503 | masm.push(FramePointer); |
| 6504 | masm.moveStackPtrTo(FramePointer); |
| 6505 | masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing); |
| 6506 | |
| 6507 | masm.setupUnalignedABICall(scratch); |
| 6508 | masm.passABIArg(contextReg); // cx |
| 6509 | masm.passABIArg(argcReg); // argc |
| 6510 | masm.passABIArg(scratch2); // argv |
| 6511 | |
| 6512 | masm.callWithABI(calleeReg); |
| 6513 | |
| 6514 | // Test for failure. |
| 6515 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
| 6516 | |
| 6517 | masm.loadValue( |
| 6518 | Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), |
| 6519 | JSReturnOperand); |
| 6520 | |
| 6521 | // Leave the exit frame. |
| 6522 | masm.moveToStackPtr(FramePointer); |
| 6523 | masm.pop(FramePointer); |
| 6524 | |
| 6525 | // Return. |
| 6526 | masm.ret(); |
| 6527 | } |
| 6528 | |
| 6529 | void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm, |
| 6530 | Label* entry, Label* vmCall) { |
| 6531 | Register calleeReg = IonGenericCallCalleeReg; |
| 6532 | Register argcReg = IonGenericCallArgcReg; |
| 6533 | Register scratch = IonGenericCallScratch; |
| 6534 | Register scratch2 = IonGenericCallScratch2; |
| 6535 | Register scratch3 = IonGenericCallScratch3; |
| 6536 | |
| 6537 | Label notFunCall; |
| 6538 | masm.branchPtr(Assembler::NotEqual, |
| 6539 | Address(calleeReg, JSFunction::offsetOfNativeOrEnv()), |
| 6540 | ImmPtr(js::fun_call), ¬FunCall); |
| 6541 | |
| 6542 | // In general, we can implement fun_call by replacing calleeReg with |
| 6543 | // |this|, sliding all the other arguments down, and decrementing argc. |
| 6544 | // |
| 6545 | // *BEFORE* *AFTER* |
| 6546 | // [argN] argc = N+1 <padding> |
| 6547 | // ... [argN] argc = N |
| 6548 | // [arg1] ... |
| 6549 | // [arg0] [arg1] <- now arg0 |
| 6550 | // [this] <- top of stack (aligned) [arg0] <- now this |
| 6551 | // |
| 6552 | // The only exception is when argc is already 0, in which case instead |
| 6553 | // of shifting arguments down we replace [this] with UndefinedValue(): |
| 6554 | // |
| 6555 | // *BEFORE* *AFTER* |
| 6556 | // [this] argc = 0 [undef] argc = 0 |
| 6557 | // |
| 6558 | // After making this transformation, we can jump back to the beginning |
| 6559 | // of this trampoline to handle the inner call. |
| 6560 | |
| 6561 | // Guard that |this| is an object. If it is, replace calleeReg. |
| 6562 | masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall); |
| 6563 | masm.movePtr(scratch, calleeReg); |
| 6564 | |
| 6565 | Label hasArgs; |
| 6566 | masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs); |
| 6567 | |
| 6568 | // No arguments. Replace |this| with |undefined| and start from the top. |
| 6569 | masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0)); |
| 6570 | masm.jump(entry); |
| 6571 | |
| 6572 | masm.bind(&hasArgs); |
| 6573 | |
| 6574 | Label doneSliding; |
| 6575 | generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2, |
| 6576 | scratch3, &doneSliding); |
| 6577 | masm.bind(&doneSliding); |
| 6578 | masm.sub32(Imm32(1), argcReg); |
| 6579 | |
| 6580 | masm.jump(entry); |
| 6581 | |
| 6582 | masm.bind(¬FunCall); |
| 6583 | } |
| 6584 | |
| 6585 | void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm, |
| 6586 | Label* entry, |
| 6587 | Label* vmCall) { |
| 6588 | Register calleeReg = IonGenericCallCalleeReg; |
| 6589 | Register argcReg = IonGenericCallArgcReg; |
| 6590 | Register scratch = IonGenericCallScratch; |
| 6591 | Register scratch2 = IonGenericCallScratch2; |
| 6592 | Register scratch3 = IonGenericCallScratch3; |
| 6593 | |
| 6594 | masm.branchTestObjClass(Assembler::NotEqual, calleeReg, |
| 6595 | &BoundFunctionObject::class_, scratch, calleeReg, |
| 6596 | vmCall); |
| 6597 | |
| 6598 | Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot()); |
| 6599 | Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot()); |
| 6600 | Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot()); |
| 6601 | Address firstInlineArgSlot( |
| 6602 | calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg()); |
| 6603 | |
| 6604 | // Check that we won't be pushing too many arguments. |
| 6605 | masm.load32(flagsSlot, scratch); |
| 6606 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch); |
| 6607 | masm.add32(argcReg, scratch); |
| 6608 | masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall); |
| 6609 | |
| 6610 | // The stack is currently correctly aligned for a jit call. We will |
| 6611 | // be updating the `this` value and potentially adding additional |
| 6612 | // arguments. On platforms with 16-byte alignment, if the number of |
| 6613 | // bound arguments is odd, we have to move the arguments that are |
| 6614 | // currently on the stack. For example, with one bound argument: |
| 6615 | // |
| 6616 | // *BEFORE* *AFTER* |
| 6617 | // [argN] <padding> |
| 6618 | // ... [argN] | |
| 6619 | // [arg1] ... | These arguments have been |
| 6620 | // [arg0] [arg1] | shifted down 8 bytes. |
| 6621 | // [this] <- top of stack (aligned) [arg0] v |
| 6622 | // [bound0] <- one bound argument (odd) |
| 6623 | // [boundThis] <- top of stack (aligned) |
| 6624 | // |
| 6625 | Label poppedThis; |
| 6626 | if (JitStackValueAlignment > 1) { |
| 6627 | Label alreadyAligned; |
| 6628 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 6629 | Imm32(1 << BoundFunctionObject::NumBoundArgsShift), |
| 6630 | &alreadyAligned); |
| 6631 | |
| 6632 | // We have an odd number of bound arguments. Shift the existing arguments |
| 6633 | // down by 8 bytes. |
| 6634 | generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2, |
| 6635 | scratch3, &poppedThis); |
| 6636 | masm.bind(&alreadyAligned); |
| 6637 | } |
| 6638 | |
| 6639 | // Pop the current `this`. It will be replaced with the bound `this`. |
| 6640 | masm.freeStack(sizeof(Value)); |
| 6641 | masm.bind(&poppedThis); |
| 6642 | |
| 6643 | // Load the number of bound arguments in scratch |
| 6644 | masm.load32(flagsSlot, scratch); |
| 6645 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch); |
| 6646 | |
| 6647 | Label donePushingBoundArguments; |
| 6648 | masm.branch32(Assembler::Equal, scratch, Imm32(0), |
| 6649 | &donePushingBoundArguments); |
| 6650 | |
| 6651 | // Update argc to include bound arguments. |
| 6652 | masm.add32(scratch, argcReg); |
| 6653 | |
| 6654 | // Load &boundArgs[0] in scratch2. |
| 6655 | Label outOfLineBoundArguments, haveBoundArguments; |
| 6656 | masm.branch32(Assembler::Above, scratch, |
| 6657 | Imm32(BoundFunctionObject::MaxInlineBoundArgs), |
| 6658 | &outOfLineBoundArguments); |
| 6659 | masm.computeEffectiveAddress(firstInlineArgSlot, scratch2); |
| 6660 | masm.jump(&haveBoundArguments); |
| 6661 | |
| 6662 | masm.bind(&outOfLineBoundArguments); |
| 6663 | masm.unboxObject(firstInlineArgSlot, scratch2); |
| 6664 | masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2); |
| 6665 | |
| 6666 | masm.bind(&haveBoundArguments); |
| 6667 | |
| 6668 | // Load &boundArgs[numBoundArgs] in scratch. |
| 6669 | BaseObjectElementIndex lastBoundArg(scratch2, scratch); |
| 6670 | masm.computeEffectiveAddress(lastBoundArg, scratch); |
| 6671 | |
| 6672 | // Push the bound arguments, starting with the last one. |
| 6673 | // Copying pre-decrements scratch until scratch2 is reached. |
| 6674 | Label boundArgumentsLoop; |
| 6675 | masm.bind(&boundArgumentsLoop); |
| 6676 | masm.subPtr(Imm32(sizeof(Value)), scratch); |
| 6677 | masm.pushValue(Address(scratch, 0)); |
| 6678 | masm.branchPtr(Assembler::Above, scratch, scratch2, &boundArgumentsLoop); |
| 6679 | masm.bind(&donePushingBoundArguments); |
| 6680 | |
| 6681 | // Push the bound `this`. |
| 6682 | masm.pushValue(thisSlot); |
| 6683 | |
| 6684 | // Load the target in calleeReg. |
| 6685 | masm.unboxObject(targetSlot, calleeReg); |
| 6686 | |
| 6687 | // At this point, all preconditions for entering the trampoline are met: |
| 6688 | // - calleeReg contains a pointer to the callee object |
| 6689 | // - argcReg contains the number of actual args (now including bound args) |
| 6690 | // - the arguments are on the stack with the correct alignment. |
| 6691 | // Instead of generating more code, we can jump back to the entry point |
| 6692 | // of the trampoline to call the bound target. |
| 6693 | masm.jump(entry); |
| 6694 | } |
| 6695 | |
| 6696 | void CodeGenerator::visitCallKnown(LCallKnown* call) { |
| 6697 | Register calleereg = ToRegister(call->getFunction()); |
| 6698 | Register objreg = ToRegister(call->getTempObject()); |
| 6699 | uint32_t unusedStack = |
| 6700 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
| 6701 | WrappedFunction* target = call->getSingleTarget(); |
| 6702 | |
| 6703 | // Native single targets (except Wasm and TrampolineNative functions) are |
| 6704 | // handled by LCallNative. |
| 6705 | MOZ_ASSERT(target->hasJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->hasJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->hasJitEntry()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->hasJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6705); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->hasJitEntry()" ")"); do { *((volatile int*)__null) = 6705; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6706 | |
| 6707 | // Missing arguments must have been explicitly appended by WarpBuilder. |
| 6708 | DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing(); |
| 6709 | MOZ_ASSERT(target->nargs() <=do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->nargs() <= call->mir()->numStackArgs () - numNonArgsOnStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->nargs() <= call ->mir()->numStackArgs() - numNonArgsOnStack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" ")"); do { *((volatile int*)__null) = 6710; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 6710 | call->mir()->numStackArgs() - numNonArgsOnStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->nargs() <= call->mir()->numStackArgs () - numNonArgsOnStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->nargs() <= call ->mir()->numStackArgs() - numNonArgsOnStack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6710); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" ")"); do { *((volatile int*)__null) = 6710; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6711 | |
| 6712 | MOZ_ASSERT_IF(call->isConstructing(), target->isConstructor())do { if (call->isConstructing()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(target->isConstructor ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(target->isConstructor()))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("target->isConstructor()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6712); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isConstructor()" ")"); do { *((volatile int*)__null) = 6712; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 6713 | |
| 6714 | masm.checkStackAlignment(); |
| 6715 | |
| 6716 | if (target->isClassConstructor() && !call->isConstructing()) { |
| 6717 | emitCallInvokeFunction(call, calleereg, call->isConstructing(), |
| 6718 | call->ignoresReturnValue(), call->numActualArgs(), |
| 6719 | unusedStack); |
| 6720 | return; |
| 6721 | } |
| 6722 | |
| 6723 | MOZ_ASSERT_IF(target->isClassConstructor(), call->isConstructing())do { if (target->isClassConstructor()) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(call-> isConstructing())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(call->isConstructing()))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("call->isConstructing()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->isConstructing()" ")"); do { *((volatile int*)__null) = 6723; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 6724 | |
| 6725 | MOZ_ASSERT(!call->mir()->needsThisCheck())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!call->mir()->needsThisCheck())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!call->mir()->needsThisCheck ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!call->mir()->needsThisCheck()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6725); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->mir()->needsThisCheck()" ")"); do { *((volatile int*)__null) = 6725; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6726 | |
| 6727 | if (call->mir()->maybeCrossRealm()) { |
| 6728 | masm.switchToObjectRealm(calleereg, objreg); |
| 6729 | } |
| 6730 | |
| 6731 | masm.loadJitCodeRaw(calleereg, objreg); |
| 6732 | |
| 6733 | // Nestle the StackPointer up to the argument vector. |
| 6734 | masm.freeStack(unusedStack); |
| 6735 | |
| 6736 | // Construct the JitFrameLayout. |
| 6737 | masm.PushCalleeToken(calleereg, call->mir()->isConstructing()); |
| 6738 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, call->numActualArgs()); |
| 6739 | |
| 6740 | // Finally call the function in objreg. |
| 6741 | ensureOsiSpace(); |
| 6742 | uint32_t callOffset = masm.callJit(objreg); |
| 6743 | markSafepointAt(callOffset, call); |
| 6744 | |
| 6745 | if (call->mir()->maybeCrossRealm()) { |
| 6746 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 6747 | "ReturnReg available as scratch after scripted calls"); |
| 6748 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 6749 | } |
| 6750 | |
| 6751 | // Restore stack pointer: pop JitFrameLayout fields still left on the stack |
| 6752 | // and undo the earlier |freeStack(unusedStack)|. |
| 6753 | int prefixGarbage = |
| 6754 | sizeof(JitFrameLayout) - JitFrameLayout::bytesPoppedAfterCall(); |
| 6755 | masm.adjustStack(prefixGarbage - unusedStack); |
| 6756 | |
| 6757 | // If the return value of the constructing function is Primitive, |
| 6758 | // replace the return value with the Object from CreateThis. |
| 6759 | if (call->mir()->isConstructing()) { |
| 6760 | Label notPrimitive; |
| 6761 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 6762 | ¬Primitive); |
| 6763 | masm.loadValue(Address(masm.getStackPointer(), unusedStack), |
| 6764 | JSReturnOperand); |
| 6765 | #ifdef DEBUG1 |
| 6766 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 6767 | ¬Primitive); |
| 6768 | masm.assumeUnreachable("CreateThis creates an object"); |
| 6769 | #endif |
| 6770 | masm.bind(¬Primitive); |
| 6771 | } |
| 6772 | } |
| 6773 | |
| 6774 | template <typename T> |
| 6775 | void CodeGenerator::emitCallInvokeFunction(T* apply) { |
| 6776 | pushArg(masm.getStackPointer()); // argv. |
| 6777 | pushArg(ToRegister(apply->getArgc())); // argc. |
| 6778 | pushArg(Imm32(apply->mir()->ignoresReturnValue())); // ignoresReturnValue. |
| 6779 | pushArg(Imm32(apply->mir()->isConstructing())); // isConstructing. |
| 6780 | pushArg(ToRegister(apply->getFunction())); // JSFunction*. |
| 6781 | |
| 6782 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
| 6783 | MutableHandleValue); |
| 6784 | callVM<Fn, jit::InvokeFunction>(apply); |
| 6785 | } |
| 6786 | |
| 6787 | // Do not bailout after the execution of this function since the stack no longer |
| 6788 | // correspond to what is expected by the snapshots. |
| 6789 | void CodeGenerator::emitAllocateSpaceForApply(Register argcreg, |
| 6790 | Register scratch) { |
| 6791 | // Use scratch register to calculate stack space (including padding). |
| 6792 | masm.movePtr(argcreg, scratch); |
| 6793 | |
| 6794 | // Align the JitFrameLayout on the JitStackAlignment. |
| 6795 | if (JitStackValueAlignment > 1) { |
| 6796 | MOZ_ASSERT(frameSize() % JitStackAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6797; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 6797 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6797; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6798 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6798); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6798; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6799 | Label noPaddingNeeded; |
| 6800 | // If the number of arguments is odd, then we do not need any padding. |
| 6801 | // |
| 6802 | // Note: The |JitStackValueAlignment == 2| condition requires that the |
| 6803 | // overall number of values on the stack is even. When we have an odd number |
| 6804 | // of arguments, we don't need any padding, because the |thisValue| is |
| 6805 | // pushed after the arguments, so the overall number of values on the stack |
| 6806 | // is even. |
| 6807 | masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded); |
| 6808 | masm.addPtr(Imm32(1), scratch); |
| 6809 | masm.bind(&noPaddingNeeded); |
| 6810 | } |
| 6811 | |
| 6812 | // Reserve space for copying the arguments. |
| 6813 | NativeObject::elementsSizeMustNotOverflow(); |
| 6814 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
| 6815 | masm.subFromStackPtr(scratch); |
| 6816 | |
| 6817 | #ifdef DEBUG1 |
| 6818 | // Put a magic value in the space reserved for padding. Note, this code cannot |
| 6819 | // be merged with the previous test, as not all architectures can write below |
| 6820 | // their stack pointers. |
| 6821 | if (JitStackValueAlignment > 1) { |
| 6822 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6822; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6823 | Label noPaddingNeeded; |
| 6824 | // If the number of arguments is odd, then we do not need any padding. |
| 6825 | masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded); |
| 6826 | BaseValueIndex dstPtr(masm.getStackPointer(), argcreg); |
| 6827 | masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr); |
| 6828 | masm.bind(&noPaddingNeeded); |
| 6829 | } |
| 6830 | #endif |
| 6831 | } |
| 6832 | |
| 6833 | // Do not bailout after the execution of this function since the stack no longer |
| 6834 | // correspond to what is expected by the snapshots. |
| 6835 | void CodeGenerator::emitAllocateSpaceForConstructAndPushNewTarget( |
| 6836 | Register argcreg, Register newTargetAndScratch) { |
| 6837 | // Align the JitFrameLayout on the JitStackAlignment. Contrary to |
| 6838 | // |emitAllocateSpaceForApply()|, we're always pushing a magic value, because |
| 6839 | // we can't write to |newTargetAndScratch| before |new.target| has been pushed |
| 6840 | // onto the stack. |
| 6841 | if (JitStackValueAlignment > 1) { |
| 6842 | MOZ_ASSERT(frameSize() % JitStackAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6843); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6843; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 6843 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackAlignment == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameSize() % JitStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("frameSize() % JitStackAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6843); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 6843; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6844 | MOZ_ASSERT(JitStackValueAlignment == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6844); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { *((volatile int*)__null) = 6844; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6845 | |
| 6846 | Label noPaddingNeeded; |
| 6847 | // If the number of arguments is even, then we do not need any padding. |
| 6848 | // |
| 6849 | // Note: The |JitStackValueAlignment == 2| condition requires that the |
| 6850 | // overall number of values on the stack is even. When we have an even |
| 6851 | // number of arguments, we don't need any padding, because |new.target| is |
| 6852 | // is pushed before the arguments and |thisValue| is pushed after all |
| 6853 | // arguments, so the overall number of values on the stack is even. |
| 6854 | masm.branchTestPtr(Assembler::Zero, argcreg, Imm32(1), &noPaddingNeeded); |
| 6855 | masm.pushValue(MagicValue(JS_ARG_POISON)); |
| 6856 | masm.bind(&noPaddingNeeded); |
| 6857 | } |
| 6858 | |
| 6859 | // Push |new.target| after the padding value, but before any arguments. |
| 6860 | masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch); |
| 6861 | |
| 6862 | // Use newTargetAndScratch to calculate stack space (including padding). |
| 6863 | masm.movePtr(argcreg, newTargetAndScratch); |
| 6864 | |
| 6865 | // Reserve space for copying the arguments. |
| 6866 | NativeObject::elementsSizeMustNotOverflow(); |
| 6867 | masm.lshiftPtr(Imm32(ValueShift), newTargetAndScratch); |
| 6868 | masm.subFromStackPtr(newTargetAndScratch); |
| 6869 | } |
| 6870 | |
| 6871 | // Destroys argvIndex and copyreg. |
| 6872 | void CodeGenerator::emitCopyValuesForApply(Register argvSrcBase, |
| 6873 | Register argvIndex, Register copyreg, |
| 6874 | size_t argvSrcOffset, |
| 6875 | size_t argvDstOffset) { |
| 6876 | Label loop; |
| 6877 | masm.bind(&loop); |
| 6878 | |
| 6879 | // As argvIndex is off by 1, and we use the decBranchPtr instruction to loop |
| 6880 | // back, we have to substract the size of the word which are copied. |
| 6881 | BaseValueIndex srcPtr(argvSrcBase, argvIndex, |
| 6882 | int32_t(argvSrcOffset) - sizeof(void*)); |
| 6883 | BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex, |
| 6884 | int32_t(argvDstOffset) - sizeof(void*)); |
| 6885 | masm.loadPtr(srcPtr, copyreg); |
| 6886 | masm.storePtr(copyreg, dstPtr); |
| 6887 | |
| 6888 | // Handle 32 bits architectures. |
| 6889 | if (sizeof(Value) == 2 * sizeof(void*)) { |
| 6890 | BaseValueIndex srcPtrLow(argvSrcBase, argvIndex, |
| 6891 | int32_t(argvSrcOffset) - 2 * sizeof(void*)); |
| 6892 | BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex, |
| 6893 | int32_t(argvDstOffset) - 2 * sizeof(void*)); |
| 6894 | masm.loadPtr(srcPtrLow, copyreg); |
| 6895 | masm.storePtr(copyreg, dstPtrLow); |
| 6896 | } |
| 6897 | |
| 6898 | masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop); |
| 6899 | } |
| 6900 | |
| 6901 | void CodeGenerator::emitRestoreStackPointerFromFP() { |
| 6902 | // This is used to restore the stack pointer after a call with a dynamic |
| 6903 | // number of arguments. |
| 6904 | |
| 6905 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6905); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 6905; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6906 | |
| 6907 | int32_t offset = -int32_t(frameSize()); |
| 6908 | masm.computeEffectiveAddress(Address(FramePointer, offset), |
| 6909 | masm.getStackPointer()); |
| 6910 | #if JS_CODEGEN_ARM64 |
| 6911 | masm.syncStackPtr(); |
| 6912 | #endif |
| 6913 | } |
| 6914 | |
| 6915 | void CodeGenerator::emitPushArguments(Register argcreg, Register scratch, |
| 6916 | Register copyreg, uint32_t extraFormals) { |
| 6917 | Label end; |
| 6918 | |
| 6919 | // Skip the copy of arguments if there are none. |
| 6920 | masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end); |
| 6921 | |
| 6922 | // clang-format off |
| 6923 | // |
| 6924 | // We are making a copy of the arguments which are above the JitFrameLayout |
| 6925 | // of the current Ion frame. |
| 6926 | // |
| 6927 | // [arg1] [arg0] <- src [this] [JitFrameLayout] [.. frameSize ..] [pad] [arg1] [arg0] <- dst |
| 6928 | // |
| 6929 | // clang-format on |
| 6930 | |
| 6931 | // Compute the source and destination offsets into the stack. |
| 6932 | // |
| 6933 | // The |extraFormals| parameter is used when copying rest-parameters and |
| 6934 | // allows to skip the initial parameters before the actual rest-parameters. |
| 6935 | Register argvSrcBase = FramePointer; |
| 6936 | size_t argvSrcOffset = |
| 6937 | JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value); |
| 6938 | size_t argvDstOffset = 0; |
| 6939 | |
| 6940 | Register argvIndex = scratch; |
| 6941 | masm.move32(argcreg, argvIndex); |
| 6942 | |
| 6943 | // Copy arguments. |
| 6944 | emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset, |
| 6945 | argvDstOffset); |
| 6946 | |
| 6947 | // Join with all arguments copied. |
| 6948 | masm.bind(&end); |
| 6949 | } |
| 6950 | |
| 6951 | void CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply) { |
| 6952 | // Holds the function nargs. |
| 6953 | Register argcreg = ToRegister(apply->getArgc()); |
| 6954 | Register copyreg = ToRegister(apply->getTempObject()); |
| 6955 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 6956 | uint32_t extraFormals = apply->numExtraFormals(); |
| 6957 | |
| 6958 | // Allocate space on the stack for arguments. |
| 6959 | emitAllocateSpaceForApply(argcreg, scratch); |
| 6960 | |
| 6961 | emitPushArguments(argcreg, scratch, copyreg, extraFormals); |
| 6962 | |
| 6963 | // Push |this|. |
| 6964 | masm.pushValue(ToValue(apply, LApplyArgsGeneric::ThisIndex)); |
| 6965 | } |
| 6966 | |
| 6967 | void CodeGenerator::emitPushArguments(LApplyArgsObj* apply) { |
| 6968 | Register argsObj = ToRegister(apply->getArgsObj()); |
| 6969 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 6970 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 6971 | |
| 6972 | // argc and argsObj are mapped to the same calltemp register. |
| 6973 | MOZ_ASSERT(argsObj == ToRegister(apply->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(argsObj == ToRegister(apply->getArgc()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(argsObj == ToRegister(apply->getArgc())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argsObj == ToRegister(apply->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 6973); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argsObj == ToRegister(apply->getArgc())" ")"); do { *((volatile int*)__null) = 6973; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6974 | |
| 6975 | // Load argc into tmpArgc. |
| 6976 | masm.loadArgumentsObjectLength(argsObj, tmpArgc); |
| 6977 | |
| 6978 | // Allocate space on the stack for arguments. |
| 6979 | emitAllocateSpaceForApply(tmpArgc, scratch); |
| 6980 | |
| 6981 | // Load arguments data. |
| 6982 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
| 6983 | argsObj); |
| 6984 | size_t argsSrcOffset = ArgumentsData::offsetOfArgs(); |
| 6985 | |
| 6986 | // This is the end of the lifetime of argsObj. |
| 6987 | // After this call, the argsObj register holds the argument count instead. |
| 6988 | emitPushArrayAsArguments(tmpArgc, argsObj, scratch, argsSrcOffset); |
| 6989 | |
| 6990 | // Push |this|. |
| 6991 | masm.pushValue(ToValue(apply, LApplyArgsObj::ThisIndex)); |
| 6992 | } |
| 6993 | |
| 6994 | void CodeGenerator::emitPushArrayAsArguments(Register tmpArgc, |
| 6995 | Register srcBaseAndArgc, |
| 6996 | Register scratch, |
| 6997 | size_t argvSrcOffset) { |
| 6998 | // Preconditions: |
| 6999 | // 1. |tmpArgc| * sizeof(Value) bytes have been allocated at the top of |
| 7000 | // the stack to hold arguments. |
| 7001 | // 2. |srcBaseAndArgc| + |srcOffset| points to an array of |tmpArgc| values. |
| 7002 | // |
| 7003 | // Postconditions: |
| 7004 | // 1. The arguments at |srcBaseAndArgc| + |srcOffset| have been copied into |
| 7005 | // the allocated space. |
| 7006 | // 2. |srcBaseAndArgc| now contains the original value of |tmpArgc|. |
| 7007 | // |
| 7008 | // |scratch| is used as a temp register within this function and clobbered. |
| 7009 | |
| 7010 | Label noCopy, epilogue; |
| 7011 | |
| 7012 | // Skip the copy of arguments if there are none. |
| 7013 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
| 7014 | { |
| 7015 | // Copy the values. This code is skipped entirely if there are no values. |
| 7016 | size_t argvDstOffset = 0; |
| 7017 | |
| 7018 | Register argvSrcBase = srcBaseAndArgc; |
| 7019 | |
| 7020 | // Stash away |tmpArgc| and adjust argvDstOffset accordingly. |
| 7021 | masm.push(tmpArgc); |
| 7022 | Register argvIndex = tmpArgc; |
| 7023 | argvDstOffset += sizeof(void*); |
| 7024 | |
| 7025 | // Copy |
| 7026 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
| 7027 | argvDstOffset); |
| 7028 | |
| 7029 | // Restore. |
| 7030 | masm.pop(srcBaseAndArgc); // srcBaseAndArgc now contains argc. |
| 7031 | masm.jump(&epilogue); |
| 7032 | } |
| 7033 | masm.bind(&noCopy); |
| 7034 | { |
| 7035 | // Clear argc if we skipped the copy step. |
| 7036 | masm.movePtr(ImmWord(0), srcBaseAndArgc); |
| 7037 | } |
| 7038 | |
| 7039 | // Join with all arguments copied. |
| 7040 | // Note, "srcBase" has become "argc". |
| 7041 | masm.bind(&epilogue); |
| 7042 | } |
| 7043 | |
| 7044 | void CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply) { |
| 7045 | Register elements = ToRegister(apply->getElements()); |
| 7046 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 7047 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7048 | |
| 7049 | // argc and elements are mapped to the same calltemp register. |
| 7050 | MOZ_ASSERT(elements == ToRegister(apply->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(elements == ToRegister(apply->getArgc()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(elements == ToRegister(apply->getArgc())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("elements == ToRegister(apply->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7050); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(apply->getArgc())" ")"); do { *((volatile int*)__null) = 7050; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7051 | |
| 7052 | // Invariants guarded in the caller: |
| 7053 | // - the array is not too long |
| 7054 | // - the array length equals its initialized length |
| 7055 | |
| 7056 | // The array length is our argc for the purposes of allocating space. |
| 7057 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
| 7058 | |
| 7059 | // Allocate space for the values. |
| 7060 | emitAllocateSpaceForApply(tmpArgc, scratch); |
| 7061 | |
| 7062 | // After this call "elements" has become "argc". |
| 7063 | size_t elementsOffset = 0; |
| 7064 | emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset); |
| 7065 | |
| 7066 | // Push |this|. |
| 7067 | masm.pushValue(ToValue(apply, LApplyArrayGeneric::ThisIndex)); |
| 7068 | } |
| 7069 | |
| 7070 | void CodeGenerator::emitPushArguments(LConstructArgsGeneric* construct) { |
| 7071 | // Holds the function nargs. |
| 7072 | Register argcreg = ToRegister(construct->getArgc()); |
| 7073 | Register copyreg = ToRegister(construct->getTempObject()); |
| 7074 | Register scratch = ToRegister(construct->getTempForArgCopy()); |
| 7075 | uint32_t extraFormals = construct->numExtraFormals(); |
| 7076 | |
| 7077 | // newTarget and scratch are mapped to the same calltemp register. |
| 7078 | MOZ_ASSERT(scratch == ToRegister(construct->getNewTarget()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch == ToRegister(construct->getNewTarget())) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(scratch == ToRegister(construct->getNewTarget())) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch == ToRegister(construct->getNewTarget())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())" ")"); do { *((volatile int*)__null) = 7078; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7079 | |
| 7080 | // Allocate space for the values. |
| 7081 | // After this call "newTarget" has become "scratch". |
| 7082 | emitAllocateSpaceForConstructAndPushNewTarget(argcreg, scratch); |
| 7083 | |
| 7084 | emitPushArguments(argcreg, scratch, copyreg, extraFormals); |
| 7085 | |
| 7086 | // Push |this|. |
| 7087 | masm.pushValue(ToValue(construct, LConstructArgsGeneric::ThisIndex)); |
| 7088 | } |
| 7089 | |
| 7090 | void CodeGenerator::emitPushArguments(LConstructArrayGeneric* construct) { |
| 7091 | Register elements = ToRegister(construct->getElements()); |
| 7092 | Register tmpArgc = ToRegister(construct->getTempObject()); |
| 7093 | Register scratch = ToRegister(construct->getTempForArgCopy()); |
| 7094 | |
| 7095 | // argc and elements are mapped to the same calltemp register. |
| 7096 | MOZ_ASSERT(elements == ToRegister(construct->getArgc()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(elements == ToRegister(construct->getArgc()))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(elements == ToRegister(construct->getArgc())))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("elements == ToRegister(construct->getArgc())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7096); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elements == ToRegister(construct->getArgc())" ")"); do { *((volatile int*)__null) = 7096; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7097 | |
| 7098 | // newTarget and scratch are mapped to the same calltemp register. |
| 7099 | MOZ_ASSERT(scratch == ToRegister(construct->getNewTarget()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch == ToRegister(construct->getNewTarget())) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(scratch == ToRegister(construct->getNewTarget())) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch == ToRegister(construct->getNewTarget())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7099); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())" ")"); do { *((volatile int*)__null) = 7099; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7100 | |
| 7101 | // Invariants guarded in the caller: |
| 7102 | // - the array is not too long |
| 7103 | // - the array length equals its initialized length |
| 7104 | |
| 7105 | // The array length is our argc for the purposes of allocating space. |
| 7106 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
| 7107 | |
| 7108 | // Allocate space for the values. |
| 7109 | // After this call "newTarget" has become "scratch". |
| 7110 | emitAllocateSpaceForConstructAndPushNewTarget(tmpArgc, scratch); |
| 7111 | |
| 7112 | // After this call "elements" has become "argc". |
| 7113 | size_t elementsOffset = 0; |
| 7114 | emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset); |
| 7115 | |
| 7116 | // Push |this|. |
| 7117 | masm.pushValue(ToValue(construct, LConstructArrayGeneric::ThisIndex)); |
| 7118 | } |
| 7119 | |
| 7120 | template <typename T> |
| 7121 | void CodeGenerator::emitApplyGeneric(T* apply) { |
| 7122 | // Holds the function object. |
| 7123 | Register calleereg = ToRegister(apply->getFunction()); |
| 7124 | |
| 7125 | // Temporary register for modifying the function object. |
| 7126 | Register objreg = ToRegister(apply->getTempObject()); |
| 7127 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7128 | |
| 7129 | // Holds the function nargs, computed in the invoker or (for ApplyArray, |
| 7130 | // ConstructArray, or ApplyArgsObj) in the argument pusher. |
| 7131 | Register argcreg = ToRegister(apply->getArgc()); |
| 7132 | |
| 7133 | // Copy the arguments of the current function. |
| 7134 | // |
| 7135 | // In the case of ApplyArray, ConstructArray, or ApplyArgsObj, also compute |
| 7136 | // argc. The argc register and the elements/argsObj register are the same; |
| 7137 | // argc must not be referenced before the call to emitPushArguments() and |
| 7138 | // elements/argsObj must not be referenced after it returns. |
| 7139 | // |
| 7140 | // In the case of ConstructArray or ConstructArgs, also overwrite newTarget; |
| 7141 | // newTarget must not be referenced after this point. |
| 7142 | // |
| 7143 | // objreg is dead across this call. |
| 7144 | emitPushArguments(apply); |
| 7145 | |
| 7146 | masm.checkStackAlignment(); |
| 7147 | |
| 7148 | bool constructing = apply->mir()->isConstructing(); |
| 7149 | |
| 7150 | // If the function is native, the call is compiled through emitApplyNative. |
| 7151 | MOZ_ASSERT_IF(apply->hasSingleTarget(),do { if (apply->hasSingleTarget()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!apply->getSingleTarget ()->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!apply->getSingleTarget() ->isNativeWithoutJitEntry()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("!apply->getSingleTarget()->isNativeWithoutJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 7152; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
| 7152 | !apply->getSingleTarget()->isNativeWithoutJitEntry())do { if (apply->hasSingleTarget()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(!apply->getSingleTarget ()->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!apply->getSingleTarget() ->isNativeWithoutJitEntry()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("!apply->getSingleTarget()->isNativeWithoutJitEntry()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 7152; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 7153 | |
| 7154 | Label end, invoke; |
| 7155 | |
| 7156 | // Unless already known, guard that calleereg is actually a function object. |
| 7157 | if (!apply->hasSingleTarget()) { |
| 7158 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleereg, objreg, |
| 7159 | calleereg, &invoke); |
| 7160 | } |
| 7161 | |
| 7162 | // Guard that calleereg is an interpreted function with a JSScript. |
| 7163 | masm.branchIfFunctionHasNoJitEntry(calleereg, &invoke); |
| 7164 | |
| 7165 | // Guard that callee allows the [[Call]] or [[Construct]] operation required. |
| 7166 | if (constructing) { |
| 7167 | masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR, |
| 7168 | Assembler::Zero, &invoke); |
| 7169 | } else { |
| 7170 | masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor, |
| 7171 | calleereg, objreg, &invoke); |
| 7172 | } |
| 7173 | |
| 7174 | // Use the slow path if CreateThis was unable to create the |this| object. |
| 7175 | if (constructing) { |
| 7176 | Address thisAddr(masm.getStackPointer(), 0); |
| 7177 | masm.branchTestNull(Assembler::Equal, thisAddr, &invoke); |
| 7178 | } |
| 7179 | |
| 7180 | // Call with an Ion frame or a rectifier frame. |
| 7181 | { |
| 7182 | if (apply->mir()->maybeCrossRealm()) { |
| 7183 | masm.switchToObjectRealm(calleereg, objreg); |
| 7184 | } |
| 7185 | |
| 7186 | // Knowing that calleereg is a non-native function, load jitcode. |
| 7187 | masm.loadJitCodeRaw(calleereg, objreg); |
| 7188 | |
| 7189 | masm.PushCalleeToken(calleereg, constructing); |
| 7190 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcreg, scratch); |
| 7191 | |
| 7192 | Label underflow, rejoin; |
| 7193 | |
| 7194 | // Check whether the provided arguments satisfy target argc. |
| 7195 | if (!apply->hasSingleTarget()) { |
| 7196 | Register nformals = scratch; |
| 7197 | masm.loadFunctionArgCount(calleereg, nformals); |
| 7198 | masm.branch32(Assembler::Below, argcreg, nformals, &underflow); |
| 7199 | } else { |
| 7200 | masm.branch32(Assembler::Below, argcreg, |
| 7201 | Imm32(apply->getSingleTarget()->nargs()), &underflow); |
| 7202 | } |
| 7203 | |
| 7204 | // Skip the construction of the rectifier frame because we have no |
| 7205 | // underflow. |
| 7206 | masm.jump(&rejoin); |
| 7207 | |
| 7208 | // Argument fixup needed. Get ready to call the argumentsRectifier. |
| 7209 | { |
| 7210 | masm.bind(&underflow); |
| 7211 | |
| 7212 | // Hardcode the address of the argumentsRectifier code. |
| 7213 | TrampolinePtr argumentsRectifier = |
| 7214 | gen->jitRuntime()->getArgumentsRectifier(); |
| 7215 | masm.movePtr(argumentsRectifier, objreg); |
| 7216 | } |
| 7217 | |
| 7218 | masm.bind(&rejoin); |
| 7219 | |
| 7220 | // Finally call the function in objreg, as assigned by one of the paths |
| 7221 | // above. |
| 7222 | ensureOsiSpace(); |
| 7223 | uint32_t callOffset = masm.callJit(objreg); |
| 7224 | markSafepointAt(callOffset, apply); |
| 7225 | |
| 7226 | if (apply->mir()->maybeCrossRealm()) { |
| 7227 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 7228 | "ReturnReg available as scratch after scripted calls"); |
| 7229 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 7230 | } |
| 7231 | |
| 7232 | // Discard JitFrameLayout fields still left on the stack. |
| 7233 | masm.freeStack(sizeof(JitFrameLayout) - |
| 7234 | JitFrameLayout::bytesPoppedAfterCall()); |
| 7235 | masm.jump(&end); |
| 7236 | } |
| 7237 | |
| 7238 | // Handle uncompiled or native functions. |
| 7239 | { |
| 7240 | masm.bind(&invoke); |
| 7241 | emitCallInvokeFunction(apply); |
| 7242 | } |
| 7243 | |
| 7244 | masm.bind(&end); |
| 7245 | |
| 7246 | // If the return value of the constructing function is Primitive, replace the |
| 7247 | // return value with the Object from CreateThis. |
| 7248 | if (constructing) { |
| 7249 | Label notPrimitive; |
| 7250 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 7251 | ¬Primitive); |
| 7252 | masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand); |
| 7253 | |
| 7254 | #ifdef DEBUG1 |
| 7255 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 7256 | ¬Primitive); |
| 7257 | masm.assumeUnreachable("CreateThis creates an object"); |
| 7258 | #endif |
| 7259 | |
| 7260 | masm.bind(¬Primitive); |
| 7261 | } |
| 7262 | |
| 7263 | // Pop arguments and continue. |
| 7264 | emitRestoreStackPointerFromFP(); |
| 7265 | } |
| 7266 | |
| 7267 | template <typename T> |
| 7268 | void CodeGenerator::emitAlignStackForApplyNative(T* apply, Register argc) { |
| 7269 | static_assert(JitStackAlignment % ABIStackAlignment == 0, |
| 7270 | "aligning on JIT stack subsumes ABI alignment"); |
| 7271 | |
| 7272 | // Align the arguments on the JitStackAlignment. |
| 7273 | if (JitStackValueAlignment > 1) { |
| 7274 | MOZ_ASSERT(JitStackValueAlignment == 2,do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" " (" "Stack padding adds exactly one Value" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ") (" "Stack padding adds exactly one Value" ")"); do { *((volatile int*)__null) = 7275; __attribute__((nomerge)) ::abort(); } while (false); } } while (false) |
| 7275 | "Stack padding adds exactly one Value")do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitStackValueAlignment == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitStackValueAlignment == 2) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("JitStackValueAlignment == 2" " (" "Stack padding adds exactly one Value" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7275); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ") (" "Stack padding adds exactly one Value" ")"); do { *((volatile int*)__null) = 7275; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
| 7276 | MOZ_ASSERT(frameSize() % JitStackValueAlignment == 0,do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackValueAlignment == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(frameSize() % JitStackValueAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("frameSize() % JitStackValueAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 7277; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 7277 | "Stack padding assumes that the frameSize is correct")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameSize() % JitStackValueAlignment == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(frameSize() % JitStackValueAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("frameSize() % JitStackValueAlignment == 0" " (" "Stack padding assumes that the frameSize is correct" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { *((volatile int*)__null) = 7277; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7278 | |
| 7279 | Assembler::Condition cond; |
| 7280 | if constexpr (T::isConstructing()) { |
| 7281 | // If the number of arguments is even, then we do not need any padding. |
| 7282 | // |
| 7283 | // Also see emitAllocateSpaceForApply(). |
| 7284 | cond = Assembler::Zero; |
| 7285 | } else { |
| 7286 | // If the number of arguments is odd, then we do not need any padding. |
| 7287 | // |
| 7288 | // Also see emitAllocateSpaceForConstructAndPushNewTarget(). |
| 7289 | cond = Assembler::NonZero; |
| 7290 | } |
| 7291 | |
| 7292 | Label noPaddingNeeded; |
| 7293 | masm.branchTestPtr(cond, argc, Imm32(1), &noPaddingNeeded); |
| 7294 | masm.pushValue(MagicValue(JS_ARG_POISON)); |
| 7295 | masm.bind(&noPaddingNeeded); |
| 7296 | } |
| 7297 | } |
| 7298 | |
| 7299 | template <typename T> |
| 7300 | void CodeGenerator::emitPushNativeArguments(T* apply) { |
| 7301 | Register argc = ToRegister(apply->getArgc()); |
| 7302 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 7303 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7304 | uint32_t extraFormals = apply->numExtraFormals(); |
| 7305 | |
| 7306 | // Align stack. |
| 7307 | emitAlignStackForApplyNative(apply, argc); |
| 7308 | |
| 7309 | // Push newTarget. |
| 7310 | if constexpr (T::isConstructing()) { |
| 7311 | masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget())); |
| 7312 | } |
| 7313 | |
| 7314 | // Push arguments. |
| 7315 | Label noCopy; |
| 7316 | masm.branchTestPtr(Assembler::Zero, argc, argc, &noCopy); |
| 7317 | { |
| 7318 | // Use scratch register to calculate stack space. |
| 7319 | masm.movePtr(argc, scratch); |
| 7320 | |
| 7321 | // Reserve space for copying the arguments. |
| 7322 | NativeObject::elementsSizeMustNotOverflow(); |
| 7323 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
| 7324 | masm.subFromStackPtr(scratch); |
| 7325 | |
| 7326 | // Compute the source and destination offsets into the stack. |
| 7327 | Register argvSrcBase = FramePointer; |
| 7328 | size_t argvSrcOffset = |
| 7329 | JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value); |
| 7330 | size_t argvDstOffset = 0; |
| 7331 | |
| 7332 | Register argvIndex = tmpArgc; |
| 7333 | masm.move32(argc, argvIndex); |
| 7334 | |
| 7335 | // Copy arguments. |
| 7336 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
| 7337 | argvDstOffset); |
| 7338 | } |
| 7339 | masm.bind(&noCopy); |
| 7340 | |
| 7341 | // Push |this|. |
| 7342 | if constexpr (T::isConstructing()) { |
| 7343 | masm.pushValue(MagicValue(JS_IS_CONSTRUCTING)); |
| 7344 | } else { |
| 7345 | masm.pushValue(ToValue(apply, T::ThisIndex)); |
| 7346 | } |
| 7347 | } |
| 7348 | |
| 7349 | template <typename T> |
| 7350 | void CodeGenerator::emitPushArrayAsNativeArguments(T* apply) { |
| 7351 | Register argc = ToRegister(apply->getArgc()); |
| 7352 | Register elements = ToRegister(apply->getElements()); |
| 7353 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 7354 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7355 | |
| 7356 | // NB: argc and elements are mapped to the same register. |
| 7357 | MOZ_ASSERT(argc == elements)do { static_assert( mozilla::detail::AssertionConditionType< decltype(argc == elements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(argc == elements))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argc == elements" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7357); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == elements" ")"); do { *((volatile int*)__null) = 7357; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7358 | |
| 7359 | // Invariants guarded in the caller: |
| 7360 | // - the array is not too long |
| 7361 | // - the array length equals its initialized length |
| 7362 | |
| 7363 | // The array length is our argc. |
| 7364 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
| 7365 | |
| 7366 | // Align stack. |
| 7367 | emitAlignStackForApplyNative(apply, tmpArgc); |
| 7368 | |
| 7369 | // Push newTarget. |
| 7370 | if constexpr (T::isConstructing()) { |
| 7371 | masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget())); |
| 7372 | } |
| 7373 | |
| 7374 | // Skip the copy of arguments if there are none. |
| 7375 | Label noCopy; |
| 7376 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
| 7377 | { |
| 7378 | // |tmpArgc| is off-by-one, so adjust the offset accordingly. |
| 7379 | BaseObjectElementIndex srcPtr(elements, tmpArgc, |
| 7380 | -int32_t(sizeof(JS::Value))); |
| 7381 | |
| 7382 | Label loop; |
| 7383 | masm.bind(&loop); |
| 7384 | masm.pushValue(srcPtr, scratch); |
| 7385 | masm.decBranchPtr(Assembler::NonZero, tmpArgc, Imm32(1), &loop); |
| 7386 | } |
| 7387 | masm.bind(&noCopy); |
| 7388 | |
| 7389 | // Set argc in preparation for calling the native function. |
| 7390 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), argc); |
| 7391 | |
| 7392 | // Push |this|. |
| 7393 | if constexpr (T::isConstructing()) { |
| 7394 | masm.pushValue(MagicValue(JS_IS_CONSTRUCTING)); |
| 7395 | } else { |
| 7396 | masm.pushValue(ToValue(apply, T::ThisIndex)); |
| 7397 | } |
| 7398 | } |
| 7399 | |
| 7400 | void CodeGenerator::emitPushArguments(LApplyArgsNative* apply) { |
| 7401 | emitPushNativeArguments(apply); |
| 7402 | } |
| 7403 | |
| 7404 | void CodeGenerator::emitPushArguments(LApplyArrayNative* apply) { |
| 7405 | emitPushArrayAsNativeArguments(apply); |
| 7406 | } |
| 7407 | |
| 7408 | void CodeGenerator::emitPushArguments(LConstructArgsNative* construct) { |
| 7409 | emitPushNativeArguments(construct); |
| 7410 | } |
| 7411 | |
| 7412 | void CodeGenerator::emitPushArguments(LConstructArrayNative* construct) { |
| 7413 | emitPushArrayAsNativeArguments(construct); |
| 7414 | } |
| 7415 | |
| 7416 | void CodeGenerator::emitPushArguments(LApplyArgsObjNative* apply) { |
| 7417 | Register argc = ToRegister(apply->getArgc()); |
| 7418 | Register argsObj = ToRegister(apply->getArgsObj()); |
| 7419 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 7420 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7421 | Register scratch2 = ToRegister(apply->getTempExtra()); |
| 7422 | |
| 7423 | // NB: argc and argsObj are mapped to the same register. |
| 7424 | MOZ_ASSERT(argc == argsObj)do { static_assert( mozilla::detail::AssertionConditionType< decltype(argc == argsObj)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(argc == argsObj))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("argc == argsObj" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argc == argsObj" ")"); do { *((volatile int*)__null) = 7424; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7425 | |
| 7426 | // Load argc into tmpArgc. |
| 7427 | masm.loadArgumentsObjectLength(argsObj, tmpArgc); |
| 7428 | |
| 7429 | // Align stack. |
| 7430 | emitAlignStackForApplyNative(apply, tmpArgc); |
| 7431 | |
| 7432 | // Push arguments. |
| 7433 | Label noCopy, epilogue; |
| 7434 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
| 7435 | { |
| 7436 | // Use scratch register to calculate stack space. |
| 7437 | masm.movePtr(tmpArgc, scratch); |
| 7438 | |
| 7439 | // Reserve space for copying the arguments. |
| 7440 | NativeObject::elementsSizeMustNotOverflow(); |
| 7441 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
| 7442 | masm.subFromStackPtr(scratch); |
| 7443 | |
| 7444 | // Load arguments data. |
| 7445 | Register argvSrcBase = argsObj; |
| 7446 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
| 7447 | argvSrcBase); |
| 7448 | size_t argvSrcOffset = ArgumentsData::offsetOfArgs(); |
| 7449 | size_t argvDstOffset = 0; |
| 7450 | |
| 7451 | Register argvIndex = scratch2; |
| 7452 | masm.move32(tmpArgc, argvIndex); |
| 7453 | |
| 7454 | // Copy the values. |
| 7455 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
| 7456 | argvDstOffset); |
| 7457 | } |
| 7458 | masm.bind(&noCopy); |
| 7459 | |
| 7460 | // Set argc in preparation for calling the native function. |
| 7461 | masm.movePtr(tmpArgc, argc); |
| 7462 | |
| 7463 | // Push |this|. |
| 7464 | masm.pushValue(ToValue(apply, LApplyArgsObjNative::ThisIndex)); |
| 7465 | } |
| 7466 | |
| 7467 | template <typename T> |
| 7468 | void CodeGenerator::emitApplyNative(T* apply) { |
| 7469 | MOZ_ASSERT(T::isConstructing() == apply->mir()->isConstructing(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(T::isConstructing() == apply->mir()->isConstructing ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(T::isConstructing() == apply->mir()->isConstructing ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("T::isConstructing() == apply->mir()->isConstructing()" " (" "isConstructing condition must be consistent" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()" ") (" "isConstructing condition must be consistent" ")"); do { *((volatile int*)__null) = 7470; __attribute__((nomerge)) :: abort(); } while (false); } } while (false) |
| 7470 | "isConstructing condition must be consistent")do { static_assert( mozilla::detail::AssertionConditionType< decltype(T::isConstructing() == apply->mir()->isConstructing ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(T::isConstructing() == apply->mir()->isConstructing ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("T::isConstructing() == apply->mir()->isConstructing()" " (" "isConstructing condition must be consistent" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()" ") (" "isConstructing condition must be consistent" ")"); do { *((volatile int*)__null) = 7470; __attribute__((nomerge)) :: abort(); } while (false); } } while (false); |
| 7471 | |
| 7472 | WrappedFunction* target = apply->mir()->getSingleTarget(); |
| 7473 | MOZ_ASSERT(target->isNativeWithoutJitEntry())do { static_assert( mozilla::detail::AssertionConditionType< decltype(target->isNativeWithoutJitEntry())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(target->isNativeWithoutJitEntry ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("target->isNativeWithoutJitEntry()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7473); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { *((volatile int*)__null) = 7473; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7474 | |
| 7475 | JSNative native = target->native(); |
| 7476 | if (apply->mir()->ignoresReturnValue() && target->hasJitInfo()) { |
| 7477 | const JSJitInfo* jitInfo = target->jitInfo(); |
| 7478 | if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) { |
| 7479 | native = jitInfo->ignoresReturnValueMethod; |
| 7480 | } |
| 7481 | } |
| 7482 | |
| 7483 | // Push arguments, including newTarget and |this|. |
| 7484 | emitPushArguments(apply); |
| 7485 | |
| 7486 | // Registers used for callWithABI() argument-passing. |
| 7487 | Register argContextReg = ToRegister(apply->getTempObject()); |
| 7488 | Register argUintNReg = ToRegister(apply->getArgc()); |
| 7489 | Register argVpReg = ToRegister(apply->getTempForArgCopy()); |
| 7490 | Register tempReg = ToRegister(apply->getTempExtra()); |
| 7491 | |
| 7492 | // No unused stack for variadic calls. |
| 7493 | uint32_t unusedStack = 0; |
| 7494 | |
| 7495 | // Pushed arguments don't change the pushed frames amount. |
| 7496 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7496); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 7496; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7497 | |
| 7498 | // Create the exit frame and call the native. |
| 7499 | emitCallNative(apply, native, argContextReg, argUintNReg, argVpReg, tempReg, |
| 7500 | unusedStack); |
| 7501 | |
| 7502 | // The exit frame is still on the stack. |
| 7503 | MOZ_ASSERT(masm.framePushed() == frameSize() + NativeExitFrameLayout::Size())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize() + NativeExitFrameLayout ::Size())>::isValid, "invalid assertion condition"); if (( __builtin_expect(!!(!(!!(masm.framePushed() == frameSize() + NativeExitFrameLayout ::Size()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7503); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()" ")"); do { *((volatile int*)__null) = 7503; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7504 | |
| 7505 | // The next instruction is removing the exit frame, so there is no need for |
| 7506 | // leaveFakeExitFrame. |
| 7507 | |
| 7508 | // Pop arguments and continue. |
| 7509 | masm.setFramePushed(frameSize()); |
| 7510 | emitRestoreStackPointerFromFP(); |
| 7511 | } |
| 7512 | |
| 7513 | template <typename T> |
| 7514 | void CodeGenerator::emitApplyArgsGuard(T* apply) { |
| 7515 | LSnapshot* snapshot = apply->snapshot(); |
| 7516 | Register argcreg = ToRegister(apply->getArgc()); |
| 7517 | |
| 7518 | // Ensure that we have a reasonable number of arguments. |
| 7519 | bailoutCmp32(Assembler::Above, argcreg, Imm32(JIT_ARGS_LENGTH_MAX), snapshot); |
| 7520 | } |
| 7521 | |
| 7522 | template <typename T> |
| 7523 | void CodeGenerator::emitApplyArgsObjGuard(T* apply) { |
| 7524 | Register argsObj = ToRegister(apply->getArgsObj()); |
| 7525 | Register temp = ToRegister(apply->getTempObject()); |
| 7526 | |
| 7527 | Label bail; |
| 7528 | masm.loadArgumentsObjectLength(argsObj, temp, &bail); |
| 7529 | masm.branch32(Assembler::Above, temp, Imm32(JIT_ARGS_LENGTH_MAX), &bail); |
| 7530 | bailoutFrom(&bail, apply->snapshot()); |
| 7531 | } |
| 7532 | |
| 7533 | template <typename T> |
| 7534 | void CodeGenerator::emitApplyArrayGuard(T* apply) { |
| 7535 | LSnapshot* snapshot = apply->snapshot(); |
| 7536 | Register elements = ToRegister(apply->getElements()); |
| 7537 | Register tmp = ToRegister(apply->getTempObject()); |
| 7538 | |
| 7539 | Address length(elements, ObjectElements::offsetOfLength()); |
| 7540 | masm.load32(length, tmp); |
| 7541 | |
| 7542 | // Ensure that we have a reasonable number of arguments. |
| 7543 | bailoutCmp32(Assembler::Above, tmp, Imm32(JIT_ARGS_LENGTH_MAX), snapshot); |
| 7544 | |
| 7545 | // Ensure that the array does not contain an uninitialized tail. |
| 7546 | |
| 7547 | Address initializedLength(elements, |
| 7548 | ObjectElements::offsetOfInitializedLength()); |
| 7549 | masm.sub32(initializedLength, tmp); |
| 7550 | bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot); |
| 7551 | } |
| 7552 | |
| 7553 | void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) { |
| 7554 | emitApplyArgsGuard(apply); |
| 7555 | emitApplyGeneric(apply); |
| 7556 | } |
| 7557 | |
| 7558 | void CodeGenerator::visitApplyArgsObj(LApplyArgsObj* apply) { |
| 7559 | emitApplyArgsObjGuard(apply); |
| 7560 | emitApplyGeneric(apply); |
| 7561 | } |
| 7562 | |
| 7563 | void CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) { |
| 7564 | emitApplyArrayGuard(apply); |
| 7565 | emitApplyGeneric(apply); |
| 7566 | } |
| 7567 | |
| 7568 | void CodeGenerator::visitConstructArgsGeneric(LConstructArgsGeneric* lir) { |
| 7569 | emitApplyArgsGuard(lir); |
| 7570 | emitApplyGeneric(lir); |
| 7571 | } |
| 7572 | |
| 7573 | void CodeGenerator::visitConstructArrayGeneric(LConstructArrayGeneric* lir) { |
| 7574 | emitApplyArrayGuard(lir); |
| 7575 | emitApplyGeneric(lir); |
| 7576 | } |
| 7577 | |
| 7578 | void CodeGenerator::visitApplyArgsNative(LApplyArgsNative* lir) { |
| 7579 | emitApplyArgsGuard(lir); |
| 7580 | emitApplyNative(lir); |
| 7581 | } |
| 7582 | |
| 7583 | void CodeGenerator::visitApplyArgsObjNative(LApplyArgsObjNative* lir) { |
| 7584 | emitApplyArgsObjGuard(lir); |
| 7585 | emitApplyNative(lir); |
| 7586 | } |
| 7587 | |
| 7588 | void CodeGenerator::visitApplyArrayNative(LApplyArrayNative* lir) { |
| 7589 | emitApplyArrayGuard(lir); |
| 7590 | emitApplyNative(lir); |
| 7591 | } |
| 7592 | |
| 7593 | void CodeGenerator::visitConstructArgsNative(LConstructArgsNative* lir) { |
| 7594 | emitApplyArgsGuard(lir); |
| 7595 | emitApplyNative(lir); |
| 7596 | } |
| 7597 | |
| 7598 | void CodeGenerator::visitConstructArrayNative(LConstructArrayNative* lir) { |
| 7599 | emitApplyArrayGuard(lir); |
| 7600 | emitApplyNative(lir); |
| 7601 | } |
| 7602 | |
| 7603 | void CodeGenerator::visitBail(LBail* lir) { bailout(lir->snapshot()); } |
| 7604 | |
| 7605 | void CodeGenerator::visitUnreachable(LUnreachable* lir) { |
| 7606 | masm.assumeUnreachable("end-of-block assumed unreachable"); |
| 7607 | } |
| 7608 | |
| 7609 | void CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot* lir) { |
| 7610 | encode(lir->snapshot()); |
| 7611 | } |
| 7612 | |
| 7613 | void CodeGenerator::visitUnreachableResultV(LUnreachableResultV* lir) { |
| 7614 | masm.assumeUnreachable("must be unreachable"); |
| 7615 | } |
| 7616 | |
| 7617 | void CodeGenerator::visitUnreachableResultT(LUnreachableResultT* lir) { |
| 7618 | masm.assumeUnreachable("must be unreachable"); |
| 7619 | } |
| 7620 | |
| 7621 | // Out-of-line path to report over-recursed error and fail. |
| 7622 | class CheckOverRecursedFailure : public OutOfLineCodeBase<CodeGenerator> { |
| 7623 | LInstruction* lir_; |
| 7624 | |
| 7625 | public: |
| 7626 | explicit CheckOverRecursedFailure(LInstruction* lir) : lir_(lir) {} |
| 7627 | |
| 7628 | void accept(CodeGenerator* codegen) override { |
| 7629 | codegen->visitCheckOverRecursedFailure(this); |
| 7630 | } |
| 7631 | |
| 7632 | LInstruction* lir() const { return lir_; } |
| 7633 | }; |
| 7634 | |
| 7635 | void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) { |
| 7636 | // If we don't push anything on the stack, skip the check. |
| 7637 | if (omitOverRecursedCheck()) { |
| 7638 | return; |
| 7639 | } |
| 7640 | |
| 7641 | // Ensure that this frame will not cross the stack limit. |
| 7642 | // This is a weak check, justified by Ion using the C stack: we must always |
| 7643 | // be some distance away from the actual limit, since if the limit is |
| 7644 | // crossed, an error must be thrown, which requires more frames. |
| 7645 | // |
| 7646 | // It must always be possible to trespass past the stack limit. |
| 7647 | // Ion may legally place frames very close to the limit. Calling additional |
| 7648 | // C functions may then violate the limit without any checking. |
| 7649 | // |
| 7650 | // Since Ion frames exist on the C stack, the stack limit may be |
| 7651 | // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota(). |
| 7652 | |
| 7653 | CheckOverRecursedFailure* ool = new (alloc()) CheckOverRecursedFailure(lir); |
| 7654 | addOutOfLineCode(ool, lir->mir()); |
| 7655 | |
| 7656 | // Conditional forward (unlikely) branch to failure. |
| 7657 | const void* limitAddr = gen->runtime->addressOfJitStackLimit(); |
| 7658 | masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), |
| 7659 | ool->entry()); |
| 7660 | masm.bind(ool->rejoin()); |
| 7661 | } |
| 7662 | |
| 7663 | void CodeGenerator::visitCheckOverRecursedFailure( |
| 7664 | CheckOverRecursedFailure* ool) { |
| 7665 | // The OOL path is hit if the recursion depth has been exceeded. |
| 7666 | // Throw an InternalError for over-recursion. |
| 7667 | |
| 7668 | // LFunctionEnvironment can appear before LCheckOverRecursed, so we have |
| 7669 | // to save all live registers to avoid crashes if CheckOverRecursed triggers |
| 7670 | // a GC. |
| 7671 | saveLive(ool->lir()); |
| 7672 | |
| 7673 | using Fn = bool (*)(JSContext*); |
| 7674 | callVM<Fn, CheckOverRecursed>(ool->lir()); |
| 7675 | |
| 7676 | restoreLive(ool->lir()); |
| 7677 | masm.jump(ool->rejoin()); |
| 7678 | } |
| 7679 | |
| 7680 | IonScriptCounts* CodeGenerator::maybeCreateScriptCounts() { |
| 7681 | // If scripts are being profiled, create a new IonScriptCounts for the |
| 7682 | // profiling data, which will be attached to the associated JSScript or |
| 7683 | // wasm module after code generation finishes. |
| 7684 | if (!gen->hasProfilingScripts()) { |
| 7685 | return nullptr; |
| 7686 | } |
| 7687 | |
| 7688 | // This test inhibits IonScriptCount creation for wasm code which is |
| 7689 | // currently incompatible with wasm codegen for two reasons: (1) wasm code |
| 7690 | // must be serializable and script count codegen bakes in absolute |
| 7691 | // addresses, (2) wasm code does not have a JSScript with which to associate |
| 7692 | // code coverage data. |
| 7693 | JSScript* script = gen->outerInfo().script(); |
| 7694 | if (!script) { |
| 7695 | return nullptr; |
| 7696 | } |
| 7697 | |
| 7698 | auto counts = MakeUnique<IonScriptCounts>(); |
| 7699 | if (!counts || !counts->init(graph.numBlocks())) { |
| 7700 | return nullptr; |
| 7701 | } |
| 7702 | |
| 7703 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
| 7704 | MBasicBlock* block = graph.getBlock(i)->mir(); |
| 7705 | |
| 7706 | uint32_t offset = 0; |
| 7707 | char* description = nullptr; |
| 7708 | if (MResumePoint* resume = block->entryResumePoint()) { |
| 7709 | // Find a PC offset in the outermost script to use. If this |
| 7710 | // block is from an inlined script, find a location in the |
| 7711 | // outer script to associate information about the inlining |
| 7712 | // with. |
| 7713 | while (resume->caller()) { |
| 7714 | resume = resume->caller(); |
| 7715 | } |
| 7716 | offset = script->pcToOffset(resume->pc()); |
| 7717 | |
| 7718 | if (block->entryResumePoint()->caller()) { |
| 7719 | // Get the filename and line number of the inner script. |
| 7720 | JSScript* innerScript = block->info().script(); |
| 7721 | description = js_pod_calloc<char>(200); |
| 7722 | if (description) { |
| 7723 | snprintf(description, 200, "%s:%u", innerScript->filename(), |
| 7724 | innerScript->lineno()); |
| 7725 | } |
| 7726 | } |
| 7727 | } |
| 7728 | |
| 7729 | if (!counts->block(i).init(block->id(), offset, description, |
| 7730 | block->numSuccessors())) { |
| 7731 | return nullptr; |
| 7732 | } |
| 7733 | |
| 7734 | for (size_t j = 0; j < block->numSuccessors(); j++) { |
| 7735 | counts->block(i).setSuccessor( |
| 7736 | j, skipTrivialBlocks(block->getSuccessor(j))->id()); |
| 7737 | } |
| 7738 | } |
| 7739 | |
| 7740 | scriptCounts_ = counts.release(); |
| 7741 | return scriptCounts_; |
| 7742 | } |
| 7743 | |
| 7744 | // Structure for managing the state tracked for a block by script counters. |
| 7745 | struct ScriptCountBlockState { |
| 7746 | IonBlockCounts& block; |
| 7747 | MacroAssembler& masm; |
| 7748 | |
| 7749 | Sprinter printer; |
| 7750 | |
| 7751 | public: |
| 7752 | ScriptCountBlockState(IonBlockCounts* block, MacroAssembler* masm) |
| 7753 | : block(*block), masm(*masm), printer(GetJitContext()->cx, false) {} |
| 7754 | |
| 7755 | bool init() { |
| 7756 | if (!printer.init()) { |
| 7757 | return false; |
| 7758 | } |
| 7759 | |
| 7760 | // Bump the hit count for the block at the start. This code is not |
| 7761 | // included in either the text for the block or the instruction byte |
| 7762 | // counts. |
| 7763 | masm.inc64(AbsoluteAddress(block.addressOfHitCount())); |
| 7764 | |
| 7765 | // Collect human readable assembly for the code generated in the block. |
| 7766 | masm.setPrinter(&printer); |
| 7767 | |
| 7768 | return true; |
| 7769 | } |
| 7770 | |
| 7771 | void visitInstruction(LInstruction* ins) { |
| 7772 | #ifdef JS_JITSPEW1 |
| 7773 | // Prefix stream of assembly instructions with their LIR instruction |
| 7774 | // name and any associated high level info. |
| 7775 | if (const char* extra = ins->getExtraName()) { |
| 7776 | printer.printf("[%s:%s]\n", ins->opName(), extra); |
| 7777 | } else { |
| 7778 | printer.printf("[%s]\n", ins->opName()); |
| 7779 | } |
| 7780 | #endif |
| 7781 | } |
| 7782 | |
| 7783 | ~ScriptCountBlockState() { |
| 7784 | masm.setPrinter(nullptr); |
| 7785 | |
| 7786 | if (JS::UniqueChars str = printer.release()) { |
| 7787 | block.setCode(str.get()); |
| 7788 | } |
| 7789 | } |
| 7790 | }; |
| 7791 | |
| 7792 | void CodeGenerator::branchIfInvalidated(Register temp, Label* invalidated) { |
| 7793 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp); |
| 7794 | masm.propagateOOM(ionScriptLabels_.append(label)); |
| 7795 | |
| 7796 | // If IonScript::invalidationCount_ != 0, the script has been invalidated. |
| 7797 | masm.branch32(Assembler::NotEqual, |
| 7798 | Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0), |
| 7799 | invalidated); |
| 7800 | } |
| 7801 | |
| 7802 | #ifdef DEBUG1 |
| 7803 | void CodeGenerator::emitAssertGCThingResult(Register input, |
| 7804 | const MDefinition* mir) { |
| 7805 | MIRType type = mir->type(); |
| 7806 | MOZ_ASSERT(type == MIRType::Object || type == MIRType::String ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7807); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 7807; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 7807 | type == MIRType::Symbol || type == MIRType::BigInt)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7807); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" ")"); do { *((volatile int*)__null) = 7807; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7808 | |
| 7809 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 7810 | regs.take(input); |
| 7811 | |
| 7812 | Register temp = regs.takeAny(); |
| 7813 | masm.push(temp); |
| 7814 | |
| 7815 | // Don't check if the script has been invalidated. In that case invalid |
| 7816 | // types are expected (until we reach the OsiPoint and bailout). |
| 7817 | Label done; |
| 7818 | branchIfInvalidated(temp, &done); |
| 7819 | |
| 7820 | # ifndef JS_SIMULATOR |
| 7821 | // Check that we have a valid GC pointer. |
| 7822 | // Disable for wasm because we don't have a context on wasm compilation |
| 7823 | // threads and this needs a context. |
| 7824 | // Also disable for simulator builds because the C++ call is a lot slower |
| 7825 | // there than on actual hardware. |
| 7826 | if (JitOptions.fullDebugChecks && !IsCompilingWasm()) { |
| 7827 | saveVolatile(); |
| 7828 | masm.setupUnalignedABICall(temp); |
| 7829 | masm.loadJSContext(temp); |
| 7830 | masm.passABIArg(temp); |
| 7831 | masm.passABIArg(input); |
| 7832 | |
| 7833 | switch (type) { |
| 7834 | case MIRType::Object: { |
| 7835 | using Fn = void (*)(JSContext* cx, JSObject* obj); |
| 7836 | masm.callWithABI<Fn, AssertValidObjectPtr>(); |
| 7837 | break; |
| 7838 | } |
| 7839 | case MIRType::String: { |
| 7840 | using Fn = void (*)(JSContext* cx, JSString* str); |
| 7841 | masm.callWithABI<Fn, AssertValidStringPtr>(); |
| 7842 | break; |
| 7843 | } |
| 7844 | case MIRType::Symbol: { |
| 7845 | using Fn = void (*)(JSContext* cx, JS::Symbol* sym); |
| 7846 | masm.callWithABI<Fn, AssertValidSymbolPtr>(); |
| 7847 | break; |
| 7848 | } |
| 7849 | case MIRType::BigInt: { |
| 7850 | using Fn = void (*)(JSContext* cx, JS::BigInt* bi); |
| 7851 | masm.callWithABI<Fn, AssertValidBigIntPtr>(); |
| 7852 | break; |
| 7853 | } |
| 7854 | default: |
| 7855 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7855); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 7855; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
| 7856 | } |
| 7857 | |
| 7858 | restoreVolatile(); |
| 7859 | } |
| 7860 | # endif |
| 7861 | |
| 7862 | masm.bind(&done); |
| 7863 | masm.pop(temp); |
| 7864 | } |
| 7865 | |
| 7866 | void CodeGenerator::emitAssertResultV(const ValueOperand input, |
| 7867 | const MDefinition* mir) { |
| 7868 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 7869 | regs.take(input); |
| 7870 | |
| 7871 | Register temp1 = regs.takeAny(); |
| 7872 | Register temp2 = regs.takeAny(); |
| 7873 | masm.push(temp1); |
| 7874 | masm.push(temp2); |
| 7875 | |
| 7876 | // Don't check if the script has been invalidated. In that case invalid |
| 7877 | // types are expected (until we reach the OsiPoint and bailout). |
| 7878 | Label done; |
| 7879 | branchIfInvalidated(temp1, &done); |
| 7880 | |
| 7881 | // Check that we have a valid GC pointer. |
| 7882 | if (JitOptions.fullDebugChecks) { |
| 7883 | saveVolatile(); |
| 7884 | |
| 7885 | masm.pushValue(input); |
| 7886 | masm.moveStackPtrTo(temp1); |
| 7887 | |
| 7888 | using Fn = void (*)(JSContext* cx, Value* v); |
| 7889 | masm.setupUnalignedABICall(temp2); |
| 7890 | masm.loadJSContext(temp2); |
| 7891 | masm.passABIArg(temp2); |
| 7892 | masm.passABIArg(temp1); |
| 7893 | masm.callWithABI<Fn, AssertValidValue>(); |
| 7894 | masm.popValue(input); |
| 7895 | restoreVolatile(); |
| 7896 | } |
| 7897 | |
| 7898 | masm.bind(&done); |
| 7899 | masm.pop(temp2); |
| 7900 | masm.pop(temp1); |
| 7901 | } |
| 7902 | |
| 7903 | void CodeGenerator::emitGCThingResultChecks(LInstruction* lir, |
| 7904 | MDefinition* mir) { |
| 7905 | if (lir->numDefs() == 0) { |
| 7906 | return; |
| 7907 | } |
| 7908 | |
| 7909 | MOZ_ASSERT(lir->numDefs() == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->numDefs() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->numDefs() == 1))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("lir->numDefs() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7909); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { *((volatile int*)__null) = 7909; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7910 | if (lir->getDef(0)->isBogusTemp()) { |
| 7911 | return; |
| 7912 | } |
| 7913 | |
| 7914 | Register output = ToRegister(lir->getDef(0)); |
| 7915 | emitAssertGCThingResult(output, mir); |
| 7916 | } |
| 7917 | |
| 7918 | void CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir) { |
| 7919 | if (lir->numDefs() == 0) { |
| 7920 | return; |
| 7921 | } |
| 7922 | |
| 7923 | MOZ_ASSERT(lir->numDefs() == BOX_PIECES)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->numDefs() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->numDefs() == 1))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("lir->numDefs() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 7923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { *((volatile int*)__null) = 7923; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7924 | if (!lir->getDef(0)->output()->isRegister()) { |
| 7925 | return; |
| 7926 | } |
| 7927 | |
| 7928 | ValueOperand output = ToOutValue(lir); |
| 7929 | |
| 7930 | emitAssertResultV(output, mir); |
| 7931 | } |
| 7932 | |
| 7933 | void CodeGenerator::emitDebugResultChecks(LInstruction* ins) { |
| 7934 | // In debug builds, check that LIR instructions return valid values. |
| 7935 | |
| 7936 | MDefinition* mir = ins->mirRaw(); |
| 7937 | if (!mir) { |
| 7938 | return; |
| 7939 | } |
| 7940 | |
| 7941 | switch (mir->type()) { |
| 7942 | case MIRType::Object: |
| 7943 | case MIRType::String: |
| 7944 | case MIRType::Symbol: |
| 7945 | case MIRType::BigInt: |
| 7946 | emitGCThingResultChecks(ins, mir); |
| 7947 | break; |
| 7948 | case MIRType::Value: |
| 7949 | emitValueResultChecks(ins, mir); |
| 7950 | break; |
| 7951 | default: |
| 7952 | break; |
| 7953 | } |
| 7954 | } |
| 7955 | |
| 7956 | void CodeGenerator::emitDebugForceBailing(LInstruction* lir) { |
| 7957 | if (MOZ_LIKELY(!gen->options.ionBailAfterEnabled())(__builtin_expect(!!(!gen->options.ionBailAfterEnabled()), 1))) { |
| 7958 | return; |
| 7959 | } |
| 7960 | if (!lir->snapshot()) { |
| 7961 | return; |
| 7962 | } |
| 7963 | if (lir->isOsiPoint()) { |
| 7964 | return; |
| 7965 | } |
| 7966 | |
| 7967 | masm.comment("emitDebugForceBailing"); |
| 7968 | const void* bailAfterCounterAddr = |
| 7969 | gen->runtime->addressOfIonBailAfterCounter(); |
| 7970 | |
| 7971 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 7972 | |
| 7973 | Label done, notBail; |
| 7974 | masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterCounterAddr), |
| 7975 | Imm32(0), &done); |
| 7976 | { |
| 7977 | Register temp = regs.takeAny(); |
| 7978 | |
| 7979 | masm.push(temp); |
| 7980 | masm.load32(AbsoluteAddress(bailAfterCounterAddr), temp); |
| 7981 | masm.sub32(Imm32(1), temp); |
| 7982 | masm.store32(temp, AbsoluteAddress(bailAfterCounterAddr)); |
| 7983 | |
| 7984 | masm.branch32(Assembler::NotEqual, temp, Imm32(0), ¬Bail); |
| 7985 | { |
| 7986 | masm.pop(temp); |
| 7987 | bailout(lir->snapshot()); |
| 7988 | } |
| 7989 | masm.bind(¬Bail); |
| 7990 | masm.pop(temp); |
| 7991 | } |
| 7992 | masm.bind(&done); |
| 7993 | } |
| 7994 | #endif |
| 7995 | |
| 7996 | bool CodeGenerator::generateBody() { |
| 7997 | JitSpewCont(JitSpew_Codegen, "\n"); |
| 7998 | AutoCreatedBy acb(masm, "CodeGenerator::generateBody"); |
| 7999 | |
| 8000 | JitSpew(JitSpew_Codegen, "==== BEGIN CodeGenerator::generateBody ===="); |
| 8001 | IonScriptCounts* counts = maybeCreateScriptCounts(); |
| 8002 | |
| 8003 | const bool compilingWasm = gen->compilingWasm(); |
| 8004 | |
| 8005 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
| 8006 | current = graph.getBlock(i); |
| 8007 | |
| 8008 | // Don't emit any code for trivial blocks, containing just a goto. Such |
| 8009 | // blocks are created to split critical edges, and if we didn't end up |
| 8010 | // putting any instructions in them, we can skip them. |
| 8011 | if (current->isTrivial()) { |
| 8012 | continue; |
| 8013 | } |
| 8014 | |
| 8015 | #ifdef JS_JITSPEW1 |
| 8016 | const char* filename = nullptr; |
| 8017 | size_t lineNumber = 0; |
| 8018 | JS::LimitedColumnNumberOneOrigin columnNumber; |
| 8019 | if (current->mir()->info().script()) { |
| 8020 | filename = current->mir()->info().script()->filename(); |
| 8021 | if (current->mir()->pc()) { |
| 8022 | lineNumber = PCToLineNumber(current->mir()->info().script(), |
| 8023 | current->mir()->pc(), &columnNumber); |
| 8024 | } |
| 8025 | } |
| 8026 | JitSpew(JitSpew_Codegen, "--------------------------------"); |
| 8027 | JitSpew(JitSpew_Codegen, "# block%zu %s:%zu:%u%s:", i, |
| 8028 | filename ? filename : "?", lineNumber, |
| 8029 | columnNumber.oneOriginValue(), |
| 8030 | current->mir()->isLoopHeader() ? " (loop header)" : ""); |
| 8031 | #endif |
| 8032 | |
| 8033 | if (current->mir()->isLoopHeader() && compilingWasm) { |
| 8034 | masm.nopAlign(CodeAlignment); |
| 8035 | } |
| 8036 | |
| 8037 | masm.bind(current->label()); |
| 8038 | |
| 8039 | mozilla::Maybe<ScriptCountBlockState> blockCounts; |
| 8040 | if (counts) { |
| 8041 | blockCounts.emplace(&counts->block(i), &masm); |
| 8042 | if (!blockCounts->init()) { |
| 8043 | return false; |
| 8044 | } |
| 8045 | } |
| 8046 | |
| 8047 | for (LInstructionIterator iter = current->begin(); iter != current->end(); |
| 8048 | iter++) { |
| 8049 | if (!alloc().ensureBallast()) { |
| 8050 | return false; |
| 8051 | } |
| 8052 | |
| 8053 | perfSpewer_.recordInstruction(masm, *iter); |
| 8054 | #ifdef JS_JITSPEW1 |
| 8055 | JitSpewStart(JitSpew_Codegen, " # LIR=%s", |
| 8056 | iter->opName()); |
| 8057 | if (const char* extra = iter->getExtraName()) { |
| 8058 | JitSpewCont(JitSpew_Codegen, ":%s", extra); |
| 8059 | } |
| 8060 | JitSpewFin(JitSpew_Codegen); |
| 8061 | #endif |
| 8062 | |
| 8063 | if (counts) { |
| 8064 | blockCounts->visitInstruction(*iter); |
| 8065 | } |
| 8066 | |
| 8067 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
| 8068 | if (iter->safepoint() && !compilingWasm) { |
| 8069 | resetOsiPointRegs(iter->safepoint()); |
| 8070 | } |
| 8071 | #endif |
| 8072 | |
| 8073 | if (!compilingWasm) { |
| 8074 | if (MDefinition* mir = iter->mirRaw()) { |
| 8075 | if (!addNativeToBytecodeEntry(mir->trackedSite())) { |
| 8076 | return false; |
| 8077 | } |
| 8078 | } |
| 8079 | } |
| 8080 | |
| 8081 | setElement(*iter); // needed to encode correct snapshot location. |
| 8082 | |
| 8083 | #ifdef DEBUG1 |
| 8084 | emitDebugForceBailing(*iter); |
| 8085 | #endif |
| 8086 | |
| 8087 | switch (iter->op()) { |
| 8088 | #ifndef JS_CODEGEN_NONE |
| 8089 | # define LIROP(op) \ |
| 8090 | case LNode::Opcode::op: \ |
| 8091 | visit##op(iter->to##op()); \ |
| 8092 | break; |
| 8093 | LIR_OPCODE_LIST(LIROP)LIROP(Phi)LIROP(Box)LIROP(OsiPoint)LIROP(MoveGroup)LIROP(Integer )LIROP(Integer64)LIROP(Pointer)LIROP(Double)LIROP(Float32)LIROP (Value)LIROP(NurseryObject)LIROP(Parameter)LIROP(Callee)LIROP (IsConstructing)LIROP(Goto)LIROP(NewArray)LIROP(NewArrayDynamicLength )LIROP(NewIterator)LIROP(NewTypedArray)LIROP(NewTypedArrayDynamicLength )LIROP(NewTypedArrayFromArray)LIROP(NewTypedArrayFromArrayBuffer )LIROP(BindFunction)LIROP(NewBoundFunction)LIROP(NewObject)LIROP (NewPlainObject)LIROP(NewArrayObject)LIROP(NewNamedLambdaObject )LIROP(NewCallObject)LIROP(NewMapObject)LIROP(NewSetObject)LIROP (NewMapObjectFromIterable)LIROP(NewSetObjectFromIterable)LIROP (NewStringObject)LIROP(InitElemGetterSetter)LIROP(MutateProto )LIROP(InitPropGetterSetter)LIROP(CheckOverRecursed)LIROP(WasmTrap )LIROP(WasmTrapIfNull)LIROP(WasmRefIsSubtypeOfConcrete)LIROP( WasmRefIsSubtypeOfAbstract)LIROP(WasmRefIsSubtypeOfConcreteAndBranch )LIROP(WasmRefIsSubtypeOfAbstractAndBranch)LIROP(WasmNewStructObject )LIROP(WasmNewArrayObject)LIROP(ReinterpretCast)LIROP(ReinterpretCastFromI64 )LIROP(ReinterpretCastToI64)LIROP(Rotate)LIROP(RotateI64)LIROP (InterruptCheck)LIROP(WasmStackSwitchToMain)LIROP(WasmStackSwitchToSuspendable )LIROP(WasmStackContinueOnSuspendable)LIROP(WasmInterruptCheck )LIROP(TypeOfV)LIROP(TypeOfO)LIROP(TypeOfName)LIROP(TypeOfIsNonPrimitiveV )LIROP(TypeOfIsNonPrimitiveO)LIROP(TypeOfIsPrimitive)LIROP(ToAsyncIter )LIROP(ToPropertyKeyCache)LIROP(CreateThis)LIROP(CreateArgumentsObject )LIROP(CreateInlinedArgumentsObject)LIROP(GetInlinedArgument) LIROP(GetInlinedArgumentHole)LIROP(GetArgumentsObjectArg)LIROP (SetArgumentsObjectArg)LIROP(LoadArgumentsObjectArg)LIROP(LoadArgumentsObjectArgHole )LIROP(InArgumentsObjectArg)LIROP(ArgumentsObjectLength)LIROP (ArrayFromArgumentsObject)LIROP(GuardArgumentsObjectFlags)LIROP (BoundFunctionNumArgs)LIROP(GuardBoundFunctionIsConstructor)LIROP (ReturnFromCtor)LIROP(BoxNonStrictThis)LIROP(ImplicitThis)LIROP (StackArgT)LIROP(StackArgV)LIROP(CallGeneric)LIROP(CallKnown) LIROP(CallNative)LIROP(CallDOMNative)LIROP(CallClassHook)LIROP (Bail)LIROP(Unreachable)LIROP(EncodeSnapshot)LIROP(UnreachableResultV )LIROP(UnreachableResultT)LIROP(GetDOMProperty)LIROP(GetDOMMemberV )LIROP(GetDOMMemberT)LIROP(SetDOMProperty)LIROP(LoadDOMExpandoValue )LIROP(LoadDOMExpandoValueGuardGeneration)LIROP(LoadDOMExpandoValueIgnoreGeneration )LIROP(GuardDOMExpandoMissingOrGuardShape)LIROP(ApplyArgsGeneric )LIROP(ApplyArgsObj)LIROP(ApplyArrayGeneric)LIROP(ConstructArgsGeneric )LIROP(ConstructArrayGeneric)LIROP(ApplyArgsNative)LIROP(ApplyArgsObjNative )LIROP(ApplyArrayNative)LIROP(ConstructArgsNative)LIROP(ConstructArrayNative )LIROP(TestIAndBranch)LIROP(TestIPtrAndBranch)LIROP(TestI64AndBranch )LIROP(TestDAndBranch)LIROP(TestFAndBranch)LIROP(TestBIAndBranch )LIROP(TestOAndBranch)LIROP(TestVAndBranch)LIROP(Compare)LIROP (CompareI64)LIROP(CompareI64AndBranch)LIROP(CompareAndBranch) LIROP(CompareD)LIROP(CompareF)LIROP(CompareDAndBranch)LIROP(CompareFAndBranch )LIROP(CompareS)LIROP(CompareSInline)LIROP(CompareSSingle)LIROP (CompareBigInt)LIROP(CompareBigIntInt32)LIROP(CompareBigIntDouble )LIROP(CompareBigIntString)LIROP(CompareBigIntInt32AndBranch) LIROP(BitAndAndBranch)LIROP(BitAnd64AndBranch)LIROP(IsNullOrLikeUndefinedV )LIROP(IsNullOrLikeUndefinedT)LIROP(IsNull)LIROP(IsUndefined) LIROP(IsNullOrLikeUndefinedAndBranchV)LIROP(IsNullOrLikeUndefinedAndBranchT )LIROP(IsNullAndBranch)LIROP(IsUndefinedAndBranch)LIROP(SameValueDouble )LIROP(SameValue)LIROP(NotI)LIROP(NotIPtr)LIROP(NotI64)LIROP( NotD)LIROP(NotF)LIROP(NotBI)LIROP(NotO)LIROP(NotV)LIROP(BitNotI )LIROP(BitNotI64)LIROP(BitOpI)LIROP(BitOpI64)LIROP(ShiftI)LIROP (ShiftI64)LIROP(SignExtendInt32)LIROP(SignExtendIntPtr)LIROP( SignExtendInt64)LIROP(UrshD)LIROP(Return)LIROP(Throw)LIROP(ThrowWithStack )LIROP(MinMaxI)LIROP(MinMaxD)LIROP(MinMaxF)LIROP(MinMaxArrayI )LIROP(MinMaxArrayD)LIROP(NegI)LIROP(NegI64)LIROP(NegD)LIROP( NegF)LIROP(AbsI)LIROP(AbsD)LIROP(AbsF)LIROP(CopySignD)LIROP(CopySignF )LIROP(ClzI)LIROP(ClzI64)LIROP(CtzI)LIROP(CtzI64)LIROP(PopcntI )LIROP(PopcntI64)LIROP(SqrtD)LIROP(SqrtF)LIROP(Atan2D)LIROP(Hypot )LIROP(PowI)LIROP(PowII)LIROP(PowD)LIROP(PowOfTwoI)LIROP(SignI )LIROP(SignD)LIROP(SignDI)LIROP(MathFunctionD)LIROP(MathFunctionF )LIROP(AddI)LIROP(AddI64)LIROP(SubI)LIROP(SubI64)LIROP(MulI64 )LIROP(MathD)LIROP(MathF)LIROP(ModD)LIROP(ModPowTwoD)LIROP(WasmBuiltinModD )LIROP(BigIntAdd)LIROP(BigIntSub)LIROP(BigIntMul)LIROP(BigIntDiv )LIROP(BigIntMod)LIROP(BigIntPow)LIROP(BigIntBitAnd)LIROP(BigIntBitOr )LIROP(BigIntBitXor)LIROP(BigIntLsh)LIROP(BigIntRsh)LIROP(BigIntIncrement )LIROP(BigIntDecrement)LIROP(BigIntNegate)LIROP(BigIntBitNot) LIROP(BigIntToIntPtr)LIROP(IntPtrToBigInt)LIROP(BigIntPtrAdd) LIROP(BigIntPtrSub)LIROP(BigIntPtrMul)LIROP(BigIntPtrDiv)LIROP (BigIntPtrDivPowTwo)LIROP(BigIntPtrMod)LIROP(BigIntPtrModPowTwo )LIROP(BigIntPtrPow)LIROP(BigIntPtrBitAnd)LIROP(BigIntPtrBitOr )LIROP(BigIntPtrBitXor)LIROP(BigIntPtrLsh)LIROP(BigIntPtrRsh) LIROP(BigIntPtrBitNot)LIROP(Int32ToStringWithBase)LIROP(NumberParseInt )LIROP(DoubleParseInt)LIROP(Concat)LIROP(LinearizeString)LIROP (LinearizeForCharAccess)LIROP(LinearizeForCodePointAccess)LIROP (ToRelativeStringIndex)LIROP(CharCodeAt)LIROP(CharCodeAtOrNegative )LIROP(CodePointAt)LIROP(CodePointAtOrNegative)LIROP(NegativeToNaN )LIROP(NegativeToUndefined)LIROP(FromCharCode)LIROP(FromCharCodeEmptyIfNegative )LIROP(FromCharCodeUndefinedIfNegative)LIROP(FromCodePoint)LIROP (StringIncludes)LIROP(StringIncludesSIMD)LIROP(StringIndexOf) LIROP(StringIndexOfSIMD)LIROP(StringLastIndexOf)LIROP(StringStartsWith )LIROP(StringStartsWithInline)LIROP(StringEndsWith)LIROP(StringEndsWithInline )LIROP(StringToLowerCase)LIROP(CharCodeToLowerCase)LIROP(StringToUpperCase )LIROP(CharCodeToUpperCase)LIROP(StringTrimStartIndex)LIROP(StringTrimEndIndex )LIROP(StringSplit)LIROP(Substr)LIROP(Int32ToDouble)LIROP(Float32ToDouble )LIROP(DoubleToFloat32)LIROP(Int32ToFloat32)LIROP(DoubleToFloat16 )LIROP(DoubleToFloat32ToFloat16)LIROP(Float32ToFloat16)LIROP( Int32ToFloat16)LIROP(ValueToDouble)LIROP(ValueToFloat32)LIROP (ValueToFloat16)LIROP(ValueToNumberInt32)LIROP(ValueTruncateToInt32 )LIROP(ValueToBigInt)LIROP(DoubleToInt32)LIROP(Float32ToInt32 )LIROP(TruncateDToInt32)LIROP(WasmBuiltinTruncateDToInt32)LIROP (TruncateFToInt32)LIROP(WasmBuiltinTruncateFToInt32)LIROP(WasmTruncateToInt32 )LIROP(WrapInt64ToInt32)LIROP(ExtendInt32ToInt64)LIROP(BooleanToString )LIROP(IntToString)LIROP(DoubleToString)LIROP(ValueToString)LIROP (PowHalfD)LIROP(NaNToZero)LIROP(OsrEntry)LIROP(OsrValue)LIROP (OsrEnvironmentChain)LIROP(OsrReturnValue)LIROP(OsrArgumentsObject )LIROP(RegExp)LIROP(RegExpMatcher)LIROP(RegExpSearcher)LIROP( RegExpSearcherLastLimit)LIROP(RegExpExecMatch)LIROP(RegExpExecTest )LIROP(RegExpHasCaptureGroups)LIROP(RegExpPrototypeOptimizable )LIROP(RegExpInstanceOptimizable)LIROP(GetFirstDollarIndex)LIROP (StringReplace)LIROP(BinaryValueCache)LIROP(BinaryBoolCache)LIROP (UnaryCache)LIROP(ModuleMetadata)LIROP(DynamicImport)LIROP(Lambda )LIROP(FunctionWithProto)LIROP(SetFunName)LIROP(KeepAliveObject )LIROP(DebugEnterGCUnsafeRegion)LIROP(DebugLeaveGCUnsafeRegion )LIROP(Slots)LIROP(Elements)LIROP(InitializedLength)LIROP(SetInitializedLength )LIROP(ArrayLength)LIROP(SetArrayLength)LIROP(FunctionLength) LIROP(FunctionName)LIROP(GetNextEntryForIterator)LIROP(ArrayBufferByteLength )LIROP(ArrayBufferViewLength)LIROP(ArrayBufferViewByteOffset) LIROP(ArrayBufferViewElements)LIROP(TypedArrayElementSize)LIROP (ResizableTypedArrayLength)LIROP(ResizableTypedArrayByteOffsetMaybeOutOfBounds )LIROP(ResizableDataViewByteLength)LIROP(GrowableSharedArrayBufferByteLength )LIROP(GuardResizableArrayBufferViewInBounds)LIROP(GuardResizableArrayBufferViewInBoundsOrDetached )LIROP(GuardHasAttachedArrayBuffer)LIROP(GuardNumberToIntPtrIndex )LIROP(BoundsCheck)LIROP(BoundsCheckRange)LIROP(BoundsCheckLower )LIROP(SpectreMaskIndex)LIROP(LoadElementV)LIROP(InArray)LIROP (GuardElementNotHole)LIROP(LoadElementHole)LIROP(StoreElementV )LIROP(StoreElementT)LIROP(StoreHoleValueElement)LIROP(StoreElementHoleV )LIROP(StoreElementHoleT)LIROP(ArrayPopShift)LIROP(ArrayPush) LIROP(ArraySlice)LIROP(ArgumentsSlice)LIROP(FrameArgumentsSlice )LIROP(InlineArgumentsSlice)LIROP(NormalizeSliceTerm)LIROP(ArrayJoin )LIROP(ObjectKeys)LIROP(ObjectKeysLength)LIROP(LoadUnboxedScalar )LIROP(LoadUnboxedInt64)LIROP(LoadDataViewElement)LIROP(LoadDataViewElement64 )LIROP(LoadTypedArrayElementHole)LIROP(LoadTypedArrayElementHoleBigInt )LIROP(StoreUnboxedScalar)LIROP(StoreUnboxedInt64)LIROP(StoreDataViewElement )LIROP(StoreDataViewElement64)LIROP(StoreTypedArrayElementHole )LIROP(StoreTypedArrayElementHoleInt64)LIROP(AtomicIsLockFree )LIROP(CompareExchangeTypedArrayElement)LIROP(AtomicExchangeTypedArrayElement )LIROP(AtomicTypedArrayElementBinop)LIROP(AtomicTypedArrayElementBinopForEffect )LIROP(AtomicLoad64)LIROP(AtomicStore64)LIROP(CompareExchangeTypedArrayElement64 )LIROP(AtomicExchangeTypedArrayElement64)LIROP(AtomicTypedArrayElementBinop64 )LIROP(AtomicTypedArrayElementBinopForEffect64)LIROP(EffectiveAddress )LIROP(ClampIToUint8)LIROP(ClampDToUint8)LIROP(ClampVToUint8) LIROP(LoadScriptedProxyHandler)LIROP(CheckScriptedProxyGetResult )LIROP(IdToStringOrSymbol)LIROP(LoadFixedSlotV)LIROP(LoadFixedSlotAndAtomize )LIROP(LoadFixedSlotT)LIROP(LoadFixedSlotAndUnbox)LIROP(LoadDynamicSlotAndUnbox )LIROP(LoadElementAndUnbox)LIROP(LoadFixedSlotUnboxAndAtomize )LIROP(LoadDynamicSlotUnboxAndAtomize)LIROP(AddAndStoreSlot)LIROP (AllocateAndStoreSlot)LIROP(AddSlotAndCallAddPropHook)LIROP(StoreFixedSlotV )LIROP(StoreFixedSlotT)LIROP(GetNameCache)LIROP(CallGetIntrinsicValue )LIROP(GetPropSuperCache)LIROP(GetPropertyCache)LIROP(BindNameCache )LIROP(CallBindVar)LIROP(LoadDynamicSlotV)LIROP(LoadDynamicSlotAndAtomize )LIROP(StoreDynamicSlotV)LIROP(StoreDynamicSlotT)LIROP(StringLength )LIROP(Floor)LIROP(FloorF)LIROP(Ceil)LIROP(CeilF)LIROP(Round) LIROP(RoundF)LIROP(Trunc)LIROP(TruncF)LIROP(NearbyInt)LIROP(NearbyIntF )LIROP(FunctionEnvironment)LIROP(HomeObject)LIROP(HomeObjectSuperBase )LIROP(NewLexicalEnvironmentObject)LIROP(NewClassBodyEnvironmentObject )LIROP(NewVarEnvironmentObject)LIROP(MegamorphicSetElement)LIROP (CallDeleteProperty)LIROP(CallDeleteElement)LIROP(ObjectToIterator )LIROP(ValueToIterator)LIROP(IteratorHasIndicesAndBranch)LIROP (LoadSlotByIteratorIndex)LIROP(StoreSlotByIteratorIndex)LIROP (SetPropertyCache)LIROP(GetIteratorCache)LIROP(OptimizeSpreadCallCache )LIROP(IteratorMore)LIROP(IsNoIterAndBranch)LIROP(IteratorEnd )LIROP(CloseIterCache)LIROP(OptimizeGetIteratorCache)LIROP(ArgumentsLength )LIROP(GetFrameArgument)LIROP(GetFrameArgumentHole)LIROP(Rest )LIROP(Int32ToIntPtr)LIROP(NonNegativeIntPtrToInt32)LIROP(IntPtrToDouble )LIROP(AdjustDataViewLength)LIROP(BooleanToInt64)LIROP(StringToInt64 )LIROP(ValueToInt64)LIROP(TruncateBigIntToInt64)LIROP(Int64ToBigInt )LIROP(Uint64ToBigInt)LIROP(Int64ToIntPtr)LIROP(IntPtrToInt64 )LIROP(PostWriteBarrierO)LIROP(PostWriteBarrierS)LIROP(PostWriteBarrierBI )LIROP(PostWriteBarrierV)LIROP(PostWriteElementBarrierO)LIROP (PostWriteElementBarrierS)LIROP(PostWriteElementBarrierBI)LIROP (PostWriteElementBarrierV)LIROP(AssertCanElidePostWriteBarrier )LIROP(GuardObjectIdentity)LIROP(GuardSpecificFunction)LIROP( GuardSpecificAtom)LIROP(GuardSpecificSymbol)LIROP(GuardSpecificInt32 )LIROP(GuardStringToIndex)LIROP(GuardStringToInt32)LIROP(GuardStringToDouble )LIROP(GuardShape)LIROP(GuardMultipleShapes)LIROP(GuardProto) LIROP(GuardNullProto)LIROP(GuardIsNativeObject)LIROP(GuardGlobalGeneration )LIROP(GuardFuse)LIROP(GuardIsProxy)LIROP(GuardIsNotProxy)LIROP (GuardIsNotDOMProxy)LIROP(ProxyGet)LIROP(ProxyGetByValue)LIROP (ProxyHasProp)LIROP(ProxySet)LIROP(ProxySetByValue)LIROP(CallSetArrayLength )LIROP(MegamorphicLoadSlot)LIROP(MegamorphicLoadSlotByValue)LIROP (MegamorphicLoadSlotPermissive)LIROP(MegamorphicLoadSlotByValuePermissive )LIROP(MegamorphicStoreSlot)LIROP(MegamorphicHasProp)LIROP(SmallObjectVariableKeyHasProp )LIROP(GuardIsNotArrayBufferMaybeShared)LIROP(GuardIsTypedArray )LIROP(GuardIsFixedLengthTypedArray)LIROP(GuardIsResizableTypedArray )LIROP(GuardHasProxyHandler)LIROP(GuardNoDenseElements)LIROP( InCache)LIROP(HasOwnCache)LIROP(CheckPrivateFieldCache)LIROP( NewPrivateName)LIROP(InstanceOfO)LIROP(InstanceOfV)LIROP(InstanceOfCache )LIROP(IsCallableO)LIROP(IsCallableV)LIROP(IsConstructor)LIROP (IsCrossRealmArrayConstructor)LIROP(IsArrayO)LIROP(IsArrayV)LIROP (IsTypedArray)LIROP(IsObject)LIROP(IsObjectAndBranch)LIROP(IsNullOrUndefined )LIROP(IsNullOrUndefinedAndBranch)LIROP(HasClass)LIROP(GuardToClass )LIROP(GuardToEitherClass)LIROP(GuardToFunction)LIROP(ObjectClassToString )LIROP(WasmSelect)LIROP(WasmSelectI64)LIROP(WasmCompareAndSelect )LIROP(WasmAddOffset)LIROP(WasmAddOffset64)LIROP(WasmBoundsCheck )LIROP(WasmBoundsCheck64)LIROP(WasmBoundsCheckRange32)LIROP(WasmExtendU32Index )LIROP(WasmWrapU32Index)LIROP(WasmClampTable64Address)LIROP(WasmAlignmentCheck )LIROP(WasmAlignmentCheck64)LIROP(WasmLoadInstance)LIROP(WasmLoadInstance64 )LIROP(WasmHeapReg)LIROP(WasmLoad)LIROP(WasmLoadI64)LIROP(WasmStore )LIROP(WasmStoreI64)LIROP(AsmJSLoadHeap)LIROP(AsmJSStoreHeap) LIROP(WasmCompareExchangeHeap)LIROP(WasmFence)LIROP(WasmAtomicExchangeHeap )LIROP(WasmAtomicBinopHeap)LIROP(WasmAtomicBinopHeapForEffect )LIROP(WasmLoadSlot)LIROP(WasmLoadElement)LIROP(WasmLoadSlotI64 )LIROP(WasmLoadElementI64)LIROP(WasmStoreSlot)LIROP(WasmStoreSlotI64 )LIROP(WasmStoreElement)LIROP(WasmStoreElementI64)LIROP(WasmStoreElementRef )LIROP(WasmLoadTableElement)LIROP(WasmDerivedPointer)LIROP(WasmDerivedIndexPointer )LIROP(WasmStoreRef)LIROP(WasmPostWriteBarrierImmediate)LIROP (WasmPostWriteBarrierIndex)LIROP(WasmParameter)LIROP(WasmParameterI64 )LIROP(WasmReturn)LIROP(WasmReturnI64)LIROP(WasmReturnVoid)LIROP (WasmStackArg)LIROP(WasmStackArgI64)LIROP(WasmNullConstant)LIROP (WasmCallIndirectAdjunctSafepoint)LIROP(WasmCall)LIROP(WasmCallLandingPrePad )LIROP(WasmRegisterResult)LIROP(WasmRegisterPairResult)LIROP( WasmStackResultArea)LIROP(WasmStackResult)LIROP(WasmStackResult64 )LIROP(AssertRangeI)LIROP(AssertRangeD)LIROP(AssertRangeF)LIROP (AssertRangeV)LIROP(AssertClass)LIROP(AssertShape)LIROP(GuardValue )LIROP(GuardNullOrUndefined)LIROP(GuardIsNotObject)LIROP(GuardFunctionFlags )LIROP(GuardFunctionIsNonBuiltinCtor)LIROP(GuardFunctionKind) LIROP(GuardFunctionScript)LIROP(IncrementWarmUpCounter)LIROP( LexicalCheck)LIROP(ThrowRuntimeLexicalError)LIROP(ThrowMsg)LIROP (GlobalDeclInstantiation)LIROP(MemoryBarrier)LIROP(Debugger)LIROP (NewTarget)LIROP(Random)LIROP(CheckReturn)LIROP(CheckIsObj)LIROP (CheckObjCoercible)LIROP(CheckClassHeritage)LIROP(CheckThis)LIROP (CheckThisReinit)LIROP(Generator)LIROP(AsyncResolve)LIROP(AsyncReject )LIROP(AsyncAwait)LIROP(CanSkipAwait)LIROP(MaybeExtractAwaitValue )LIROP(DebugCheckSelfHosted)LIROP(IsPackedArray)LIROP(GuardArrayIsPacked )LIROP(GetPrototypeOf)LIROP(ObjectWithProto)LIROP(ObjectStaticProto )LIROP(BuiltinObject)LIROP(SuperFunction)LIROP(InitHomeObject )LIROP(IsTypedArrayConstructor)LIROP(LoadValueTag)LIROP(GuardTagNotEqual )LIROP(LoadWrapperTarget)LIROP(GuardHasGetterSetter)LIROP(GuardIsExtensible )LIROP(GuardInt32IsNonNegative)LIROP(GuardInt32Range)LIROP(GuardIndexIsNotDenseElement )LIROP(GuardIndexIsValidUpdateOrAdd)LIROP(CallAddOrUpdateSparseElement )LIROP(CallGetSparseElement)LIROP(CallNativeGetElement)LIROP( CallNativeGetElementSuper)LIROP(CallObjectHasSparseElement)LIROP (BigIntAsIntN)LIROP(GuardNonGCThing)LIROP(ToHashableNonGCThing )LIROP(ToHashableString)LIROP(ToHashableValue)LIROP(HashNonGCThing )LIROP(HashString)LIROP(HashSymbol)LIROP(HashBigInt)LIROP(HashObject )LIROP(HashValue)LIROP(SetObjectHasNonBigInt)LIROP(SetObjectHasBigInt )LIROP(SetObjectHasValue)LIROP(SetObjectHasValueVMCall)LIROP( SetObjectSize)LIROP(MapObjectHasNonBigInt)LIROP(MapObjectHasBigInt )LIROP(MapObjectHasValue)LIROP(MapObjectHasValueVMCall)LIROP( MapObjectGetNonBigInt)LIROP(MapObjectGetBigInt)LIROP(MapObjectGetValueVMCall )LIROP(MapObjectSize)LIROP(BigIntAsUintN)LIROP(IonToWasmCall) LIROP(IonToWasmCallV)LIROP(IonToWasmCallI64)LIROP(WasmAnyRefFromJSValue )LIROP(WasmAnyRefFromJSObject)LIROP(WasmAnyRefFromJSString)LIROP (WasmAnyRefIsJSString)LIROP(WasmTrapIfAnyRefIsNotJSString)LIROP (WasmAnyRefJSStringLength)LIROP(WasmNewI31Ref)LIROP(WasmI31RefGet )LIROP(Simd128)LIROP(WasmTernarySimd128)LIROP(WasmBinarySimd128 )LIROP(WasmBinarySimd128WithConstant)LIROP(WasmVariableShiftSimd128 )LIROP(WasmConstantShiftSimd128)LIROP(WasmSignReplicationSimd128 )LIROP(WasmShuffleSimd128)LIROP(WasmPermuteSimd128)LIROP(WasmReplaceLaneSimd128 )LIROP(WasmReplaceInt64LaneSimd128)LIROP(WasmScalarToSimd128) LIROP(WasmInt64ToSimd128)LIROP(WasmUnarySimd128)LIROP(WasmReduceSimd128 )LIROP(WasmReduceAndBranchSimd128)LIROP(WasmReduceSimd128ToInt64 )LIROP(WasmLoadLaneSimd128)LIROP(WasmStoreLaneSimd128)LIROP(Unbox )LIROP(UnboxFloatingPoint)LIROP(WasmUint32ToDouble)LIROP(WasmUint32ToFloat32 )LIROP(DivI)LIROP(ModI)LIROP(DivPowTwoI)LIROP(ModPowTwoI)LIROP (TableSwitch)LIROP(TableSwitchV)LIROP(MulI)LIROP(DivOrModI64) LIROP(UDivOrModI64)LIROP(DivOrModConstantI)LIROP(UDivOrMod)LIROP (UDivOrModConstant)LIROP(WasmTruncateToInt64)LIROP(Int64ToFloatingPoint )LIROP(AddDisposableResource)LIROP(TakeDisposeCapability)LIROP (CreateSuppressedError)LIROP(AtomicPause)LIROP(SetObjectDelete )LIROP(SetObjectAdd)LIROP(MapObjectGetValue)LIROP(MapObjectDelete )LIROP(MapObjectSet)LIROP(DateFillLocalTimeSlots)LIROP(DateHoursFromSecondsIntoYear )LIROP(DateMinutesFromSecondsIntoYear)LIROP(DateSecondsFromSecondsIntoYear ) |
| 8094 | # undef LIROP |
| 8095 | #endif |
| 8096 | case LNode::Opcode::Invalid: |
| 8097 | default: |
| 8098 | MOZ_CRASH("Invalid LIR op")do { do { } while (false); MOZ_ReportCrash("" "Invalid LIR op" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8098); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid LIR op" ")"); do { *((volatile int*)__null) = 8098; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 8099 | } |
| 8100 | |
| 8101 | #ifdef DEBUG1 |
| 8102 | if (!counts) { |
| 8103 | emitDebugResultChecks(*iter); |
| 8104 | } |
| 8105 | #endif |
| 8106 | } |
| 8107 | if (masm.oom()) { |
| 8108 | return false; |
| 8109 | } |
| 8110 | } |
| 8111 | |
| 8112 | JitSpew(JitSpew_Codegen, "==== END CodeGenerator::generateBody ====\n"); |
| 8113 | return true; |
| 8114 | } |
| 8115 | |
| 8116 | // Out-of-line object allocation for LNewArray. |
| 8117 | class OutOfLineNewArray : public OutOfLineCodeBase<CodeGenerator> { |
| 8118 | LNewArray* lir_; |
| 8119 | |
| 8120 | public: |
| 8121 | explicit OutOfLineNewArray(LNewArray* lir) : lir_(lir) {} |
| 8122 | |
| 8123 | void accept(CodeGenerator* codegen) override { |
| 8124 | codegen->visitOutOfLineNewArray(this); |
| 8125 | } |
| 8126 | |
| 8127 | LNewArray* lir() const { return lir_; } |
| 8128 | }; |
| 8129 | |
| 8130 | void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) { |
| 8131 | Register objReg = ToRegister(lir->output()); |
| 8132 | |
| 8133 | MOZ_ASSERT(!lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8133); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { *((volatile int*)__null) = 8133; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8134 | saveLive(lir); |
| 8135 | |
| 8136 | JSObject* templateObject = lir->mir()->templateObject(); |
| 8137 | |
| 8138 | if (templateObject) { |
| 8139 | pushArg(ImmGCPtr(templateObject->shape())); |
| 8140 | pushArg(Imm32(lir->mir()->length())); |
| 8141 | |
| 8142 | using Fn = ArrayObject* (*)(JSContext*, uint32_t, Handle<Shape*>); |
| 8143 | callVM<Fn, NewArrayWithShape>(lir); |
| 8144 | } else { |
| 8145 | pushArg(Imm32(GenericObject)); |
| 8146 | pushArg(Imm32(lir->mir()->length())); |
| 8147 | |
| 8148 | using Fn = ArrayObject* (*)(JSContext*, uint32_t, NewObjectKind); |
| 8149 | callVM<Fn, NewArrayOperation>(lir); |
| 8150 | } |
| 8151 | |
| 8152 | masm.storeCallPointerResult(objReg); |
| 8153 | |
| 8154 | MOZ_ASSERT(!lir->safepoint()->liveRegs().has(objReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->safepoint()->liveRegs().has(objReg))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(!lir->safepoint()->liveRegs().has(objReg)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->safepoint()->liveRegs().has(objReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8154); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)" ")"); do { *((volatile int*)__null) = 8154; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8155 | restoreLive(lir); |
| 8156 | } |
| 8157 | |
| 8158 | void CodeGenerator::visitAtan2D(LAtan2D* lir) { |
| 8159 | FloatRegister y = ToFloatRegister(lir->y()); |
| 8160 | FloatRegister x = ToFloatRegister(lir->x()); |
| 8161 | |
| 8162 | using Fn = double (*)(double x, double y); |
| 8163 | masm.setupAlignedABICall(); |
| 8164 | masm.passABIArg(y, ABIType::Float64); |
| 8165 | masm.passABIArg(x, ABIType::Float64); |
| 8166 | masm.callWithABI<Fn, ecmaAtan2>(ABIType::Float64); |
| 8167 | |
| 8168 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8168); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 8168; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8169 | } |
| 8170 | |
| 8171 | void CodeGenerator::visitHypot(LHypot* lir) { |
| 8172 | uint32_t numArgs = lir->numArgs(); |
| 8173 | masm.setupAlignedABICall(); |
| 8174 | |
| 8175 | for (uint32_t i = 0; i < numArgs; ++i) { |
| 8176 | masm.passABIArg(ToFloatRegister(lir->getOperand(i)), ABIType::Float64); |
| 8177 | } |
| 8178 | |
| 8179 | switch (numArgs) { |
| 8180 | case 2: { |
| 8181 | using Fn = double (*)(double x, double y); |
| 8182 | masm.callWithABI<Fn, ecmaHypot>(ABIType::Float64); |
| 8183 | break; |
| 8184 | } |
| 8185 | case 3: { |
| 8186 | using Fn = double (*)(double x, double y, double z); |
| 8187 | masm.callWithABI<Fn, hypot3>(ABIType::Float64); |
| 8188 | break; |
| 8189 | } |
| 8190 | case 4: { |
| 8191 | using Fn = double (*)(double x, double y, double z, double w); |
| 8192 | masm.callWithABI<Fn, hypot4>(ABIType::Float64); |
| 8193 | break; |
| 8194 | } |
| 8195 | default: |
| 8196 | MOZ_CRASH("Unexpected number of arguments to hypot function.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected number of arguments to hypot function." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8196); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected number of arguments to hypot function." ")"); do { *((volatile int*)__null) = 8196; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 8197 | } |
| 8198 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 8198; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8199 | } |
| 8200 | |
| 8201 | void CodeGenerator::visitNewArray(LNewArray* lir) { |
| 8202 | Register objReg = ToRegister(lir->output()); |
| 8203 | Register tempReg = ToRegister(lir->temp()); |
| 8204 | DebugOnly<uint32_t> length = lir->mir()->length(); |
| 8205 | |
| 8206 | MOZ_ASSERT(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8206); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT" ")"); do { *((volatile int*)__null) = 8206; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8207 | |
| 8208 | if (lir->mir()->isVMCall()) { |
| 8209 | visitNewArrayCallVM(lir); |
| 8210 | return; |
| 8211 | } |
| 8212 | |
| 8213 | OutOfLineNewArray* ool = new (alloc()) OutOfLineNewArray(lir); |
| 8214 | addOutOfLineCode(ool, lir->mir()); |
| 8215 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 8216 | #ifdef DEBUG1 |
| 8217 | size_t numInlineElements = gc::GetGCKindSlots(templateObject.getAllocKind()) - |
| 8218 | ObjectElements::VALUES_PER_HEADER; |
| 8219 | MOZ_ASSERT(length <= numInlineElements,do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= numInlineElements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length <= numInlineElements ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "length <= numInlineElements" " (" "Inline allocation only supports inline elements" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8220); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements" ")") ; do { *((volatile int*)__null) = 8220; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 8220 | "Inline allocation only supports inline elements")do { static_assert( mozilla::detail::AssertionConditionType< decltype(length <= numInlineElements)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length <= numInlineElements ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "length <= numInlineElements" " (" "Inline allocation only supports inline elements" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8220); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements" ")") ; do { *((volatile int*)__null) = 8220; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8221 | #endif |
| 8222 | masm.createGCObject(objReg, tempReg, templateObject, |
| 8223 | lir->mir()->initialHeap(), ool->entry()); |
| 8224 | |
| 8225 | masm.bind(ool->rejoin()); |
| 8226 | } |
| 8227 | |
| 8228 | void CodeGenerator::visitOutOfLineNewArray(OutOfLineNewArray* ool) { |
| 8229 | visitNewArrayCallVM(ool->lir()); |
| 8230 | masm.jump(ool->rejoin()); |
| 8231 | } |
| 8232 | |
| 8233 | void CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) { |
| 8234 | Register lengthReg = ToRegister(lir->length()); |
| 8235 | Register objReg = ToRegister(lir->output()); |
| 8236 | Register tempReg = ToRegister(lir->temp0()); |
| 8237 | |
| 8238 | JSObject* templateObject = lir->mir()->templateObject(); |
| 8239 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
| 8240 | |
| 8241 | using Fn = ArrayObject* (*)(JSContext*, Handle<ArrayObject*>, int32_t length, |
| 8242 | gc::AllocSite*); |
| 8243 | OutOfLineCode* ool = oolCallVM<Fn, ArrayConstructorOneArg>( |
| 8244 | lir, ArgList(ImmGCPtr(templateObject), lengthReg, ImmPtr(nullptr)), |
| 8245 | StoreRegisterTo(objReg)); |
| 8246 | |
| 8247 | bool canInline = true; |
| 8248 | size_t inlineLength = 0; |
| 8249 | if (templateObject->as<ArrayObject>().hasFixedElements()) { |
| 8250 | size_t numSlots = |
| 8251 | gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); |
| 8252 | inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; |
| 8253 | } else { |
| 8254 | canInline = false; |
| 8255 | } |
| 8256 | |
| 8257 | if (canInline) { |
| 8258 | // Try to do the allocation inline if the template object is big enough |
| 8259 | // for the length in lengthReg. If the length is bigger we could still |
| 8260 | // use the template object and not allocate the elements, but it's more |
| 8261 | // efficient to do a single big allocation than (repeatedly) reallocating |
| 8262 | // the array later on when filling it. |
| 8263 | masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength), |
| 8264 | ool->entry()); |
| 8265 | |
| 8266 | TemplateObject templateObj(templateObject); |
| 8267 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, |
| 8268 | ool->entry()); |
| 8269 | |
| 8270 | size_t lengthOffset = NativeObject::offsetOfFixedElements() + |
| 8271 | ObjectElements::offsetOfLength(); |
| 8272 | masm.store32(lengthReg, Address(objReg, lengthOffset)); |
| 8273 | } else { |
| 8274 | masm.jump(ool->entry()); |
| 8275 | } |
| 8276 | |
| 8277 | masm.bind(ool->rejoin()); |
| 8278 | } |
| 8279 | |
| 8280 | void CodeGenerator::visitNewIterator(LNewIterator* lir) { |
| 8281 | Register objReg = ToRegister(lir->output()); |
| 8282 | Register tempReg = ToRegister(lir->temp0()); |
| 8283 | |
| 8284 | OutOfLineCode* ool; |
| 8285 | switch (lir->mir()->type()) { |
| 8286 | case MNewIterator::ArrayIterator: { |
| 8287 | using Fn = ArrayIteratorObject* (*)(JSContext*); |
| 8288 | ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(), |
| 8289 | StoreRegisterTo(objReg)); |
| 8290 | break; |
| 8291 | } |
| 8292 | case MNewIterator::StringIterator: { |
| 8293 | using Fn = StringIteratorObject* (*)(JSContext*); |
| 8294 | ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(), |
| 8295 | StoreRegisterTo(objReg)); |
| 8296 | break; |
| 8297 | } |
| 8298 | case MNewIterator::RegExpStringIterator: { |
| 8299 | using Fn = RegExpStringIteratorObject* (*)(JSContext*); |
| 8300 | ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(), |
| 8301 | StoreRegisterTo(objReg)); |
| 8302 | break; |
| 8303 | } |
| 8304 | default: |
| 8305 | MOZ_CRASH("unexpected iterator type")do { do { } while (false); MOZ_ReportCrash("" "unexpected iterator type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8305); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected iterator type" ")"); do { *((volatile int*)__null) = 8305; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 8306 | } |
| 8307 | |
| 8308 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 8309 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
| 8310 | ool->entry()); |
| 8311 | |
| 8312 | masm.bind(ool->rejoin()); |
| 8313 | } |
| 8314 | |
| 8315 | void CodeGenerator::visitNewTypedArray(LNewTypedArray* lir) { |
| 8316 | Register objReg = ToRegister(lir->output()); |
| 8317 | Register tempReg = ToRegister(lir->temp0()); |
| 8318 | Register lengthReg = ToRegister(lir->temp1()); |
| 8319 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
| 8320 | |
| 8321 | JSObject* templateObject = lir->mir()->templateObject(); |
| 8322 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
| 8323 | |
| 8324 | auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>(); |
| 8325 | |
| 8326 | size_t n = ttemplate->length(); |
| 8327 | MOZ_ASSERT(n <= INT32_MAX,do { static_assert( mozilla::detail::AssertionConditionType< decltype(n <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)" " (" "Template objects are only created for int32 lengths" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")" ); do { *((volatile int*)__null) = 8328; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 8328 | "Template objects are only created for int32 lengths")do { static_assert( mozilla::detail::AssertionConditionType< decltype(n <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(n <= (2147483647)))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("n <= (2147483647)" " (" "Template objects are only created for int32 lengths" ")" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")" ); do { *((volatile int*)__null) = 8328; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8329 | |
| 8330 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length); |
| 8331 | OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
| 8332 | lir, ArgList(ImmGCPtr(templateObject), Imm32(n)), |
| 8333 | StoreRegisterTo(objReg)); |
| 8334 | |
| 8335 | TemplateObject templateObj(templateObject); |
| 8336 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry()); |
| 8337 | |
| 8338 | masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(), |
| 8339 | ttemplate, MacroAssembler::TypedArrayLength::Fixed); |
| 8340 | |
| 8341 | masm.bind(ool->rejoin()); |
| 8342 | } |
| 8343 | |
| 8344 | void CodeGenerator::visitNewTypedArrayDynamicLength( |
| 8345 | LNewTypedArrayDynamicLength* lir) { |
| 8346 | Register lengthReg = ToRegister(lir->length()); |
| 8347 | Register objReg = ToRegister(lir->output()); |
| 8348 | Register tempReg = ToRegister(lir->temp0()); |
| 8349 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
| 8350 | |
| 8351 | JSObject* templateObject = lir->mir()->templateObject(); |
| 8352 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
| 8353 | |
| 8354 | auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>(); |
| 8355 | |
| 8356 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length); |
| 8357 | OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
| 8358 | lir, ArgList(ImmGCPtr(templateObject), lengthReg), |
| 8359 | StoreRegisterTo(objReg)); |
| 8360 | |
| 8361 | // Volatile |lengthReg| is saved across the ABI call in |initTypedArraySlots|. |
| 8362 | MOZ_ASSERT_IF(lengthReg.volatile_(), liveRegs.has(lengthReg))do { if (lengthReg.volatile_()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(liveRegs.has(lengthReg ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(liveRegs.has(lengthReg)))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("liveRegs.has(lengthReg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "liveRegs.has(lengthReg)" ")"); do { *((volatile int*)__null) = 8362; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 8363 | |
| 8364 | TemplateObject templateObj(templateObject); |
| 8365 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry()); |
| 8366 | |
| 8367 | masm.initTypedArraySlots(objReg, tempReg, lengthReg, liveRegs, ool->entry(), |
| 8368 | ttemplate, |
| 8369 | MacroAssembler::TypedArrayLength::Dynamic); |
| 8370 | |
| 8371 | masm.bind(ool->rejoin()); |
| 8372 | } |
| 8373 | |
| 8374 | void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) { |
| 8375 | pushArg(ToRegister(lir->array())); |
| 8376 | pushArg(ImmGCPtr(lir->mir()->templateObject())); |
| 8377 | |
| 8378 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject); |
| 8379 | callVM<Fn, js::NewTypedArrayWithTemplateAndArray>(lir); |
| 8380 | } |
| 8381 | |
| 8382 | void CodeGenerator::visitNewTypedArrayFromArrayBuffer( |
| 8383 | LNewTypedArrayFromArrayBuffer* lir) { |
| 8384 | pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::LengthIndex)); |
| 8385 | pushArg(ToValue(lir, LNewTypedArrayFromArrayBuffer::ByteOffsetIndex)); |
| 8386 | pushArg(ToRegister(lir->arrayBuffer())); |
| 8387 | pushArg(ImmGCPtr(lir->mir()->templateObject())); |
| 8388 | |
| 8389 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject, |
| 8390 | HandleValue, HandleValue); |
| 8391 | callVM<Fn, js::NewTypedArrayWithTemplateAndBuffer>(lir); |
| 8392 | } |
| 8393 | |
| 8394 | void CodeGenerator::visitBindFunction(LBindFunction* lir) { |
| 8395 | Register target = ToRegister(lir->target()); |
| 8396 | Register temp1 = ToRegister(lir->temp0()); |
| 8397 | Register temp2 = ToRegister(lir->temp1()); |
| 8398 | |
| 8399 | // Try to allocate a new BoundFunctionObject we can pass to the VM function. |
| 8400 | // If this fails, we set temp1 to nullptr so we do the allocation in C++. |
| 8401 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 8402 | Label allocOk, allocFailed; |
| 8403 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
| 8404 | &allocFailed); |
| 8405 | masm.jump(&allocOk); |
| 8406 | |
| 8407 | masm.bind(&allocFailed); |
| 8408 | masm.movePtr(ImmWord(0), temp1); |
| 8409 | |
| 8410 | masm.bind(&allocOk); |
| 8411 | |
| 8412 | // Set temp2 to the address of the first argument on the stack. |
| 8413 | // Note that the Value slots used for arguments are currently aligned for a |
| 8414 | // JIT call, even though that's not strictly necessary for calling into C++. |
| 8415 | uint32_t argc = lir->mir()->numStackArgs(); |
| 8416 | if (JitStackValueAlignment > 1) { |
| 8417 | argc = AlignBytes(argc, JitStackValueAlignment); |
| 8418 | } |
| 8419 | uint32_t unusedStack = UnusedStackBytesForCall(argc); |
| 8420 | masm.computeEffectiveAddress(Address(masm.getStackPointer(), unusedStack), |
| 8421 | temp2); |
| 8422 | |
| 8423 | pushArg(temp1); |
| 8424 | pushArg(Imm32(lir->mir()->numStackArgs())); |
| 8425 | pushArg(temp2); |
| 8426 | pushArg(target); |
| 8427 | |
| 8428 | using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*, |
| 8429 | uint32_t, Handle<BoundFunctionObject*>); |
| 8430 | callVM<Fn, js::BoundFunctionObject::functionBindImpl>(lir); |
| 8431 | } |
| 8432 | |
| 8433 | void CodeGenerator::visitNewBoundFunction(LNewBoundFunction* lir) { |
| 8434 | Register output = ToRegister(lir->output()); |
| 8435 | Register temp = ToRegister(lir->temp0()); |
| 8436 | |
| 8437 | JSObject* templateObj = lir->mir()->templateObj(); |
| 8438 | |
| 8439 | using Fn = BoundFunctionObject* (*)(JSContext*, Handle<BoundFunctionObject*>); |
| 8440 | OutOfLineCode* ool = oolCallVM<Fn, BoundFunctionObject::createWithTemplate>( |
| 8441 | lir, ArgList(ImmGCPtr(templateObj)), StoreRegisterTo(output)); |
| 8442 | |
| 8443 | TemplateObject templateObject(templateObj); |
| 8444 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 8445 | ool->entry()); |
| 8446 | |
| 8447 | masm.bind(ool->rejoin()); |
| 8448 | } |
| 8449 | |
| 8450 | // Out-of-line object allocation for JSOp::NewObject. |
| 8451 | class OutOfLineNewObject : public OutOfLineCodeBase<CodeGenerator> { |
| 8452 | LNewObject* lir_; |
| 8453 | |
| 8454 | public: |
| 8455 | explicit OutOfLineNewObject(LNewObject* lir) : lir_(lir) {} |
| 8456 | |
| 8457 | void accept(CodeGenerator* codegen) override { |
| 8458 | codegen->visitOutOfLineNewObject(this); |
| 8459 | } |
| 8460 | |
| 8461 | LNewObject* lir() const { return lir_; } |
| 8462 | }; |
| 8463 | |
| 8464 | void CodeGenerator::visitNewObjectVMCall(LNewObject* lir) { |
| 8465 | Register objReg = ToRegister(lir->output()); |
| 8466 | |
| 8467 | MOZ_ASSERT(!lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8467); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { *((volatile int*)__null) = 8467; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8468 | saveLive(lir); |
| 8469 | |
| 8470 | JSObject* templateObject = lir->mir()->templateObject(); |
| 8471 | |
| 8472 | // If we're making a new object with a class prototype (that is, an object |
| 8473 | // that derives its class from its prototype instead of being |
| 8474 | // PlainObject::class_'d) from self-hosted code, we need a different init |
| 8475 | // function. |
| 8476 | switch (lir->mir()->mode()) { |
| 8477 | case MNewObject::ObjectLiteral: { |
| 8478 | MOZ_ASSERT(!templateObject)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!templateObject)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!templateObject))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!templateObject" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8478); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateObject" ")"); do { *((volatile int*)__null) = 8478; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8479 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
| 8480 | pushArg(ImmGCPtr(lir->mir()->block()->info().script())); |
| 8481 | |
| 8482 | using Fn = JSObject* (*)(JSContext*, HandleScript, const jsbytecode* pc); |
| 8483 | callVM<Fn, NewObjectOperation>(lir); |
| 8484 | break; |
| 8485 | } |
| 8486 | case MNewObject::ObjectCreate: { |
| 8487 | pushArg(ImmGCPtr(templateObject)); |
| 8488 | |
| 8489 | using Fn = PlainObject* (*)(JSContext*, Handle<PlainObject*>); |
| 8490 | callVM<Fn, ObjectCreateWithTemplate>(lir); |
| 8491 | break; |
| 8492 | } |
| 8493 | } |
| 8494 | |
| 8495 | masm.storeCallPointerResult(objReg); |
| 8496 | |
| 8497 | MOZ_ASSERT(!lir->safepoint()->liveRegs().has(objReg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!lir->safepoint()->liveRegs().has(objReg))> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(!lir->safepoint()->liveRegs().has(objReg)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!lir->safepoint()->liveRegs().has(objReg)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8497); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)" ")"); do { *((volatile int*)__null) = 8497; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8498 | restoreLive(lir); |
| 8499 | } |
| 8500 | |
| 8501 | static bool ShouldInitFixedSlots(MIRGenerator* gen, LNewPlainObject* lir, |
| 8502 | const Shape* shape, uint32_t nfixed) { |
| 8503 | // Look for StoreFixedSlot instructions following an object allocation |
| 8504 | // that write to this object before a GC is triggered or this object is |
| 8505 | // passed to a VM call. If all fixed slots will be initialized, the |
| 8506 | // allocation code doesn't need to set the slots to |undefined|. |
| 8507 | |
| 8508 | if (nfixed == 0) { |
| 8509 | return false; |
| 8510 | } |
| 8511 | |
| 8512 | #ifdef DEBUG1 |
| 8513 | // The bailAfter testing function can trigger a bailout between allocating the |
| 8514 | // object and initializing the slots. |
| 8515 | if (gen->options.ionBailAfterEnabled()) { |
| 8516 | return true; |
| 8517 | } |
| 8518 | #endif |
| 8519 | |
| 8520 | // Keep track of the fixed slots that are initialized. initializedSlots is |
| 8521 | // a bit mask with a bit for each slot. |
| 8522 | MOZ_ASSERT(nfixed <= NativeObject::MAX_FIXED_SLOTS)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nfixed <= NativeObject::MAX_FIXED_SLOTS)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nfixed <= NativeObject::MAX_FIXED_SLOTS))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nfixed <= NativeObject::MAX_FIXED_SLOTS" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8522); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nfixed <= NativeObject::MAX_FIXED_SLOTS" ")"); do { *((volatile int*)__null) = 8522; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8523 | static_assert(NativeObject::MAX_FIXED_SLOTS <= 32, |
| 8524 | "Slot bits must fit in 32 bits"); |
| 8525 | uint32_t initializedSlots = 0; |
| 8526 | uint32_t numInitialized = 0; |
| 8527 | |
| 8528 | MInstruction* allocMir = lir->mir(); |
| 8529 | MBasicBlock* block = allocMir->block(); |
| 8530 | |
| 8531 | // Skip the allocation instruction. |
| 8532 | MInstructionIterator iter = block->begin(allocMir); |
| 8533 | MOZ_ASSERT(*iter == allocMir)do { static_assert( mozilla::detail::AssertionConditionType< decltype(*iter == allocMir)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*iter == allocMir))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*iter == allocMir" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "*iter == allocMir" ")"); do { *((volatile int*)__null) = 8533; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8534 | iter++; |
| 8535 | |
| 8536 | // Handle the leading shape guard, if present. |
| 8537 | for (; iter != block->end(); iter++) { |
| 8538 | if (iter->isConstant()) { |
| 8539 | // This instruction won't trigger a GC or read object slots. |
| 8540 | continue; |
| 8541 | } |
| 8542 | if (iter->isGuardShape()) { |
| 8543 | auto* guard = iter->toGuardShape(); |
| 8544 | if (guard->object() != allocMir || guard->shape() != shape) { |
| 8545 | return true; |
| 8546 | } |
| 8547 | allocMir = guard; |
| 8548 | iter++; |
| 8549 | } |
| 8550 | break; |
| 8551 | } |
| 8552 | |
| 8553 | for (; iter != block->end(); iter++) { |
| 8554 | if (iter->isConstant() || iter->isPostWriteBarrier()) { |
| 8555 | // These instructions won't trigger a GC or read object slots. |
| 8556 | continue; |
| 8557 | } |
| 8558 | |
| 8559 | if (iter->isStoreFixedSlot()) { |
| 8560 | MStoreFixedSlot* store = iter->toStoreFixedSlot(); |
| 8561 | if (store->object() != allocMir) { |
| 8562 | return true; |
| 8563 | } |
| 8564 | |
| 8565 | // We may not initialize this object slot on allocation, so the |
| 8566 | // pre-barrier could read uninitialized memory. Simply disable |
| 8567 | // the barrier for this store: the object was just initialized |
| 8568 | // so the barrier is not necessary. |
| 8569 | store->setNeedsBarrier(false); |
| 8570 | |
| 8571 | uint32_t slot = store->slot(); |
| 8572 | MOZ_ASSERT(slot < nfixed)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot < nfixed)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot < nfixed))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot < nfixed" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot < nfixed" ")"); do { *((volatile int*)__null) = 8572; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8573 | if ((initializedSlots & (1 << slot)) == 0) { |
| 8574 | numInitialized++; |
| 8575 | initializedSlots |= (1 << slot); |
| 8576 | |
| 8577 | if (numInitialized == nfixed) { |
| 8578 | // All fixed slots will be initialized. |
| 8579 | MOZ_ASSERT(mozilla::CountPopulation32(initializedSlots) == nfixed)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::CountPopulation32(initializedSlots) == nfixed )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mozilla::CountPopulation32(initializedSlots) == nfixed ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mozilla::CountPopulation32(initializedSlots) == nfixed", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::CountPopulation32(initializedSlots) == nfixed" ")"); do { *((volatile int*)__null) = 8579; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8580 | return false; |
| 8581 | } |
| 8582 | } |
| 8583 | continue; |
| 8584 | } |
| 8585 | |
| 8586 | // Unhandled instruction, assume it bails or reads object slots. |
| 8587 | return true; |
| 8588 | } |
| 8589 | |
| 8590 | MOZ_CRASH("Shouldn't get here")do { do { } while (false); MOZ_ReportCrash("" "Shouldn't get here" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8590); AnnotateMozCrashReason("MOZ_CRASH(" "Shouldn't get here" ")"); do { *((volatile int*)__null) = 8590; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 8591 | } |
| 8592 | |
| 8593 | void CodeGenerator::visitNewObject(LNewObject* lir) { |
| 8594 | Register objReg = ToRegister(lir->output()); |
| 8595 | Register tempReg = ToRegister(lir->temp()); |
| 8596 | |
| 8597 | if (lir->mir()->isVMCall()) { |
| 8598 | visitNewObjectVMCall(lir); |
| 8599 | return; |
| 8600 | } |
| 8601 | |
| 8602 | OutOfLineNewObject* ool = new (alloc()) OutOfLineNewObject(lir); |
| 8603 | addOutOfLineCode(ool, lir->mir()); |
| 8604 | |
| 8605 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 8606 | |
| 8607 | masm.createGCObject(objReg, tempReg, templateObject, |
| 8608 | lir->mir()->initialHeap(), ool->entry()); |
| 8609 | |
| 8610 | masm.bind(ool->rejoin()); |
| 8611 | } |
| 8612 | |
| 8613 | void CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject* ool) { |
| 8614 | visitNewObjectVMCall(ool->lir()); |
| 8615 | masm.jump(ool->rejoin()); |
| 8616 | } |
| 8617 | |
| 8618 | void CodeGenerator::visitNewPlainObject(LNewPlainObject* lir) { |
| 8619 | Register objReg = ToRegister(lir->output()); |
| 8620 | Register temp0Reg = ToRegister(lir->temp0()); |
| 8621 | Register temp1Reg = ToRegister(lir->temp1()); |
| 8622 | Register shapeReg = ToRegister(lir->temp2()); |
| 8623 | |
| 8624 | auto* mir = lir->mir(); |
| 8625 | const Shape* shape = mir->shape(); |
| 8626 | gc::Heap initialHeap = mir->initialHeap(); |
| 8627 | gc::AllocKind allocKind = mir->allocKind(); |
| 8628 | |
| 8629 | using Fn = |
| 8630 | JSObject* (*)(JSContext*, Handle<SharedShape*>, gc::AllocKind, gc::Heap); |
| 8631 | OutOfLineCode* ool = oolCallVM<Fn, NewPlainObjectOptimizedFallback>( |
| 8632 | lir, |
| 8633 | ArgList(ImmGCPtr(shape), Imm32(int32_t(allocKind)), |
| 8634 | Imm32(int32_t(initialHeap))), |
| 8635 | StoreRegisterTo(objReg)); |
| 8636 | |
| 8637 | bool initContents = |
| 8638 | ShouldInitFixedSlots(gen, lir, shape, mir->numFixedSlots()); |
| 8639 | |
| 8640 | masm.movePtr(ImmGCPtr(shape), shapeReg); |
| 8641 | masm.createPlainGCObject( |
| 8642 | objReg, shapeReg, temp0Reg, temp1Reg, mir->numFixedSlots(), |
| 8643 | mir->numDynamicSlots(), allocKind, initialHeap, ool->entry(), |
| 8644 | AllocSiteInput(gc::CatchAllAllocSite::Optimized), initContents); |
| 8645 | |
| 8646 | #ifdef DEBUG1 |
| 8647 | // ShouldInitFixedSlots expects that the leading GuardShape will never fail, |
| 8648 | // so ensure the newly created object has the correct shape. Should the guard |
| 8649 | // ever fail, we may end up with uninitialized fixed slots, which can confuse |
| 8650 | // the GC. |
| 8651 | Label ok; |
| 8652 | masm.branchTestObjShape(Assembler::Equal, objReg, shape, temp0Reg, objReg, |
| 8653 | &ok); |
| 8654 | masm.assumeUnreachable("Newly created object has the correct shape"); |
| 8655 | masm.bind(&ok); |
| 8656 | #endif |
| 8657 | |
| 8658 | masm.bind(ool->rejoin()); |
| 8659 | } |
| 8660 | |
| 8661 | void CodeGenerator::visitNewArrayObject(LNewArrayObject* lir) { |
| 8662 | Register objReg = ToRegister(lir->output()); |
| 8663 | Register temp0Reg = ToRegister(lir->temp0()); |
| 8664 | Register shapeReg = ToRegister(lir->temp1()); |
| 8665 | |
| 8666 | auto* mir = lir->mir(); |
| 8667 | uint32_t arrayLength = mir->length(); |
| 8668 | |
| 8669 | gc::AllocKind allocKind = GuessArrayGCKind(arrayLength); |
| 8670 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(allocKind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8670); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 8670; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8671 | allocKind = ForegroundToBackgroundAllocKind(allocKind); |
| 8672 | |
| 8673 | uint32_t slotCount = GetGCKindSlots(allocKind); |
| 8674 | MOZ_ASSERT(slotCount >= ObjectElements::VALUES_PER_HEADER)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotCount >= ObjectElements::VALUES_PER_HEADER)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(slotCount >= ObjectElements::VALUES_PER_HEADER))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("slotCount >= ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotCount >= ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 8674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8675 | uint32_t arrayCapacity = slotCount - ObjectElements::VALUES_PER_HEADER; |
| 8676 | |
| 8677 | const Shape* shape = mir->shape(); |
| 8678 | |
| 8679 | NewObjectKind objectKind = |
| 8680 | mir->initialHeap() == gc::Heap::Tenured ? TenuredObject : GenericObject; |
| 8681 | |
| 8682 | using Fn = |
| 8683 | ArrayObject* (*)(JSContext*, uint32_t, gc::AllocKind, NewObjectKind); |
| 8684 | OutOfLineCode* ool = oolCallVM<Fn, NewArrayObjectOptimizedFallback>( |
| 8685 | lir, |
| 8686 | ArgList(Imm32(arrayLength), Imm32(int32_t(allocKind)), Imm32(objectKind)), |
| 8687 | StoreRegisterTo(objReg)); |
| 8688 | |
| 8689 | masm.movePtr(ImmPtr(shape), shapeReg); |
| 8690 | masm.createArrayWithFixedElements( |
| 8691 | objReg, shapeReg, temp0Reg, InvalidReg, arrayLength, arrayCapacity, 0, 0, |
| 8692 | allocKind, mir->initialHeap(), ool->entry(), |
| 8693 | AllocSiteInput(gc::CatchAllAllocSite::Optimized)); |
| 8694 | masm.bind(ool->rejoin()); |
| 8695 | } |
| 8696 | |
| 8697 | void CodeGenerator::visitNewNamedLambdaObject(LNewNamedLambdaObject* lir) { |
| 8698 | Register objReg = ToRegister(lir->output()); |
| 8699 | Register tempReg = ToRegister(lir->temp0()); |
| 8700 | const CompileInfo& info = lir->mir()->block()->info(); |
| 8701 | |
| 8702 | using Fn = js::NamedLambdaObject* (*)(JSContext*, HandleFunction); |
| 8703 | OutOfLineCode* ool = oolCallVM<Fn, NamedLambdaObject::createWithoutEnclosing>( |
| 8704 | lir, ArgList(info.funMaybeLazy()), StoreRegisterTo(objReg)); |
| 8705 | |
| 8706 | TemplateObject templateObject(lir->mir()->templateObj()); |
| 8707 | |
| 8708 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
| 8709 | ool->entry()); |
| 8710 | |
| 8711 | masm.bind(ool->rejoin()); |
| 8712 | } |
| 8713 | |
| 8714 | void CodeGenerator::visitNewCallObject(LNewCallObject* lir) { |
| 8715 | Register objReg = ToRegister(lir->output()); |
| 8716 | Register tempReg = ToRegister(lir->temp0()); |
| 8717 | |
| 8718 | CallObject* templateObj = lir->mir()->templateObject(); |
| 8719 | |
| 8720 | using Fn = CallObject* (*)(JSContext*, Handle<SharedShape*>); |
| 8721 | OutOfLineCode* ool = oolCallVM<Fn, CallObject::createWithShape>( |
| 8722 | lir, ArgList(ImmGCPtr(templateObj->sharedShape())), |
| 8723 | StoreRegisterTo(objReg)); |
| 8724 | |
| 8725 | // Inline call object creation, using the OOL path only for tricky cases. |
| 8726 | TemplateObject templateObject(templateObj); |
| 8727 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
| 8728 | ool->entry()); |
| 8729 | |
| 8730 | masm.bind(ool->rejoin()); |
| 8731 | } |
| 8732 | |
| 8733 | void CodeGenerator::visitNewMapObject(LNewMapObject* lir) { |
| 8734 | Register output = ToRegister(lir->output()); |
| 8735 | Register temp = ToRegister(lir->temp0()); |
| 8736 | |
| 8737 | // Note: pass nullptr for |proto| to use |Map.prototype|. |
| 8738 | using Fn = MapObject* (*)(JSContext*, HandleObject); |
| 8739 | auto* ool = oolCallVM<Fn, MapObject::create>(lir, ArgList(ImmPtr(nullptr)), |
| 8740 | StoreRegisterTo(output)); |
| 8741 | |
| 8742 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 8743 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 8744 | ool->entry()); |
| 8745 | masm.bind(ool->rejoin()); |
| 8746 | } |
| 8747 | |
| 8748 | void CodeGenerator::visitNewSetObject(LNewSetObject* lir) { |
| 8749 | Register output = ToRegister(lir->output()); |
| 8750 | Register temp = ToRegister(lir->temp0()); |
| 8751 | |
| 8752 | // Note: pass nullptr for |proto| to use |Set.prototype|. |
| 8753 | using Fn = SetObject* (*)(JSContext*, HandleObject); |
| 8754 | auto* ool = oolCallVM<Fn, SetObject::create>(lir, ArgList(ImmPtr(nullptr)), |
| 8755 | StoreRegisterTo(output)); |
| 8756 | |
| 8757 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 8758 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 8759 | ool->entry()); |
| 8760 | masm.bind(ool->rejoin()); |
| 8761 | } |
| 8762 | |
| 8763 | void CodeGenerator::visitNewMapObjectFromIterable( |
| 8764 | LNewMapObjectFromIterable* lir) { |
| 8765 | ValueOperand iterable = |
| 8766 | ToValue(lir, LNewMapObjectFromIterable::IterableIndex); |
| 8767 | Register output = ToRegister(lir->output()); |
| 8768 | Register temp1 = ToRegister(lir->temp0()); |
| 8769 | Register temp2 = ToRegister(lir->temp1()); |
| 8770 | |
| 8771 | // Allocate a new MapObject. If this fails we pass nullptr for |
| 8772 | // allocatedFromJit. |
| 8773 | Label failedAlloc, vmCall, done; |
| 8774 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 8775 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
| 8776 | &failedAlloc); |
| 8777 | |
| 8778 | // We're done if |iterable| is null or undefined. |
| 8779 | masm.branchIfNotNullOrUndefined(iterable, &vmCall); |
| 8780 | masm.movePtr(temp1, output); |
| 8781 | masm.jump(&done); |
| 8782 | |
| 8783 | masm.bind(&failedAlloc); |
| 8784 | masm.movePtr(ImmPtr(nullptr), temp1); |
| 8785 | |
| 8786 | masm.bind(&vmCall); |
| 8787 | |
| 8788 | pushArg(temp1); // allocatedFromJit |
| 8789 | pushArg(iterable); |
| 8790 | pushArg(ImmPtr(nullptr)); // proto |
| 8791 | |
| 8792 | using Fn = MapObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>, |
| 8793 | Handle<MapObject*>); |
| 8794 | callVM<Fn, MapObject::createFromIterable>(lir); |
| 8795 | |
| 8796 | masm.bind(&done); |
| 8797 | } |
| 8798 | |
| 8799 | void CodeGenerator::visitNewSetObjectFromIterable( |
| 8800 | LNewSetObjectFromIterable* lir) { |
| 8801 | ValueOperand iterable = |
| 8802 | ToValue(lir, LNewSetObjectFromIterable::IterableIndex); |
| 8803 | Register output = ToRegister(lir->output()); |
| 8804 | Register temp1 = ToRegister(lir->temp0()); |
| 8805 | Register temp2 = ToRegister(lir->temp1()); |
| 8806 | |
| 8807 | // Allocate a new SetObject. If this fails we pass nullptr for |
| 8808 | // allocatedFromJit. |
| 8809 | Label failedAlloc, vmCall, done; |
| 8810 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 8811 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
| 8812 | &failedAlloc); |
| 8813 | |
| 8814 | // We're done if |iterable| is null or undefined. |
| 8815 | masm.branchIfNotNullOrUndefined(iterable, &vmCall); |
| 8816 | masm.movePtr(temp1, output); |
| 8817 | masm.jump(&done); |
| 8818 | |
| 8819 | masm.bind(&failedAlloc); |
| 8820 | masm.movePtr(ImmPtr(nullptr), temp1); |
| 8821 | |
| 8822 | masm.bind(&vmCall); |
| 8823 | |
| 8824 | pushArg(temp1); // allocatedFromJit |
| 8825 | pushArg(iterable); |
| 8826 | pushArg(ImmPtr(nullptr)); // proto |
| 8827 | |
| 8828 | using Fn = SetObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>, |
| 8829 | Handle<SetObject*>); |
| 8830 | callVM<Fn, SetObject::createFromIterable>(lir); |
| 8831 | |
| 8832 | masm.bind(&done); |
| 8833 | } |
| 8834 | |
| 8835 | void CodeGenerator::visitNewStringObject(LNewStringObject* lir) { |
| 8836 | Register input = ToRegister(lir->input()); |
| 8837 | Register output = ToRegister(lir->output()); |
| 8838 | Register temp = ToRegister(lir->temp0()); |
| 8839 | |
| 8840 | StringObject* templateObj = lir->mir()->templateObj(); |
| 8841 | |
| 8842 | using Fn = JSObject* (*)(JSContext*, HandleString); |
| 8843 | OutOfLineCode* ool = oolCallVM<Fn, NewStringObject>(lir, ArgList(input), |
| 8844 | StoreRegisterTo(output)); |
| 8845 | |
| 8846 | TemplateObject templateObject(templateObj); |
| 8847 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 8848 | ool->entry()); |
| 8849 | |
| 8850 | masm.loadStringLength(input, temp); |
| 8851 | |
| 8852 | masm.storeValue(JSVAL_TYPE_STRING, input, |
| 8853 | Address(output, StringObject::offsetOfPrimitiveValue())); |
| 8854 | masm.storeValue(JSVAL_TYPE_INT32, temp, |
| 8855 | Address(output, StringObject::offsetOfLength())); |
| 8856 | |
| 8857 | masm.bind(ool->rejoin()); |
| 8858 | } |
| 8859 | |
| 8860 | void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) { |
| 8861 | Register obj = ToRegister(lir->object()); |
| 8862 | Register value = ToRegister(lir->value()); |
| 8863 | |
| 8864 | pushArg(value); |
| 8865 | pushArg(ToValue(lir, LInitElemGetterSetter::IdIndex)); |
| 8866 | pushArg(obj); |
| 8867 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
| 8868 | |
| 8869 | using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue, |
| 8870 | HandleObject); |
| 8871 | callVM<Fn, InitElemGetterSetterOperation>(lir); |
| 8872 | } |
| 8873 | |
| 8874 | void CodeGenerator::visitMutateProto(LMutateProto* lir) { |
| 8875 | Register objReg = ToRegister(lir->object()); |
| 8876 | |
| 8877 | pushArg(ToValue(lir, LMutateProto::ValueIndex)); |
| 8878 | pushArg(objReg); |
| 8879 | |
| 8880 | using Fn = |
| 8881 | bool (*)(JSContext* cx, Handle<PlainObject*> obj, HandleValue value); |
| 8882 | callVM<Fn, MutatePrototype>(lir); |
| 8883 | } |
| 8884 | |
| 8885 | void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) { |
| 8886 | Register obj = ToRegister(lir->object()); |
| 8887 | Register value = ToRegister(lir->value()); |
| 8888 | |
| 8889 | pushArg(value); |
| 8890 | pushArg(ImmGCPtr(lir->mir()->name())); |
| 8891 | pushArg(obj); |
| 8892 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
| 8893 | |
| 8894 | using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, |
| 8895 | Handle<PropertyName*>, HandleObject); |
| 8896 | callVM<Fn, InitPropGetterSetterOperation>(lir); |
| 8897 | } |
| 8898 | |
| 8899 | void CodeGenerator::visitCreateThis(LCreateThis* lir) { |
| 8900 | const LAllocation* callee = lir->callee(); |
| 8901 | const LAllocation* newTarget = lir->newTarget(); |
| 8902 | |
| 8903 | if (newTarget->isConstant()) { |
| 8904 | pushArg(ImmGCPtr(&newTarget->toConstant()->toObject())); |
| 8905 | } else { |
| 8906 | pushArg(ToRegister(newTarget)); |
| 8907 | } |
| 8908 | |
| 8909 | if (callee->isConstant()) { |
| 8910 | pushArg(ImmGCPtr(&callee->toConstant()->toObject())); |
| 8911 | } else { |
| 8912 | pushArg(ToRegister(callee)); |
| 8913 | } |
| 8914 | |
| 8915 | using Fn = bool (*)(JSContext* cx, HandleObject callee, |
| 8916 | HandleObject newTarget, MutableHandleValue rval); |
| 8917 | callVM<Fn, jit::CreateThisFromIon>(lir); |
| 8918 | } |
| 8919 | |
| 8920 | void CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) { |
| 8921 | // This should be getting constructed in the first block only, and not any OSR |
| 8922 | // entry blocks. |
| 8923 | MOZ_ASSERT(lir->mir()->block()->id() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->block()->id() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->block()->id() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->block()->id() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 8923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->block()->id() == 0" ")"); do { *((volatile int*)__null) = 8923; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8924 | |
| 8925 | Register callObj = ToRegister(lir->callObject()); |
| 8926 | Register temp0 = ToRegister(lir->temp0()); |
| 8927 | Label done; |
| 8928 | |
| 8929 | if (ArgumentsObject* templateObj = lir->mir()->templateObject()) { |
| 8930 | Register objTemp = ToRegister(lir->temp1()); |
| 8931 | Register cxTemp = ToRegister(lir->temp2()); |
| 8932 | |
| 8933 | masm.Push(callObj); |
| 8934 | |
| 8935 | // Try to allocate an arguments object. This will leave the reserved |
| 8936 | // slots uninitialized, so it's important we don't GC until we |
| 8937 | // initialize these slots in ArgumentsObject::finishForIonPure. |
| 8938 | Label failure; |
| 8939 | TemplateObject templateObject(templateObj); |
| 8940 | masm.createGCObject(objTemp, temp0, templateObject, gc::Heap::Default, |
| 8941 | &failure, |
| 8942 | /* initContents = */ false); |
| 8943 | |
| 8944 | masm.moveStackPtrTo(temp0); |
| 8945 | masm.addPtr(Imm32(masm.framePushed()), temp0); |
| 8946 | |
| 8947 | using Fn = ArgumentsObject* (*)(JSContext* cx, jit::JitFrameLayout* frame, |
| 8948 | JSObject* scopeChain, ArgumentsObject* obj); |
| 8949 | masm.setupAlignedABICall(); |
| 8950 | masm.loadJSContext(cxTemp); |
| 8951 | masm.passABIArg(cxTemp); |
| 8952 | masm.passABIArg(temp0); |
| 8953 | masm.passABIArg(callObj); |
| 8954 | masm.passABIArg(objTemp); |
| 8955 | |
| 8956 | masm.callWithABI<Fn, ArgumentsObject::finishForIonPure>(); |
| 8957 | masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure); |
| 8958 | |
| 8959 | // Discard saved callObj on the stack. |
| 8960 | masm.addToStackPtr(Imm32(sizeof(uintptr_t))); |
| 8961 | masm.jump(&done); |
| 8962 | |
| 8963 | masm.bind(&failure); |
| 8964 | masm.Pop(callObj); |
| 8965 | } |
| 8966 | |
| 8967 | masm.moveStackPtrTo(temp0); |
| 8968 | masm.addPtr(Imm32(frameSize()), temp0); |
| 8969 | |
| 8970 | pushArg(callObj); |
| 8971 | pushArg(temp0); |
| 8972 | |
| 8973 | using Fn = ArgumentsObject* (*)(JSContext*, JitFrameLayout*, HandleObject); |
| 8974 | callVM<Fn, ArgumentsObject::createForIon>(lir); |
| 8975 | |
| 8976 | masm.bind(&done); |
| 8977 | } |
| 8978 | |
| 8979 | void CodeGenerator::visitCreateInlinedArgumentsObject( |
| 8980 | LCreateInlinedArgumentsObject* lir) { |
| 8981 | Register callObj = ToRegister(lir->getCallObject()); |
| 8982 | Register callee = ToRegister(lir->getCallee()); |
| 8983 | Register argsAddress = ToRegister(lir->temp1()); |
| 8984 | Register argsObj = ToRegister(lir->temp2()); |
| 8985 | |
| 8986 | // TODO: Do we have to worry about alignment here? |
| 8987 | |
| 8988 | // Create a contiguous array of values for ArgumentsObject::create |
| 8989 | // by pushing the arguments onto the stack in reverse order. |
| 8990 | uint32_t argc = lir->mir()->numActuals(); |
| 8991 | for (uint32_t i = 0; i < argc; i++) { |
| 8992 | uint32_t argNum = argc - i - 1; |
| 8993 | uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(argNum); |
| 8994 | ConstantOrRegister arg = |
| 8995 | toConstantOrRegister(lir, index, lir->mir()->getArg(argNum)->type()); |
| 8996 | masm.Push(arg); |
| 8997 | } |
| 8998 | masm.moveStackPtrTo(argsAddress); |
| 8999 | |
| 9000 | Label done; |
| 9001 | if (ArgumentsObject* templateObj = lir->mir()->templateObject()) { |
| 9002 | LiveRegisterSet liveRegs; |
| 9003 | liveRegs.add(callObj); |
| 9004 | liveRegs.add(callee); |
| 9005 | |
| 9006 | masm.PushRegsInMask(liveRegs); |
| 9007 | |
| 9008 | // We are free to clobber all registers, as LCreateInlinedArgumentsObject is |
| 9009 | // a call instruction. |
| 9010 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 9011 | allRegs.take(callObj); |
| 9012 | allRegs.take(callee); |
| 9013 | allRegs.take(argsObj); |
| 9014 | allRegs.take(argsAddress); |
| 9015 | |
| 9016 | Register temp3 = allRegs.takeAny(); |
| 9017 | Register temp4 = allRegs.takeAny(); |
| 9018 | |
| 9019 | // Try to allocate an arguments object. This will leave the reserved slots |
| 9020 | // uninitialized, so it's important we don't GC until we initialize these |
| 9021 | // slots in ArgumentsObject::finishForIonPure. |
| 9022 | Label failure; |
| 9023 | TemplateObject templateObject(templateObj); |
| 9024 | masm.createGCObject(argsObj, temp3, templateObject, gc::Heap::Default, |
| 9025 | &failure, |
| 9026 | /* initContents = */ false); |
| 9027 | |
| 9028 | Register numActuals = temp3; |
| 9029 | masm.move32(Imm32(argc), numActuals); |
| 9030 | |
| 9031 | using Fn = ArgumentsObject* (*)(JSContext*, JSObject*, JSFunction*, Value*, |
| 9032 | uint32_t, ArgumentsObject*); |
| 9033 | masm.setupAlignedABICall(); |
| 9034 | masm.loadJSContext(temp4); |
| 9035 | masm.passABIArg(temp4); |
| 9036 | masm.passABIArg(callObj); |
| 9037 | masm.passABIArg(callee); |
| 9038 | masm.passABIArg(argsAddress); |
| 9039 | masm.passABIArg(numActuals); |
| 9040 | masm.passABIArg(argsObj); |
| 9041 | |
| 9042 | masm.callWithABI<Fn, ArgumentsObject::finishInlineForIonPure>(); |
| 9043 | masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure); |
| 9044 | |
| 9045 | // Discard saved callObj, callee, and values array on the stack. |
| 9046 | masm.addToStackPtr( |
| 9047 | Imm32(MacroAssembler::PushRegsInMaskSizeInBytes(liveRegs) + |
| 9048 | argc * sizeof(Value))); |
| 9049 | masm.jump(&done); |
| 9050 | |
| 9051 | masm.bind(&failure); |
| 9052 | masm.PopRegsInMask(liveRegs); |
| 9053 | |
| 9054 | // Reload argsAddress because it may have been overridden. |
| 9055 | masm.moveStackPtrTo(argsAddress); |
| 9056 | } |
| 9057 | |
| 9058 | pushArg(Imm32(argc)); |
| 9059 | pushArg(callObj); |
| 9060 | pushArg(callee); |
| 9061 | pushArg(argsAddress); |
| 9062 | |
| 9063 | using Fn = ArgumentsObject* (*)(JSContext*, Value*, HandleFunction, |
| 9064 | HandleObject, uint32_t); |
| 9065 | callVM<Fn, ArgumentsObject::createForInlinedIon>(lir); |
| 9066 | |
| 9067 | // Discard the array of values. |
| 9068 | masm.freeStack(argc * sizeof(Value)); |
| 9069 | |
| 9070 | masm.bind(&done); |
| 9071 | } |
| 9072 | |
| 9073 | template <class GetInlinedArgument> |
| 9074 | void CodeGenerator::emitGetInlinedArgument(GetInlinedArgument* lir, |
| 9075 | Register index, |
| 9076 | ValueOperand output) { |
| 9077 | uint32_t numActuals = lir->mir()->numActuals(); |
| 9078 | MOZ_ASSERT(numActuals <= ArgumentsObject::MaxInlinedArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(numActuals <= ArgumentsObject::MaxInlinedArgs)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(numActuals <= ArgumentsObject::MaxInlinedArgs))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("numActuals <= ArgumentsObject::MaxInlinedArgs" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "numActuals <= ArgumentsObject::MaxInlinedArgs" ")"); do { *((volatile int*)__null) = 9078; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9079 | |
| 9080 | // The index has already been bounds-checked, so the code we |
| 9081 | // generate here should be unreachable. We can end up in this |
| 9082 | // situation in self-hosted code using GetArgument(), or in a |
| 9083 | // monomorphically inlined function if we've inlined some CacheIR |
| 9084 | // that was created for a different caller. |
| 9085 | if (numActuals == 0) { |
| 9086 | masm.assumeUnreachable("LGetInlinedArgument: invalid index"); |
| 9087 | return; |
| 9088 | } |
| 9089 | |
| 9090 | // Check the first n-1 possible indices. |
| 9091 | Label done; |
| 9092 | for (uint32_t i = 0; i < numActuals - 1; i++) { |
| 9093 | Label skip; |
| 9094 | ConstantOrRegister arg = toConstantOrRegister( |
| 9095 | lir, GetInlinedArgument::ArgIndex(i), lir->mir()->getArg(i)->type()); |
| 9096 | masm.branch32(Assembler::NotEqual, index, Imm32(i), &skip); |
| 9097 | masm.moveValue(arg, output); |
| 9098 | |
| 9099 | masm.jump(&done); |
| 9100 | masm.bind(&skip); |
| 9101 | } |
| 9102 | |
| 9103 | #ifdef DEBUG1 |
| 9104 | Label skip; |
| 9105 | masm.branch32(Assembler::Equal, index, Imm32(numActuals - 1), &skip); |
| 9106 | masm.assumeUnreachable("LGetInlinedArgument: invalid index"); |
| 9107 | masm.bind(&skip); |
| 9108 | #endif |
| 9109 | |
| 9110 | // The index has already been bounds-checked, so load the last argument. |
| 9111 | uint32_t lastIdx = numActuals - 1; |
| 9112 | ConstantOrRegister arg = |
| 9113 | toConstantOrRegister(lir, GetInlinedArgument::ArgIndex(lastIdx), |
| 9114 | lir->mir()->getArg(lastIdx)->type()); |
| 9115 | masm.moveValue(arg, output); |
| 9116 | masm.bind(&done); |
| 9117 | } |
| 9118 | |
| 9119 | void CodeGenerator::visitGetInlinedArgument(LGetInlinedArgument* lir) { |
| 9120 | Register index = ToRegister(lir->getIndex()); |
| 9121 | ValueOperand output = ToOutValue(lir); |
| 9122 | |
| 9123 | emitGetInlinedArgument(lir, index, output); |
| 9124 | } |
| 9125 | |
| 9126 | void CodeGenerator::visitGetInlinedArgumentHole(LGetInlinedArgumentHole* lir) { |
| 9127 | Register index = ToRegister(lir->getIndex()); |
| 9128 | ValueOperand output = ToOutValue(lir); |
| 9129 | |
| 9130 | uint32_t numActuals = lir->mir()->numActuals(); |
| 9131 | |
| 9132 | if (numActuals == 0) { |
| 9133 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 9134 | masm.moveValue(UndefinedValue(), output); |
| 9135 | return; |
| 9136 | } |
| 9137 | |
| 9138 | Label outOfBounds, done; |
| 9139 | masm.branch32(Assembler::AboveOrEqual, index, Imm32(numActuals), |
| 9140 | &outOfBounds); |
| 9141 | |
| 9142 | emitGetInlinedArgument(lir, index, output); |
| 9143 | masm.jump(&done); |
| 9144 | |
| 9145 | masm.bind(&outOfBounds); |
| 9146 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 9147 | masm.moveValue(UndefinedValue(), output); |
| 9148 | |
| 9149 | masm.bind(&done); |
| 9150 | } |
| 9151 | |
| 9152 | void CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir) { |
| 9153 | Register temp = ToRegister(lir->temp0()); |
| 9154 | Register argsObj = ToRegister(lir->argsObject()); |
| 9155 | ValueOperand out = ToOutValue(lir); |
| 9156 | |
| 9157 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
| 9158 | temp); |
| 9159 | Address argAddr(temp, ArgumentsData::offsetOfArgs() + |
| 9160 | lir->mir()->argno() * sizeof(Value)); |
| 9161 | masm.loadValue(argAddr, out); |
| 9162 | #ifdef DEBUG1 |
| 9163 | Label success; |
| 9164 | masm.branchTestMagic(Assembler::NotEqual, out, &success); |
| 9165 | masm.assumeUnreachable( |
| 9166 | "Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); |
| 9167 | masm.bind(&success); |
| 9168 | #endif |
| 9169 | } |
| 9170 | |
| 9171 | void CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir) { |
| 9172 | Register temp = ToRegister(lir->getTemp(0)); |
| 9173 | Register argsObj = ToRegister(lir->argsObject()); |
| 9174 | ValueOperand value = ToValue(lir, LSetArgumentsObjectArg::ValueIndex); |
| 9175 | |
| 9176 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
| 9177 | temp); |
| 9178 | Address argAddr(temp, ArgumentsData::offsetOfArgs() + |
| 9179 | lir->mir()->argno() * sizeof(Value)); |
| 9180 | emitPreBarrier(argAddr); |
| 9181 | #ifdef DEBUG1 |
| 9182 | Label success; |
| 9183 | masm.branchTestMagic(Assembler::NotEqual, argAddr, &success); |
| 9184 | masm.assumeUnreachable( |
| 9185 | "Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); |
| 9186 | masm.bind(&success); |
| 9187 | #endif |
| 9188 | masm.storeValue(value, argAddr); |
| 9189 | } |
| 9190 | |
| 9191 | void CodeGenerator::visitLoadArgumentsObjectArg(LLoadArgumentsObjectArg* lir) { |
| 9192 | Register temp = ToRegister(lir->temp0()); |
| 9193 | Register argsObj = ToRegister(lir->argsObject()); |
| 9194 | Register index = ToRegister(lir->index()); |
| 9195 | ValueOperand out = ToOutValue(lir); |
| 9196 | |
| 9197 | Label bail; |
| 9198 | masm.loadArgumentsObjectElement(argsObj, index, out, temp, &bail); |
| 9199 | bailoutFrom(&bail, lir->snapshot()); |
| 9200 | } |
| 9201 | |
| 9202 | void CodeGenerator::visitLoadArgumentsObjectArgHole( |
| 9203 | LLoadArgumentsObjectArgHole* lir) { |
| 9204 | Register temp = ToRegister(lir->temp0()); |
| 9205 | Register argsObj = ToRegister(lir->argsObject()); |
| 9206 | Register index = ToRegister(lir->index()); |
| 9207 | ValueOperand out = ToOutValue(lir); |
| 9208 | |
| 9209 | Label bail; |
| 9210 | masm.loadArgumentsObjectElementHole(argsObj, index, out, temp, &bail); |
| 9211 | bailoutFrom(&bail, lir->snapshot()); |
| 9212 | } |
| 9213 | |
| 9214 | void CodeGenerator::visitInArgumentsObjectArg(LInArgumentsObjectArg* lir) { |
| 9215 | Register temp = ToRegister(lir->temp0()); |
| 9216 | Register argsObj = ToRegister(lir->argsObject()); |
| 9217 | Register index = ToRegister(lir->index()); |
| 9218 | Register out = ToRegister(lir->output()); |
| 9219 | |
| 9220 | Label bail; |
| 9221 | masm.loadArgumentsObjectElementExists(argsObj, index, out, temp, &bail); |
| 9222 | bailoutFrom(&bail, lir->snapshot()); |
| 9223 | } |
| 9224 | |
| 9225 | void CodeGenerator::visitArgumentsObjectLength(LArgumentsObjectLength* lir) { |
| 9226 | Register argsObj = ToRegister(lir->argsObject()); |
| 9227 | Register out = ToRegister(lir->output()); |
| 9228 | |
| 9229 | Label bail; |
| 9230 | masm.loadArgumentsObjectLength(argsObj, out, &bail); |
| 9231 | bailoutFrom(&bail, lir->snapshot()); |
| 9232 | } |
| 9233 | |
| 9234 | void CodeGenerator::visitArrayFromArgumentsObject( |
| 9235 | LArrayFromArgumentsObject* lir) { |
| 9236 | pushArg(ToRegister(lir->argsObject())); |
| 9237 | |
| 9238 | using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>); |
| 9239 | callVM<Fn, js::ArrayFromArgumentsObject>(lir); |
| 9240 | } |
| 9241 | |
| 9242 | void CodeGenerator::visitGuardArgumentsObjectFlags( |
| 9243 | LGuardArgumentsObjectFlags* lir) { |
| 9244 | Register argsObj = ToRegister(lir->argsObject()); |
| 9245 | Register temp = ToRegister(lir->temp0()); |
| 9246 | |
| 9247 | Label bail; |
| 9248 | masm.branchTestArgumentsObjectFlags(argsObj, temp, lir->mir()->flags(), |
| 9249 | Assembler::NonZero, &bail); |
| 9250 | bailoutFrom(&bail, lir->snapshot()); |
| 9251 | } |
| 9252 | |
| 9253 | void CodeGenerator::visitBoundFunctionNumArgs(LBoundFunctionNumArgs* lir) { |
| 9254 | Register obj = ToRegister(lir->object()); |
| 9255 | Register output = ToRegister(lir->output()); |
| 9256 | |
| 9257 | masm.unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()), |
| 9258 | output); |
| 9259 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), output); |
| 9260 | } |
| 9261 | |
| 9262 | void CodeGenerator::visitGuardBoundFunctionIsConstructor( |
| 9263 | LGuardBoundFunctionIsConstructor* lir) { |
| 9264 | Register obj = ToRegister(lir->object()); |
| 9265 | |
| 9266 | Label bail; |
| 9267 | Address flagsSlot(obj, BoundFunctionObject::offsetOfFlagsSlot()); |
| 9268 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 9269 | Imm32(BoundFunctionObject::IsConstructorFlag), &bail); |
| 9270 | bailoutFrom(&bail, lir->snapshot()); |
| 9271 | } |
| 9272 | |
| 9273 | void CodeGenerator::visitReturnFromCtor(LReturnFromCtor* lir) { |
| 9274 | ValueOperand value = ToValue(lir, LReturnFromCtor::ValueIndex); |
| 9275 | Register obj = ToRegister(lir->object()); |
| 9276 | Register output = ToRegister(lir->output()); |
| 9277 | |
| 9278 | Label valueIsObject, end; |
| 9279 | |
| 9280 | masm.branchTestObject(Assembler::Equal, value, &valueIsObject); |
| 9281 | |
| 9282 | // Value is not an object. Return that other object. |
| 9283 | masm.movePtr(obj, output); |
| 9284 | masm.jump(&end); |
| 9285 | |
| 9286 | // Value is an object. Return unbox(Value). |
| 9287 | masm.bind(&valueIsObject); |
| 9288 | Register payload = masm.extractObject(value, output); |
| 9289 | if (payload != output) { |
| 9290 | masm.movePtr(payload, output); |
| 9291 | } |
| 9292 | |
| 9293 | masm.bind(&end); |
| 9294 | } |
| 9295 | |
| 9296 | class OutOfLineBoxNonStrictThis : public OutOfLineCodeBase<CodeGenerator> { |
| 9297 | LBoxNonStrictThis* ins_; |
| 9298 | |
| 9299 | public: |
| 9300 | explicit OutOfLineBoxNonStrictThis(LBoxNonStrictThis* ins) : ins_(ins) {} |
| 9301 | void accept(CodeGenerator* codegen) override { |
| 9302 | codegen->visitOutOfLineBoxNonStrictThis(this); |
| 9303 | } |
| 9304 | LBoxNonStrictThis* ins() const { return ins_; } |
| 9305 | }; |
| 9306 | |
| 9307 | void CodeGenerator::visitBoxNonStrictThis(LBoxNonStrictThis* lir) { |
| 9308 | ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex); |
| 9309 | Register output = ToRegister(lir->output()); |
| 9310 | |
| 9311 | auto* ool = new (alloc()) OutOfLineBoxNonStrictThis(lir); |
| 9312 | addOutOfLineCode(ool, lir->mir()); |
| 9313 | |
| 9314 | masm.fallibleUnboxObject(value, output, ool->entry()); |
| 9315 | masm.bind(ool->rejoin()); |
| 9316 | } |
| 9317 | |
| 9318 | void CodeGenerator::visitOutOfLineBoxNonStrictThis( |
| 9319 | OutOfLineBoxNonStrictThis* ool) { |
| 9320 | LBoxNonStrictThis* lir = ool->ins(); |
| 9321 | |
| 9322 | ValueOperand value = ToValue(lir, LBoxNonStrictThis::ValueIndex); |
| 9323 | Register output = ToRegister(lir->output()); |
| 9324 | |
| 9325 | Label notNullOrUndefined; |
| 9326 | { |
| 9327 | Label isNullOrUndefined; |
| 9328 | ScratchTagScope tag(masm, value); |
| 9329 | masm.splitTagForTest(value, tag); |
| 9330 | masm.branchTestUndefined(Assembler::Equal, tag, &isNullOrUndefined); |
| 9331 | masm.branchTestNull(Assembler::NotEqual, tag, ¬NullOrUndefined); |
| 9332 | masm.bind(&isNullOrUndefined); |
| 9333 | masm.movePtr(ImmGCPtr(lir->mir()->globalThis()), output); |
| 9334 | masm.jump(ool->rejoin()); |
| 9335 | } |
| 9336 | |
| 9337 | masm.bind(¬NullOrUndefined); |
| 9338 | |
| 9339 | saveLive(lir); |
| 9340 | |
| 9341 | pushArg(value); |
| 9342 | using Fn = JSObject* (*)(JSContext*, HandleValue); |
| 9343 | callVM<Fn, BoxNonStrictThis>(lir); |
| 9344 | |
| 9345 | StoreRegisterTo(output).generate(this); |
| 9346 | restoreLiveIgnore(lir, StoreRegisterTo(output).clobbered()); |
| 9347 | |
| 9348 | masm.jump(ool->rejoin()); |
| 9349 | } |
| 9350 | |
| 9351 | void CodeGenerator::visitImplicitThis(LImplicitThis* lir) { |
| 9352 | Register env = ToRegister(lir->env()); |
| 9353 | ValueOperand output = ToOutValue(lir); |
| 9354 | |
| 9355 | using Fn = void (*)(JSContext*, HandleObject, MutableHandleValue); |
| 9356 | auto* ool = oolCallVM<Fn, ImplicitThisOperation>(lir, ArgList(env), |
| 9357 | StoreValueTo(output)); |
| 9358 | |
| 9359 | masm.computeImplicitThis(env, output, ool->entry()); |
| 9360 | masm.bind(ool->rejoin()); |
| 9361 | } |
| 9362 | |
| 9363 | void CodeGenerator::visitArrayLength(LArrayLength* lir) { |
| 9364 | Register elements = ToRegister(lir->elements()); |
| 9365 | Register output = ToRegister(lir->output()); |
| 9366 | |
| 9367 | Address length(elements, ObjectElements::offsetOfLength()); |
| 9368 | masm.load32(length, output); |
| 9369 | |
| 9370 | // Bail out if the length doesn't fit in int32. |
| 9371 | bailoutTest32(Assembler::Signed, output, output, lir->snapshot()); |
| 9372 | } |
| 9373 | |
| 9374 | static void SetLengthFromIndex(MacroAssembler& masm, const LAllocation* index, |
| 9375 | const Address& length) { |
| 9376 | if (index->isConstant()) { |
| 9377 | masm.store32(Imm32(ToInt32(index) + 1), length); |
| 9378 | } else { |
| 9379 | Register newLength = ToRegister(index); |
| 9380 | masm.add32(Imm32(1), newLength); |
| 9381 | masm.store32(newLength, length); |
| 9382 | masm.sub32(Imm32(1), newLength); |
| 9383 | } |
| 9384 | } |
| 9385 | |
| 9386 | void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) { |
| 9387 | Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); |
| 9388 | SetLengthFromIndex(masm, lir->index(), length); |
| 9389 | } |
| 9390 | |
| 9391 | void CodeGenerator::visitFunctionLength(LFunctionLength* lir) { |
| 9392 | Register function = ToRegister(lir->function()); |
| 9393 | Register output = ToRegister(lir->output()); |
| 9394 | |
| 9395 | Label bail; |
| 9396 | |
| 9397 | // Get the JSFunction flags. |
| 9398 | masm.load32(Address(function, JSFunction::offsetOfFlagsAndArgCount()), |
| 9399 | output); |
| 9400 | |
| 9401 | // Functions with a SelfHostedLazyScript must be compiled with the slow-path |
| 9402 | // before the function length is known. If the length was previously resolved, |
| 9403 | // the length property may be shadowed. |
| 9404 | masm.branchTest32( |
| 9405 | Assembler::NonZero, output, |
| 9406 | Imm32(FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH), |
| 9407 | &bail); |
| 9408 | |
| 9409 | masm.loadFunctionLength(function, output, output, &bail); |
| 9410 | |
| 9411 | bailoutFrom(&bail, lir->snapshot()); |
| 9412 | } |
| 9413 | |
| 9414 | void CodeGenerator::visitFunctionName(LFunctionName* lir) { |
| 9415 | Register function = ToRegister(lir->function()); |
| 9416 | Register output = ToRegister(lir->output()); |
| 9417 | |
| 9418 | Label bail; |
| 9419 | |
| 9420 | const JSAtomState& names = gen->runtime->names(); |
| 9421 | masm.loadFunctionName(function, output, ImmGCPtr(names.empty_), &bail); |
| 9422 | |
| 9423 | bailoutFrom(&bail, lir->snapshot()); |
| 9424 | } |
| 9425 | |
| 9426 | template <class TableObject> |
| 9427 | static void TableIteratorLoadEntry(MacroAssembler&, Register, Register, |
| 9428 | Register); |
| 9429 | |
| 9430 | template <> |
| 9431 | void TableIteratorLoadEntry<MapObject>(MacroAssembler& masm, Register iter, |
| 9432 | Register i, Register front) { |
| 9433 | masm.unboxObject(Address(iter, MapIteratorObject::offsetOfTarget()), front); |
| 9434 | masm.loadPrivate(Address(front, MapObject::offsetOfData()), front); |
| 9435 | |
| 9436 | static_assert(MapObject::Table::offsetOfImplDataElement() == 0, |
| 9437 | "offsetof(Data, element) is 0"); |
| 9438 | static_assert(MapObject::Table::sizeofImplData() == 24, "sizeof(Data) is 24"); |
| 9439 | masm.mulBy3(i, i); |
| 9440 | masm.lshiftPtr(Imm32(3), i); |
| 9441 | masm.addPtr(i, front); |
| 9442 | } |
| 9443 | |
| 9444 | template <> |
| 9445 | void TableIteratorLoadEntry<SetObject>(MacroAssembler& masm, Register iter, |
| 9446 | Register i, Register front) { |
| 9447 | masm.unboxObject(Address(iter, SetIteratorObject::offsetOfTarget()), front); |
| 9448 | masm.loadPrivate(Address(front, SetObject::offsetOfData()), front); |
| 9449 | |
| 9450 | static_assert(SetObject::Table::offsetOfImplDataElement() == 0, |
| 9451 | "offsetof(Data, element) is 0"); |
| 9452 | static_assert(SetObject::Table::sizeofImplData() == 16, "sizeof(Data) is 16"); |
| 9453 | masm.lshiftPtr(Imm32(4), i); |
| 9454 | masm.addPtr(i, front); |
| 9455 | } |
| 9456 | |
| 9457 | template <class TableObject> |
| 9458 | static void TableIteratorAdvance(MacroAssembler& masm, Register iter, |
| 9459 | Register front, Register dataLength, |
| 9460 | Register temp) { |
| 9461 | Register i = temp; |
| 9462 | |
| 9463 | // Note: |count| and |index| are stored as PrivateUint32Value. We use add32 |
| 9464 | // and store32 to change the payload. |
| 9465 | masm.add32(Imm32(1), Address(iter, TableIteratorObject::offsetOfCount())); |
| 9466 | |
| 9467 | masm.unboxInt32(Address(iter, TableIteratorObject::offsetOfIndex()), i); |
| 9468 | |
| 9469 | Label done, seek; |
| 9470 | masm.bind(&seek); |
| 9471 | masm.add32(Imm32(1), i); |
| 9472 | masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done); |
| 9473 | |
| 9474 | // We can add sizeof(Data) to |front| to select the next element, because |
| 9475 | // |front| and |mapOrSetObject.data[i]| point to the same location. |
| 9476 | static_assert(TableObject::Table::offsetOfImplDataElement() == 0, |
| 9477 | "offsetof(Data, element) is 0"); |
| 9478 | masm.addPtr(Imm32(TableObject::Table::sizeofImplData()), front); |
| 9479 | |
| 9480 | masm.branchTestMagic(Assembler::Equal, |
| 9481 | Address(front, TableObject::Table::offsetOfEntryKey()), |
| 9482 | JS_HASH_KEY_EMPTY, &seek); |
| 9483 | |
| 9484 | masm.bind(&done); |
| 9485 | masm.store32(i, Address(iter, TableIteratorObject::offsetOfIndex())); |
| 9486 | } |
| 9487 | |
| 9488 | // Corresponds to TableIteratorObject::finish. |
| 9489 | static void TableIteratorFinish(MacroAssembler& masm, Register iter, |
| 9490 | Register temp0, Register temp1) { |
| 9491 | Register next = temp0; |
| 9492 | Register prevp = temp1; |
| 9493 | masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfNext()), next); |
| 9494 | masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfPrevPtr()), |
| 9495 | prevp); |
| 9496 | masm.storePtr(next, Address(prevp, 0)); |
| 9497 | |
| 9498 | Label hasNoNext; |
| 9499 | masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext); |
| 9500 | masm.storePrivateValue(prevp, |
| 9501 | Address(next, TableIteratorObject::offsetOfPrevPtr())); |
| 9502 | masm.bind(&hasNoNext); |
| 9503 | |
| 9504 | // Mark iterator inactive. |
| 9505 | Address targetAddr(iter, TableIteratorObject::offsetOfTarget()); |
| 9506 | masm.guardedCallPreBarrier(targetAddr, MIRType::Value); |
| 9507 | masm.storeValue(UndefinedValue(), targetAddr); |
| 9508 | } |
| 9509 | |
| 9510 | template <> |
| 9511 | void CodeGenerator::emitLoadIteratorValues<MapObject>(Register result, |
| 9512 | Register temp, |
| 9513 | Register front) { |
| 9514 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
| 9515 | |
| 9516 | Address keyAddress(front, MapObject::Table::Entry::offsetOfKey()); |
| 9517 | Address valueAddress(front, MapObject::Table::Entry::offsetOfValue()); |
| 9518 | Address keyElemAddress(result, elementsOffset); |
| 9519 | Address valueElemAddress(result, elementsOffset + sizeof(Value)); |
| 9520 | masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value); |
| 9521 | masm.guardedCallPreBarrier(valueElemAddress, MIRType::Value); |
| 9522 | masm.storeValue(keyAddress, keyElemAddress, temp); |
| 9523 | masm.storeValue(valueAddress, valueElemAddress, temp); |
| 9524 | |
| 9525 | Label emitBarrier, skipBarrier; |
| 9526 | masm.branchValueIsNurseryCell(Assembler::Equal, keyAddress, temp, |
| 9527 | &emitBarrier); |
| 9528 | masm.branchValueIsNurseryCell(Assembler::NotEqual, valueAddress, temp, |
| 9529 | &skipBarrier); |
| 9530 | { |
| 9531 | masm.bind(&emitBarrier); |
| 9532 | saveVolatile(temp); |
| 9533 | emitPostWriteBarrier(result); |
| 9534 | restoreVolatile(temp); |
| 9535 | } |
| 9536 | masm.bind(&skipBarrier); |
| 9537 | } |
| 9538 | |
| 9539 | template <> |
| 9540 | void CodeGenerator::emitLoadIteratorValues<SetObject>(Register result, |
| 9541 | Register temp, |
| 9542 | Register front) { |
| 9543 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
| 9544 | |
| 9545 | Address keyAddress(front, SetObject::Table::offsetOfEntryKey()); |
| 9546 | Address keyElemAddress(result, elementsOffset); |
| 9547 | masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value); |
| 9548 | masm.storeValue(keyAddress, keyElemAddress, temp); |
| 9549 | |
| 9550 | Label skipBarrier; |
| 9551 | masm.branchValueIsNurseryCell(Assembler::NotEqual, keyAddress, temp, |
| 9552 | &skipBarrier); |
| 9553 | { |
| 9554 | saveVolatile(temp); |
| 9555 | emitPostWriteBarrier(result); |
| 9556 | restoreVolatile(temp); |
| 9557 | } |
| 9558 | masm.bind(&skipBarrier); |
| 9559 | } |
| 9560 | |
| 9561 | template <class IteratorObject, class TableObject> |
| 9562 | void CodeGenerator::emitGetNextEntryForIterator(LGetNextEntryForIterator* lir) { |
| 9563 | Register iter = ToRegister(lir->iter()); |
| 9564 | Register result = ToRegister(lir->result()); |
| 9565 | Register temp = ToRegister(lir->temp0()); |
| 9566 | Register dataLength = ToRegister(lir->temp1()); |
| 9567 | Register front = ToRegister(lir->temp2()); |
| 9568 | Register output = ToRegister(lir->output()); |
| 9569 | |
| 9570 | #ifdef DEBUG1 |
| 9571 | // Self-hosted code is responsible for ensuring GetNextEntryForIterator is |
| 9572 | // only called with the correct iterator class. Assert here all self- |
| 9573 | // hosted callers of GetNextEntryForIterator perform this class check. |
| 9574 | // No Spectre mitigations are needed because this is DEBUG-only code. |
| 9575 | Label success; |
| 9576 | masm.branchTestObjClassNoSpectreMitigations( |
| 9577 | Assembler::Equal, iter, &IteratorObject::class_, temp, &success); |
| 9578 | masm.assumeUnreachable("Iterator object should have the correct class."); |
| 9579 | masm.bind(&success); |
| 9580 | #endif |
| 9581 | |
| 9582 | // If the iterator has no target, it's already done. |
| 9583 | // See TableIteratorObject::isActive. |
| 9584 | Label iterAlreadyDone, iterDone, done; |
| 9585 | masm.branchTestUndefined(Assembler::Equal, |
| 9586 | Address(iter, IteratorObject::offsetOfTarget()), |
| 9587 | &iterAlreadyDone); |
| 9588 | |
| 9589 | // Load |iter->index| in |temp| and |iter->target->dataLength| in |
| 9590 | // |dataLength|. Both values are stored as PrivateUint32Value. |
| 9591 | masm.unboxInt32(Address(iter, IteratorObject::offsetOfIndex()), temp); |
| 9592 | masm.unboxObject(Address(iter, IteratorObject::offsetOfTarget()), dataLength); |
| 9593 | masm.unboxInt32(Address(dataLength, TableObject::offsetOfDataLength()), |
| 9594 | dataLength); |
| 9595 | masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone); |
| 9596 | { |
| 9597 | TableIteratorLoadEntry<TableObject>(masm, iter, temp, front); |
| 9598 | |
| 9599 | emitLoadIteratorValues<TableObject>(result, temp, front); |
| 9600 | |
| 9601 | TableIteratorAdvance<TableObject>(masm, iter, front, dataLength, temp); |
| 9602 | |
| 9603 | masm.move32(Imm32(0), output); |
| 9604 | masm.jump(&done); |
| 9605 | } |
| 9606 | { |
| 9607 | masm.bind(&iterDone); |
| 9608 | TableIteratorFinish(masm, iter, temp, dataLength); |
| 9609 | |
| 9610 | masm.bind(&iterAlreadyDone); |
| 9611 | masm.move32(Imm32(1), output); |
| 9612 | } |
| 9613 | masm.bind(&done); |
| 9614 | } |
| 9615 | |
| 9616 | void CodeGenerator::visitGetNextEntryForIterator( |
| 9617 | LGetNextEntryForIterator* lir) { |
| 9618 | if (lir->mir()->mode() == MGetNextEntryForIterator::Map) { |
| 9619 | emitGetNextEntryForIterator<MapIteratorObject, MapObject>(lir); |
| 9620 | } else { |
| 9621 | MOZ_ASSERT(lir->mir()->mode() == MGetNextEntryForIterator::Set)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->mode() == MGetNextEntryForIterator ::Set)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->mode() == MGetNextEntryForIterator ::Set))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->mode() == MGetNextEntryForIterator::Set", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9621); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MGetNextEntryForIterator::Set" ")"); do { *((volatile int*)__null) = 9621; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9622 | emitGetNextEntryForIterator<SetIteratorObject, SetObject>(lir); |
| 9623 | } |
| 9624 | } |
| 9625 | |
| 9626 | // The point of these is to inform Ion of where these values already are; they |
| 9627 | // don't normally generate (much) code. |
| 9628 | void CodeGenerator::visitWasmRegisterPairResult(LWasmRegisterPairResult* lir) {} |
| 9629 | void CodeGenerator::visitWasmStackResult(LWasmStackResult* lir) {} |
| 9630 | void CodeGenerator::visitWasmStackResult64(LWasmStackResult64* lir) {} |
| 9631 | |
| 9632 | void CodeGenerator::visitWasmStackResultArea(LWasmStackResultArea* lir) { |
| 9633 | LAllocation* output = lir->getDef(0)->output(); |
| 9634 | MOZ_ASSERT(output->isStackArea())do { static_assert( mozilla::detail::AssertionConditionType< decltype(output->isStackArea())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output->isStackArea()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output->isStackArea()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9634); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output->isStackArea()" ")"); do { *((volatile int*)__null) = 9634; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9635 | bool tempInit = false; |
| 9636 | for (auto iter = output->toStackArea()->results(); iter; iter.next()) { |
| 9637 | // Zero out ref stack results. |
| 9638 | if (iter.isWasmAnyRef()) { |
| 9639 | Register temp = ToRegister(lir->temp0()); |
| 9640 | if (!tempInit) { |
| 9641 | masm.xorPtr(temp, temp); |
| 9642 | tempInit = true; |
| 9643 | } |
| 9644 | masm.storePtr(temp, ToAddress(iter.alloc())); |
| 9645 | } |
| 9646 | } |
| 9647 | } |
| 9648 | |
| 9649 | void CodeGenerator::visitWasmRegisterResult(LWasmRegisterResult* lir) { |
| 9650 | #ifdef JS_64BIT1 |
| 9651 | if (MWasmRegisterResult* mir = lir->mir()) { |
| 9652 | if (mir->type() == MIRType::Int32) { |
| 9653 | masm.widenInt32(ToRegister(lir->output())); |
| 9654 | } |
| 9655 | } |
| 9656 | #endif |
| 9657 | } |
| 9658 | |
| 9659 | void CodeGenerator::visitWasmCall(LWasmCall* lir) { |
| 9660 | const MWasmCallBase* callBase = lir->callBase(); |
| 9661 | bool isReturnCall = lir->isReturnCall(); |
| 9662 | |
| 9663 | // If this call is in Wasm try code block, initialise a wasm::TryNote for this |
| 9664 | // call. |
| 9665 | bool inTry = callBase->inTry(); |
| 9666 | if (inTry) { |
| 9667 | size_t tryNoteIndex = callBase->tryNoteIndex(); |
| 9668 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
| 9669 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
| 9670 | tryNote.setTryBodyBegin(masm.currentOffset()); |
| 9671 | } |
| 9672 | |
| 9673 | MOZ_ASSERT((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment ==do { static_assert( mozilla::detail::AssertionConditionType< decltype((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" ")"); do { *((volatile int*)__null) = 9674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 9674 | 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!((sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9674); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" ")"); do { *((volatile int*)__null) = 9674; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9675 | static_assert( |
| 9676 | WasmStackAlignment >= ABIStackAlignment && |
| 9677 | WasmStackAlignment % ABIStackAlignment == 0, |
| 9678 | "The wasm stack alignment should subsume the ABI-required alignment"); |
| 9679 | |
| 9680 | #ifdef DEBUG1 |
| 9681 | Label ok; |
| 9682 | masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok); |
| 9683 | masm.breakpoint(); |
| 9684 | masm.bind(&ok); |
| 9685 | #endif |
| 9686 | |
| 9687 | // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the |
| 9688 | // instance and pinned regs. The only case where where we don't have to |
| 9689 | // reload the instance and pinned regs is when the callee preserves them. |
| 9690 | bool reloadRegs = true; |
| 9691 | bool switchRealm = true; |
| 9692 | |
| 9693 | const wasm::CallSiteDesc& desc = callBase->desc(); |
| 9694 | const wasm::CalleeDesc& callee = callBase->callee(); |
| 9695 | CodeOffset retOffset; |
| 9696 | CodeOffset secondRetOffset; |
| 9697 | switch (callee.which()) { |
| 9698 | case wasm::CalleeDesc::Func: |
| 9699 | if (isReturnCall) { |
| 9700 | ReturnCallAdjustmentInfo retCallInfo( |
| 9701 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
| 9702 | masm.wasmReturnCall(desc, callee.funcIndex(), retCallInfo); |
| 9703 | // The rest of the method is unnecessary for a return call. |
| 9704 | return; |
| 9705 | } |
| 9706 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9706); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9706; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9707 | retOffset = masm.call(desc, callee.funcIndex()); |
| 9708 | reloadRegs = false; |
| 9709 | switchRealm = false; |
| 9710 | break; |
| 9711 | case wasm::CalleeDesc::Import: |
| 9712 | if (isReturnCall) { |
| 9713 | ReturnCallAdjustmentInfo retCallInfo( |
| 9714 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
| 9715 | masm.wasmReturnCallImport(desc, callee, retCallInfo); |
| 9716 | // The rest of the method is unnecessary for a return call. |
| 9717 | return; |
| 9718 | } |
| 9719 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9719); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9719; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9720 | retOffset = masm.wasmCallImport(desc, callee); |
| 9721 | break; |
| 9722 | case wasm::CalleeDesc::AsmJSTable: |
| 9723 | retOffset = masm.asmCallIndirect(desc, callee); |
| 9724 | break; |
| 9725 | case wasm::CalleeDesc::WasmTable: { |
| 9726 | Label* boundsCheckFailed = nullptr; |
| 9727 | if (lir->needsBoundsCheck()) { |
| 9728 | OutOfLineAbortingWasmTrap* ool = |
| 9729 | new (alloc()) OutOfLineAbortingWasmTrap(desc.toTrapSiteDesc(), |
| 9730 | wasm::Trap::OutOfBounds); |
| 9731 | if (lir->isCatchable()) { |
| 9732 | addOutOfLineCode(ool, lir->mirCatchable()); |
| 9733 | } else if (isReturnCall) { |
| 9734 | addOutOfLineCode(ool, lir->mirReturnCall()); |
| 9735 | } else { |
| 9736 | addOutOfLineCode(ool, lir->mirUncatchable()); |
| 9737 | } |
| 9738 | boundsCheckFailed = ool->entry(); |
| 9739 | } |
| 9740 | Label* nullCheckFailed = nullptr; |
| 9741 | #ifndef WASM_HAS_HEAPREG1 |
| 9742 | { |
| 9743 | OutOfLineAbortingWasmTrap* ool = |
| 9744 | new (alloc()) OutOfLineAbortingWasmTrap( |
| 9745 | desc.toTrapSiteDesc(), wasm::Trap::IndirectCallToNull); |
| 9746 | if (lir->isCatchable()) { |
| 9747 | addOutOfLineCode(ool, lir->mirCatchable()); |
| 9748 | } else if (isReturnCall) { |
| 9749 | addOutOfLineCode(ool, lir->mirReturnCall()); |
| 9750 | } else { |
| 9751 | addOutOfLineCode(ool, lir->mirUncatchable()); |
| 9752 | } |
| 9753 | nullCheckFailed = ool->entry(); |
| 9754 | } |
| 9755 | #endif |
| 9756 | if (isReturnCall) { |
| 9757 | ReturnCallAdjustmentInfo retCallInfo( |
| 9758 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
| 9759 | masm.wasmReturnCallIndirect(desc, callee, boundsCheckFailed, |
| 9760 | nullCheckFailed, mozilla::Nothing(), |
| 9761 | retCallInfo); |
| 9762 | // The rest of the method is unnecessary for a return call. |
| 9763 | return; |
| 9764 | } |
| 9765 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9765); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9765; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9766 | masm.wasmCallIndirect(desc, callee, boundsCheckFailed, nullCheckFailed, |
| 9767 | lir->tableSize(), &retOffset, &secondRetOffset); |
| 9768 | // Register reloading and realm switching are handled dynamically inside |
| 9769 | // wasmCallIndirect. There are two return offsets, one for each call |
| 9770 | // instruction (fast path and slow path). |
| 9771 | reloadRegs = false; |
| 9772 | switchRealm = false; |
| 9773 | break; |
| 9774 | } |
| 9775 | case wasm::CalleeDesc::Builtin: |
| 9776 | retOffset = masm.call(desc, callee.builtin()); |
| 9777 | reloadRegs = false; |
| 9778 | switchRealm = false; |
| 9779 | break; |
| 9780 | case wasm::CalleeDesc::BuiltinInstanceMethod: |
| 9781 | retOffset = masm.wasmCallBuiltinInstanceMethod( |
| 9782 | desc, callBase->instanceArg(), callee.builtin(), |
| 9783 | callBase->builtinMethodFailureMode()); |
| 9784 | switchRealm = false; |
| 9785 | break; |
| 9786 | case wasm::CalleeDesc::FuncRef: |
| 9787 | if (isReturnCall) { |
| 9788 | ReturnCallAdjustmentInfo retCallInfo( |
| 9789 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
| 9790 | masm.wasmReturnCallRef(desc, callee, retCallInfo); |
| 9791 | // The rest of the method is unnecessary for a return call. |
| 9792 | return; |
| 9793 | } |
| 9794 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9794); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9794; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9795 | // Register reloading and realm switching are handled dynamically inside |
| 9796 | // wasmCallRef. There are two return offsets, one for each call |
| 9797 | // instruction (fast path and slow path). |
| 9798 | masm.wasmCallRef(desc, callee, &retOffset, &secondRetOffset); |
| 9799 | reloadRegs = false; |
| 9800 | switchRealm = false; |
| 9801 | break; |
| 9802 | } |
| 9803 | |
| 9804 | // Note the assembler offset for the associated LSafePoint. |
| 9805 | MOZ_ASSERT(!isReturnCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!isReturnCall)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!isReturnCall))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!isReturnCall", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9805); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { *((volatile int*)__null) = 9805; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9806 | markSafepointAt(retOffset.offset(), lir); |
| 9807 | |
| 9808 | // Now that all the outbound in-memory args are on the stack, note the |
| 9809 | // required lower boundary point of the associated StackMap. |
| 9810 | uint32_t framePushedAtStackMapBase = |
| 9811 | masm.framePushed() - |
| 9812 | wasm::AlignStackArgAreaSize(callBase->stackArgAreaSizeUnaligned()); |
| 9813 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAtStackMapBase); |
| 9814 | MOZ_ASSERT(lir->safepoint()->wasmSafepointKind() ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->safepoint()->wasmSafepointKind() == WasmSafepointKind ::LirCall)>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(lir->safepoint()->wasmSafepointKind () == WasmSafepointKind::LirCall))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9815); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" ")"); do { *((volatile int*)__null) = 9815; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 9815 | WasmSafepointKind::LirCall)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->safepoint()->wasmSafepointKind() == WasmSafepointKind ::LirCall)>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(lir->safepoint()->wasmSafepointKind () == WasmSafepointKind::LirCall))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9815); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" ")"); do { *((volatile int*)__null) = 9815; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9816 | |
| 9817 | // Note the assembler offset and framePushed for use by the adjunct |
| 9818 | // LSafePoint, see visitor for LWasmCallIndirectAdjunctSafepoint below. |
| 9819 | if (callee.which() == wasm::CalleeDesc::WasmTable || |
| 9820 | callee.which() == wasm::CalleeDesc::FuncRef) { |
| 9821 | lir->adjunctSafepoint()->recordSafepointInfo(secondRetOffset, |
| 9822 | framePushedAtStackMapBase); |
| 9823 | } |
| 9824 | |
| 9825 | if (reloadRegs) { |
| 9826 | masm.loadPtr( |
| 9827 | Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall), |
| 9828 | InstanceReg); |
| 9829 | masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); |
| 9830 | if (switchRealm) { |
| 9831 | masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1); |
| 9832 | } |
| 9833 | } else { |
| 9834 | MOZ_ASSERT(!switchRealm)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!switchRealm)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!switchRealm))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!switchRealm", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9834); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!switchRealm" ")"); do { *((volatile int*)__null) = 9834; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9835 | } |
| 9836 | |
| 9837 | switch (callee.which()) { |
| 9838 | case wasm::CalleeDesc::Func: |
| 9839 | case wasm::CalleeDesc::Import: |
| 9840 | case wasm::CalleeDesc::WasmTable: |
| 9841 | case wasm::CalleeDesc::FuncRef: |
| 9842 | // Stack allocation could change during Wasm (return) calls, |
| 9843 | // recover pre-call state. |
| 9844 | masm.freeStackTo(masm.framePushed()); |
| 9845 | break; |
| 9846 | default: |
| 9847 | break; |
| 9848 | } |
| 9849 | |
| 9850 | if (inTry) { |
| 9851 | // Set the end of the try note range |
| 9852 | size_t tryNoteIndex = callBase->tryNoteIndex(); |
| 9853 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
| 9854 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
| 9855 | |
| 9856 | // Don't set the end of the try note if we've OOM'ed, as the above |
| 9857 | // instructions may not have been emitted, which will trigger an assert |
| 9858 | // about zero-length try-notes. This is okay as this compilation will be |
| 9859 | // thrown away. |
| 9860 | if (!masm.oom()) { |
| 9861 | tryNote.setTryBodyEnd(masm.currentOffset()); |
| 9862 | } |
| 9863 | |
| 9864 | // This instruction or the adjunct safepoint must be the last instruction |
| 9865 | // in the block. No other instructions may be inserted. |
| 9866 | LBlock* block = lir->block(); |
| 9867 | MOZ_RELEASE_ASSERT(*block->rbegin() == lir ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 9868 | (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 9869 | *(++block->rbegin()) == lir))do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir || (block->rbegin()-> isWasmCallIndirectAdjunctSafepoint() && *(++block-> rbegin()) == lir))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9869); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir || (block->rbegin()->isWasmCallIndirectAdjunctSafepoint() && *(++block->rbegin()) == lir)" ")"); do { *((volatile int*)__null) = 9869; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9870 | |
| 9871 | // Jump to the fallthrough block |
| 9872 | jumpToBlock(lir->mirCatchable()->getSuccessor( |
| 9873 | MWasmCallCatchable::FallthroughBranchIndex)); |
| 9874 | } |
| 9875 | } |
| 9876 | |
| 9877 | #ifdef ENABLE_WASM_JSPI1 |
| 9878 | void CodeGenerator::callWasmUpdateSuspenderState( |
| 9879 | wasm::UpdateSuspenderStateAction kind, Register suspender, Register temp) { |
| 9880 | masm.Push(InstanceReg); |
| 9881 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 9882 | |
| 9883 | masm.move32(Imm32(uint32_t(kind)), temp); |
| 9884 | |
| 9885 | masm.setupWasmABICall(); |
| 9886 | masm.passABIArg(InstanceReg); |
| 9887 | masm.passABIArg(suspender); |
| 9888 | masm.passABIArg(temp); |
| 9889 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 9890 | masm.callWithABI(wasm::BytecodeOffset(0), |
| 9891 | wasm::SymbolicAddress::UpdateSuspenderState, |
| 9892 | mozilla::Some(instanceOffset)); |
| 9893 | |
| 9894 | masm.Pop(InstanceReg); |
| 9895 | } |
| 9896 | |
| 9897 | void CodeGenerator::prepareWasmStackSwitchTrampolineCall(Register suspender, |
| 9898 | Register data) { |
| 9899 | // Reserve stack space for the wasm call. |
| 9900 | unsigned argDecrement; |
| 9901 | { |
| 9902 | WasmABIArgGenerator abi; |
| 9903 | ABIArg arg; |
| 9904 | arg = abi.next(MIRType::Pointer); |
| 9905 | arg = abi.next(MIRType::Pointer); |
| 9906 | argDecrement = StackDecrementForCall(WasmStackAlignment, 0, |
| 9907 | abi.stackBytesConsumedSoFar()); |
| 9908 | } |
| 9909 | masm.reserveStack(argDecrement); |
| 9910 | |
| 9911 | // Pass the suspender and data params through the wasm function ABI registers. |
| 9912 | WasmABIArgGenerator abi; |
| 9913 | ABIArg arg; |
| 9914 | arg = abi.next(MIRType::Pointer); |
| 9915 | if (arg.kind() == ABIArg::GPR) { |
| 9916 | masm.movePtr(suspender, arg.gpr()); |
| 9917 | } else { |
| 9918 | MOZ_ASSERT(arg.kind() == ABIArg::Stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(arg.kind() == ABIArg::Stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(arg.kind() == ABIArg::Stack) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arg.kind() == ABIArg::Stack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9918); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack" ")"); do { *((volatile int*)__null) = 9918; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9919 | masm.storePtr(suspender, |
| 9920 | Address(masm.getStackPointer(), arg.offsetFromArgBase())); |
| 9921 | } |
| 9922 | arg = abi.next(MIRType::Pointer); |
| 9923 | if (arg.kind() == ABIArg::GPR) { |
| 9924 | masm.movePtr(data, arg.gpr()); |
| 9925 | } else { |
| 9926 | MOZ_ASSERT(arg.kind() == ABIArg::Stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(arg.kind() == ABIArg::Stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(arg.kind() == ABIArg::Stack) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("arg.kind() == ABIArg::Stack" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 9926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "arg.kind() == ABIArg::Stack" ")"); do { *((volatile int*)__null) = 9926; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9927 | masm.storePtr(data, |
| 9928 | Address(masm.getStackPointer(), arg.offsetFromArgBase())); |
| 9929 | } |
| 9930 | |
| 9931 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
| 9932 | WasmCallerInstanceOffsetBeforeCall)); |
| 9933 | } |
| 9934 | #endif // ENABLE_WASM_JSPI |
| 9935 | |
| 9936 | void CodeGenerator::visitWasmStackSwitchToSuspendable( |
| 9937 | LWasmStackSwitchToSuspendable* lir) { |
| 9938 | #ifdef ENABLE_WASM_JSPI1 |
| 9939 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
| 9940 | const Register FnReg = lir->fn()->toRegister().gpr(); |
| 9941 | const Register DataReg = lir->data()->toRegister().gpr(); |
| 9942 | const Register SuspenderDataReg = ABINonArgReg3; |
| 9943 | |
| 9944 | # ifdef JS_CODEGEN_ARM64 |
| 9945 | vixl::UseScratchRegisterScope temps(&masm); |
| 9946 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
| 9947 | # elif defined(JS_CODEGEN_X86) |
| 9948 | const Register ScratchReg1 = ABINonArgReg3; |
| 9949 | # elif defined(JS_CODEGEN_X641) |
| 9950 | const Register ScratchReg1 = ScratchReg; |
| 9951 | # elif defined(JS_CODEGEN_ARM) |
| 9952 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
| 9953 | # elif defined(JS_CODEGEN_LOONG64) |
| 9954 | SecondScratchRegisterScope scratch2(masm); |
| 9955 | const Register ScratchReg1 = scratch2; |
| 9956 | # else |
| 9957 | # error "NYI: scratch register" |
| 9958 | # endif |
| 9959 | |
| 9960 | masm.Push(SuspenderReg); |
| 9961 | masm.Push(FnReg); |
| 9962 | masm.Push(DataReg); |
| 9963 | |
| 9964 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Enter, |
| 9965 | SuspenderReg, ScratchReg1); |
| 9966 | masm.Pop(DataReg); |
| 9967 | masm.Pop(FnReg); |
| 9968 | masm.Pop(SuspenderReg); |
| 9969 | |
| 9970 | masm.Push(SuspenderReg); |
| 9971 | int32_t framePushedAtSuspender = masm.framePushed(); |
| 9972 | masm.Push(InstanceReg); |
| 9973 | |
| 9974 | wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch); |
| 9975 | CodeLabel returnCallsite; |
| 9976 | |
| 9977 | // Aligning stack before trampoline call. |
| 9978 | uint32_t reserve = ComputeByteAlignment( |
| 9979 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
| 9980 | masm.reserveStack(reserve); |
| 9981 | |
| 9982 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
| 9983 | wasm::SuspenderObjectDataSlot)), |
| 9984 | SuspenderDataReg); |
| 9985 | |
| 9986 | // Switch stacks to suspendable, keep original FP to maintain |
| 9987 | // frames chain between main and suspendable stack segments. |
| 9988 | masm.storeStackPtr( |
| 9989 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
| 9990 | masm.storePtr( |
| 9991 | FramePointer, |
| 9992 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP())); |
| 9993 | |
| 9994 | masm.loadStackPtr(Address( |
| 9995 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
| 9996 | |
| 9997 | masm.assertStackAlignment(WasmStackAlignment); |
| 9998 | |
| 9999 | // The FramePointer is not changed for SwitchToSuspendable. |
| 10000 | uint32_t framePushed = masm.framePushed(); |
| 10001 | |
| 10002 | // On different stack, reset framePushed. FramePointer is not valid here. |
| 10003 | masm.setFramePushed(0); |
| 10004 | |
| 10005 | prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg); |
| 10006 | |
| 10007 | // Get wasm instance pointer for callee. |
| 10008 | size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot( |
| 10009 | FunctionExtended::WASM_INSTANCE_SLOT); |
| 10010 | masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg); |
| 10011 | |
| 10012 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
| 10013 | WasmCalleeInstanceOffsetBeforeCall)); |
| 10014 | masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); |
| 10015 | |
| 10016 | masm.assertStackAlignment(WasmStackAlignment); |
| 10017 | |
| 10018 | const Register ReturnAddressReg = ScratchReg1; |
| 10019 | |
| 10020 | // DataReg is not needed anymore, using it as a scratch register. |
| 10021 | const Register ScratchReg2 = DataReg; |
| 10022 | |
| 10023 | // Save future of suspendable stack exit frame pointer. |
| 10024 | masm.computeEffectiveAddress( |
| 10025 | Address(masm.getStackPointer(), -int32_t(sizeof(wasm::Frame))), |
| 10026 | ScratchReg2); |
| 10027 | masm.storePtr( |
| 10028 | ScratchReg2, |
| 10029 | Address(SuspenderDataReg, |
| 10030 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP())); |
| 10031 | |
| 10032 | masm.mov(&returnCallsite, ReturnAddressReg); |
| 10033 | |
| 10034 | // Call wasm function fast. |
| 10035 | # ifdef JS_USE_LINK_REGISTER |
| 10036 | # if defined(JS_CODEGEN_LOONG64) |
| 10037 | masm.mov(ReturnAddressReg, ra); |
| 10038 | # else |
| 10039 | masm.mov(ReturnAddressReg, lr); |
| 10040 | # endif |
| 10041 | # else |
| 10042 | masm.Push(ReturnAddressReg); |
| 10043 | # endif |
| 10044 | // Get funcUncheckedCallEntry() from the function's |
| 10045 | // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot. |
| 10046 | size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot( |
| 10047 | FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT); |
| 10048 | masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2); |
| 10049 | masm.jump(ScratchReg2); |
| 10050 | |
| 10051 | // About to use valid FramePointer -- restore framePushed. |
| 10052 | masm.setFramePushed(framePushed); |
| 10053 | |
| 10054 | // For IsPlausibleStackMapKey check for the following callsite. |
| 10055 | masm.wasmTrapInstruction(); |
| 10056 | |
| 10057 | // Callsite for return from main stack. |
| 10058 | masm.bind(&returnCallsite); |
| 10059 | masm.append(desc, *returnCallsite.target()); |
| 10060 | masm.addCodeLabel(returnCallsite); |
| 10061 | |
| 10062 | masm.assertStackAlignment(WasmStackAlignment); |
| 10063 | |
| 10064 | markSafepointAt(returnCallsite.target()->offset(), lir); |
| 10065 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
| 10066 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
| 10067 | // Rooting SuspenderReg. |
| 10068 | masm.propagateOOM( |
| 10069 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
| 10070 | |
| 10071 | masm.freeStackTo(framePushed); |
| 10072 | |
| 10073 | masm.freeStack(reserve); |
| 10074 | masm.Pop(InstanceReg); |
| 10075 | masm.Pop(SuspenderReg); |
| 10076 | |
| 10077 | masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2); |
| 10078 | |
| 10079 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave, |
| 10080 | SuspenderReg, ScratchReg1); |
| 10081 | #else |
| 10082 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10082); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 10082; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
| 10083 | #endif // ENABLE_WASM_JSPI |
| 10084 | } |
| 10085 | |
| 10086 | void CodeGenerator::visitWasmStackSwitchToMain(LWasmStackSwitchToMain* lir) { |
| 10087 | #ifdef ENABLE_WASM_JSPI1 |
| 10088 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
| 10089 | const Register FnReg = lir->fn()->toRegister().gpr(); |
| 10090 | const Register DataReg = lir->data()->toRegister().gpr(); |
| 10091 | const Register SuspenderDataReg = ABINonArgReg3; |
| 10092 | |
| 10093 | # ifdef JS_CODEGEN_ARM64 |
| 10094 | vixl::UseScratchRegisterScope temps(&masm); |
| 10095 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
| 10096 | # elif defined(JS_CODEGEN_X86) |
| 10097 | const Register ScratchReg1 = ABINonArgReg3; |
| 10098 | # elif defined(JS_CODEGEN_X641) |
| 10099 | const Register ScratchReg1 = ScratchReg; |
| 10100 | # elif defined(JS_CODEGEN_ARM) |
| 10101 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
| 10102 | # elif defined(JS_CODEGEN_LOONG64) |
| 10103 | SecondScratchRegisterScope scratch2(masm); |
| 10104 | const Register ScratchReg1 = scratch2; |
| 10105 | # else |
| 10106 | # error "NYI: scratch register" |
| 10107 | # endif |
| 10108 | |
| 10109 | masm.Push(SuspenderReg); |
| 10110 | masm.Push(FnReg); |
| 10111 | masm.Push(DataReg); |
| 10112 | |
| 10113 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Suspend, |
| 10114 | SuspenderReg, ScratchReg1); |
| 10115 | |
| 10116 | masm.Pop(DataReg); |
| 10117 | masm.Pop(FnReg); |
| 10118 | masm.Pop(SuspenderReg); |
| 10119 | |
| 10120 | masm.Push(SuspenderReg); |
| 10121 | int32_t framePushedAtSuspender = masm.framePushed(); |
| 10122 | masm.Push(InstanceReg); |
| 10123 | |
| 10124 | wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch); |
| 10125 | CodeLabel returnCallsite; |
| 10126 | |
| 10127 | // Aligning stack before trampoline call. |
| 10128 | uint32_t reserve = ComputeByteAlignment( |
| 10129 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
| 10130 | masm.reserveStack(reserve); |
| 10131 | |
| 10132 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
| 10133 | wasm::SuspenderObjectDataSlot)), |
| 10134 | SuspenderDataReg); |
| 10135 | |
| 10136 | // Switch stacks to main. |
| 10137 | masm.storeStackPtr(Address( |
| 10138 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
| 10139 | masm.storePtr(FramePointer, |
| 10140 | Address(SuspenderDataReg, |
| 10141 | wasm::SuspenderObjectData::offsetOfSuspendableFP())); |
| 10142 | |
| 10143 | masm.loadStackPtr( |
| 10144 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
| 10145 | masm.loadPtr( |
| 10146 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP()), |
| 10147 | FramePointer); |
| 10148 | |
| 10149 | // Set main_ra field to returnCallsite. |
| 10150 | # ifdef JS_CODEGEN_X86 |
| 10151 | // SuspenderDataReg is also ScratchReg1, use DataReg as a scratch register. |
| 10152 | MOZ_ASSERT(ScratchReg1 == SuspenderDataReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ScratchReg1 == SuspenderDataReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ScratchReg1 == SuspenderDataReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ScratchReg1 == SuspenderDataReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 == SuspenderDataReg" ")"); do { *((volatile int*)__null) = 10152; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10153 | masm.push(DataReg); |
| 10154 | masm.mov(&returnCallsite, DataReg); |
| 10155 | masm.storePtr( |
| 10156 | DataReg, |
| 10157 | Address(SuspenderDataReg, |
| 10158 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress())); |
| 10159 | masm.pop(DataReg); |
| 10160 | # else |
| 10161 | MOZ_ASSERT(ScratchReg1 != SuspenderDataReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ScratchReg1 != SuspenderDataReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ScratchReg1 != SuspenderDataReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ScratchReg1 != SuspenderDataReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ScratchReg1 != SuspenderDataReg" ")"); do { *((volatile int*)__null) = 10161; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10162 | masm.mov(&returnCallsite, ScratchReg1); |
| 10163 | masm.storePtr( |
| 10164 | ScratchReg1, |
| 10165 | Address(SuspenderDataReg, |
| 10166 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress())); |
| 10167 | # endif |
| 10168 | |
| 10169 | masm.assertStackAlignment(WasmStackAlignment); |
| 10170 | |
| 10171 | // The FramePointer is pointing to the same |
| 10172 | // place as before switch happened. |
| 10173 | uint32_t framePushed = masm.framePushed(); |
| 10174 | |
| 10175 | // On different stack, reset framePushed. FramePointer is not valid here. |
| 10176 | masm.setFramePushed(0); |
| 10177 | |
| 10178 | prepareWasmStackSwitchTrampolineCall(SuspenderReg, DataReg); |
| 10179 | |
| 10180 | // Get wasm instance pointer for callee. |
| 10181 | size_t instanceSlotOffset = FunctionExtended::offsetOfExtendedSlot( |
| 10182 | FunctionExtended::WASM_INSTANCE_SLOT); |
| 10183 | masm.loadPtr(Address(FnReg, instanceSlotOffset), InstanceReg); |
| 10184 | |
| 10185 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
| 10186 | WasmCalleeInstanceOffsetBeforeCall)); |
| 10187 | masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); |
| 10188 | |
| 10189 | masm.assertStackAlignment(WasmStackAlignment); |
| 10190 | |
| 10191 | const Register ReturnAddressReg = ScratchReg1; |
| 10192 | // DataReg is not needed anymore, using it as a scratch register. |
| 10193 | const Register ScratchReg2 = DataReg; |
| 10194 | |
| 10195 | // Save future of main stack exit frame pointer. |
| 10196 | masm.computeEffectiveAddress( |
| 10197 | Address(masm.getStackPointer(), -int32_t(sizeof(wasm::Frame))), |
| 10198 | ScratchReg2); |
| 10199 | masm.storePtr(ScratchReg2, |
| 10200 | Address(SuspenderDataReg, |
| 10201 | wasm::SuspenderObjectData::offsetOfMainExitFP())); |
| 10202 | |
| 10203 | // Load InstanceReg from suspendable stack exit frame. |
| 10204 | masm.loadPtr(Address(SuspenderDataReg, |
| 10205 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
| 10206 | ScratchReg2); |
| 10207 | masm.loadPtr( |
| 10208 | Address(ScratchReg2, wasm::FrameWithInstances::callerInstanceOffset()), |
| 10209 | ScratchReg2); |
| 10210 | masm.storePtr(ScratchReg2, Address(masm.getStackPointer(), |
| 10211 | WasmCallerInstanceOffsetBeforeCall)); |
| 10212 | |
| 10213 | // Load RA from suspendable stack exit frame. |
| 10214 | masm.loadPtr(Address(SuspenderDataReg, |
| 10215 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
| 10216 | ScratchReg1); |
| 10217 | masm.loadPtr(Address(ScratchReg1, wasm::Frame::returnAddressOffset()), |
| 10218 | ReturnAddressReg); |
| 10219 | |
| 10220 | // Call wasm function fast. |
| 10221 | # ifdef JS_USE_LINK_REGISTER |
| 10222 | # if defined(JS_CODEGEN_LOONG64) |
| 10223 | masm.mov(ReturnAddressReg, ra); |
| 10224 | # else |
| 10225 | masm.mov(ReturnAddressReg, lr); |
| 10226 | # endif |
| 10227 | # else |
| 10228 | masm.Push(ReturnAddressReg); |
| 10229 | # endif |
| 10230 | // Get funcUncheckedCallEntry() from the function's |
| 10231 | // WASM_FUNC_UNCHECKED_ENTRY_SLOT extended slot. |
| 10232 | size_t uncheckedEntrySlotOffset = FunctionExtended::offsetOfExtendedSlot( |
| 10233 | FunctionExtended::WASM_FUNC_UNCHECKED_ENTRY_SLOT); |
| 10234 | masm.loadPtr(Address(FnReg, uncheckedEntrySlotOffset), ScratchReg2); |
| 10235 | masm.jump(ScratchReg2); |
| 10236 | |
| 10237 | // About to use valid FramePointer -- restore framePushed. |
| 10238 | masm.setFramePushed(framePushed); |
| 10239 | |
| 10240 | // For IsPlausibleStackMapKey check for the following callsite. |
| 10241 | masm.wasmTrapInstruction(); |
| 10242 | |
| 10243 | // Callsite for return from suspendable stack. |
| 10244 | masm.bind(&returnCallsite); |
| 10245 | masm.append(desc, *returnCallsite.target()); |
| 10246 | masm.addCodeLabel(returnCallsite); |
| 10247 | |
| 10248 | masm.assertStackAlignment(WasmStackAlignment); |
| 10249 | |
| 10250 | markSafepointAt(returnCallsite.target()->offset(), lir); |
| 10251 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
| 10252 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
| 10253 | // Rooting SuspenderReg. |
| 10254 | masm.propagateOOM( |
| 10255 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
| 10256 | |
| 10257 | masm.freeStackTo(framePushed); |
| 10258 | |
| 10259 | // Push ReturnReg that is passed from ContinueOnSuspended on the stack after, |
| 10260 | // the SuspenderReg has been restored (see ScratchReg1 push below). |
| 10261 | // (On some platforms SuspenderReg == ReturnReg) |
| 10262 | masm.mov(ReturnReg, ScratchReg1); |
| 10263 | |
| 10264 | masm.freeStack(reserve); |
| 10265 | masm.Pop(InstanceReg); |
| 10266 | masm.Pop(SuspenderReg); |
| 10267 | |
| 10268 | masm.Push(ScratchReg1); |
| 10269 | |
| 10270 | masm.switchToWasmInstanceRealm(ScratchReg1, ScratchReg2); |
| 10271 | |
| 10272 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Resume, |
| 10273 | SuspenderReg, ScratchReg1); |
| 10274 | |
| 10275 | masm.Pop(ToRegister(lir->output())); |
| 10276 | |
| 10277 | #else |
| 10278 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10278); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 10278; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
| 10279 | #endif // ENABLE_WASM_JSPI |
| 10280 | } |
| 10281 | |
| 10282 | void CodeGenerator::visitWasmStackContinueOnSuspendable( |
| 10283 | LWasmStackContinueOnSuspendable* lir) { |
| 10284 | #ifdef ENABLE_WASM_JSPI1 |
| 10285 | const Register SuspenderReg = lir->suspender()->toRegister().gpr(); |
| 10286 | const Register ResultReg = lir->result()->toRegister().gpr(); |
| 10287 | const Register SuspenderDataReg = ABINonArgReg3; |
| 10288 | |
| 10289 | # ifdef JS_CODEGEN_ARM64 |
| 10290 | vixl::UseScratchRegisterScope temps(&masm); |
| 10291 | const Register ScratchReg1 = temps.AcquireX().asUnsized(); |
| 10292 | # elif defined(JS_CODEGEN_X86) |
| 10293 | const Register ScratchReg1 = ABINonArgReturnReg1; |
| 10294 | # elif defined(JS_CODEGEN_X641) |
| 10295 | const Register ScratchReg1 = ScratchReg; |
| 10296 | # elif defined(JS_CODEGEN_ARM) |
| 10297 | const Register ScratchReg1 = ABINonArgReturnVolatileReg; |
| 10298 | # elif defined(JS_CODEGEN_LOONG64) |
| 10299 | SecondScratchRegisterScope scratch2(masm); |
| 10300 | const Register ScratchReg1 = scratch2; |
| 10301 | # else |
| 10302 | # error "NYI: scratch register" |
| 10303 | # endif |
| 10304 | const Register ScratchReg2 = ABINonArgReg1; |
| 10305 | |
| 10306 | masm.Push(SuspenderReg); |
| 10307 | int32_t framePushedAtSuspender = masm.framePushed(); |
| 10308 | masm.Push(InstanceReg); |
| 10309 | |
| 10310 | wasm::CallSiteDesc desc(wasm::CallSiteKind::StackSwitch); |
| 10311 | CodeLabel returnCallsite; |
| 10312 | |
| 10313 | // Aligning stack before trampoline call. |
| 10314 | uint32_t reserve = ComputeByteAlignment( |
| 10315 | masm.framePushed() - sizeof(wasm::Frame), WasmStackAlignment); |
| 10316 | masm.reserveStack(reserve); |
| 10317 | |
| 10318 | masm.loadPrivate(Address(SuspenderReg, NativeObject::getFixedSlotOffset( |
| 10319 | wasm::SuspenderObjectDataSlot)), |
| 10320 | SuspenderDataReg); |
| 10321 | masm.storeStackPtr( |
| 10322 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainSP())); |
| 10323 | masm.storePtr( |
| 10324 | FramePointer, |
| 10325 | Address(SuspenderDataReg, wasm::SuspenderObjectData::offsetOfMainFP())); |
| 10326 | |
| 10327 | // Adjust exit frame FP. |
| 10328 | masm.loadPtr(Address(SuspenderDataReg, |
| 10329 | wasm::SuspenderObjectData::offsetOfSuspendableExitFP()), |
| 10330 | ScratchReg1); |
| 10331 | masm.storePtr(FramePointer, |
| 10332 | Address(ScratchReg1, wasm::Frame::callerFPOffset())); |
| 10333 | |
| 10334 | // Adjust exit frame RA. |
| 10335 | masm.mov(&returnCallsite, ScratchReg2); |
| 10336 | |
| 10337 | masm.storePtr(ScratchReg2, |
| 10338 | Address(ScratchReg1, wasm::Frame::returnAddressOffset())); |
| 10339 | // Adjust exit frame caller instance slot. |
| 10340 | masm.storePtr( |
| 10341 | InstanceReg, |
| 10342 | Address(ScratchReg1, wasm::FrameWithInstances::callerInstanceOffset())); |
| 10343 | |
| 10344 | // Switch stacks to suspendable. |
| 10345 | masm.loadStackPtr(Address( |
| 10346 | SuspenderDataReg, wasm::SuspenderObjectData::offsetOfSuspendableSP())); |
| 10347 | masm.loadPtr(Address(SuspenderDataReg, |
| 10348 | wasm::SuspenderObjectData::offsetOfSuspendableFP()), |
| 10349 | FramePointer); |
| 10350 | |
| 10351 | masm.assertStackAlignment(WasmStackAlignment); |
| 10352 | |
| 10353 | // The FramePointer is pointing to the same |
| 10354 | // place as before switch happened. |
| 10355 | uint32_t framePushed = masm.framePushed(); |
| 10356 | |
| 10357 | // On different stack, reset framePushed. FramePointer is not valid here. |
| 10358 | masm.setFramePushed(0); |
| 10359 | |
| 10360 | // Restore shadow stack area and instance slots. |
| 10361 | WasmABIArgGenerator abi; |
| 10362 | unsigned reserveBeforeCall = abi.stackBytesConsumedSoFar(); |
| 10363 | MOZ_ASSERT(masm.framePushed() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("masm.framePushed() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10363); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { *((volatile int*)__null) = 10363; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10364 | unsigned argDecrement = |
| 10365 | StackDecrementForCall(WasmStackAlignment, 0, reserveBeforeCall); |
| 10366 | masm.reserveStack(argDecrement); |
| 10367 | |
| 10368 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
| 10369 | WasmCallerInstanceOffsetBeforeCall)); |
| 10370 | masm.storePtr(InstanceReg, Address(masm.getStackPointer(), |
| 10371 | WasmCalleeInstanceOffsetBeforeCall)); |
| 10372 | |
| 10373 | masm.assertStackAlignment(WasmStackAlignment); |
| 10374 | |
| 10375 | // Transfer results to ReturnReg so it will appear at SwitchToMain return. |
| 10376 | masm.mov(ResultReg, ReturnReg); |
| 10377 | |
| 10378 | const Register ReturnAddressReg = ScratchReg1; |
| 10379 | |
| 10380 | // Pretend we just returned from the function. |
| 10381 | masm.loadPtr( |
| 10382 | Address(SuspenderDataReg, |
| 10383 | wasm::SuspenderObjectData::offsetOfSuspendedReturnAddress()), |
| 10384 | ReturnAddressReg); |
| 10385 | masm.jump(ReturnAddressReg); |
| 10386 | |
| 10387 | // About to use valid FramePointer -- restore framePushed. |
| 10388 | masm.setFramePushed(framePushed); |
| 10389 | |
| 10390 | // For IsPlausibleStackMapKey check for the following callsite. |
| 10391 | masm.wasmTrapInstruction(); |
| 10392 | |
| 10393 | // Callsite for return from suspendable stack. |
| 10394 | masm.bind(&returnCallsite); |
| 10395 | masm.append(desc, *returnCallsite.target()); |
| 10396 | masm.addCodeLabel(returnCallsite); |
| 10397 | |
| 10398 | masm.assertStackAlignment(WasmStackAlignment); |
| 10399 | |
| 10400 | markSafepointAt(returnCallsite.target()->offset(), lir); |
| 10401 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
| 10402 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
| 10403 | // Rooting SuspenderReg. |
| 10404 | masm.propagateOOM( |
| 10405 | lir->safepoint()->addWasmAnyRefSlot(true, framePushedAtSuspender)); |
| 10406 | |
| 10407 | masm.freeStackTo(framePushed); |
| 10408 | |
| 10409 | masm.freeStack(reserve); |
| 10410 | masm.Pop(InstanceReg); |
| 10411 | masm.Pop(SuspenderReg); |
| 10412 | |
| 10413 | // Using SuspenderDataReg and ABINonArgReg2 as temps. |
| 10414 | masm.switchToWasmInstanceRealm(SuspenderDataReg, ABINonArgReg2); |
| 10415 | |
| 10416 | callWasmUpdateSuspenderState(wasm::UpdateSuspenderStateAction::Leave, |
| 10417 | SuspenderReg, ScratchReg1); |
| 10418 | #else |
| 10419 | MOZ_CRASH("NYI")do { do { } while (false); MOZ_ReportCrash("" "NYI", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10419); AnnotateMozCrashReason("MOZ_CRASH(" "NYI" ")"); do { *((volatile int*)__null) = 10419; __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
| 10420 | #endif // ENABLE_WASM_JSPI |
| 10421 | } |
| 10422 | |
| 10423 | void CodeGenerator::visitWasmCallLandingPrePad(LWasmCallLandingPrePad* lir) { |
| 10424 | LBlock* block = lir->block(); |
| 10425 | MWasmCallLandingPrePad* mir = lir->mir(); |
| 10426 | MBasicBlock* mirBlock = mir->block(); |
| 10427 | MBasicBlock* callMirBlock = mir->callBlock(); |
| 10428 | |
| 10429 | // This block must be the pre-pad successor of the call block. No blocks may |
| 10430 | // be inserted between us, such as for critical edge splitting. |
| 10431 | MOZ_RELEASE_ASSERT(mirBlock == callMirBlock->getSuccessor(do { static_assert( mozilla::detail::AssertionConditionType< decltype(mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable ::PrePadBranchIndex))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mirBlock == callMirBlock-> getSuccessor( MWasmCallCatchable::PrePadBranchIndex)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10432); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" ")"); do { *((volatile int*)__null) = 10432; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 10432 | MWasmCallCatchable::PrePadBranchIndex))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable ::PrePadBranchIndex))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mirBlock == callMirBlock-> getSuccessor( MWasmCallCatchable::PrePadBranchIndex)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10432); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" ")"); do { *((volatile int*)__null) = 10432; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10433 | |
| 10434 | // This instruction or a move group must be the first instruction in the |
| 10435 | // block. No other instructions may be inserted. |
| 10436 | MOZ_RELEASE_ASSERT(*block->begin() == lir || (block->begin()->isMoveGroup() &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir))>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10437); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" ")"); do { *((volatile int*)__null) = 10437; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 10437 | *(++block->begin()) == lir))do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir))>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(*block->begin() == lir || (block->begin()-> isMoveGroup() && *(++block->begin()) == lir)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10437); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" ")"); do { *((volatile int*)__null) = 10437; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10438 | |
| 10439 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
| 10440 | wasm::TryNote& tryNote = tryNotes[mir->tryNoteIndex()]; |
| 10441 | // Set the entry point for the call try note to be the beginning of this |
| 10442 | // block. The above assertions (and assertions in visitWasmCall) guarantee |
| 10443 | // that we are not skipping over instructions that should be executed. |
| 10444 | tryNote.setLandingPad(block->label()->offset(), masm.framePushed()); |
| 10445 | } |
| 10446 | |
| 10447 | void CodeGenerator::visitWasmCallIndirectAdjunctSafepoint( |
| 10448 | LWasmCallIndirectAdjunctSafepoint* lir) { |
| 10449 | markSafepointAt(lir->safepointLocation().offset(), lir); |
| 10450 | lir->safepoint()->setFramePushedAtStackMapBase( |
| 10451 | lir->framePushedAtStackMapBase()); |
| 10452 | } |
| 10453 | |
| 10454 | template <typename InstructionWithMaybeTrapSite> |
| 10455 | void EmitSignalNullCheckTrapSite(MacroAssembler& masm, |
| 10456 | InstructionWithMaybeTrapSite* ins, |
| 10457 | FaultingCodeOffset fco, |
| 10458 | wasm::TrapMachineInsn tmi) { |
| 10459 | if (!ins->maybeTrap()) { |
| 10460 | return; |
| 10461 | } |
| 10462 | masm.append(wasm::Trap::NullPointerDereference, |
| 10463 | wasm::TrapSite(tmi, fco, *ins->maybeTrap())); |
| 10464 | } |
| 10465 | |
| 10466 | template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex> |
| 10467 | void CodeGenerator::emitWasmValueLoad(InstructionWithMaybeTrapSite* ins, |
| 10468 | MIRType type, MWideningOp wideningOp, |
| 10469 | AddressOrBaseIndex addr, |
| 10470 | AnyRegister dst) { |
| 10471 | FaultingCodeOffset fco; |
| 10472 | switch (type) { |
| 10473 | case MIRType::Int32: |
| 10474 | switch (wideningOp) { |
| 10475 | case MWideningOp::None: |
| 10476 | fco = masm.load32(addr, dst.gpr()); |
| 10477 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10478 | wasm::TrapMachineInsn::Load32); |
| 10479 | break; |
| 10480 | case MWideningOp::FromU16: |
| 10481 | fco = masm.load16ZeroExtend(addr, dst.gpr()); |
| 10482 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10483 | wasm::TrapMachineInsn::Load16); |
| 10484 | break; |
| 10485 | case MWideningOp::FromS16: |
| 10486 | fco = masm.load16SignExtend(addr, dst.gpr()); |
| 10487 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10488 | wasm::TrapMachineInsn::Load16); |
| 10489 | break; |
| 10490 | case MWideningOp::FromU8: |
| 10491 | fco = masm.load8ZeroExtend(addr, dst.gpr()); |
| 10492 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10493 | wasm::TrapMachineInsn::Load8); |
| 10494 | break; |
| 10495 | case MWideningOp::FromS8: |
| 10496 | fco = masm.load8SignExtend(addr, dst.gpr()); |
| 10497 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10498 | wasm::TrapMachineInsn::Load8); |
| 10499 | break; |
| 10500 | default: |
| 10501 | MOZ_CRASH("unexpected widening op in ::visitWasmLoadElement")do { do { } while (false); MOZ_ReportCrash("" "unexpected widening op in ::visitWasmLoadElement" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10501); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected widening op in ::visitWasmLoadElement" ")"); do { *((volatile int*)__null) = 10501; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 10502 | } |
| 10503 | break; |
| 10504 | case MIRType::Float32: |
| 10505 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10505; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10506 | fco = masm.loadFloat32(addr, dst.fpu()); |
| 10507 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10508 | wasm::TrapMachineInsn::Load32); |
| 10509 | break; |
| 10510 | case MIRType::Double: |
| 10511 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10511); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10511; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10512 | fco = masm.loadDouble(addr, dst.fpu()); |
| 10513 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10514 | wasm::TrapMachineInsn::Load64); |
| 10515 | break; |
| 10516 | case MIRType::Pointer: |
| 10517 | case MIRType::WasmAnyRef: |
| 10518 | case MIRType::WasmArrayData: |
| 10519 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10519); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10519; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10520 | fco = masm.loadPtr(addr, dst.gpr()); |
| 10521 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10522 | wasm::TrapMachineInsnForLoadWord()); |
| 10523 | break; |
| 10524 | default: |
| 10525 | MOZ_CRASH("unexpected type in ::emitWasmValueLoad")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueLoad" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10525); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueLoad" ")"); do { *((volatile int*)__null) = 10525; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 10526 | } |
| 10527 | } |
| 10528 | |
| 10529 | template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndex> |
| 10530 | void CodeGenerator::emitWasmValueStore(InstructionWithMaybeTrapSite* ins, |
| 10531 | MIRType type, MNarrowingOp narrowingOp, |
| 10532 | AnyRegister src, |
| 10533 | AddressOrBaseIndex addr) { |
| 10534 | FaultingCodeOffset fco; |
| 10535 | switch (type) { |
| 10536 | case MIRType::Int32: |
| 10537 | switch (narrowingOp) { |
| 10538 | case MNarrowingOp::None: |
| 10539 | fco = masm.store32(src.gpr(), addr); |
| 10540 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10541 | wasm::TrapMachineInsn::Store32); |
| 10542 | break; |
| 10543 | case MNarrowingOp::To16: |
| 10544 | fco = masm.store16(src.gpr(), addr); |
| 10545 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10546 | wasm::TrapMachineInsn::Store16); |
| 10547 | break; |
| 10548 | case MNarrowingOp::To8: |
| 10549 | fco = masm.store8(src.gpr(), addr); |
| 10550 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10551 | wasm::TrapMachineInsn::Store8); |
| 10552 | break; |
| 10553 | default: |
| 10554 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10554); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 10554; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
| 10555 | } |
| 10556 | break; |
| 10557 | case MIRType::Float32: |
| 10558 | fco = masm.storeFloat32(src.fpu(), addr); |
| 10559 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10560 | wasm::TrapMachineInsn::Store32); |
| 10561 | break; |
| 10562 | case MIRType::Double: |
| 10563 | fco = masm.storeDouble(src.fpu(), addr); |
| 10564 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10565 | wasm::TrapMachineInsn::Store64); |
| 10566 | break; |
| 10567 | case MIRType::Pointer: |
| 10568 | // This could be correct, but it would be a new usage, so check carefully. |
| 10569 | MOZ_CRASH("Unexpected type in ::emitWasmValueStore.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type in ::emitWasmValueStore." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10569); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected type in ::emitWasmValueStore." ")"); do { *((volatile int*)__null) = 10569; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 10570 | case MIRType::WasmAnyRef: |
| 10571 | MOZ_CRASH("Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef.")do { do { } while (false); MOZ_ReportCrash("" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef." , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10571); AnnotateMozCrashReason("MOZ_CRASH(" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef." ")"); do { *((volatile int*)__null) = 10571; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 10572 | default: |
| 10573 | MOZ_CRASH("unexpected type in ::emitWasmValueStore")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueStore" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10573); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type in ::emitWasmValueStore" ")"); do { *((volatile int*)__null) = 10573; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 10574 | } |
| 10575 | } |
| 10576 | |
| 10577 | void CodeGenerator::visitWasmLoadSlot(LWasmLoadSlot* ins) { |
| 10578 | MIRType type = ins->type(); |
| 10579 | MWideningOp wideningOp = ins->wideningOp(); |
| 10580 | Register container = ToRegister(ins->containerRef()); |
| 10581 | Address addr(container, ins->offset()); |
| 10582 | AnyRegister dst = ToAnyRegister(ins->output()); |
| 10583 | |
| 10584 | #ifdef ENABLE_WASM_SIMD1 |
| 10585 | if (type == MIRType::Simd128) { |
| 10586 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10586; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10587 | FaultingCodeOffset fco = masm.loadUnalignedSimd128(addr, dst.fpu()); |
| 10588 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128); |
| 10589 | return; |
| 10590 | } |
| 10591 | #endif |
| 10592 | emitWasmValueLoad(ins, type, wideningOp, addr, dst); |
| 10593 | } |
| 10594 | |
| 10595 | void CodeGenerator::visitWasmLoadElement(LWasmLoadElement* ins) { |
| 10596 | MIRType type = ins->type(); |
| 10597 | MWideningOp wideningOp = ins->wideningOp(); |
| 10598 | Scale scale = ins->scale(); |
| 10599 | Register base = ToRegister(ins->base()); |
| 10600 | Register index = ToRegister(ins->index()); |
| 10601 | AnyRegister dst = ToAnyRegister(ins->output()); |
| 10602 | |
| 10603 | #ifdef ENABLE_WASM_SIMD1 |
| 10604 | if (type == MIRType::Simd128) { |
| 10605 | MOZ_ASSERT(wideningOp == MWideningOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wideningOp == MWideningOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wideningOp == MWideningOp::None ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wideningOp == MWideningOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { *((volatile int*)__null) = 10605; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10606 | FaultingCodeOffset fco; |
| 10607 | Register temp = ToRegister(ins->temp0()); |
| 10608 | masm.movePtr(index, temp); |
| 10609 | masm.lshiftPtr(Imm32(4), temp); |
| 10610 | fco = masm.loadUnalignedSimd128(BaseIndex(base, temp, Scale::TimesOne), |
| 10611 | dst.fpu()); |
| 10612 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load128); |
| 10613 | return; |
| 10614 | } |
| 10615 | #endif |
| 10616 | emitWasmValueLoad(ins, type, wideningOp, BaseIndex(base, index, scale), dst); |
| 10617 | } |
| 10618 | |
| 10619 | void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) { |
| 10620 | MIRType type = ins->type(); |
| 10621 | MNarrowingOp narrowingOp = ins->narrowingOp(); |
| 10622 | Register container = ToRegister(ins->containerRef()); |
| 10623 | Address addr(container, ins->offset()); |
| 10624 | AnyRegister src = ToAnyRegister(ins->value()); |
| 10625 | if (type != MIRType::Int32) { |
| 10626 | MOZ_RELEASE_ASSERT(narrowingOp == MNarrowingOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(narrowingOp == MNarrowingOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(narrowingOp == MNarrowingOp:: None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("narrowingOp == MNarrowingOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10626); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None" ")"); do { *((volatile int*)__null) = 10626; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10627 | } |
| 10628 | |
| 10629 | #ifdef ENABLE_WASM_SIMD1 |
| 10630 | if (type == MIRType::Simd128) { |
| 10631 | FaultingCodeOffset fco = masm.storeUnalignedSimd128(src.fpu(), addr); |
| 10632 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10633 | wasm::TrapMachineInsn::Store128); |
| 10634 | return; |
| 10635 | } |
| 10636 | #endif |
| 10637 | emitWasmValueStore(ins, type, narrowingOp, src, addr); |
| 10638 | } |
| 10639 | |
| 10640 | void CodeGenerator::visitWasmStoreElement(LWasmStoreElement* ins) { |
| 10641 | MIRType type = ins->type(); |
| 10642 | MNarrowingOp narrowingOp = ins->narrowingOp(); |
| 10643 | Scale scale = ins->scale(); |
| 10644 | Register base = ToRegister(ins->base()); |
| 10645 | Register index = ToRegister(ins->index()); |
| 10646 | AnyRegister src = ToAnyRegister(ins->value()); |
| 10647 | if (type != MIRType::Int32) { |
| 10648 | MOZ_RELEASE_ASSERT(narrowingOp == MNarrowingOp::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(narrowingOp == MNarrowingOp::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(narrowingOp == MNarrowingOp:: None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("narrowingOp == MNarrowingOp::None", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10648); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None" ")"); do { *((volatile int*)__null) = 10648; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10649 | } |
| 10650 | |
| 10651 | #ifdef ENABLE_WASM_SIMD1 |
| 10652 | if (type == MIRType::Simd128) { |
| 10653 | Register temp = ToRegister(ins->temp0()); |
| 10654 | masm.movePtr(index, temp); |
| 10655 | masm.lshiftPtr(Imm32(4), temp); |
| 10656 | FaultingCodeOffset fco = masm.storeUnalignedSimd128( |
| 10657 | src.fpu(), BaseIndex(base, temp, Scale::TimesOne)); |
| 10658 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10659 | wasm::TrapMachineInsn::Store128); |
| 10660 | return; |
| 10661 | } |
| 10662 | #endif |
| 10663 | emitWasmValueStore(ins, type, narrowingOp, src, |
| 10664 | BaseIndex(base, index, scale)); |
| 10665 | } |
| 10666 | |
| 10667 | void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) { |
| 10668 | Register elements = ToRegister(ins->elements()); |
| 10669 | Register index = ToRegister(ins->index()); |
| 10670 | Register output = ToRegister(ins->output()); |
| 10671 | masm.loadPtr(BaseIndex(elements, index, ScalePointer), output); |
| 10672 | } |
| 10673 | |
| 10674 | void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) { |
| 10675 | masm.movePtr(ToRegister(ins->base()), ToRegister(ins->output())); |
| 10676 | masm.addPtr(Imm32(int32_t(ins->offset())), ToRegister(ins->output())); |
| 10677 | } |
| 10678 | |
| 10679 | void CodeGenerator::visitWasmDerivedIndexPointer( |
| 10680 | LWasmDerivedIndexPointer* ins) { |
| 10681 | Register base = ToRegister(ins->base()); |
| 10682 | Register index = ToRegister(ins->index()); |
| 10683 | Register output = ToRegister(ins->output()); |
| 10684 | masm.computeEffectiveAddress(BaseIndex(base, index, ins->scale()), output); |
| 10685 | } |
| 10686 | |
| 10687 | void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) { |
| 10688 | Register instance = ToRegister(ins->instance()); |
| 10689 | Register valueBase = ToRegister(ins->valueBase()); |
| 10690 | size_t offset = ins->offset(); |
| 10691 | Register value = ToRegister(ins->value()); |
| 10692 | Register temp = ToRegister(ins->temp0()); |
| 10693 | |
| 10694 | if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) { |
| 10695 | Label skipPreBarrier; |
| 10696 | wasm::EmitWasmPreBarrierGuard(masm, instance, temp, |
| 10697 | Address(valueBase, offset), &skipPreBarrier, |
| 10698 | ins->maybeTrap()); |
| 10699 | wasm::EmitWasmPreBarrierCallImmediate(masm, instance, temp, valueBase, |
| 10700 | offset); |
| 10701 | masm.bind(&skipPreBarrier); |
| 10702 | } |
| 10703 | |
| 10704 | FaultingCodeOffset fco = masm.storePtr(value, Address(valueBase, offset)); |
| 10705 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10706 | wasm::TrapMachineInsnForStoreWord()); |
| 10707 | // The postbarrier is handled separately. |
| 10708 | } |
| 10709 | |
| 10710 | void CodeGenerator::visitWasmStoreElementRef(LWasmStoreElementRef* ins) { |
| 10711 | Register instance = ToRegister(ins->instance()); |
| 10712 | Register base = ToRegister(ins->base()); |
| 10713 | Register index = ToRegister(ins->index()); |
| 10714 | Register value = ToRegister(ins->value()); |
| 10715 | Register temp0 = ToTempRegisterOrInvalid(ins->temp0()); |
| 10716 | Register temp1 = ToTempRegisterOrInvalid(ins->temp1()); |
| 10717 | |
| 10718 | BaseIndex addr(base, index, ScalePointer); |
| 10719 | |
| 10720 | if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) { |
| 10721 | Label skipPreBarrier; |
| 10722 | wasm::EmitWasmPreBarrierGuard(masm, instance, temp0, addr, &skipPreBarrier, |
| 10723 | ins->maybeTrap()); |
| 10724 | wasm::EmitWasmPreBarrierCallIndex(masm, instance, temp0, temp1, addr); |
| 10725 | masm.bind(&skipPreBarrier); |
| 10726 | } |
| 10727 | |
| 10728 | FaultingCodeOffset fco = masm.storePtr(value, addr); |
| 10729 | EmitSignalNullCheckTrapSite(masm, ins, fco, |
| 10730 | wasm::TrapMachineInsnForStoreWord()); |
| 10731 | // The postbarrier is handled separately. |
| 10732 | } |
| 10733 | |
| 10734 | // Out-of-line path to update the store buffer for wasm references. |
| 10735 | class OutOfLineWasmCallPostWriteBarrierImmediate |
| 10736 | : public OutOfLineCodeBase<CodeGenerator> { |
| 10737 | LInstruction* lir_; |
| 10738 | Register valueBase_; |
| 10739 | Register temp_; |
| 10740 | uint32_t valueOffset_; |
| 10741 | |
| 10742 | public: |
| 10743 | OutOfLineWasmCallPostWriteBarrierImmediate(LInstruction* lir, |
| 10744 | Register valueBase, Register temp, |
| 10745 | uint32_t valueOffset) |
| 10746 | : lir_(lir), |
| 10747 | valueBase_(valueBase), |
| 10748 | temp_(temp), |
| 10749 | valueOffset_(valueOffset) {} |
| 10750 | |
| 10751 | void accept(CodeGenerator* codegen) override { |
| 10752 | codegen->visitOutOfLineWasmCallPostWriteBarrierImmediate(this); |
| 10753 | } |
| 10754 | |
| 10755 | LInstruction* lir() const { return lir_; } |
| 10756 | Register valueBase() const { return valueBase_; } |
| 10757 | Register temp() const { return temp_; } |
| 10758 | uint32_t valueOffset() const { return valueOffset_; } |
| 10759 | }; |
| 10760 | |
| 10761 | void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierImmediate( |
| 10762 | OutOfLineWasmCallPostWriteBarrierImmediate* ool) { |
| 10763 | saveLiveVolatile(ool->lir()); |
| 10764 | masm.Push(InstanceReg); |
| 10765 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 10766 | |
| 10767 | // Fold the value offset into the value base |
| 10768 | Register valueAddr = ool->valueBase(); |
| 10769 | Register temp = ool->temp(); |
| 10770 | masm.computeEffectiveAddress(Address(valueAddr, ool->valueOffset()), temp); |
| 10771 | |
| 10772 | // Call Instance::postBarrier |
| 10773 | masm.setupWasmABICall(); |
| 10774 | masm.passABIArg(InstanceReg); |
| 10775 | masm.passABIArg(temp); |
| 10776 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 10777 | masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier, |
| 10778 | mozilla::Some(instanceOffset), ABIType::General); |
| 10779 | |
| 10780 | masm.Pop(InstanceReg); |
| 10781 | restoreLiveVolatile(ool->lir()); |
| 10782 | |
| 10783 | masm.jump(ool->rejoin()); |
| 10784 | } |
| 10785 | |
| 10786 | void CodeGenerator::visitWasmPostWriteBarrierImmediate( |
| 10787 | LWasmPostWriteBarrierImmediate* lir) { |
| 10788 | Register object = ToRegister(lir->object()); |
| 10789 | Register value = ToRegister(lir->value()); |
| 10790 | Register valueBase = ToRegister(lir->valueBase()); |
| 10791 | Register temp = ToRegister(lir->temp0()); |
| 10792 | MOZ_ASSERT(ToRegister(lir->instance()) == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->instance()) == InstanceReg)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->instance()) == InstanceReg))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->instance()) == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10792); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg" ")"); do { *((volatile int*)__null) = 10792; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10793 | auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierImmediate( |
| 10794 | lir, valueBase, temp, lir->valueOffset()); |
| 10795 | addOutOfLineCode(ool, lir->mir()); |
| 10796 | |
| 10797 | wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, |
| 10798 | ool->rejoin()); |
| 10799 | masm.jump(ool->entry()); |
| 10800 | masm.bind(ool->rejoin()); |
| 10801 | } |
| 10802 | |
| 10803 | // Out-of-line path to update the store buffer for wasm references. |
| 10804 | class OutOfLineWasmCallPostWriteBarrierIndex |
| 10805 | : public OutOfLineCodeBase<CodeGenerator> { |
| 10806 | LInstruction* lir_; |
| 10807 | Register valueBase_; |
| 10808 | Register index_; |
| 10809 | Register temp_; |
| 10810 | uint32_t elemSize_; |
| 10811 | |
| 10812 | public: |
| 10813 | OutOfLineWasmCallPostWriteBarrierIndex(LInstruction* lir, Register valueBase, |
| 10814 | Register index, Register temp, |
| 10815 | uint32_t elemSize) |
| 10816 | : lir_(lir), |
| 10817 | valueBase_(valueBase), |
| 10818 | index_(index), |
| 10819 | temp_(temp), |
| 10820 | elemSize_(elemSize) { |
| 10821 | MOZ_ASSERT(elemSize == 1 || elemSize == 2 || elemSize == 4 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" ")"); do { *((volatile int*)__null) = 10822; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 10822 | elemSize == 8 || elemSize == 16)do { static_assert( mozilla::detail::AssertionConditionType< decltype(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10822); AnnotateMozCrashReason("MOZ_ASSERT" "(" "elemSize == 1 || elemSize == 2 || elemSize == 4 || elemSize == 8 || elemSize == 16" ")"); do { *((volatile int*)__null) = 10822; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10823 | } |
| 10824 | |
| 10825 | void accept(CodeGenerator* codegen) override { |
| 10826 | codegen->visitOutOfLineWasmCallPostWriteBarrierIndex(this); |
| 10827 | } |
| 10828 | |
| 10829 | LInstruction* lir() const { return lir_; } |
| 10830 | Register valueBase() const { return valueBase_; } |
| 10831 | Register index() const { return index_; } |
| 10832 | Register temp() const { return temp_; } |
| 10833 | uint32_t elemSize() const { return elemSize_; } |
| 10834 | }; |
| 10835 | |
| 10836 | void CodeGenerator::visitOutOfLineWasmCallPostWriteBarrierIndex( |
| 10837 | OutOfLineWasmCallPostWriteBarrierIndex* ool) { |
| 10838 | saveLiveVolatile(ool->lir()); |
| 10839 | masm.Push(InstanceReg); |
| 10840 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 10841 | |
| 10842 | // Fold the value offset into the value base |
| 10843 | Register temp = ool->temp(); |
| 10844 | if (ool->elemSize() == 16) { |
| 10845 | masm.movePtr(ool->index(), temp); |
| 10846 | masm.lshiftPtr(Imm32(4), temp); |
| 10847 | masm.addPtr(ool->valueBase(), temp); |
| 10848 | } else { |
| 10849 | masm.computeEffectiveAddress(BaseIndex(ool->valueBase(), ool->index(), |
| 10850 | ScaleFromElemWidth(ool->elemSize())), |
| 10851 | temp); |
| 10852 | } |
| 10853 | |
| 10854 | // Call Instance::postBarrier |
| 10855 | masm.setupWasmABICall(); |
| 10856 | masm.passABIArg(InstanceReg); |
| 10857 | masm.passABIArg(temp); |
| 10858 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 10859 | masm.callWithABI(wasm::BytecodeOffset(0), wasm::SymbolicAddress::PostBarrier, |
| 10860 | mozilla::Some(instanceOffset), ABIType::General); |
| 10861 | |
| 10862 | masm.Pop(InstanceReg); |
| 10863 | restoreLiveVolatile(ool->lir()); |
| 10864 | |
| 10865 | masm.jump(ool->rejoin()); |
| 10866 | } |
| 10867 | |
| 10868 | void CodeGenerator::visitWasmPostWriteBarrierIndex( |
| 10869 | LWasmPostWriteBarrierIndex* lir) { |
| 10870 | Register object = ToRegister(lir->object()); |
| 10871 | Register value = ToRegister(lir->value()); |
| 10872 | Register valueBase = ToRegister(lir->valueBase()); |
| 10873 | Register index = ToRegister(lir->index()); |
| 10874 | Register temp = ToRegister(lir->temp0()); |
| 10875 | MOZ_ASSERT(ToRegister(lir->instance()) == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->instance()) == InstanceReg)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToRegister(lir->instance()) == InstanceReg))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->instance()) == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10875); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg" ")"); do { *((volatile int*)__null) = 10875; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10876 | auto* ool = new (alloc()) OutOfLineWasmCallPostWriteBarrierIndex( |
| 10877 | lir, valueBase, index, temp, lir->elemSize()); |
| 10878 | addOutOfLineCode(ool, lir->mir()); |
| 10879 | |
| 10880 | wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, |
| 10881 | ool->rejoin()); |
| 10882 | masm.jump(ool->entry()); |
| 10883 | masm.bind(ool->rejoin()); |
| 10884 | } |
| 10885 | |
| 10886 | void CodeGenerator::visitWasmLoadSlotI64(LWasmLoadSlotI64* ins) { |
| 10887 | Register container = ToRegister(ins->containerRef()); |
| 10888 | Address addr(container, ins->offset()); |
| 10889 | Register64 output = ToOutRegister64(ins); |
| 10890 | // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one |
| 10891 | // transaction will always trap before the other, so it seems safest to |
| 10892 | // register both of them as potentially trapping. |
| 10893 | #ifdef JS_64BIT1 |
| 10894 | FaultingCodeOffset fco = masm.load64(addr, output); |
| 10895 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64); |
| 10896 | #else |
| 10897 | FaultingCodeOffsetPair fcop = masm.load64(addr, output); |
| 10898 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
| 10899 | wasm::TrapMachineInsn::Load32); |
| 10900 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
| 10901 | wasm::TrapMachineInsn::Load32); |
| 10902 | #endif |
| 10903 | } |
| 10904 | |
| 10905 | void CodeGenerator::visitWasmLoadElementI64(LWasmLoadElementI64* ins) { |
| 10906 | Register base = ToRegister(ins->base()); |
| 10907 | Register index = ToRegister(ins->index()); |
| 10908 | BaseIndex addr(base, index, Scale::TimesEight); |
| 10909 | Register64 output = ToOutRegister64(ins); |
| 10910 | // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one |
| 10911 | // transaction will always trap before the other, so it seems safest to |
| 10912 | // register both of them as potentially trapping. |
| 10913 | #ifdef JS_64BIT1 |
| 10914 | FaultingCodeOffset fco = masm.load64(addr, output); |
| 10915 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Load64); |
| 10916 | #else |
| 10917 | FaultingCodeOffsetPair fcop = masm.load64(addr, output); |
| 10918 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
| 10919 | wasm::TrapMachineInsn::Load32); |
| 10920 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
| 10921 | wasm::TrapMachineInsn::Load32); |
| 10922 | #endif |
| 10923 | } |
| 10924 | |
| 10925 | void CodeGenerator::visitWasmStoreSlotI64(LWasmStoreSlotI64* ins) { |
| 10926 | Register container = ToRegister(ins->containerRef()); |
| 10927 | Address addr(container, ins->offset()); |
| 10928 | Register64 value = ToRegister64(ins->value()); |
| 10929 | // Either 1 or 2 words. As above we register both transactions in the |
| 10930 | // 2-word case. |
| 10931 | #ifdef JS_64BIT1 |
| 10932 | FaultingCodeOffset fco = masm.store64(value, addr); |
| 10933 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64); |
| 10934 | #else |
| 10935 | FaultingCodeOffsetPair fcop = masm.store64(value, addr); |
| 10936 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
| 10937 | wasm::TrapMachineInsn::Store32); |
| 10938 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
| 10939 | wasm::TrapMachineInsn::Store32); |
| 10940 | #endif |
| 10941 | } |
| 10942 | |
| 10943 | void CodeGenerator::visitWasmStoreElementI64(LWasmStoreElementI64* ins) { |
| 10944 | Register base = ToRegister(ins->base()); |
| 10945 | Register index = ToRegister(ins->index()); |
| 10946 | BaseIndex addr(base, index, Scale::TimesEight); |
| 10947 | Register64 value = ToRegister64(ins->value()); |
| 10948 | // Either 1 or 2 words. As above we register both transactions in the |
| 10949 | // 2-word case. |
| 10950 | #ifdef JS_64BIT1 |
| 10951 | FaultingCodeOffset fco = masm.store64(value, addr); |
| 10952 | EmitSignalNullCheckTrapSite(masm, ins, fco, wasm::TrapMachineInsn::Store64); |
| 10953 | #else |
| 10954 | FaultingCodeOffsetPair fcop = masm.store64(value, addr); |
| 10955 | EmitSignalNullCheckTrapSite(masm, ins, fcop.first, |
| 10956 | wasm::TrapMachineInsn::Store32); |
| 10957 | EmitSignalNullCheckTrapSite(masm, ins, fcop.second, |
| 10958 | wasm::TrapMachineInsn::Store32); |
| 10959 | #endif |
| 10960 | } |
| 10961 | |
| 10962 | void CodeGenerator::visitWasmClampTable64Address( |
| 10963 | LWasmClampTable64Address* lir) { |
| 10964 | #ifdef ENABLE_WASM_MEMORY641 |
| 10965 | Register64 address = ToRegister64(lir->address()); |
| 10966 | Register out = ToRegister(lir->output()); |
| 10967 | masm.wasmClampTable64Address(address, out); |
| 10968 | #else |
| 10969 | MOZ_CRASH("table64 addresses should not be valid without memory64")do { do { } while (false); MOZ_ReportCrash("" "table64 addresses should not be valid without memory64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 10969); AnnotateMozCrashReason("MOZ_CRASH(" "table64 addresses should not be valid without memory64" ")"); do { *((volatile int*)__null) = 10969; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 10970 | #endif |
| 10971 | } |
| 10972 | |
| 10973 | void CodeGenerator::visitArrayBufferByteLength(LArrayBufferByteLength* lir) { |
| 10974 | Register obj = ToRegister(lir->object()); |
| 10975 | Register out = ToRegister(lir->output()); |
| 10976 | masm.loadArrayBufferByteLengthIntPtr(obj, out); |
| 10977 | } |
| 10978 | |
| 10979 | void CodeGenerator::visitArrayBufferViewLength(LArrayBufferViewLength* lir) { |
| 10980 | Register obj = ToRegister(lir->object()); |
| 10981 | Register out = ToRegister(lir->output()); |
| 10982 | masm.loadArrayBufferViewLengthIntPtr(obj, out); |
| 10983 | } |
| 10984 | |
| 10985 | void CodeGenerator::visitArrayBufferViewByteOffset( |
| 10986 | LArrayBufferViewByteOffset* lir) { |
| 10987 | Register obj = ToRegister(lir->object()); |
| 10988 | Register out = ToRegister(lir->output()); |
| 10989 | masm.loadArrayBufferViewByteOffsetIntPtr(obj, out); |
| 10990 | } |
| 10991 | |
| 10992 | void CodeGenerator::visitArrayBufferViewElements( |
| 10993 | LArrayBufferViewElements* lir) { |
| 10994 | Register obj = ToRegister(lir->object()); |
| 10995 | Register out = ToRegister(lir->output()); |
| 10996 | masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), out); |
| 10997 | } |
| 10998 | |
| 10999 | void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) { |
| 11000 | Register obj = ToRegister(lir->object()); |
| 11001 | Register out = ToRegister(lir->output()); |
| 11002 | |
| 11003 | masm.typedArrayElementSize(obj, out); |
| 11004 | } |
| 11005 | |
| 11006 | void CodeGenerator::visitResizableTypedArrayByteOffsetMaybeOutOfBounds( |
| 11007 | LResizableTypedArrayByteOffsetMaybeOutOfBounds* lir) { |
| 11008 | Register obj = ToRegister(lir->object()); |
| 11009 | Register out = ToRegister(lir->output()); |
| 11010 | Register temp = ToRegister(lir->temp0()); |
| 11011 | |
| 11012 | masm.loadResizableTypedArrayByteOffsetMaybeOutOfBoundsIntPtr(obj, out, temp); |
| 11013 | } |
| 11014 | |
| 11015 | void CodeGenerator::visitResizableTypedArrayLength( |
| 11016 | LResizableTypedArrayLength* lir) { |
| 11017 | Register obj = ToRegister(lir->object()); |
| 11018 | Register out = ToRegister(lir->output()); |
| 11019 | Register temp = ToRegister(lir->temp0()); |
| 11020 | |
| 11021 | masm.loadResizableTypedArrayLengthIntPtr(lir->synchronization(), obj, out, |
| 11022 | temp); |
| 11023 | } |
| 11024 | |
| 11025 | void CodeGenerator::visitResizableDataViewByteLength( |
| 11026 | LResizableDataViewByteLength* lir) { |
| 11027 | Register obj = ToRegister(lir->object()); |
| 11028 | Register out = ToRegister(lir->output()); |
| 11029 | Register temp = ToRegister(lir->temp0()); |
| 11030 | |
| 11031 | masm.loadResizableDataViewByteLengthIntPtr(lir->synchronization(), obj, out, |
| 11032 | temp); |
| 11033 | } |
| 11034 | |
| 11035 | void CodeGenerator::visitGrowableSharedArrayBufferByteLength( |
| 11036 | LGrowableSharedArrayBufferByteLength* lir) { |
| 11037 | Register obj = ToRegister(lir->object()); |
| 11038 | Register out = ToRegister(lir->output()); |
| 11039 | |
| 11040 | // Explicit |byteLength| accesses are seq-consistent atomic loads. |
| 11041 | auto sync = Synchronization::Load(); |
| 11042 | |
| 11043 | masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out); |
| 11044 | } |
| 11045 | |
| 11046 | void CodeGenerator::visitGuardResizableArrayBufferViewInBounds( |
| 11047 | LGuardResizableArrayBufferViewInBounds* lir) { |
| 11048 | Register obj = ToRegister(lir->object()); |
| 11049 | Register temp = ToRegister(lir->temp0()); |
| 11050 | |
| 11051 | Label bail; |
| 11052 | masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail); |
| 11053 | bailoutFrom(&bail, lir->snapshot()); |
| 11054 | } |
| 11055 | |
| 11056 | void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached( |
| 11057 | LGuardResizableArrayBufferViewInBoundsOrDetached* lir) { |
| 11058 | Register obj = ToRegister(lir->object()); |
| 11059 | Register temp = ToRegister(lir->temp0()); |
| 11060 | |
| 11061 | Label done, bail; |
| 11062 | masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done); |
| 11063 | masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail); |
| 11064 | masm.bind(&done); |
| 11065 | bailoutFrom(&bail, lir->snapshot()); |
| 11066 | } |
| 11067 | |
| 11068 | void CodeGenerator::visitGuardHasAttachedArrayBuffer( |
| 11069 | LGuardHasAttachedArrayBuffer* lir) { |
| 11070 | Register obj = ToRegister(lir->object()); |
| 11071 | Register temp = ToRegister(lir->temp0()); |
| 11072 | |
| 11073 | Label bail; |
| 11074 | masm.branchIfHasDetachedArrayBuffer(obj, temp, &bail); |
| 11075 | bailoutFrom(&bail, lir->snapshot()); |
| 11076 | } |
| 11077 | |
| 11078 | class OutOfLineGuardNumberToIntPtrIndex |
| 11079 | : public OutOfLineCodeBase<CodeGenerator> { |
| 11080 | LGuardNumberToIntPtrIndex* lir_; |
| 11081 | |
| 11082 | public: |
| 11083 | explicit OutOfLineGuardNumberToIntPtrIndex(LGuardNumberToIntPtrIndex* lir) |
| 11084 | : lir_(lir) {} |
| 11085 | |
| 11086 | void accept(CodeGenerator* codegen) override { |
| 11087 | codegen->visitOutOfLineGuardNumberToIntPtrIndex(this); |
| 11088 | } |
| 11089 | LGuardNumberToIntPtrIndex* lir() const { return lir_; } |
| 11090 | }; |
| 11091 | |
| 11092 | void CodeGenerator::visitGuardNumberToIntPtrIndex( |
| 11093 | LGuardNumberToIntPtrIndex* lir) { |
| 11094 | FloatRegister input = ToFloatRegister(lir->input()); |
| 11095 | Register output = ToRegister(lir->output()); |
| 11096 | |
| 11097 | if (!lir->mir()->supportOOB()) { |
| 11098 | Label bail; |
| 11099 | masm.convertDoubleToPtr(input, output, &bail, false); |
| 11100 | bailoutFrom(&bail, lir->snapshot()); |
| 11101 | return; |
| 11102 | } |
| 11103 | |
| 11104 | auto* ool = new (alloc()) OutOfLineGuardNumberToIntPtrIndex(lir); |
| 11105 | addOutOfLineCode(ool, lir->mir()); |
| 11106 | |
| 11107 | masm.convertDoubleToPtr(input, output, ool->entry(), false); |
| 11108 | masm.bind(ool->rejoin()); |
| 11109 | } |
| 11110 | |
| 11111 | void CodeGenerator::visitOutOfLineGuardNumberToIntPtrIndex( |
| 11112 | OutOfLineGuardNumberToIntPtrIndex* ool) { |
| 11113 | // Substitute the invalid index with an arbitrary out-of-bounds index. |
| 11114 | masm.movePtr(ImmWord(-1), ToRegister(ool->lir()->output())); |
| 11115 | masm.jump(ool->rejoin()); |
| 11116 | } |
| 11117 | |
| 11118 | void CodeGenerator::visitStringLength(LStringLength* lir) { |
| 11119 | Register input = ToRegister(lir->string()); |
| 11120 | Register output = ToRegister(lir->output()); |
| 11121 | |
| 11122 | masm.loadStringLength(input, output); |
| 11123 | } |
| 11124 | |
| 11125 | void CodeGenerator::visitMinMaxI(LMinMaxI* ins) { |
| 11126 | Register first = ToRegister(ins->first()); |
| 11127 | Register output = ToRegister(ins->output()); |
| 11128 | |
| 11129 | MOZ_ASSERT(first == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(first == output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(first == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("first == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11129); AnnotateMozCrashReason("MOZ_ASSERT" "(" "first == output" ")"); do { *((volatile int*)__null) = 11129; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11130 | |
| 11131 | Assembler::Condition cond = |
| 11132 | ins->mir()->isMax() ? Assembler::GreaterThan : Assembler::LessThan; |
| 11133 | |
| 11134 | if (ins->second()->isConstant()) { |
| 11135 | Label done; |
| 11136 | masm.branch32(cond, first, Imm32(ToInt32(ins->second())), &done); |
| 11137 | masm.move32(Imm32(ToInt32(ins->second())), output); |
| 11138 | masm.bind(&done); |
| 11139 | } else { |
| 11140 | Register second = ToRegister(ins->second()); |
| 11141 | masm.cmp32Move32(cond, second, first, second, output); |
| 11142 | } |
| 11143 | } |
| 11144 | |
| 11145 | void CodeGenerator::visitMinMaxArrayI(LMinMaxArrayI* ins) { |
| 11146 | Register array = ToRegister(ins->array()); |
| 11147 | Register output = ToRegister(ins->output()); |
| 11148 | Register temp1 = ToRegister(ins->temp1()); |
| 11149 | Register temp2 = ToRegister(ins->temp2()); |
| 11150 | Register temp3 = ToRegister(ins->temp3()); |
| 11151 | bool isMax = ins->isMax(); |
| 11152 | |
| 11153 | Label bail; |
| 11154 | masm.minMaxArrayInt32(array, output, temp1, temp2, temp3, isMax, &bail); |
| 11155 | bailoutFrom(&bail, ins->snapshot()); |
| 11156 | } |
| 11157 | |
| 11158 | void CodeGenerator::visitMinMaxArrayD(LMinMaxArrayD* ins) { |
| 11159 | Register array = ToRegister(ins->array()); |
| 11160 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11161 | Register temp1 = ToRegister(ins->temp1()); |
| 11162 | Register temp2 = ToRegister(ins->temp2()); |
| 11163 | FloatRegister floatTemp = ToFloatRegister(ins->floatTemp()); |
| 11164 | bool isMax = ins->isMax(); |
| 11165 | |
| 11166 | Label bail; |
| 11167 | masm.minMaxArrayNumber(array, output, floatTemp, temp1, temp2, isMax, &bail); |
| 11168 | bailoutFrom(&bail, ins->snapshot()); |
| 11169 | } |
| 11170 | |
| 11171 | // For Abs*, lowering will have tied input to output on platforms where that is |
| 11172 | // sensible, and otherwise left them untied. |
| 11173 | |
| 11174 | void CodeGenerator::visitAbsI(LAbsI* ins) { |
| 11175 | Register input = ToRegister(ins->input()); |
| 11176 | Register output = ToRegister(ins->output()); |
| 11177 | |
| 11178 | if (ins->mir()->fallible()) { |
| 11179 | Label positive; |
| 11180 | if (input != output) { |
| 11181 | masm.move32(input, output); |
| 11182 | } |
| 11183 | masm.branchTest32(Assembler::NotSigned, output, output, &positive); |
| 11184 | Label bail; |
| 11185 | masm.branchNeg32(Assembler::Overflow, output, &bail); |
| 11186 | bailoutFrom(&bail, ins->snapshot()); |
| 11187 | masm.bind(&positive); |
| 11188 | } else { |
| 11189 | masm.abs32(input, output); |
| 11190 | } |
| 11191 | } |
| 11192 | |
| 11193 | void CodeGenerator::visitAbsD(LAbsD* ins) { |
| 11194 | masm.absDouble(ToFloatRegister(ins->input()), ToFloatRegister(ins->output())); |
| 11195 | } |
| 11196 | |
| 11197 | void CodeGenerator::visitAbsF(LAbsF* ins) { |
| 11198 | masm.absFloat32(ToFloatRegister(ins->input()), |
| 11199 | ToFloatRegister(ins->output())); |
| 11200 | } |
| 11201 | |
| 11202 | void CodeGenerator::visitPowII(LPowII* ins) { |
| 11203 | Register value = ToRegister(ins->value()); |
| 11204 | Register power = ToRegister(ins->power()); |
| 11205 | Register output = ToRegister(ins->output()); |
| 11206 | Register temp0 = ToRegister(ins->temp0()); |
| 11207 | Register temp1 = ToRegister(ins->temp1()); |
| 11208 | |
| 11209 | Label bailout; |
| 11210 | masm.pow32(value, power, output, temp0, temp1, &bailout); |
| 11211 | bailoutFrom(&bailout, ins->snapshot()); |
| 11212 | } |
| 11213 | |
| 11214 | void CodeGenerator::visitPowI(LPowI* ins) { |
| 11215 | FloatRegister value = ToFloatRegister(ins->value()); |
| 11216 | Register power = ToRegister(ins->power()); |
| 11217 | |
| 11218 | using Fn = double (*)(double x, int32_t y); |
| 11219 | masm.setupAlignedABICall(); |
| 11220 | masm.passABIArg(value, ABIType::Float64); |
| 11221 | masm.passABIArg(power); |
| 11222 | |
| 11223 | masm.callWithABI<Fn, js::powi>(ABIType::Float64); |
| 11224 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11224); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11224; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11225 | } |
| 11226 | |
| 11227 | void CodeGenerator::visitPowD(LPowD* ins) { |
| 11228 | FloatRegister value = ToFloatRegister(ins->value()); |
| 11229 | FloatRegister power = ToFloatRegister(ins->power()); |
| 11230 | |
| 11231 | using Fn = double (*)(double x, double y); |
| 11232 | masm.setupAlignedABICall(); |
| 11233 | masm.passABIArg(value, ABIType::Float64); |
| 11234 | masm.passABIArg(power, ABIType::Float64); |
| 11235 | masm.callWithABI<Fn, ecmaPow>(ABIType::Float64); |
| 11236 | |
| 11237 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11237; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11238 | } |
| 11239 | |
| 11240 | void CodeGenerator::visitPowOfTwoI(LPowOfTwoI* ins) { |
| 11241 | Register power = ToRegister(ins->power()); |
| 11242 | Register output = ToRegister(ins->output()); |
| 11243 | |
| 11244 | uint32_t base = ins->base(); |
| 11245 | MOZ_ASSERT(mozilla::IsPowerOfTwo(base))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::IsPowerOfTwo(base))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mozilla::IsPowerOfTwo(base)) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mozilla::IsPowerOfTwo(base)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(base)" ")"); do { *((volatile int*)__null) = 11245; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11246 | |
| 11247 | uint32_t n = mozilla::FloorLog2(base); |
| 11248 | MOZ_ASSERT(n != 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(n != 0)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(n != 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("n != 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11248); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n != 0" ")" ); do { *((volatile int*)__null) = 11248; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 11249 | |
| 11250 | // Hacker's Delight, 2nd edition, theorem D2. |
| 11251 | auto ceilingDiv = [](uint32_t x, uint32_t y) { return (x + y - 1) / y; }; |
| 11252 | |
| 11253 | // Take bailout if |power| is greater-or-equals |log_y(2^31)| or is negative. |
| 11254 | // |2^(n*y) < 2^31| must hold, hence |n*y < 31| resp. |y < 31/n|. |
| 11255 | // |
| 11256 | // Note: it's important for this condition to match the code in CacheIR.cpp |
| 11257 | // (CanAttachInt32Pow) to prevent failure loops. |
| 11258 | bailoutCmp32(Assembler::AboveOrEqual, power, Imm32(ceilingDiv(31, n)), |
| 11259 | ins->snapshot()); |
| 11260 | |
| 11261 | // Compute (2^n)^y as 2^(n*y) using repeated shifts. We could directly scale |
| 11262 | // |power| and perform a single shift, but due to the lack of necessary |
| 11263 | // MacroAssembler functionality, like multiplying a register with an |
| 11264 | // immediate, we restrict the number of generated shift instructions when |
| 11265 | // lowering this operation. |
| 11266 | masm.move32(Imm32(1), output); |
| 11267 | do { |
| 11268 | masm.lshift32(power, output); |
| 11269 | n--; |
| 11270 | } while (n > 0); |
| 11271 | } |
| 11272 | |
| 11273 | void CodeGenerator::visitSqrtD(LSqrtD* ins) { |
| 11274 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11275 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11276 | masm.sqrtDouble(input, output); |
| 11277 | } |
| 11278 | |
| 11279 | void CodeGenerator::visitSqrtF(LSqrtF* ins) { |
| 11280 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11281 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11282 | masm.sqrtFloat32(input, output); |
| 11283 | } |
| 11284 | |
| 11285 | void CodeGenerator::visitSignI(LSignI* ins) { |
| 11286 | Register input = ToRegister(ins->input()); |
| 11287 | Register output = ToRegister(ins->output()); |
| 11288 | masm.signInt32(input, output); |
| 11289 | } |
| 11290 | |
| 11291 | void CodeGenerator::visitSignD(LSignD* ins) { |
| 11292 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11293 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11294 | masm.signDouble(input, output); |
| 11295 | } |
| 11296 | |
| 11297 | void CodeGenerator::visitSignDI(LSignDI* ins) { |
| 11298 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11299 | FloatRegister temp = ToFloatRegister(ins->temp0()); |
| 11300 | Register output = ToRegister(ins->output()); |
| 11301 | |
| 11302 | Label bail; |
| 11303 | masm.signDoubleToInt32(input, output, temp, &bail); |
| 11304 | bailoutFrom(&bail, ins->snapshot()); |
| 11305 | } |
| 11306 | |
| 11307 | void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) { |
| 11308 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11309 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11309; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11310 | |
| 11311 | UnaryMathFunction fun = ins->mir()->function(); |
| 11312 | UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(fun); |
| 11313 | |
| 11314 | masm.setupAlignedABICall(); |
| 11315 | |
| 11316 | masm.passABIArg(input, ABIType::Float64); |
| 11317 | masm.callWithABI(DynamicFunction<UnaryMathFunctionType>(funPtr), |
| 11318 | ABIType::Float64); |
| 11319 | } |
| 11320 | |
| 11321 | void CodeGenerator::visitMathFunctionF(LMathFunctionF* ins) { |
| 11322 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11323 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnFloat32Reg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnFloat32Reg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnFloat32Reg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnFloat32Reg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnFloat32Reg" ")"); do { *((volatile int*)__null) = 11323; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11324 | |
| 11325 | masm.setupAlignedABICall(); |
| 11326 | masm.passABIArg(input, ABIType::Float32); |
| 11327 | |
| 11328 | using Fn = float (*)(float x); |
| 11329 | Fn funptr = nullptr; |
| 11330 | CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check; |
| 11331 | switch (ins->mir()->function()) { |
| 11332 | case UnaryMathFunction::Floor: |
| 11333 | funptr = floorf; |
| 11334 | check = CheckUnsafeCallWithABI::DontCheckOther; |
| 11335 | break; |
| 11336 | case UnaryMathFunction::Round: |
| 11337 | funptr = math_roundf_impl; |
| 11338 | break; |
| 11339 | case UnaryMathFunction::Trunc: |
| 11340 | funptr = math_truncf_impl; |
| 11341 | break; |
| 11342 | case UnaryMathFunction::Ceil: |
| 11343 | funptr = ceilf; |
| 11344 | check = CheckUnsafeCallWithABI::DontCheckOther; |
| 11345 | break; |
| 11346 | default: |
| 11347 | MOZ_CRASH("Unknown or unsupported float32 math function")do { do { } while (false); MOZ_ReportCrash("" "Unknown or unsupported float32 math function" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11347); AnnotateMozCrashReason("MOZ_CRASH(" "Unknown or unsupported float32 math function" ")"); do { *((volatile int*)__null) = 11347; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 11348 | } |
| 11349 | |
| 11350 | masm.callWithABI(DynamicFunction<Fn>(funptr), ABIType::Float32, check); |
| 11351 | } |
| 11352 | |
| 11353 | void CodeGenerator::visitModD(LModD* ins) { |
| 11354 | MOZ_ASSERT(!gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!gen->compilingWasm()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 11354; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11355 | |
| 11356 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
| 11357 | FloatRegister rhs = ToFloatRegister(ins->rhs()); |
| 11358 | |
| 11359 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11359); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11359; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11360 | |
| 11361 | using Fn = double (*)(double a, double b); |
| 11362 | masm.setupAlignedABICall(); |
| 11363 | masm.passABIArg(lhs, ABIType::Float64); |
| 11364 | masm.passABIArg(rhs, ABIType::Float64); |
| 11365 | masm.callWithABI<Fn, NumberMod>(ABIType::Float64); |
| 11366 | } |
| 11367 | |
| 11368 | void CodeGenerator::visitModPowTwoD(LModPowTwoD* ins) { |
| 11369 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
| 11370 | uint32_t divisor = ins->divisor(); |
| 11371 | MOZ_ASSERT(mozilla::IsPowerOfTwo(divisor))do { static_assert( mozilla::detail::AssertionConditionType< decltype(mozilla::IsPowerOfTwo(divisor))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mozilla::IsPowerOfTwo(divisor )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mozilla::IsPowerOfTwo(divisor)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11371); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mozilla::IsPowerOfTwo(divisor)" ")"); do { *((volatile int*)__null) = 11371; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11372 | |
| 11373 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11374 | |
| 11375 | // Compute |n % d| using |copysign(n - (d * trunc(n / d)), n)|. |
| 11376 | // |
| 11377 | // This doesn't work if |d| isn't a power of two, because we may lose too much |
| 11378 | // precision. For example |Number.MAX_VALUE % 3 == 2|, but |
| 11379 | // |3 * trunc(Number.MAX_VALUE / 3) == Infinity|. |
| 11380 | |
| 11381 | Label done; |
| 11382 | { |
| 11383 | ScratchDoubleScope scratch(masm); |
| 11384 | |
| 11385 | // Subnormals can lead to performance degradation, which can make calling |
| 11386 | // |fmod| faster than this inline implementation. Work around this issue by |
| 11387 | // directly returning the input for any value in the interval ]-1, +1[. |
| 11388 | Label notSubnormal; |
| 11389 | masm.loadConstantDouble(1.0, scratch); |
| 11390 | masm.loadConstantDouble(-1.0, output); |
| 11391 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, lhs, scratch, |
| 11392 | ¬Subnormal); |
| 11393 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, lhs, output, |
| 11394 | ¬Subnormal); |
| 11395 | |
| 11396 | masm.moveDouble(lhs, output); |
| 11397 | masm.jump(&done); |
| 11398 | |
| 11399 | masm.bind(¬Subnormal); |
| 11400 | |
| 11401 | if (divisor == 1) { |
| 11402 | // The pattern |n % 1 == 0| is used to detect integer numbers. We can skip |
| 11403 | // the multiplication by one in this case. |
| 11404 | masm.moveDouble(lhs, output); |
| 11405 | masm.nearbyIntDouble(RoundingMode::TowardsZero, output, scratch); |
| 11406 | masm.subDouble(scratch, output); |
| 11407 | } else { |
| 11408 | masm.loadConstantDouble(1.0 / double(divisor), scratch); |
| 11409 | masm.loadConstantDouble(double(divisor), output); |
| 11410 | |
| 11411 | masm.mulDouble(lhs, scratch); |
| 11412 | masm.nearbyIntDouble(RoundingMode::TowardsZero, scratch, scratch); |
| 11413 | masm.mulDouble(output, scratch); |
| 11414 | |
| 11415 | masm.moveDouble(lhs, output); |
| 11416 | masm.subDouble(scratch, output); |
| 11417 | } |
| 11418 | } |
| 11419 | |
| 11420 | masm.copySignDouble(output, lhs, output); |
| 11421 | masm.bind(&done); |
| 11422 | } |
| 11423 | |
| 11424 | void CodeGenerator::visitWasmBuiltinModD(LWasmBuiltinModD* ins) { |
| 11425 | masm.Push(InstanceReg); |
| 11426 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 11427 | |
| 11428 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
| 11429 | FloatRegister rhs = ToFloatRegister(ins->rhs()); |
| 11430 | |
| 11431 | MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(ins->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(ins->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(ins->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11431); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 11431; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11432 | |
| 11433 | masm.setupWasmABICall(); |
| 11434 | masm.passABIArg(lhs, ABIType::Float64); |
| 11435 | masm.passABIArg(rhs, ABIType::Float64); |
| 11436 | |
| 11437 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 11438 | masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD, |
| 11439 | mozilla::Some(instanceOffset), ABIType::Float64); |
| 11440 | |
| 11441 | masm.Pop(InstanceReg); |
| 11442 | } |
| 11443 | |
| 11444 | void CodeGenerator::visitClzI(LClzI* ins) { |
| 11445 | Register input = ToRegister(ins->input()); |
| 11446 | Register output = ToRegister(ins->output()); |
| 11447 | bool knownNotZero = ins->mir()->operandIsNeverZero(); |
| 11448 | |
| 11449 | masm.clz32(input, output, knownNotZero); |
| 11450 | } |
| 11451 | |
| 11452 | void CodeGenerator::visitCtzI(LCtzI* ins) { |
| 11453 | Register input = ToRegister(ins->input()); |
| 11454 | Register output = ToRegister(ins->output()); |
| 11455 | bool knownNotZero = ins->mir()->operandIsNeverZero(); |
| 11456 | |
| 11457 | masm.ctz32(input, output, knownNotZero); |
| 11458 | } |
| 11459 | |
| 11460 | void CodeGenerator::visitPopcntI(LPopcntI* ins) { |
| 11461 | Register input = ToRegister(ins->input()); |
| 11462 | Register output = ToRegister(ins->output()); |
| 11463 | Register temp = ToRegister(ins->temp0()); |
| 11464 | |
| 11465 | masm.popcnt32(input, output, temp); |
| 11466 | } |
| 11467 | |
| 11468 | void CodeGenerator::visitClzI64(LClzI64* ins) { |
| 11469 | Register64 input = ToRegister64(ins->num()); |
| 11470 | Register64 output = ToOutRegister64(ins); |
| 11471 | |
| 11472 | masm.clz64(input, output); |
| 11473 | } |
| 11474 | |
| 11475 | void CodeGenerator::visitCtzI64(LCtzI64* ins) { |
| 11476 | Register64 input = ToRegister64(ins->num()); |
| 11477 | Register64 output = ToOutRegister64(ins); |
| 11478 | |
| 11479 | masm.ctz64(input, output); |
| 11480 | } |
| 11481 | |
| 11482 | void CodeGenerator::visitPopcntI64(LPopcntI64* ins) { |
| 11483 | Register64 input = ToRegister64(ins->num()); |
| 11484 | Register64 output = ToOutRegister64(ins); |
| 11485 | Register temp = ToRegister(ins->temp0()); |
| 11486 | |
| 11487 | masm.popcnt64(input, output, temp); |
| 11488 | } |
| 11489 | |
| 11490 | void CodeGenerator::visitBigIntAdd(LBigIntAdd* ins) { |
| 11491 | pushArg(ToRegister(ins->rhs())); |
| 11492 | pushArg(ToRegister(ins->lhs())); |
| 11493 | |
| 11494 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11495 | callVM<Fn, BigInt::add>(ins); |
| 11496 | } |
| 11497 | |
| 11498 | void CodeGenerator::visitBigIntSub(LBigIntSub* ins) { |
| 11499 | pushArg(ToRegister(ins->rhs())); |
| 11500 | pushArg(ToRegister(ins->lhs())); |
| 11501 | |
| 11502 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11503 | callVM<Fn, BigInt::sub>(ins); |
| 11504 | } |
| 11505 | |
| 11506 | void CodeGenerator::visitBigIntMul(LBigIntMul* ins) { |
| 11507 | pushArg(ToRegister(ins->rhs())); |
| 11508 | pushArg(ToRegister(ins->lhs())); |
| 11509 | |
| 11510 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11511 | callVM<Fn, BigInt::mul>(ins); |
| 11512 | } |
| 11513 | |
| 11514 | void CodeGenerator::visitBigIntDiv(LBigIntDiv* ins) { |
| 11515 | pushArg(ToRegister(ins->rhs())); |
| 11516 | pushArg(ToRegister(ins->lhs())); |
| 11517 | |
| 11518 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11519 | callVM<Fn, BigInt::div>(ins); |
| 11520 | } |
| 11521 | |
| 11522 | void CodeGenerator::visitBigIntMod(LBigIntMod* ins) { |
| 11523 | pushArg(ToRegister(ins->rhs())); |
| 11524 | pushArg(ToRegister(ins->lhs())); |
| 11525 | |
| 11526 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11527 | callVM<Fn, BigInt::mod>(ins); |
| 11528 | } |
| 11529 | |
| 11530 | void CodeGenerator::visitBigIntPow(LBigIntPow* ins) { |
| 11531 | pushArg(ToRegister(ins->rhs())); |
| 11532 | pushArg(ToRegister(ins->lhs())); |
| 11533 | |
| 11534 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11535 | callVM<Fn, BigInt::pow>(ins); |
| 11536 | } |
| 11537 | |
| 11538 | void CodeGenerator::visitBigIntBitAnd(LBigIntBitAnd* ins) { |
| 11539 | pushArg(ToRegister(ins->rhs())); |
| 11540 | pushArg(ToRegister(ins->lhs())); |
| 11541 | |
| 11542 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11543 | callVM<Fn, BigInt::bitAnd>(ins); |
| 11544 | } |
| 11545 | |
| 11546 | void CodeGenerator::visitBigIntBitOr(LBigIntBitOr* ins) { |
| 11547 | pushArg(ToRegister(ins->rhs())); |
| 11548 | pushArg(ToRegister(ins->lhs())); |
| 11549 | |
| 11550 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11551 | callVM<Fn, BigInt::bitOr>(ins); |
| 11552 | } |
| 11553 | |
| 11554 | void CodeGenerator::visitBigIntBitXor(LBigIntBitXor* ins) { |
| 11555 | pushArg(ToRegister(ins->rhs())); |
| 11556 | pushArg(ToRegister(ins->lhs())); |
| 11557 | |
| 11558 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11559 | callVM<Fn, BigInt::bitXor>(ins); |
| 11560 | } |
| 11561 | |
| 11562 | void CodeGenerator::visitBigIntLsh(LBigIntLsh* ins) { |
| 11563 | pushArg(ToRegister(ins->rhs())); |
| 11564 | pushArg(ToRegister(ins->lhs())); |
| 11565 | |
| 11566 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11567 | callVM<Fn, BigInt::lsh>(ins); |
| 11568 | } |
| 11569 | |
| 11570 | void CodeGenerator::visitBigIntRsh(LBigIntRsh* ins) { |
| 11571 | pushArg(ToRegister(ins->rhs())); |
| 11572 | pushArg(ToRegister(ins->lhs())); |
| 11573 | |
| 11574 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 11575 | callVM<Fn, BigInt::rsh>(ins); |
| 11576 | } |
| 11577 | |
| 11578 | void CodeGenerator::visitBigIntIncrement(LBigIntIncrement* ins) { |
| 11579 | pushArg(ToRegister(ins->input())); |
| 11580 | |
| 11581 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
| 11582 | callVM<Fn, BigInt::inc>(ins); |
| 11583 | } |
| 11584 | |
| 11585 | void CodeGenerator::visitBigIntDecrement(LBigIntDecrement* ins) { |
| 11586 | pushArg(ToRegister(ins->input())); |
| 11587 | |
| 11588 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
| 11589 | callVM<Fn, BigInt::dec>(ins); |
| 11590 | } |
| 11591 | |
| 11592 | void CodeGenerator::visitBigIntNegate(LBigIntNegate* ins) { |
| 11593 | Register input = ToRegister(ins->input()); |
| 11594 | Register temp = ToRegister(ins->temp0()); |
| 11595 | Register output = ToRegister(ins->output()); |
| 11596 | |
| 11597 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
| 11598 | auto* ool = |
| 11599 | oolCallVM<Fn, BigInt::neg>(ins, ArgList(input), StoreRegisterTo(output)); |
| 11600 | |
| 11601 | // -0n == 0n |
| 11602 | Label lhsNonZero; |
| 11603 | masm.branchIfBigIntIsNonZero(input, &lhsNonZero); |
| 11604 | masm.movePtr(input, output); |
| 11605 | masm.jump(ool->rejoin()); |
| 11606 | masm.bind(&lhsNonZero); |
| 11607 | |
| 11608 | // Call into the VM when the input uses heap digits. |
| 11609 | masm.copyBigIntWithInlineDigits(input, output, temp, initialBigIntHeap(), |
| 11610 | ool->entry()); |
| 11611 | |
| 11612 | // Flip the sign bit. |
| 11613 | masm.xor32(Imm32(BigInt::signBitMask()), |
| 11614 | Address(output, BigInt::offsetOfFlags())); |
| 11615 | |
| 11616 | masm.bind(ool->rejoin()); |
| 11617 | } |
| 11618 | |
| 11619 | void CodeGenerator::visitBigIntBitNot(LBigIntBitNot* ins) { |
| 11620 | pushArg(ToRegister(ins->input())); |
| 11621 | |
| 11622 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
| 11623 | callVM<Fn, BigInt::bitNot>(ins); |
| 11624 | } |
| 11625 | |
| 11626 | void CodeGenerator::visitBigIntToIntPtr(LBigIntToIntPtr* ins) { |
| 11627 | Register input = ToRegister(ins->input()); |
| 11628 | Register output = ToRegister(ins->output()); |
| 11629 | |
| 11630 | Label bail; |
| 11631 | masm.loadBigIntPtr(input, output, &bail); |
| 11632 | bailoutFrom(&bail, ins->snapshot()); |
| 11633 | } |
| 11634 | |
| 11635 | void CodeGenerator::visitIntPtrToBigInt(LIntPtrToBigInt* ins) { |
| 11636 | Register input = ToRegister(ins->input()); |
| 11637 | Register temp = ToRegister(ins->temp0()); |
| 11638 | Register output = ToRegister(ins->output()); |
| 11639 | |
| 11640 | using Fn = BigInt* (*)(JSContext*, intptr_t); |
| 11641 | auto* ool = oolCallVM<Fn, JS::BigInt::createFromIntPtr>( |
| 11642 | ins, ArgList(input), StoreRegisterTo(output)); |
| 11643 | |
| 11644 | masm.newGCBigInt(output, temp, initialBigIntHeap(), ool->entry()); |
| 11645 | masm.movePtr(input, temp); |
| 11646 | masm.initializeBigIntPtr(output, temp); |
| 11647 | |
| 11648 | masm.bind(ool->rejoin()); |
| 11649 | } |
| 11650 | |
| 11651 | void CodeGenerator::visitBigIntPtrAdd(LBigIntPtrAdd* ins) { |
| 11652 | Register lhs = ToRegister(ins->lhs()); |
| 11653 | const LAllocation* rhs = ins->rhs(); |
| 11654 | Register output = ToRegister(ins->output()); |
| 11655 | |
| 11656 | if (rhs->isConstant()) { |
| 11657 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 11658 | } else { |
| 11659 | masm.movePtr(ToRegister(rhs), output); |
| 11660 | } |
| 11661 | |
| 11662 | Label bail; |
| 11663 | masm.branchAddPtr(Assembler::Overflow, lhs, output, &bail); |
| 11664 | bailoutFrom(&bail, ins->snapshot()); |
| 11665 | } |
| 11666 | |
| 11667 | void CodeGenerator::visitBigIntPtrSub(LBigIntPtrSub* ins) { |
| 11668 | Register lhs = ToRegister(ins->lhs()); |
| 11669 | Register rhs = ToRegister(ins->rhs()); |
| 11670 | Register output = ToRegister(ins->output()); |
| 11671 | |
| 11672 | Label bail; |
| 11673 | masm.movePtr(lhs, output); |
| 11674 | masm.branchSubPtr(Assembler::Overflow, rhs, output, &bail); |
| 11675 | bailoutFrom(&bail, ins->snapshot()); |
| 11676 | } |
| 11677 | |
| 11678 | void CodeGenerator::visitBigIntPtrMul(LBigIntPtrMul* ins) { |
| 11679 | Register lhs = ToRegister(ins->lhs()); |
| 11680 | const LAllocation* rhs = ins->rhs(); |
| 11681 | Register output = ToRegister(ins->output()); |
| 11682 | |
| 11683 | if (rhs->isConstant()) { |
| 11684 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 11685 | } else { |
| 11686 | masm.movePtr(ToRegister(rhs), output); |
| 11687 | } |
| 11688 | |
| 11689 | Label bail; |
| 11690 | masm.branchMulPtr(Assembler::Overflow, lhs, output, &bail); |
| 11691 | bailoutFrom(&bail, ins->snapshot()); |
| 11692 | } |
| 11693 | |
| 11694 | void CodeGenerator::visitBigIntPtrDiv(LBigIntPtrDiv* ins) { |
| 11695 | Register lhs = ToRegister(ins->lhs()); |
| 11696 | Register rhs = ToRegister(ins->rhs()); |
| 11697 | Register output = ToRegister(ins->output()); |
| 11698 | |
| 11699 | // x / 0 throws an error. |
| 11700 | Label bail; |
| 11701 | if (ins->mir()->canBeDivideByZero()) { |
| 11702 | masm.branchPtr(Assembler::Equal, rhs, Imm32(0), &bail); |
| 11703 | } |
| 11704 | |
| 11705 | static constexpr auto DigitMin = std::numeric_limits< |
| 11706 | mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min(); |
| 11707 | |
| 11708 | // Handle an integer overflow from INT{32,64}_MIN / -1. |
| 11709 | Label notOverflow; |
| 11710 | masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), ¬Overflow); |
| 11711 | masm.branchPtr(Assembler::Equal, rhs, Imm32(-1), &bail); |
| 11712 | masm.bind(¬Overflow); |
| 11713 | |
| 11714 | emitBigIntPtrDiv(ins, lhs, rhs, output); |
| 11715 | |
| 11716 | bailoutFrom(&bail, ins->snapshot()); |
| 11717 | } |
| 11718 | |
| 11719 | void CodeGenerator::visitBigIntPtrDivPowTwo(LBigIntPtrDivPowTwo* ins) { |
| 11720 | Register lhs = ToRegister(ins->lhs()); |
| 11721 | Register output = ToRegister(ins->output()); |
| 11722 | int32_t shift = ins->shift(); |
| 11723 | bool negativeDivisor = ins->negativeDivisor(); |
| 11724 | |
| 11725 | masm.movePtr(lhs, output); |
| 11726 | |
| 11727 | if (shift) { |
| 11728 | // Adjust the value so that shifting produces a correctly rounded result |
| 11729 | // when the numerator is negative. |
| 11730 | // See 10-1 "Signed Division by a Known Power of 2" in Henry S. Warren, |
| 11731 | // Jr.'s Hacker's Delight. |
| 11732 | |
| 11733 | constexpr size_t bits = BigInt::DigitBits; |
| 11734 | |
| 11735 | if (shift > 1) { |
| 11736 | // Copy the sign bit of the numerator. (= (2^bits - 1) or 0) |
| 11737 | masm.rshiftPtrArithmetic(Imm32(bits - 1), output); |
| 11738 | } |
| 11739 | |
| 11740 | // Divide by 2^(bits - shift) |
| 11741 | // i.e. (= (2^bits - 1) / 2^(bits - shift) or 0) |
| 11742 | // i.e. (= (2^shift - 1) or 0) |
| 11743 | masm.rshiftPtr(Imm32(bits - shift), output); |
| 11744 | |
| 11745 | // If signed, make any 1 bit below the shifted bits to bubble up, such that |
| 11746 | // once shifted the value would be rounded towards 0. |
| 11747 | masm.addPtr(lhs, output); |
| 11748 | |
| 11749 | masm.rshiftPtrArithmetic(Imm32(shift), output); |
| 11750 | |
| 11751 | if (negativeDivisor) { |
| 11752 | masm.negPtr(output); |
| 11753 | } |
| 11754 | } else if (negativeDivisor) { |
| 11755 | Label bail; |
| 11756 | masm.branchNegPtr(Assembler::Overflow, output, &bail); |
| 11757 | bailoutFrom(&bail, ins->snapshot()); |
| 11758 | } |
| 11759 | } |
| 11760 | |
| 11761 | void CodeGenerator::visitBigIntPtrMod(LBigIntPtrMod* ins) { |
| 11762 | Register lhs = ToRegister(ins->lhs()); |
| 11763 | Register rhs = ToRegister(ins->rhs()); |
| 11764 | Register output = ToRegister(ins->output()); |
| 11765 | Register temp = ToRegister(ins->temp0()); |
| 11766 | |
| 11767 | // x % 0 throws an error. |
| 11768 | if (ins->mir()->canBeDivideByZero()) { |
| 11769 | bailoutCmpPtr(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); |
| 11770 | } |
| 11771 | |
| 11772 | static constexpr auto DigitMin = std::numeric_limits< |
| 11773 | mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min(); |
| 11774 | |
| 11775 | masm.movePtr(lhs, temp); |
| 11776 | |
| 11777 | // Handle an integer overflow from INT{32,64}_MIN / -1. |
| 11778 | Label notOverflow; |
| 11779 | masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), ¬Overflow); |
| 11780 | masm.branchPtr(Assembler::NotEqual, rhs, Imm32(-1), ¬Overflow); |
| 11781 | masm.movePtr(ImmWord(0), temp); |
| 11782 | masm.bind(¬Overflow); |
| 11783 | |
| 11784 | emitBigIntPtrMod(ins, temp, rhs, output); |
| 11785 | } |
| 11786 | |
| 11787 | void CodeGenerator::visitBigIntPtrModPowTwo(LBigIntPtrModPowTwo* ins) { |
| 11788 | Register lhs = ToRegister(ins->lhs()); |
| 11789 | Register output = ToRegister(ins->output()); |
| 11790 | Register temp = ToRegister(ins->temp0()); |
| 11791 | int32_t shift = ins->shift(); |
| 11792 | |
| 11793 | masm.movePtr(lhs, output); |
| 11794 | masm.movePtr(ImmWord((uintptr_t(1) << shift) - uintptr_t(1)), temp); |
| 11795 | |
| 11796 | // Switch based on sign of the lhs. |
| 11797 | |
| 11798 | // Positive numbers are just a bitmask. |
| 11799 | Label negative; |
| 11800 | masm.branchTestPtr(Assembler::Signed, lhs, lhs, &negative); |
| 11801 | |
| 11802 | masm.andPtr(temp, output); |
| 11803 | |
| 11804 | Label done; |
| 11805 | masm.jump(&done); |
| 11806 | |
| 11807 | // Negative numbers need a negate, bitmask, negate |
| 11808 | masm.bind(&negative); |
| 11809 | |
| 11810 | masm.negPtr(output); |
| 11811 | masm.andPtr(temp, output); |
| 11812 | masm.negPtr(output); |
| 11813 | |
| 11814 | masm.bind(&done); |
| 11815 | } |
| 11816 | |
| 11817 | void CodeGenerator::visitBigIntPtrPow(LBigIntPtrPow* ins) { |
| 11818 | Register lhs = ToRegister(ins->lhs()); |
| 11819 | Register rhs = ToRegister(ins->rhs()); |
| 11820 | Register output = ToRegister(ins->output()); |
| 11821 | Register temp0 = ToRegister(ins->temp0()); |
| 11822 | Register temp1 = ToRegister(ins->temp1()); |
| 11823 | |
| 11824 | Label bail; |
| 11825 | masm.powPtr(lhs, rhs, output, temp0, temp1, &bail); |
| 11826 | bailoutFrom(&bail, ins->snapshot()); |
| 11827 | } |
| 11828 | |
| 11829 | void CodeGenerator::visitBigIntPtrBitAnd(LBigIntPtrBitAnd* ins) { |
| 11830 | Register lhs = ToRegister(ins->lhs()); |
| 11831 | const LAllocation* rhs = ins->rhs(); |
| 11832 | Register output = ToRegister(ins->output()); |
| 11833 | |
| 11834 | if (rhs->isConstant()) { |
| 11835 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 11836 | } else { |
| 11837 | masm.movePtr(ToRegister(rhs), output); |
| 11838 | } |
| 11839 | masm.andPtr(lhs, output); |
| 11840 | } |
| 11841 | |
| 11842 | void CodeGenerator::visitBigIntPtrBitOr(LBigIntPtrBitOr* ins) { |
| 11843 | Register lhs = ToRegister(ins->lhs()); |
| 11844 | const LAllocation* rhs = ins->rhs(); |
| 11845 | Register output = ToRegister(ins->output()); |
| 11846 | |
| 11847 | if (rhs->isConstant()) { |
| 11848 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 11849 | } else { |
| 11850 | masm.movePtr(ToRegister(rhs), output); |
| 11851 | } |
| 11852 | masm.orPtr(lhs, output); |
| 11853 | } |
| 11854 | |
| 11855 | void CodeGenerator::visitBigIntPtrBitXor(LBigIntPtrBitXor* ins) { |
| 11856 | Register lhs = ToRegister(ins->lhs()); |
| 11857 | const LAllocation* rhs = ins->rhs(); |
| 11858 | Register output = ToRegister(ins->output()); |
| 11859 | |
| 11860 | if (rhs->isConstant()) { |
| 11861 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 11862 | } else { |
| 11863 | masm.movePtr(ToRegister(rhs), output); |
| 11864 | } |
| 11865 | masm.xorPtr(lhs, output); |
| 11866 | } |
| 11867 | |
| 11868 | void CodeGenerator::visitBigIntPtrLsh(LBigIntPtrLsh* ins) { |
| 11869 | Register lhs = ToRegister(ins->lhs()); |
| 11870 | Register output = ToRegister(ins->output()); |
| 11871 | Register temp = ToTempRegisterOrInvalid(ins->temp0()); |
| 11872 | Register tempShift = ToTempRegisterOrInvalid(ins->temp1()); |
| 11873 | |
| 11874 | if (ins->rhs()->isConstant()) { |
| 11875 | intptr_t rhs = ToIntPtr(ins->rhs()); |
| 11876 | |
| 11877 | if (rhs >= intptr_t(BigInt::DigitBits)) { |
| 11878 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11878); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11878; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11879 | |
| 11880 | // x << DigitBits with x != 0n always exceeds pointer-sized storage. |
| 11881 | masm.movePtr(ImmWord(0), output); |
| 11882 | bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot()); |
| 11883 | } else if (rhs <= -intptr_t(BigInt::DigitBits)) { |
| 11884 | MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11884); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11884; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11885 | |
| 11886 | // x << -DigitBits == x >> DigitBits, which is either 0n or -1n. |
| 11887 | masm.movePtr(lhs, output); |
| 11888 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
| 11889 | } else if (rhs <= 0) { |
| 11890 | MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11890); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11890; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11891 | |
| 11892 | // |x << -y| is computed as |x >> y|. |
| 11893 | masm.movePtr(lhs, output); |
| 11894 | masm.rshiftPtrArithmetic(Imm32(-rhs), output); |
| 11895 | } else { |
| 11896 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11896); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11896; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11897 | |
| 11898 | masm.movePtr(lhs, output); |
| 11899 | masm.lshiftPtr(Imm32(rhs), output); |
| 11900 | |
| 11901 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
| 11902 | masm.movePtr(output, temp); |
| 11903 | masm.rshiftPtrArithmetic(Imm32(rhs), temp); |
| 11904 | bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot()); |
| 11905 | } |
| 11906 | } else { |
| 11907 | Register rhs = ToRegister(ins->rhs()); |
| 11908 | |
| 11909 | Label done, bail; |
| 11910 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11910); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11910; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11911 | |
| 11912 | masm.movePtr(lhs, output); |
| 11913 | |
| 11914 | // 0n << x == 0n |
| 11915 | masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done); |
| 11916 | |
| 11917 | // x << DigitBits with x != 0n always exceeds pointer-sized storage. |
| 11918 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(BigInt::DigitBits), |
| 11919 | &bail); |
| 11920 | |
| 11921 | // x << -DigitBits == x >> DigitBits, which is either 0n or -1n. |
| 11922 | Label shift; |
| 11923 | masm.branchPtr(Assembler::GreaterThan, rhs, |
| 11924 | Imm32(-int32_t(BigInt::DigitBits)), &shift); |
| 11925 | { |
| 11926 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
| 11927 | masm.jump(&done); |
| 11928 | } |
| 11929 | masm.bind(&shift); |
| 11930 | |
| 11931 | // Move |rhs| into the designated shift register. |
| 11932 | masm.movePtr(rhs, tempShift); |
| 11933 | |
| 11934 | // |x << -y| is computed as |x >> y|. |
| 11935 | Label leftShift; |
| 11936 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &leftShift); |
| 11937 | { |
| 11938 | masm.negPtr(tempShift); |
| 11939 | masm.rshiftPtrArithmetic(tempShift, output); |
| 11940 | masm.jump(&done); |
| 11941 | } |
| 11942 | masm.bind(&leftShift); |
| 11943 | |
| 11944 | masm.lshiftPtr(tempShift, output); |
| 11945 | |
| 11946 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
| 11947 | masm.movePtr(output, temp); |
| 11948 | masm.rshiftPtrArithmetic(tempShift, temp); |
| 11949 | masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail); |
| 11950 | |
| 11951 | masm.bind(&done); |
| 11952 | bailoutFrom(&bail, ins->snapshot()); |
| 11953 | } |
| 11954 | } |
| 11955 | |
| 11956 | void CodeGenerator::visitBigIntPtrRsh(LBigIntPtrRsh* ins) { |
| 11957 | Register lhs = ToRegister(ins->lhs()); |
| 11958 | Register output = ToRegister(ins->output()); |
| 11959 | Register temp = ToTempRegisterOrInvalid(ins->temp0()); |
| 11960 | Register tempShift = ToTempRegisterOrInvalid(ins->temp1()); |
| 11961 | |
| 11962 | if (ins->rhs()->isConstant()) { |
| 11963 | intptr_t rhs = ToIntPtr(ins->rhs()); |
| 11964 | |
| 11965 | if (rhs <= -intptr_t(BigInt::DigitBits)) { |
| 11966 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11966; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11967 | |
| 11968 | // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage. |
| 11969 | masm.movePtr(ImmWord(0), output); |
| 11970 | bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot()); |
| 11971 | } else if (rhs >= intptr_t(BigInt::DigitBits)) { |
| 11972 | MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11972; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11973 | |
| 11974 | // x >> DigitBits is either 0n or -1n. |
| 11975 | masm.movePtr(lhs, output); |
| 11976 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
| 11977 | } else if (rhs < 0) { |
| 11978 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11978); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11978; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11979 | |
| 11980 | // |x >> -y| is computed as |x << y|. |
| 11981 | masm.movePtr(lhs, output); |
| 11982 | masm.lshiftPtr(Imm32(-rhs), output); |
| 11983 | |
| 11984 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
| 11985 | masm.movePtr(output, temp); |
| 11986 | masm.rshiftPtrArithmetic(Imm32(-rhs), temp); |
| 11987 | bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot()); |
| 11988 | } else { |
| 11989 | MOZ_ASSERT(!ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->fallible( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11989); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11989; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11990 | |
| 11991 | masm.movePtr(lhs, output); |
| 11992 | masm.rshiftPtrArithmetic(Imm32(rhs), output); |
| 11993 | } |
| 11994 | } else { |
| 11995 | Register rhs = ToRegister(ins->rhs()); |
| 11996 | |
| 11997 | Label done, bail; |
| 11998 | MOZ_ASSERT(ins->mir()->fallible())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->fallible())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->fallible() ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ins->mir()->fallible()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 11998); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { *((volatile int*)__null) = 11998; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11999 | |
| 12000 | masm.movePtr(lhs, output); |
| 12001 | |
| 12002 | // 0n >> x == 0n |
| 12003 | masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done); |
| 12004 | |
| 12005 | // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage. |
| 12006 | masm.branchPtr(Assembler::LessThanOrEqual, rhs, |
| 12007 | Imm32(-int32_t(BigInt::DigitBits)), &bail); |
| 12008 | |
| 12009 | // x >> DigitBits is either 0n or -1n. |
| 12010 | Label shift; |
| 12011 | masm.branchPtr(Assembler::LessThan, rhs, Imm32(BigInt::DigitBits), &shift); |
| 12012 | { |
| 12013 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
| 12014 | masm.jump(&done); |
| 12015 | } |
| 12016 | masm.bind(&shift); |
| 12017 | |
| 12018 | // Move |rhs| into the designated shift register. |
| 12019 | masm.movePtr(rhs, tempShift); |
| 12020 | |
| 12021 | // |x >> -y| is computed as |x << y|. |
| 12022 | Label rightShift; |
| 12023 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &rightShift); |
| 12024 | { |
| 12025 | masm.negPtr(tempShift); |
| 12026 | masm.lshiftPtr(tempShift, output); |
| 12027 | |
| 12028 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
| 12029 | masm.movePtr(output, temp); |
| 12030 | masm.rshiftPtrArithmetic(tempShift, temp); |
| 12031 | masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail); |
| 12032 | |
| 12033 | masm.jump(&done); |
| 12034 | } |
| 12035 | masm.bind(&rightShift); |
| 12036 | |
| 12037 | masm.rshiftPtrArithmetic(tempShift, output); |
| 12038 | |
| 12039 | masm.bind(&done); |
| 12040 | bailoutFrom(&bail, ins->snapshot()); |
| 12041 | } |
| 12042 | } |
| 12043 | |
| 12044 | void CodeGenerator::visitBigIntPtrBitNot(LBigIntPtrBitNot* ins) { |
| 12045 | Register input = ToRegister(ins->input()); |
| 12046 | Register output = ToRegister(ins->output()); |
| 12047 | |
| 12048 | masm.movePtr(input, output); |
| 12049 | masm.notPtr(output); |
| 12050 | } |
| 12051 | |
| 12052 | void CodeGenerator::visitInt32ToStringWithBase(LInt32ToStringWithBase* lir) { |
| 12053 | Register input = ToRegister(lir->input()); |
| 12054 | RegisterOrInt32 base = ToRegisterOrInt32(lir->base()); |
| 12055 | Register output = ToRegister(lir->output()); |
| 12056 | Register temp0 = ToRegister(lir->temp0()); |
| 12057 | Register temp1 = ToRegister(lir->temp1()); |
| 12058 | |
| 12059 | bool lowerCase = lir->mir()->lowerCase(); |
| 12060 | |
| 12061 | using Fn = JSLinearString* (*)(JSContext*, int32_t, int32_t, bool); |
| 12062 | if (base.is<Register>()) { |
| 12063 | auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>( |
| 12064 | lir, ArgList(input, base.as<Register>(), Imm32(lowerCase)), |
| 12065 | StoreRegisterTo(output)); |
| 12066 | |
| 12067 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
| 12068 | masm.loadInt32ToStringWithBase(input, base.as<Register>(), output, temp0, |
| 12069 | temp1, gen->runtime->staticStrings(), |
| 12070 | liveRegs, lowerCase, ool->entry()); |
| 12071 | masm.bind(ool->rejoin()); |
| 12072 | } else { |
| 12073 | auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>( |
| 12074 | lir, ArgList(input, Imm32(base.as<int32_t>()), Imm32(lowerCase)), |
| 12075 | StoreRegisterTo(output)); |
| 12076 | |
| 12077 | masm.loadInt32ToStringWithBase(input, base.as<int32_t>(), output, temp0, |
| 12078 | temp1, gen->runtime->staticStrings(), |
| 12079 | lowerCase, ool->entry()); |
| 12080 | masm.bind(ool->rejoin()); |
| 12081 | } |
| 12082 | } |
| 12083 | |
| 12084 | void CodeGenerator::visitNumberParseInt(LNumberParseInt* lir) { |
| 12085 | Register string = ToRegister(lir->string()); |
| 12086 | Register radix = ToRegister(lir->radix()); |
| 12087 | ValueOperand output = ToOutValue(lir); |
| 12088 | Register temp = ToRegister(lir->temp0()); |
| 12089 | |
| 12090 | #ifdef DEBUG1 |
| 12091 | Label ok; |
| 12092 | masm.branch32(Assembler::Equal, radix, Imm32(0), &ok); |
| 12093 | masm.branch32(Assembler::Equal, radix, Imm32(10), &ok); |
| 12094 | masm.assumeUnreachable("radix must be 0 or 10 for indexed value fast path"); |
| 12095 | masm.bind(&ok); |
| 12096 | #endif |
| 12097 | |
| 12098 | // Use indexed value as fast path if possible. |
| 12099 | Label vmCall, done; |
| 12100 | masm.loadStringIndexValue(string, temp, &vmCall); |
| 12101 | masm.tagValue(JSVAL_TYPE_INT32, temp, output); |
| 12102 | masm.jump(&done); |
| 12103 | { |
| 12104 | masm.bind(&vmCall); |
| 12105 | |
| 12106 | pushArg(radix); |
| 12107 | pushArg(string); |
| 12108 | |
| 12109 | using Fn = bool (*)(JSContext*, HandleString, int32_t, MutableHandleValue); |
| 12110 | callVM<Fn, js::NumberParseInt>(lir); |
| 12111 | } |
| 12112 | masm.bind(&done); |
| 12113 | } |
| 12114 | |
| 12115 | void CodeGenerator::visitDoubleParseInt(LDoubleParseInt* lir) { |
| 12116 | FloatRegister number = ToFloatRegister(lir->number()); |
| 12117 | Register output = ToRegister(lir->output()); |
| 12118 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 12119 | |
| 12120 | Label bail; |
| 12121 | masm.branchDouble(Assembler::DoubleUnordered, number, number, &bail); |
| 12122 | masm.branchTruncateDoubleToInt32(number, output, &bail); |
| 12123 | |
| 12124 | Label ok; |
| 12125 | masm.branch32(Assembler::NotEqual, output, Imm32(0), &ok); |
| 12126 | { |
| 12127 | // Accept both +0 and -0 and return 0. |
| 12128 | masm.loadConstantDouble(0.0, temp); |
| 12129 | masm.branchDouble(Assembler::DoubleEqual, number, temp, &ok); |
| 12130 | |
| 12131 | // Fail if a non-zero input is in the exclusive range (-1, 1.0e-6). |
| 12132 | masm.loadConstantDouble(DOUBLE_DECIMAL_IN_SHORTEST_LOW, temp); |
| 12133 | masm.branchDouble(Assembler::DoubleLessThan, number, temp, &bail); |
| 12134 | } |
| 12135 | masm.bind(&ok); |
| 12136 | |
| 12137 | bailoutFrom(&bail, lir->snapshot()); |
| 12138 | } |
| 12139 | |
| 12140 | void CodeGenerator::visitFloor(LFloor* lir) { |
| 12141 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12142 | Register output = ToRegister(lir->output()); |
| 12143 | |
| 12144 | Label bail; |
| 12145 | masm.floorDoubleToInt32(input, output, &bail); |
| 12146 | bailoutFrom(&bail, lir->snapshot()); |
| 12147 | } |
| 12148 | |
| 12149 | void CodeGenerator::visitFloorF(LFloorF* lir) { |
| 12150 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12151 | Register output = ToRegister(lir->output()); |
| 12152 | |
| 12153 | Label bail; |
| 12154 | masm.floorFloat32ToInt32(input, output, &bail); |
| 12155 | bailoutFrom(&bail, lir->snapshot()); |
| 12156 | } |
| 12157 | |
| 12158 | void CodeGenerator::visitCeil(LCeil* lir) { |
| 12159 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12160 | Register output = ToRegister(lir->output()); |
| 12161 | |
| 12162 | Label bail; |
| 12163 | masm.ceilDoubleToInt32(input, output, &bail); |
| 12164 | bailoutFrom(&bail, lir->snapshot()); |
| 12165 | } |
| 12166 | |
| 12167 | void CodeGenerator::visitCeilF(LCeilF* lir) { |
| 12168 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12169 | Register output = ToRegister(lir->output()); |
| 12170 | |
| 12171 | Label bail; |
| 12172 | masm.ceilFloat32ToInt32(input, output, &bail); |
| 12173 | bailoutFrom(&bail, lir->snapshot()); |
| 12174 | } |
| 12175 | |
| 12176 | void CodeGenerator::visitRound(LRound* lir) { |
| 12177 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12178 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 12179 | Register output = ToRegister(lir->output()); |
| 12180 | |
| 12181 | Label bail; |
| 12182 | masm.roundDoubleToInt32(input, output, temp, &bail); |
| 12183 | bailoutFrom(&bail, lir->snapshot()); |
| 12184 | } |
| 12185 | |
| 12186 | void CodeGenerator::visitRoundF(LRoundF* lir) { |
| 12187 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12188 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 12189 | Register output = ToRegister(lir->output()); |
| 12190 | |
| 12191 | Label bail; |
| 12192 | masm.roundFloat32ToInt32(input, output, temp, &bail); |
| 12193 | bailoutFrom(&bail, lir->snapshot()); |
| 12194 | } |
| 12195 | |
| 12196 | void CodeGenerator::visitTrunc(LTrunc* lir) { |
| 12197 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12198 | Register output = ToRegister(lir->output()); |
| 12199 | |
| 12200 | Label bail; |
| 12201 | masm.truncDoubleToInt32(input, output, &bail); |
| 12202 | bailoutFrom(&bail, lir->snapshot()); |
| 12203 | } |
| 12204 | |
| 12205 | void CodeGenerator::visitTruncF(LTruncF* lir) { |
| 12206 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12207 | Register output = ToRegister(lir->output()); |
| 12208 | |
| 12209 | Label bail; |
| 12210 | masm.truncFloat32ToInt32(input, output, &bail); |
| 12211 | bailoutFrom(&bail, lir->snapshot()); |
| 12212 | } |
| 12213 | |
| 12214 | void CodeGenerator::visitCompareS(LCompareS* lir) { |
| 12215 | JSOp op = lir->mir()->jsop(); |
| 12216 | Register left = ToRegister(lir->left()); |
| 12217 | Register right = ToRegister(lir->right()); |
| 12218 | Register output = ToRegister(lir->output()); |
| 12219 | |
| 12220 | OutOfLineCode* ool = nullptr; |
| 12221 | |
| 12222 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 12223 | if (op == JSOp::Eq || op == JSOp::StrictEq) { |
| 12224 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
| 12225 | lir, ArgList(left, right), StoreRegisterTo(output)); |
| 12226 | } else if (op == JSOp::Ne || op == JSOp::StrictNe) { |
| 12227 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
| 12228 | lir, ArgList(left, right), StoreRegisterTo(output)); |
| 12229 | } else if (op == JSOp::Lt) { |
| 12230 | ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>( |
| 12231 | lir, ArgList(left, right), StoreRegisterTo(output)); |
| 12232 | } else if (op == JSOp::Le) { |
| 12233 | // Push the operands in reverse order for JSOp::Le: |
| 12234 | // - |left <= right| is implemented as |right >= left|. |
| 12235 | ool = |
| 12236 | oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>( |
| 12237 | lir, ArgList(right, left), StoreRegisterTo(output)); |
| 12238 | } else if (op == JSOp::Gt) { |
| 12239 | // Push the operands in reverse order for JSOp::Gt: |
| 12240 | // - |left > right| is implemented as |right < left|. |
| 12241 | ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>( |
| 12242 | lir, ArgList(right, left), StoreRegisterTo(output)); |
| 12243 | } else { |
| 12244 | MOZ_ASSERT(op == JSOp::Ge)do { static_assert( mozilla::detail::AssertionConditionType< decltype(op == JSOp::Ge)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(op == JSOp::Ge))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("op == JSOp::Ge" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ge" ")"); do { *((volatile int*)__null) = 12244; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12245 | ool = |
| 12246 | oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>( |
| 12247 | lir, ArgList(left, right), StoreRegisterTo(output)); |
| 12248 | } |
| 12249 | |
| 12250 | masm.compareStrings(op, left, right, output, ool->entry()); |
| 12251 | |
| 12252 | masm.bind(ool->rejoin()); |
| 12253 | } |
| 12254 | |
| 12255 | void CodeGenerator::visitCompareSInline(LCompareSInline* lir) { |
| 12256 | JSOp op = lir->mir()->jsop(); |
| 12257 | MOZ_ASSERT(IsEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsEqualityOp(op)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12257); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12257; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12258 | |
| 12259 | Register input = ToRegister(lir->input()); |
| 12260 | Register output = ToRegister(lir->output()); |
| 12261 | |
| 12262 | const JSLinearString* str = lir->constant(); |
| 12263 | MOZ_ASSERT(str->length() > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(str->length() > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(str->length() > 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("str->length() > 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12263); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() > 0" ")"); do { *((volatile int*)__null) = 12263; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12264 | |
| 12265 | OutOfLineCode* ool = nullptr; |
| 12266 | |
| 12267 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 12268 | if (op == JSOp::Eq || op == JSOp::StrictEq) { |
| 12269 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
| 12270 | lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output)); |
| 12271 | } else { |
| 12272 | MOZ_ASSERT(op == JSOp::Ne || op == JSOp::StrictNe)do { static_assert( mozilla::detail::AssertionConditionType< decltype(op == JSOp::Ne || op == JSOp::StrictNe)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(op == JSOp::Ne || op == JSOp::StrictNe))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("op == JSOp::Ne || op == JSOp::StrictNe" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "op == JSOp::Ne || op == JSOp::StrictNe" ")"); do { *((volatile int*)__null) = 12272; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12273 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
| 12274 | lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output)); |
| 12275 | } |
| 12276 | |
| 12277 | Label compareChars; |
| 12278 | { |
| 12279 | Label notPointerEqual; |
| 12280 | |
| 12281 | // If operands point to the same instance, the strings are trivially equal. |
| 12282 | masm.branchPtr(Assembler::NotEqual, input, ImmGCPtr(str), ¬PointerEqual); |
| 12283 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
| 12284 | masm.jump(ool->rejoin()); |
| 12285 | |
| 12286 | masm.bind(¬PointerEqual); |
| 12287 | |
| 12288 | Label setNotEqualResult; |
| 12289 | |
| 12290 | if (str->isAtom()) { |
| 12291 | // Atoms cannot be equal to each other if they point to different strings. |
| 12292 | Imm32 atomBit(JSString::ATOM_BIT); |
| 12293 | masm.branchTest32(Assembler::NonZero, |
| 12294 | Address(input, JSString::offsetOfFlags()), atomBit, |
| 12295 | &setNotEqualResult); |
| 12296 | } |
| 12297 | |
| 12298 | if (str->hasTwoByteChars()) { |
| 12299 | // Pure two-byte strings can't be equal to Latin-1 strings. |
| 12300 | JS::AutoCheckCannotGC nogc; |
| 12301 | if (!mozilla::IsUtf16Latin1(str->twoByteRange(nogc))) { |
| 12302 | masm.branchLatin1String(input, &setNotEqualResult); |
| 12303 | } |
| 12304 | } |
| 12305 | |
| 12306 | // Strings of different length can never be equal. |
| 12307 | masm.branch32(Assembler::NotEqual, |
| 12308 | Address(input, JSString::offsetOfLength()), |
| 12309 | Imm32(str->length()), &setNotEqualResult); |
| 12310 | |
| 12311 | if (str->isAtom()) { |
| 12312 | Label forwardedPtrEqual; |
| 12313 | masm.tryFastAtomize(input, output, output, &compareChars); |
| 12314 | |
| 12315 | // We now have two atoms. Just check pointer equality. |
| 12316 | masm.branchPtr(Assembler::Equal, output, ImmGCPtr(str), |
| 12317 | &forwardedPtrEqual); |
| 12318 | |
| 12319 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
| 12320 | masm.jump(ool->rejoin()); |
| 12321 | |
| 12322 | masm.bind(&forwardedPtrEqual); |
| 12323 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
| 12324 | masm.jump(ool->rejoin()); |
| 12325 | } else { |
| 12326 | masm.jump(&compareChars); |
| 12327 | } |
| 12328 | |
| 12329 | masm.bind(&setNotEqualResult); |
| 12330 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
| 12331 | masm.jump(ool->rejoin()); |
| 12332 | } |
| 12333 | |
| 12334 | masm.bind(&compareChars); |
| 12335 | |
| 12336 | // Load the input string's characters. |
| 12337 | Register stringChars = output; |
| 12338 | masm.loadStringCharsForCompare(input, str, stringChars, ool->entry()); |
| 12339 | |
| 12340 | // Start comparing character by character. |
| 12341 | masm.compareStringChars(op, stringChars, str, output); |
| 12342 | |
| 12343 | masm.bind(ool->rejoin()); |
| 12344 | } |
| 12345 | |
| 12346 | void CodeGenerator::visitCompareSSingle(LCompareSSingle* lir) { |
| 12347 | JSOp op = lir->jsop(); |
| 12348 | MOZ_ASSERT(IsRelationalOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsRelationalOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsRelationalOp(op)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsRelationalOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12348); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsRelationalOp(op)" ")"); do { *((volatile int*)__null) = 12348; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12349 | |
| 12350 | Register input = ToRegister(lir->input()); |
| 12351 | Register output = ToRegister(lir->output()); |
| 12352 | Register temp = ToRegister(lir->temp0()); |
| 12353 | |
| 12354 | const JSLinearString* str = lir->constant(); |
| 12355 | MOZ_ASSERT(str->length() == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(str->length() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(str->length() == 1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("str->length() == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12355); AnnotateMozCrashReason("MOZ_ASSERT" "(" "str->length() == 1" ")"); do { *((volatile int*)__null) = 12355; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12356 | |
| 12357 | char16_t ch = str->latin1OrTwoByteChar(0); |
| 12358 | |
| 12359 | masm.movePtr(input, temp); |
| 12360 | |
| 12361 | // Check if the string is empty. |
| 12362 | Label compareLength; |
| 12363 | masm.branch32(Assembler::Equal, Address(temp, JSString::offsetOfLength()), |
| 12364 | Imm32(0), &compareLength); |
| 12365 | |
| 12366 | // The first character is in the left-most rope child. |
| 12367 | Label notRope; |
| 12368 | masm.branchIfNotRope(temp, ¬Rope); |
| 12369 | { |
| 12370 | // Unwind ropes at the start if possible. |
| 12371 | Label unwindRope; |
| 12372 | masm.bind(&unwindRope); |
| 12373 | masm.loadRopeLeftChild(temp, output); |
| 12374 | masm.movePtr(output, temp); |
| 12375 | |
| 12376 | #ifdef DEBUG1 |
| 12377 | Label notEmpty; |
| 12378 | masm.branch32(Assembler::NotEqual, |
| 12379 | Address(temp, JSString::offsetOfLength()), Imm32(0), |
| 12380 | ¬Empty); |
| 12381 | masm.assumeUnreachable("rope children are non-empty"); |
| 12382 | masm.bind(¬Empty); |
| 12383 | #endif |
| 12384 | |
| 12385 | // Otherwise keep unwinding ropes. |
| 12386 | masm.branchIfRope(temp, &unwindRope); |
| 12387 | } |
| 12388 | masm.bind(¬Rope); |
| 12389 | |
| 12390 | // Load the first character into |output|. |
| 12391 | auto loadFirstChar = [&](auto encoding) { |
| 12392 | masm.loadStringChars(temp, output, encoding); |
| 12393 | masm.loadChar(Address(output, 0), output, encoding); |
| 12394 | }; |
| 12395 | |
| 12396 | Label done; |
| 12397 | if (ch <= JSString::MAX_LATIN1_CHAR) { |
| 12398 | // Handle both encodings when the search character is Latin-1. |
| 12399 | Label twoByte, compare; |
| 12400 | masm.branchTwoByteString(temp, &twoByte); |
| 12401 | |
| 12402 | loadFirstChar(CharEncoding::Latin1); |
| 12403 | masm.jump(&compare); |
| 12404 | |
| 12405 | masm.bind(&twoByte); |
| 12406 | loadFirstChar(CharEncoding::TwoByte); |
| 12407 | |
| 12408 | masm.bind(&compare); |
| 12409 | } else { |
| 12410 | // The search character is a two-byte character, so it can't be equal to any |
| 12411 | // character of a Latin-1 string. |
| 12412 | masm.move32(Imm32(int32_t(op == JSOp::Lt || op == JSOp::Le)), output); |
| 12413 | masm.branchLatin1String(temp, &done); |
| 12414 | |
| 12415 | loadFirstChar(CharEncoding::TwoByte); |
| 12416 | } |
| 12417 | |
| 12418 | // Compare the string length when the search character is equal to the |
| 12419 | // input's first character. |
| 12420 | masm.branch32(Assembler::Equal, output, Imm32(ch), &compareLength); |
| 12421 | |
| 12422 | // Otherwise compute the result and jump to the end. |
| 12423 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), output, Imm32(ch), |
| 12424 | output); |
| 12425 | masm.jump(&done); |
| 12426 | |
| 12427 | // Compare the string length to compute the overall result. |
| 12428 | masm.bind(&compareLength); |
| 12429 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), |
| 12430 | Address(temp, JSString::offsetOfLength()), Imm32(1), output); |
| 12431 | |
| 12432 | masm.bind(&done); |
| 12433 | } |
| 12434 | |
| 12435 | void CodeGenerator::visitCompareBigInt(LCompareBigInt* lir) { |
| 12436 | JSOp op = lir->mir()->jsop(); |
| 12437 | Register left = ToRegister(lir->left()); |
| 12438 | Register right = ToRegister(lir->right()); |
| 12439 | Register temp0 = ToRegister(lir->temp0()); |
| 12440 | Register temp1 = ToRegister(lir->temp1()); |
| 12441 | Register temp2 = ToRegister(lir->temp2()); |
| 12442 | Register output = ToRegister(lir->output()); |
| 12443 | |
| 12444 | Label notSame; |
| 12445 | Label compareSign; |
| 12446 | Label compareLength; |
| 12447 | Label compareDigit; |
| 12448 | |
| 12449 | Label* notSameSign; |
| 12450 | Label* notSameLength; |
| 12451 | Label* notSameDigit; |
| 12452 | if (IsEqualityOp(op)) { |
| 12453 | notSameSign = ¬Same; |
| 12454 | notSameLength = ¬Same; |
| 12455 | notSameDigit = ¬Same; |
| 12456 | } else { |
| 12457 | notSameSign = &compareSign; |
| 12458 | notSameLength = &compareLength; |
| 12459 | notSameDigit = &compareDigit; |
| 12460 | } |
| 12461 | |
| 12462 | masm.equalBigInts(left, right, temp0, temp1, temp2, output, notSameSign, |
| 12463 | notSameLength, notSameDigit); |
| 12464 | |
| 12465 | Label done; |
| 12466 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le || |
| 12467 | op == JSOp::Ge), |
| 12468 | output); |
| 12469 | masm.jump(&done); |
| 12470 | |
| 12471 | if (IsEqualityOp(op)) { |
| 12472 | masm.bind(¬Same); |
| 12473 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
| 12474 | } else { |
| 12475 | Label invertWhenNegative; |
| 12476 | |
| 12477 | // There are two cases when sign(left) != sign(right): |
| 12478 | // 1. sign(left) = positive and sign(right) = negative, |
| 12479 | // 2. or the dual case with reversed signs. |
| 12480 | // |
| 12481 | // For case 1, |left| <cmp> |right| is true for cmp=Gt or cmp=Ge and false |
| 12482 | // for cmp=Lt or cmp=Le. Initialize the result for case 1 and handle case 2 |
| 12483 | // with |invertWhenNegative|. |
| 12484 | masm.bind(&compareSign); |
| 12485 | masm.move32(Imm32(op == JSOp::Gt || op == JSOp::Ge), output); |
| 12486 | masm.jump(&invertWhenNegative); |
| 12487 | |
| 12488 | // For sign(left) = sign(right) and len(digits(left)) != len(digits(right)), |
| 12489 | // we have to consider the two cases: |
| 12490 | // 1. len(digits(left)) < len(digits(right)) |
| 12491 | // 2. len(digits(left)) > len(digits(right)) |
| 12492 | // |
| 12493 | // For |left| <cmp> |right| with cmp=Lt: |
| 12494 | // Assume both BigInts are positive, then |left < right| is true for case 1 |
| 12495 | // and false for case 2. When both are negative, the result is reversed. |
| 12496 | // |
| 12497 | // The other comparison operators can be handled similarly. |
| 12498 | // |
| 12499 | // |temp0| holds the digits length of the right-hand side operand. |
| 12500 | masm.bind(&compareLength); |
| 12501 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), |
| 12502 | Address(left, BigInt::offsetOfLength()), temp0, output); |
| 12503 | masm.jump(&invertWhenNegative); |
| 12504 | |
| 12505 | // Similar to the case above, compare the current digit to determine the |
| 12506 | // overall comparison result. |
| 12507 | // |
| 12508 | // |temp1| points to the current digit of the left-hand side operand. |
| 12509 | // |output| holds the current digit of the right-hand side operand. |
| 12510 | masm.bind(&compareDigit); |
| 12511 | masm.cmpPtrSet(JSOpToCondition(op, /* isSigned = */ false), |
| 12512 | Address(temp1, 0), output, output); |
| 12513 | |
| 12514 | Label nonNegative; |
| 12515 | masm.bind(&invertWhenNegative); |
| 12516 | masm.branchIfBigIntIsNonNegative(left, &nonNegative); |
| 12517 | masm.xor32(Imm32(1), output); |
| 12518 | masm.bind(&nonNegative); |
| 12519 | } |
| 12520 | |
| 12521 | masm.bind(&done); |
| 12522 | } |
| 12523 | |
| 12524 | void CodeGenerator::visitCompareBigIntInt32(LCompareBigIntInt32* lir) { |
| 12525 | JSOp op = lir->mir()->jsop(); |
| 12526 | Register left = ToRegister(lir->left()); |
| 12527 | Register temp0 = ToRegister(lir->temp0()); |
| 12528 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
| 12529 | Register output = ToRegister(lir->output()); |
| 12530 | |
| 12531 | Label ifTrue, ifFalse; |
| 12532 | if (lir->right()->isConstant()) { |
| 12533 | MOZ_ASSERT(temp1 == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 == InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 == InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp1 == InvalidReg" ")"); do { *((volatile int*)__null) = 12533; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12534 | |
| 12535 | Imm32 right = Imm32(ToInt32(lir->right())); |
| 12536 | masm.compareBigIntAndInt32(op, left, right, temp0, &ifTrue, &ifFalse); |
| 12537 | } else { |
| 12538 | MOZ_ASSERT(temp1 != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 != InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 != InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp1 != InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12538); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp1 != InvalidReg" ")"); do { *((volatile int*)__null) = 12538; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12539 | |
| 12540 | Register right = ToRegister(lir->right()); |
| 12541 | masm.compareBigIntAndInt32(op, left, right, temp0, temp1, &ifTrue, |
| 12542 | &ifFalse); |
| 12543 | } |
| 12544 | |
| 12545 | Label done; |
| 12546 | masm.bind(&ifFalse); |
| 12547 | masm.move32(Imm32(0), output); |
| 12548 | masm.jump(&done); |
| 12549 | masm.bind(&ifTrue); |
| 12550 | masm.move32(Imm32(1), output); |
| 12551 | masm.bind(&done); |
| 12552 | } |
| 12553 | |
| 12554 | void CodeGenerator::visitCompareBigIntInt32AndBranch( |
| 12555 | LCompareBigIntInt32AndBranch* lir) { |
| 12556 | JSOp op = lir->cmpMir()->jsop(); |
| 12557 | Register left = ToRegister(lir->left()); |
| 12558 | Register temp1 = ToRegister(lir->temp1()); |
| 12559 | Register temp2 = ToTempRegisterOrInvalid(lir->temp2()); |
| 12560 | |
| 12561 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
| 12562 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
| 12563 | |
| 12564 | // compareBigIntAndInt32 falls through to the false case. If the next block |
| 12565 | // is the true case, negate the comparison so we can fall through. |
| 12566 | if (isNextBlock(lir->ifTrue()->lir())) { |
| 12567 | op = NegateCompareOp(op); |
| 12568 | std::swap(ifTrue, ifFalse); |
| 12569 | } |
| 12570 | |
| 12571 | if (lir->right()->isConstant()) { |
| 12572 | MOZ_ASSERT(temp2 == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 == InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 == InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp2 == InvalidReg" ")"); do { *((volatile int*)__null) = 12572; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12573 | |
| 12574 | Imm32 right = Imm32(ToInt32(lir->right())); |
| 12575 | masm.compareBigIntAndInt32(op, left, right, temp1, ifTrue, ifFalse); |
| 12576 | } else { |
| 12577 | MOZ_ASSERT(temp2 != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 != InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 != InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp2 != InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12577); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp2 != InvalidReg" ")"); do { *((volatile int*)__null) = 12577; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12578 | |
| 12579 | Register right = ToRegister(lir->right()); |
| 12580 | masm.compareBigIntAndInt32(op, left, right, temp1, temp2, ifTrue, ifFalse); |
| 12581 | } |
| 12582 | |
| 12583 | if (!isNextBlock(lir->ifTrue()->lir())) { |
| 12584 | jumpToBlock(lir->ifFalse()); |
| 12585 | } |
| 12586 | } |
| 12587 | |
| 12588 | void CodeGenerator::visitCompareBigIntDouble(LCompareBigIntDouble* lir) { |
| 12589 | JSOp op = lir->mir()->jsop(); |
| 12590 | Register left = ToRegister(lir->left()); |
| 12591 | FloatRegister right = ToFloatRegister(lir->right()); |
| 12592 | Register output = ToRegister(lir->output()); |
| 12593 | |
| 12594 | masm.setupAlignedABICall(); |
| 12595 | |
| 12596 | // Push the operands in reverse order for JSOp::Le and JSOp::Gt: |
| 12597 | // - |left <= right| is implemented as |right >= left|. |
| 12598 | // - |left > right| is implemented as |right < left|. |
| 12599 | if (op == JSOp::Le || op == JSOp::Gt) { |
| 12600 | masm.passABIArg(right, ABIType::Float64); |
| 12601 | masm.passABIArg(left); |
| 12602 | } else { |
| 12603 | masm.passABIArg(left); |
| 12604 | masm.passABIArg(right, ABIType::Float64); |
| 12605 | } |
| 12606 | |
| 12607 | using FnBigIntNumber = bool (*)(BigInt*, double); |
| 12608 | using FnNumberBigInt = bool (*)(double, BigInt*); |
| 12609 | switch (op) { |
| 12610 | case JSOp::Eq: { |
| 12611 | masm.callWithABI<FnBigIntNumber, |
| 12612 | jit::BigIntNumberEqual<EqualityKind::Equal>>(); |
| 12613 | break; |
| 12614 | } |
| 12615 | case JSOp::Ne: { |
| 12616 | masm.callWithABI<FnBigIntNumber, |
| 12617 | jit::BigIntNumberEqual<EqualityKind::NotEqual>>(); |
| 12618 | break; |
| 12619 | } |
| 12620 | case JSOp::Lt: { |
| 12621 | masm.callWithABI<FnBigIntNumber, |
| 12622 | jit::BigIntNumberCompare<ComparisonKind::LessThan>>(); |
| 12623 | break; |
| 12624 | } |
| 12625 | case JSOp::Gt: { |
| 12626 | masm.callWithABI<FnNumberBigInt, |
| 12627 | jit::NumberBigIntCompare<ComparisonKind::LessThan>>(); |
| 12628 | break; |
| 12629 | } |
| 12630 | case JSOp::Le: { |
| 12631 | masm.callWithABI< |
| 12632 | FnNumberBigInt, |
| 12633 | jit::NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>>(); |
| 12634 | break; |
| 12635 | } |
| 12636 | case JSOp::Ge: { |
| 12637 | masm.callWithABI< |
| 12638 | FnBigIntNumber, |
| 12639 | jit::BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>>(); |
| 12640 | break; |
| 12641 | } |
| 12642 | default: |
| 12643 | MOZ_CRASH("unhandled op")do { do { } while (false); MOZ_ReportCrash("" "unhandled op", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12643); AnnotateMozCrashReason("MOZ_CRASH(" "unhandled op" ")" ); do { *((volatile int*)__null) = 12643; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 12644 | } |
| 12645 | |
| 12646 | masm.storeCallBoolResult(output); |
| 12647 | } |
| 12648 | |
| 12649 | void CodeGenerator::visitCompareBigIntString(LCompareBigIntString* lir) { |
| 12650 | JSOp op = lir->mir()->jsop(); |
| 12651 | Register left = ToRegister(lir->left()); |
| 12652 | Register right = ToRegister(lir->right()); |
| 12653 | |
| 12654 | // Push the operands in reverse order for JSOp::Le and JSOp::Gt: |
| 12655 | // - |left <= right| is implemented as |right >= left|. |
| 12656 | // - |left > right| is implemented as |right < left|. |
| 12657 | if (op == JSOp::Le || op == JSOp::Gt) { |
| 12658 | pushArg(left); |
| 12659 | pushArg(right); |
| 12660 | } else { |
| 12661 | pushArg(right); |
| 12662 | pushArg(left); |
| 12663 | } |
| 12664 | |
| 12665 | using FnBigIntString = |
| 12666 | bool (*)(JSContext*, HandleBigInt, HandleString, bool*); |
| 12667 | using FnStringBigInt = |
| 12668 | bool (*)(JSContext*, HandleString, HandleBigInt, bool*); |
| 12669 | |
| 12670 | switch (op) { |
| 12671 | case JSOp::Eq: { |
| 12672 | constexpr auto Equal = EqualityKind::Equal; |
| 12673 | callVM<FnBigIntString, BigIntStringEqual<Equal>>(lir); |
| 12674 | break; |
| 12675 | } |
| 12676 | case JSOp::Ne: { |
| 12677 | constexpr auto NotEqual = EqualityKind::NotEqual; |
| 12678 | callVM<FnBigIntString, BigIntStringEqual<NotEqual>>(lir); |
| 12679 | break; |
| 12680 | } |
| 12681 | case JSOp::Lt: { |
| 12682 | constexpr auto LessThan = ComparisonKind::LessThan; |
| 12683 | callVM<FnBigIntString, BigIntStringCompare<LessThan>>(lir); |
| 12684 | break; |
| 12685 | } |
| 12686 | case JSOp::Gt: { |
| 12687 | constexpr auto LessThan = ComparisonKind::LessThan; |
| 12688 | callVM<FnStringBigInt, StringBigIntCompare<LessThan>>(lir); |
| 12689 | break; |
| 12690 | } |
| 12691 | case JSOp::Le: { |
| 12692 | constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual; |
| 12693 | callVM<FnStringBigInt, StringBigIntCompare<GreaterThanOrEqual>>(lir); |
| 12694 | break; |
| 12695 | } |
| 12696 | case JSOp::Ge: { |
| 12697 | constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual; |
| 12698 | callVM<FnBigIntString, BigIntStringCompare<GreaterThanOrEqual>>(lir); |
| 12699 | break; |
| 12700 | } |
| 12701 | default: |
| 12702 | MOZ_CRASH("Unexpected compare op")do { do { } while (false); MOZ_ReportCrash("" "Unexpected compare op" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12702); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected compare op" ")"); do { *((volatile int*)__null) = 12702; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 12703 | } |
| 12704 | } |
| 12705 | |
| 12706 | void CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir) { |
| 12707 | MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12708); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12708; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 12708 | lir->mir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12708); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12708; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12709 | |
| 12710 | JSOp op = lir->mir()->jsop(); |
| 12711 | MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12711; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12712 | |
| 12713 | const ValueOperand value = ToValue(lir, LIsNullOrLikeUndefinedV::ValueIndex); |
| 12714 | Register output = ToRegister(lir->output()); |
| 12715 | |
| 12716 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 12717 | if (!intact) { |
| 12718 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 12719 | addOutOfLineCode(ool, lir->mir()); |
| 12720 | |
| 12721 | Label* nullOrLikeUndefined = ool->label1(); |
| 12722 | Label* notNullOrLikeUndefined = ool->label2(); |
| 12723 | |
| 12724 | { |
| 12725 | ScratchTagScope tag(masm, value); |
| 12726 | masm.splitTagForTest(value, tag); |
| 12727 | |
| 12728 | masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined); |
| 12729 | masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined); |
| 12730 | |
| 12731 | // Check whether it's a truthy object or a falsy object that emulates |
| 12732 | // undefined. |
| 12733 | masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined); |
| 12734 | } |
| 12735 | |
| 12736 | Register objreg = |
| 12737 | masm.extractObject(value, ToTempUnboxRegister(lir->temp0())); |
| 12738 | branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, |
| 12739 | notNullOrLikeUndefined, output, ool); |
| 12740 | // fall through |
| 12741 | |
| 12742 | Label done; |
| 12743 | |
| 12744 | // It's not null or undefined, and if it's an object it doesn't |
| 12745 | // emulate undefined, so it's not like undefined. |
| 12746 | masm.move32(Imm32(op == JSOp::Ne), output); |
| 12747 | masm.jump(&done); |
| 12748 | |
| 12749 | masm.bind(nullOrLikeUndefined); |
| 12750 | masm.move32(Imm32(op == JSOp::Eq), output); |
| 12751 | |
| 12752 | // Both branches meet here. |
| 12753 | masm.bind(&done); |
| 12754 | } else { |
| 12755 | Label nullOrUndefined, notNullOrLikeUndefined; |
| 12756 | #if defined(DEBUG1) || defined(FUZZING) |
| 12757 | Register objreg = Register::Invalid(); |
| 12758 | #endif |
| 12759 | { |
| 12760 | ScratchTagScope tag(masm, value); |
| 12761 | masm.splitTagForTest(value, tag); |
| 12762 | |
| 12763 | masm.branchTestNull(Assembler::Equal, tag, &nullOrUndefined); |
| 12764 | masm.branchTestUndefined(Assembler::Equal, tag, &nullOrUndefined); |
| 12765 | |
| 12766 | #if defined(DEBUG1) || defined(FUZZING) |
| 12767 | // Check whether it's a truthy object or a falsy object that emulates |
| 12768 | // undefined. |
| 12769 | masm.branchTestObject(Assembler::NotEqual, tag, ¬NullOrLikeUndefined); |
| 12770 | objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp0())); |
| 12771 | #endif |
| 12772 | } |
| 12773 | |
| 12774 | #if defined(DEBUG1) || defined(FUZZING) |
| 12775 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
| 12776 | masm.bind(¬NullOrLikeUndefined); |
| 12777 | #endif |
| 12778 | |
| 12779 | Label done; |
| 12780 | |
| 12781 | // It's not null or undefined, and if it's an object it doesn't |
| 12782 | // emulate undefined. |
| 12783 | masm.move32(Imm32(op == JSOp::Ne), output); |
| 12784 | masm.jump(&done); |
| 12785 | |
| 12786 | masm.bind(&nullOrUndefined); |
| 12787 | masm.move32(Imm32(op == JSOp::Eq), output); |
| 12788 | |
| 12789 | // Both branches meet here. |
| 12790 | masm.bind(&done); |
| 12791 | } |
| 12792 | } |
| 12793 | |
| 12794 | void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV( |
| 12795 | LIsNullOrLikeUndefinedAndBranchV* lir) { |
| 12796 | MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12797; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 12797 | lir->cmpMir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12797; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12798 | |
| 12799 | JSOp op = lir->cmpMir()->jsop(); |
| 12800 | MOZ_ASSERT(IsLooseEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12800); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12800; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12801 | |
| 12802 | const ValueOperand value = |
| 12803 | ToValue(lir, LIsNullOrLikeUndefinedAndBranchV::Value); |
| 12804 | |
| 12805 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 12806 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 12807 | |
| 12808 | if (op == JSOp::Ne) { |
| 12809 | // Swap branches. |
| 12810 | std::swap(ifTrue, ifFalse); |
| 12811 | } |
| 12812 | |
| 12813 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 12814 | |
| 12815 | Label* ifTrueLabel = getJumpLabelForBranch(ifTrue); |
| 12816 | Label* ifFalseLabel = getJumpLabelForBranch(ifFalse); |
| 12817 | |
| 12818 | { |
| 12819 | ScratchTagScope tag(masm, value); |
| 12820 | masm.splitTagForTest(value, tag); |
| 12821 | |
| 12822 | masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel); |
| 12823 | masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel); |
| 12824 | |
| 12825 | masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel); |
| 12826 | } |
| 12827 | |
| 12828 | bool extractObject = !intact; |
Value stored to 'extractObject' during its initialization is never read | |
| 12829 | #if defined(DEBUG1) || defined(FUZZING) |
| 12830 | // always extract objreg if we're in debug and |
| 12831 | // assertObjectDoesNotEmulateUndefined; |
| 12832 | extractObject = true; |
| 12833 | #endif |
| 12834 | |
| 12835 | Register objreg = Register::Invalid(); |
| 12836 | Register scratch = ToRegister(lir->temp()); |
| 12837 | if (extractObject) { |
| 12838 | objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox())); |
| 12839 | } |
| 12840 | if (!intact) { |
| 12841 | // Objects that emulate undefined are loosely equal to null/undefined. |
| 12842 | OutOfLineTestObject* ool = new (alloc()) OutOfLineTestObject(); |
| 12843 | addOutOfLineCode(ool, lir->cmpMir()); |
| 12844 | testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch, |
| 12845 | ool); |
| 12846 | } else { |
| 12847 | assertObjectDoesNotEmulateUndefined(objreg, scratch, lir->cmpMir()); |
| 12848 | // Bug 1874905. This would be nice to optimize out at the MIR level. |
| 12849 | masm.jump(ifFalseLabel); |
| 12850 | } |
| 12851 | } |
| 12852 | |
| 12853 | void CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir) { |
| 12854 | MOZ_ASSERT(lir->mir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12855; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 12855 | lir->mir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12855; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12856 | MOZ_ASSERT(lir->mir()->lhs()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->lhs()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->lhs()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->lhs()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12856); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->lhs()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 12856; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12857 | |
| 12858 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 12859 | JSOp op = lir->mir()->jsop(); |
| 12860 | Register output = ToRegister(lir->output()); |
| 12861 | Register objreg = ToRegister(lir->input()); |
| 12862 | if (!intact) { |
| 12863 | MOZ_ASSERT(IsLooseEqualityOp(op),do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12864; __attribute__((nomerge)) ::abort (); } while (false); } } while (false) |
| 12864 | "Strict equality should have been folded")do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12864); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12864; __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
| 12865 | |
| 12866 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 12867 | addOutOfLineCode(ool, lir->mir()); |
| 12868 | |
| 12869 | Label* emulatesUndefined = ool->label1(); |
| 12870 | Label* doesntEmulateUndefined = ool->label2(); |
| 12871 | |
| 12872 | branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, |
| 12873 | doesntEmulateUndefined, output, ool); |
| 12874 | |
| 12875 | Label done; |
| 12876 | |
| 12877 | masm.move32(Imm32(op == JSOp::Ne), output); |
| 12878 | masm.jump(&done); |
| 12879 | |
| 12880 | masm.bind(emulatesUndefined); |
| 12881 | masm.move32(Imm32(op == JSOp::Eq), output); |
| 12882 | masm.bind(&done); |
| 12883 | } else { |
| 12884 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
| 12885 | masm.move32(Imm32(op == JSOp::Ne), output); |
| 12886 | } |
| 12887 | } |
| 12888 | |
| 12889 | void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT( |
| 12890 | LIsNullOrLikeUndefinedAndBranchT* lir) { |
| 12891 | MOZ_ASSERT(lir->cmpMir()->compareType() == MCompare::Compare_Undefined ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12892; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 12892 | lir->cmpMir()->compareType() == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12892); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12892; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12893 | MOZ_ASSERT(lir->cmpMir()->lhs()->type() == MIRType::Object)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->cmpMir()->lhs()->type() == MIRType::Object )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->cmpMir()->lhs()->type() == MIRType::Object ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->cmpMir()->lhs()->type() == MIRType::Object", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12893); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->lhs()->type() == MIRType::Object" ")"); do { *((volatile int*)__null) = 12893; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12894 | |
| 12895 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 12896 | |
| 12897 | JSOp op = lir->cmpMir()->jsop(); |
| 12898 | MOZ_ASSERT(IsLooseEqualityOp(op), "Strict equality should have been folded")do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsLooseEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsLooseEqualityOp(op)))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("IsLooseEqualityOp(op)" " (" "Strict equality should have been folded" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12898); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { * ((volatile int*)__null) = 12898; __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
| 12899 | |
| 12900 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 12901 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 12902 | |
| 12903 | if (op == JSOp::Ne) { |
| 12904 | // Swap branches. |
| 12905 | std::swap(ifTrue, ifFalse); |
| 12906 | } |
| 12907 | |
| 12908 | Register input = ToRegister(lir->getOperand(0)); |
| 12909 | Register scratch = ToRegister(lir->temp()); |
| 12910 | Label* ifTrueLabel = getJumpLabelForBranch(ifTrue); |
| 12911 | Label* ifFalseLabel = getJumpLabelForBranch(ifFalse); |
| 12912 | |
| 12913 | if (intact) { |
| 12914 | // Bug 1874905. Ideally branches like this would be optimized out. |
| 12915 | assertObjectDoesNotEmulateUndefined(input, scratch, lir->mir()); |
| 12916 | masm.jump(ifFalseLabel); |
| 12917 | } else { |
| 12918 | auto* ool = new (alloc()) OutOfLineTestObject(); |
| 12919 | addOutOfLineCode(ool, lir->cmpMir()); |
| 12920 | |
| 12921 | // Objects that emulate undefined are loosely equal to null/undefined. |
| 12922 | testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool); |
| 12923 | } |
| 12924 | } |
| 12925 | |
| 12926 | void CodeGenerator::visitIsNull(LIsNull* lir) { |
| 12927 | MCompare::CompareType compareType = lir->mir()->compareType(); |
| 12928 | MOZ_ASSERT(compareType == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Null)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(compareType == MCompare::Compare_Null))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12928); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12928; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12929 | |
| 12930 | JSOp op = lir->mir()->jsop(); |
| 12931 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12931); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12931; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12932 | |
| 12933 | const ValueOperand value = ToValue(lir, LIsNull::ValueIndex); |
| 12934 | Register output = ToRegister(lir->output()); |
| 12935 | |
| 12936 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
| 12937 | masm.testNullSet(cond, value, output); |
| 12938 | } |
| 12939 | |
| 12940 | void CodeGenerator::visitIsUndefined(LIsUndefined* lir) { |
| 12941 | MCompare::CompareType compareType = lir->mir()->compareType(); |
| 12942 | MOZ_ASSERT(compareType == MCompare::Compare_Undefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Undefined)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(compareType == MCompare::Compare_Undefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Undefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12942); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined" ")"); do { *((volatile int*)__null) = 12942; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12943 | |
| 12944 | JSOp op = lir->mir()->jsop(); |
| 12945 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12945); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12945; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12946 | |
| 12947 | const ValueOperand value = ToValue(lir, LIsUndefined::ValueIndex); |
| 12948 | Register output = ToRegister(lir->output()); |
| 12949 | |
| 12950 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
| 12951 | masm.testUndefinedSet(cond, value, output); |
| 12952 | } |
| 12953 | |
| 12954 | void CodeGenerator::visitIsNullAndBranch(LIsNullAndBranch* lir) { |
| 12955 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
| 12956 | MOZ_ASSERT(compareType == MCompare::Compare_Null)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Null)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(compareType == MCompare::Compare_Null))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Null" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12956); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")"); do { *((volatile int*)__null) = 12956; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12957 | |
| 12958 | JSOp op = lir->cmpMir()->jsop(); |
| 12959 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12959); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12959; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12960 | |
| 12961 | const ValueOperand value = ToValue(lir, LIsNullAndBranch::Value); |
| 12962 | |
| 12963 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
| 12964 | |
| 12965 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 12966 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 12967 | |
| 12968 | if (isNextBlock(ifFalse->lir())) { |
| 12969 | masm.branchTestNull(cond, value, getJumpLabelForBranch(ifTrue)); |
| 12970 | } else { |
| 12971 | masm.branchTestNull(Assembler::InvertCondition(cond), value, |
| 12972 | getJumpLabelForBranch(ifFalse)); |
| 12973 | jumpToBlock(ifTrue); |
| 12974 | } |
| 12975 | } |
| 12976 | |
| 12977 | void CodeGenerator::visitIsUndefinedAndBranch(LIsUndefinedAndBranch* lir) { |
| 12978 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
| 12979 | MOZ_ASSERT(compareType == MCompare::Compare_Undefined)do { static_assert( mozilla::detail::AssertionConditionType< decltype(compareType == MCompare::Compare_Undefined)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(compareType == MCompare::Compare_Undefined))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("compareType == MCompare::Compare_Undefined" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12979); AnnotateMozCrashReason("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined" ")"); do { *((volatile int*)__null) = 12979; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12980 | |
| 12981 | JSOp op = lir->cmpMir()->jsop(); |
| 12982 | MOZ_ASSERT(IsStrictEqualityOp(op))do { static_assert( mozilla::detail::AssertionConditionType< decltype(IsStrictEqualityOp(op))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(IsStrictEqualityOp(op)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("IsStrictEqualityOp(op)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 12982); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { *((volatile int*)__null) = 12982; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12983 | |
| 12984 | const ValueOperand value = ToValue(lir, LIsUndefinedAndBranch::Value); |
| 12985 | |
| 12986 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
| 12987 | |
| 12988 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 12989 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 12990 | |
| 12991 | if (isNextBlock(ifFalse->lir())) { |
| 12992 | masm.branchTestUndefined(cond, value, getJumpLabelForBranch(ifTrue)); |
| 12993 | } else { |
| 12994 | masm.branchTestUndefined(Assembler::InvertCondition(cond), value, |
| 12995 | getJumpLabelForBranch(ifFalse)); |
| 12996 | jumpToBlock(ifTrue); |
| 12997 | } |
| 12998 | } |
| 12999 | |
| 13000 | void CodeGenerator::visitSameValueDouble(LSameValueDouble* lir) { |
| 13001 | FloatRegister left = ToFloatRegister(lir->left()); |
| 13002 | FloatRegister right = ToFloatRegister(lir->right()); |
| 13003 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 13004 | Register output = ToRegister(lir->output()); |
| 13005 | |
| 13006 | masm.sameValueDouble(left, right, temp, output); |
| 13007 | } |
| 13008 | |
| 13009 | void CodeGenerator::visitSameValue(LSameValue* lir) { |
| 13010 | ValueOperand lhs = ToValue(lir, LSameValue::LhsIndex); |
| 13011 | ValueOperand rhs = ToValue(lir, LSameValue::RhsIndex); |
| 13012 | Register output = ToRegister(lir->output()); |
| 13013 | |
| 13014 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*); |
| 13015 | OutOfLineCode* ool = |
| 13016 | oolCallVM<Fn, SameValue>(lir, ArgList(lhs, rhs), StoreRegisterTo(output)); |
| 13017 | |
| 13018 | // First check to see if the values have identical bits. |
| 13019 | // This is correct for SameValue because SameValue(NaN,NaN) is true, |
| 13020 | // and SameValue(0,-0) is false. |
| 13021 | masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(), |
| 13022 | ool->entry()); |
| 13023 | masm.move32(Imm32(1), output); |
| 13024 | |
| 13025 | // If this fails, call SameValue. |
| 13026 | masm.bind(ool->rejoin()); |
| 13027 | } |
| 13028 | |
| 13029 | void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs, |
| 13030 | Register output) { |
| 13031 | using Fn = |
| 13032 | JSString* (*)(JSContext*, HandleString, HandleString, js::gc::Heap); |
| 13033 | OutOfLineCode* ool = oolCallVM<Fn, ConcatStrings<CanGC>>( |
| 13034 | lir, ArgList(lhs, rhs, static_cast<Imm32>(int32_t(gc::Heap::Default))), |
| 13035 | StoreRegisterTo(output)); |
| 13036 | |
| 13037 | JitCode* stringConcatStub = |
| 13038 | snapshot_->getZoneStub(JitZone::StubKind::StringConcat); |
| 13039 | masm.call(stringConcatStub); |
| 13040 | masm.branchTestPtr(Assembler::Zero, output, output, ool->entry()); |
| 13041 | |
| 13042 | masm.bind(ool->rejoin()); |
| 13043 | } |
| 13044 | |
| 13045 | void CodeGenerator::visitConcat(LConcat* lir) { |
| 13046 | Register lhs = ToRegister(lir->lhs()); |
| 13047 | Register rhs = ToRegister(lir->rhs()); |
| 13048 | |
| 13049 | Register output = ToRegister(lir->output()); |
| 13050 | |
| 13051 | MOZ_ASSERT(lhs == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lhs == CallTempReg0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lhs == CallTempReg0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lhs == CallTempReg0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13051); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lhs == CallTempReg0" ")"); do { *((volatile int*)__null) = 13051; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13052 | MOZ_ASSERT(rhs == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(rhs == CallTempReg1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(rhs == CallTempReg1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("rhs == CallTempReg1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13052); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rhs == CallTempReg1" ")"); do { *((volatile int*)__null) = 13052; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13053 | MOZ_ASSERT(ToRegister(lir->temp0()) == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp0()) == CallTempReg0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp0()) == CallTempReg0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp0()) == CallTempReg0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp0()) == CallTempReg0" ")"); do { *((volatile int*)__null) = 13053; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13054 | MOZ_ASSERT(ToRegister(lir->temp1()) == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp1()) == CallTempReg1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp1()) == CallTempReg1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp1()) == CallTempReg1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13054); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp1()) == CallTempReg1" ")"); do { *((volatile int*)__null) = 13054; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13055 | MOZ_ASSERT(ToRegister(lir->temp2()) == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp2()) == CallTempReg2)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp2()) == CallTempReg2))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp2()) == CallTempReg2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp2()) == CallTempReg2" ")"); do { *((volatile int*)__null) = 13055; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13056 | MOZ_ASSERT(ToRegister(lir->temp3()) == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp3()) == CallTempReg3)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp3()) == CallTempReg3))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp3()) == CallTempReg3" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp3()) == CallTempReg3" ")"); do { *((volatile int*)__null) = 13056; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13057 | MOZ_ASSERT(ToRegister(lir->temp4()) == CallTempReg4)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->temp4()) == CallTempReg4)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->temp4()) == CallTempReg4))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->temp4()) == CallTempReg4" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13057); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->temp4()) == CallTempReg4" ")"); do { *((volatile int*)__null) = 13057; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13058 | MOZ_ASSERT(output == CallTempReg5)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output == CallTempReg5)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output == CallTempReg5))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("output == CallTempReg5" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13058); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == CallTempReg5" ")"); do { *((volatile int*)__null) = 13058; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13059 | |
| 13060 | emitConcat(lir, lhs, rhs, output); |
| 13061 | } |
| 13062 | |
| 13063 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
| 13064 | Register len, Register byteOpScratch, |
| 13065 | CharEncoding fromEncoding, CharEncoding toEncoding, |
| 13066 | size_t maximumLength = SIZE_MAX(18446744073709551615UL)) { |
| 13067 | // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0 |
| 13068 | // (checked below in debug builds), and when done |to| must point to the |
| 13069 | // next available char. |
| 13070 | |
| 13071 | #ifdef DEBUG1 |
| 13072 | Label ok; |
| 13073 | masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); |
| 13074 | masm.assumeUnreachable("Length should be greater than 0."); |
| 13075 | masm.bind(&ok); |
| 13076 | |
| 13077 | if (maximumLength != SIZE_MAX(18446744073709551615UL)) { |
| 13078 | MOZ_ASSERT(maximumLength <= INT32_MAX, "maximum length fits into int32")do { static_assert( mozilla::detail::AssertionConditionType< decltype(maximumLength <= (2147483647))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(maximumLength <= (2147483647 )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("maximumLength <= (2147483647)" " (" "maximum length fits into int32" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13078); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maximumLength <= (2147483647)" ") (" "maximum length fits into int32" ")"); do { *((volatile int*)__null) = 13078; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
| 13079 | |
| 13080 | Label ok; |
| 13081 | masm.branchPtr(Assembler::BelowOrEqual, len, Imm32(maximumLength), &ok); |
| 13082 | masm.assumeUnreachable("Length should not exceed maximum length."); |
| 13083 | masm.bind(&ok); |
| 13084 | } |
| 13085 | #endif |
| 13086 | |
| 13087 | MOZ_ASSERT_IF(toEncoding == CharEncoding::Latin1,do { if (toEncoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(fromEncoding == CharEncoding::Latin1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fromEncoding == CharEncoding ::Latin1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("fromEncoding == CharEncoding::Latin1", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13088); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1" ")"); do { *((volatile int*)__null) = 13088; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 13088 | fromEncoding == CharEncoding::Latin1)do { if (toEncoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(fromEncoding == CharEncoding::Latin1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fromEncoding == CharEncoding ::Latin1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("fromEncoding == CharEncoding::Latin1", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13088); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1" ")"); do { *((volatile int*)__null) = 13088; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 13089 | |
| 13090 | size_t fromWidth = |
| 13091 | fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); |
| 13092 | size_t toWidth = |
| 13093 | toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); |
| 13094 | |
| 13095 | // Try to copy multiple characters at once when both encoding are equal. |
| 13096 | if (fromEncoding == toEncoding) { |
| 13097 | constexpr size_t ptrWidth = sizeof(uintptr_t); |
| 13098 | |
| 13099 | // Copy |width| bytes and then adjust |from| and |to|. |
| 13100 | auto copyCharacters = [&](size_t width) { |
| 13101 | static_assert(ptrWidth <= 8, "switch handles only up to eight bytes"); |
| 13102 | |
| 13103 | switch (width) { |
| 13104 | case 1: |
| 13105 | masm.load8ZeroExtend(Address(from, 0), byteOpScratch); |
| 13106 | masm.store8(byteOpScratch, Address(to, 0)); |
| 13107 | break; |
| 13108 | case 2: |
| 13109 | masm.load16ZeroExtend(Address(from, 0), byteOpScratch); |
| 13110 | masm.store16(byteOpScratch, Address(to, 0)); |
| 13111 | break; |
| 13112 | case 4: |
| 13113 | masm.load32(Address(from, 0), byteOpScratch); |
| 13114 | masm.store32(byteOpScratch, Address(to, 0)); |
| 13115 | break; |
| 13116 | case 8: |
| 13117 | MOZ_ASSERT(width == ptrWidth)do { static_assert( mozilla::detail::AssertionConditionType< decltype(width == ptrWidth)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(width == ptrWidth))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("width == ptrWidth" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13117); AnnotateMozCrashReason("MOZ_ASSERT" "(" "width == ptrWidth" ")"); do { *((volatile int*)__null) = 13117; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13118 | masm.loadPtr(Address(from, 0), byteOpScratch); |
| 13119 | masm.storePtr(byteOpScratch, Address(to, 0)); |
| 13120 | break; |
| 13121 | } |
| 13122 | |
| 13123 | masm.addPtr(Imm32(width), from); |
| 13124 | masm.addPtr(Imm32(width), to); |
| 13125 | }; |
| 13126 | |
| 13127 | // First align |len| to pointer width. |
| 13128 | Label done; |
| 13129 | for (size_t width = fromWidth; width < ptrWidth; width *= 2) { |
| 13130 | // Number of characters which fit into |width| bytes. |
| 13131 | size_t charsPerWidth = width / fromWidth; |
| 13132 | |
| 13133 | if (charsPerWidth < maximumLength) { |
| 13134 | Label next; |
| 13135 | masm.branchTest32(Assembler::Zero, len, Imm32(charsPerWidth), &next); |
| 13136 | |
| 13137 | copyCharacters(width); |
| 13138 | |
| 13139 | masm.branchSub32(Assembler::Zero, Imm32(charsPerWidth), len, &done); |
| 13140 | masm.bind(&next); |
| 13141 | } else if (charsPerWidth == maximumLength) { |
| 13142 | copyCharacters(width); |
| 13143 | masm.sub32(Imm32(charsPerWidth), len); |
| 13144 | } |
| 13145 | } |
| 13146 | |
| 13147 | size_t maxInlineLength; |
| 13148 | if (fromEncoding == CharEncoding::Latin1) { |
| 13149 | maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; |
| 13150 | } else { |
| 13151 | maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
| 13152 | } |
| 13153 | |
| 13154 | // Number of characters which fit into a single register. |
| 13155 | size_t charsPerPtr = ptrWidth / fromWidth; |
| 13156 | |
| 13157 | // Unroll small loops. |
| 13158 | constexpr size_t unrollLoopLimit = 3; |
| 13159 | size_t loopCount = std::min(maxInlineLength, maximumLength) / charsPerPtr; |
| 13160 | |
| 13161 | #ifdef JS_64BIT1 |
| 13162 | static constexpr size_t latin1MaxInlineByteLength = |
| 13163 | JSFatInlineString::MAX_LENGTH_LATIN1 * sizeof(char); |
| 13164 | static constexpr size_t twoByteMaxInlineByteLength = |
| 13165 | JSFatInlineString::MAX_LENGTH_TWO_BYTE * sizeof(char16_t); |
| 13166 | |
| 13167 | // |unrollLoopLimit| should be large enough to allow loop unrolling on |
| 13168 | // 64-bit targets. |
| 13169 | static_assert(latin1MaxInlineByteLength / ptrWidth == unrollLoopLimit, |
| 13170 | "Latin-1 loops are unrolled on 64-bit"); |
| 13171 | static_assert(twoByteMaxInlineByteLength / ptrWidth == unrollLoopLimit, |
| 13172 | "Two-byte loops are unrolled on 64-bit"); |
| 13173 | #endif |
| 13174 | |
| 13175 | if (loopCount <= unrollLoopLimit) { |
| 13176 | Label labels[unrollLoopLimit]; |
| 13177 | |
| 13178 | // Check up front how many characters can be copied. |
| 13179 | for (size_t i = 1; i < loopCount; i++) { |
| 13180 | masm.branch32(Assembler::Below, len, Imm32((i + 1) * charsPerPtr), |
| 13181 | &labels[i]); |
| 13182 | } |
| 13183 | |
| 13184 | // Generate the unrolled loop body. |
| 13185 | for (size_t i = loopCount; i > 0; i--) { |
| 13186 | copyCharacters(ptrWidth); |
| 13187 | masm.sub32(Imm32(charsPerPtr), len); |
| 13188 | |
| 13189 | // Jump target for the previous length check. |
| 13190 | if (i != 1) { |
| 13191 | masm.bind(&labels[i - 1]); |
| 13192 | } |
| 13193 | } |
| 13194 | } else { |
| 13195 | Label start; |
| 13196 | masm.bind(&start); |
| 13197 | copyCharacters(ptrWidth); |
| 13198 | masm.branchSub32(Assembler::NonZero, Imm32(charsPerPtr), len, &start); |
| 13199 | } |
| 13200 | |
| 13201 | masm.bind(&done); |
| 13202 | } else { |
| 13203 | Label start; |
| 13204 | masm.bind(&start); |
| 13205 | masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding); |
| 13206 | masm.storeChar(byteOpScratch, Address(to, 0), toEncoding); |
| 13207 | masm.addPtr(Imm32(fromWidth), from); |
| 13208 | masm.addPtr(Imm32(toWidth), to); |
| 13209 | masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start); |
| 13210 | } |
| 13211 | } |
| 13212 | |
| 13213 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
| 13214 | Register len, Register byteOpScratch, |
| 13215 | CharEncoding encoding, size_t maximumLength) { |
| 13216 | CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding, |
| 13217 | maximumLength); |
| 13218 | } |
| 13219 | |
| 13220 | static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, |
| 13221 | Register destChars, Register temp1, |
| 13222 | Register temp2) { |
| 13223 | // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may |
| 13224 | // have to inflate. |
| 13225 | |
| 13226 | Label isLatin1, done; |
| 13227 | masm.loadStringLength(input, temp1); |
| 13228 | masm.branchLatin1String(input, &isLatin1); |
| 13229 | { |
| 13230 | masm.loadStringChars(input, temp2, CharEncoding::TwoByte); |
| 13231 | masm.movePtr(temp2, input); |
| 13232 | CopyStringChars(masm, destChars, input, temp1, temp2, |
| 13233 | CharEncoding::TwoByte); |
| 13234 | masm.jump(&done); |
| 13235 | } |
| 13236 | masm.bind(&isLatin1); |
| 13237 | { |
| 13238 | masm.loadStringChars(input, temp2, CharEncoding::Latin1); |
| 13239 | masm.movePtr(temp2, input); |
| 13240 | CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1, |
| 13241 | CharEncoding::TwoByte); |
| 13242 | } |
| 13243 | masm.bind(&done); |
| 13244 | } |
| 13245 | |
| 13246 | static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output, |
| 13247 | Register length, Register temp, |
| 13248 | gc::Heap initialStringHeap, |
| 13249 | Label* failure, |
| 13250 | CharEncoding encoding) { |
| 13251 | #ifdef DEBUG1 |
| 13252 | size_t maxInlineLength; |
| 13253 | if (encoding == CharEncoding::Latin1) { |
| 13254 | maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; |
| 13255 | } else { |
| 13256 | maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
| 13257 | } |
| 13258 | |
| 13259 | Label ok; |
| 13260 | masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok); |
| 13261 | masm.assumeUnreachable("string length too large to be allocated as inline"); |
| 13262 | masm.bind(&ok); |
| 13263 | #endif |
| 13264 | |
| 13265 | size_t maxThinInlineLength; |
| 13266 | if (encoding == CharEncoding::Latin1) { |
| 13267 | maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1; |
| 13268 | } else { |
| 13269 | maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
| 13270 | } |
| 13271 | |
| 13272 | Label isFat, allocDone; |
| 13273 | masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat); |
| 13274 | { |
| 13275 | uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS; |
| 13276 | if (encoding == CharEncoding::Latin1) { |
| 13277 | flags |= JSString::LATIN1_CHARS_BIT; |
| 13278 | } |
| 13279 | masm.newGCString(output, temp, initialStringHeap, failure); |
| 13280 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
| 13281 | masm.jump(&allocDone); |
| 13282 | } |
| 13283 | masm.bind(&isFat); |
| 13284 | { |
| 13285 | uint32_t flags = JSString::INIT_FAT_INLINE_FLAGS; |
| 13286 | if (encoding == CharEncoding::Latin1) { |
| 13287 | flags |= JSString::LATIN1_CHARS_BIT; |
| 13288 | } |
| 13289 | masm.newGCFatInlineString(output, temp, initialStringHeap, failure); |
| 13290 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
| 13291 | } |
| 13292 | masm.bind(&allocDone); |
| 13293 | |
| 13294 | // Store length. |
| 13295 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
| 13296 | } |
| 13297 | |
| 13298 | static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, |
| 13299 | Register output, Register temp1, Register temp2, |
| 13300 | Register temp3, gc::Heap initialStringHeap, |
| 13301 | Label* failure, CharEncoding encoding) { |
| 13302 | JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)", |
| 13303 | (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
| 13304 | |
| 13305 | // State: result length in temp2. |
| 13306 | |
| 13307 | // Ensure both strings are linear. |
| 13308 | masm.branchIfRope(lhs, failure); |
| 13309 | masm.branchIfRope(rhs, failure); |
| 13310 | |
| 13311 | // Allocate a JSThinInlineString or JSFatInlineString. |
| 13312 | AllocateThinOrFatInlineString(masm, output, temp2, temp1, initialStringHeap, |
| 13313 | failure, encoding); |
| 13314 | |
| 13315 | // Load chars pointer in temp2. |
| 13316 | masm.loadInlineStringCharsForStore(output, temp2); |
| 13317 | |
| 13318 | auto copyChars = [&](Register src) { |
| 13319 | if (encoding == CharEncoding::TwoByte) { |
| 13320 | CopyStringCharsMaybeInflate(masm, src, temp2, temp1, temp3); |
| 13321 | } else { |
| 13322 | masm.loadStringLength(src, temp3); |
| 13323 | masm.loadStringChars(src, temp1, CharEncoding::Latin1); |
| 13324 | masm.movePtr(temp1, src); |
| 13325 | CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1); |
| 13326 | } |
| 13327 | }; |
| 13328 | |
| 13329 | // Copy lhs chars. Note that this advances temp2 to point to the next |
| 13330 | // char. This also clobbers the lhs register. |
| 13331 | copyChars(lhs); |
| 13332 | |
| 13333 | // Copy rhs chars. Clobbers the rhs register. |
| 13334 | copyChars(rhs); |
| 13335 | } |
| 13336 | |
| 13337 | void CodeGenerator::visitSubstr(LSubstr* lir) { |
| 13338 | Register string = ToRegister(lir->string()); |
| 13339 | Register begin = ToRegister(lir->begin()); |
| 13340 | Register length = ToRegister(lir->length()); |
| 13341 | Register output = ToRegister(lir->output()); |
| 13342 | Register temp0 = ToRegister(lir->temp0()); |
| 13343 | Register temp2 = ToRegister(lir->temp2()); |
| 13344 | |
| 13345 | // On x86 there are not enough registers. In that case reuse the string |
| 13346 | // register as temporary. |
| 13347 | Register temp1 = |
| 13348 | lir->temp1()->isBogusTemp() ? string : ToRegister(lir->temp1()); |
| 13349 | |
| 13350 | size_t maximumLength = SIZE_MAX(18446744073709551615UL); |
| 13351 | |
| 13352 | Range* range = lir->mir()->length()->range(); |
| 13353 | if (range && range->hasInt32UpperBound()) { |
| 13354 | MOZ_ASSERT(range->upper() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(range->upper() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(range->upper() >= 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("range->upper() >= 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 13354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "range->upper() >= 0" ")"); do { *((volatile int*)__null) = 13354; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13355 | maximumLength = size_t(range->upper()); |
| 13356 | } |
| 13357 | |
| 13358 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <= |
| 13359 | JSThinInlineString::MAX_LENGTH_LATIN1); |
| 13360 | |
| 13361 | static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE <= |
| 13362 | JSFatInlineString::MAX_LENGTH_LATIN1); |
| 13363 | |
| 13364 | bool tryFatInlineOrDependent = |
| 13365 | maximumLength > JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
| 13366 | bool tryDependent = maximumLength > JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
| 13367 | |
| 13368 | #ifdef DEBUG1 |
| 13369 | if (maximumLength != SIZE_MAX(18446744073709551615UL)) { |
| 13370 | Label ok; |
| 13371 | masm.branch32(Assembler::BelowOrEqual, length, Imm32(maximumLength), &ok); |
| 13372 | masm.assumeUnreachable("length should not exceed maximum length"); |
| 13373 | masm.bind(&ok); |
| 13374 | } |
| 13375 | #endif |
| 13376 | |
| 13377 | Label nonZero, nonInput; |
| 13378 | |
| 13379 | // For every edge case use the C++ variant. |
| 13380 | // Note: we also use this upon allocation failure in newGCString and |
| 13381 | // newGCFatInlineString. To squeeze out even more performance those failures |
| 13382 | // can be handled by allocate in ool code and returning to jit code to fill |
| 13383 | // in all data. |
| 13384 | using Fn = JSString* (*)(JSContext* cx, HandleString str, int32_t begin, |
| 13385 | int32_t len); |
| 13386 | OutOfLineCode* ool = oolCallVM<Fn, SubstringKernel>( |
| 13387 | lir, ArgList(string, begin, length), StoreRegisterTo(output)); |
| 13388 | Label* slowPath = ool->entry(); |
| 13389 | Label* done = ool->rejoin(); |
| 13390 | |
| 13391 | // Zero length, return emptystring. |
| 13392 | masm.branchTest32(Assembler::NonZero, length, length, &nonZero); |
| 13393 | const JSAtomState& names = gen->runtime->names(); |
| 13394 | masm.movePtr(ImmGCPtr(names.empty_), output); |
| 13395 | masm.jump(done); |
| 13396 | |
| 13397 | // Substring from 0..|str.length|, return str. |
| 13398 | masm.bind(&nonZero); |
| 13399 | masm.branch32(Assembler::NotEqual, |
| 13400 | Address(string, JSString::offsetOfLength()), length, &nonInput); |
| 13401 | #ifdef DEBUG1 |
| 13402 | { |
| 13403 | Label ok; |
| 13404 | masm.branchTest32(Assembler::Zero, begin, begin, &ok); |
| 13405 | masm.assumeUnreachable("length == str.length implies begin == 0"); |
| 13406 | masm.bind(&ok); |
| 13407 | } |
| 13408 | #endif |
| 13409 | masm.movePtr(string, output); |
| 13410 | masm.jump(done); |
| 13411 | |
| 13412 | // Use slow path for ropes. |
| 13413 | masm.bind(&nonInput); |
| 13414 | masm.branchIfRope(string, slowPath); |
| 13415 | |
| 13416 | // Optimize one and two character strings. |
| 13417 | Label nonStatic; |
| 13418 | masm.branch32(Assembler::Above, length, Imm32(2), &nonStatic); |
| 13419 | { |
| 13420 | Label loadLengthOne, loadLengthTwo; |
| 13421 | |
| 13422 | auto loadChars = [&](CharEncoding encoding, bool fallthru) { |
| 13423 | size_t size = encoding == CharEncoding::Latin1 ? sizeof(JS::Latin1Char) |
| 13424 | : sizeof(char16_t); |
| 13425 | |
| 13426 | masm.loadStringChars(string, temp0, encoding); |
| 13427 | masm.loadChar(temp0, begin, temp2, encoding); |
| 13428 | masm.branch32(Assembler::Equal, length, Imm32(1), &loadLengthOne); |
| 13429 | masm.loadChar(temp0, begin, temp0, encoding, int32_t(size)); |
| 13430 | if (!fallthru) { |
| 13431 | masm.jump(&loadLengthTwo); |
| 13432 | } |
| 13433 | }; |
| 13434 | |
| 13435 | Label isLatin1; |
| 13436 | masm.branchLatin1String(string, &isLatin1); |
| 13437 | loadChars(CharEncoding::TwoByte, /* fallthru = */ false); |
| 13438 | |
| 13439 | masm.bind(&isLatin1); |
| 13440 | loadChars(CharEncoding::Latin1, /* fallthru = */ true); |
| 13441 | |
| 13442 | // Try to load a length-two static string. |
| 13443 | masm.bind(&loadLengthTwo); |
| 13444 | masm.lookupStaticString(temp2, temp0, output, gen->runtime->staticStrings(), |
| 13445 | &nonStatic); |
| 13446 | masm.jump(done); |
| 13447 | |
| 13448 | // Try to load a length-one static string. |
| 13449 | masm.bind(&loadLengthOne); |
| 13450 | masm.lookupStaticString(temp2, output, gen->runtime->staticStrings(), |
| 13451 | &nonStatic); |
| 13452 | masm.jump(done); |
| 13453 | } |
| 13454 | masm.bind(&nonStatic); |
| 13455 | |
| 13456 | // Allocate either a JSThinInlineString or JSFatInlineString, or jump to |
| 13457 | // notInline if we need a dependent string. |
| 13458 | Label notInline; |
| 13459 | { |
| 13460 | static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 < |
| 13461 | JSFatInlineString::MAX_LENGTH_LATIN1); |
| 13462 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE < |
| 13463 | JSFatInlineString::MAX_LENGTH_TWO_BYTE); |
| 13464 | |
| 13465 | // Use temp2 to store the JS(Thin|Fat)InlineString flags. This avoids having |
| 13466 | // duplicate newGCString/newGCFatInlineString codegen for Latin1 vs TwoByte |
| 13467 | // strings. |
| 13468 | |
| 13469 | Label allocFat, allocDone; |
| 13470 | if (tryFatInlineOrDependent) { |
| 13471 | Label isLatin1, allocThin; |
| 13472 | masm.branchLatin1String(string, &isLatin1); |
| 13473 | { |
| 13474 | if (tryDependent) { |
| 13475 | masm.branch32(Assembler::Above, length, |
| 13476 | Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), |
| 13477 | ¬Inline); |
| 13478 | } |
| 13479 | masm.move32(Imm32(0), temp2); |
| 13480 | masm.branch32(Assembler::Above, length, |
| 13481 | Imm32(JSThinInlineString::MAX_LENGTH_TWO_BYTE), |
| 13482 | &allocFat); |
| 13483 | masm.jump(&allocThin); |
| 13484 | } |
| 13485 | |
| 13486 | masm.bind(&isLatin1); |
| 13487 | { |
| 13488 | if (tryDependent) { |
| 13489 | masm.branch32(Assembler::Above, length, |
| 13490 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), |
| 13491 | ¬Inline); |
| 13492 | } |
| 13493 | masm.move32(Imm32(JSString::LATIN1_CHARS_BIT), temp2); |
| 13494 | masm.branch32(Assembler::Above, length, |
| 13495 | Imm32(JSThinInlineString::MAX_LENGTH_LATIN1), &allocFat); |
| 13496 | } |
| 13497 | |
| 13498 | masm.bind(&allocThin); |
| 13499 | } else { |
| 13500 | masm.load32(Address(string, JSString::offsetOfFlags()), temp2); |
| 13501 | masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp2); |
| 13502 | } |
| 13503 | |
| 13504 | { |
| 13505 | masm.newGCString(output, temp0, initialStringHeap(), slowPath); |
| 13506 | masm.or32(Imm32(JSString::INIT_THIN_INLINE_FLAGS), temp2); |
| 13507 | } |
| 13508 | |
| 13509 | if (tryFatInlineOrDependent) { |
| 13510 | masm.jump(&allocDone); |
| 13511 | |
| 13512 | masm.bind(&allocFat); |
| 13513 | { |
| 13514 | masm.newGCFatInlineString(output, temp0, initialStringHeap(), slowPath); |
| 13515 | masm.or32(Imm32(JSString::INIT_FAT_INLINE_FLAGS), temp2); |
| 13516 | } |
| 13517 | |
| 13518 | masm.bind(&allocDone); |
| 13519 | } |
| 13520 | |
| 13521 | masm.store32(temp2, Address(output, JSString::offsetOfFlags())); |
| 13522 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
| 13523 | |
| 13524 | auto initializeInlineString = [&](CharEncoding encoding) { |
| 13525 | masm.loadStringChars(string, temp0, encoding); |
| 13526 | masm.addToCharPtr(temp0, begin, encoding); |
| 13527 | if (temp1 == string) { |
| 13528 | masm.push(string); |
| 13529 | } |
| 13530 | masm.loadInlineStringCharsForStore(output, temp1); |
| 13531 | CopyStringChars(masm, temp1, temp0, length, temp2, encoding, |
| 13532 | maximumLength); |
| 13533 | masm.loadStringLength(output, length); |
| 13534 | if (temp1 == string) { |
| 13535 | masm.pop(string); |
| 13536 | } |
| 13537 | }; |
| 13538 | |
| 13539 | Label isInlineLatin1; |
| 13540 | masm.branchTest32(Assembler::NonZero, temp2, |
| 13541 | Imm32(JSString::LATIN1_CHARS_BIT), &isInlineLatin1); |
| 13542 | initializeInlineString(CharEncoding::TwoByte); |
| 13543 | masm.jump(done); |
| 13544 | |
| 13545 | masm.bind(&isInlineLatin1); |
| 13546 | initializeInlineString(CharEncoding::Latin1); |
| 13547 | } |
| 13548 | |
| 13549 | // Handle other cases with a DependentString. |
| 13550 | if (tryDependent) { |
| 13551 | masm.jump(done); |
| 13552 | |
| 13553 | masm.bind(¬Inline); |
| 13554 | masm.newGCString(output, temp0, gen->initialStringHeap(), slowPath); |
| 13555 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
| 13556 | |
| 13557 | // Note: no post barrier is needed because the dependent string is either |
| 13558 | // allocated in the nursery or both strings are tenured (if nursery strings |
| 13559 | // are disabled for this zone). |
| 13560 | EmitInitDependentStringBase(masm, output, string, temp0, temp2, |
| 13561 | /* needsPostBarrier = */ false); |
| 13562 | |
| 13563 | auto initializeDependentString = [&](CharEncoding encoding) { |
| 13564 | uint32_t flags = JSString::INIT_DEPENDENT_FLAGS; |
| 13565 | if (encoding == CharEncoding::Latin1) { |
| 13566 | flags |= JSString::LATIN1_CHARS_BIT; |
| 13567 | } |
| 13568 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
| 13569 | masm.loadNonInlineStringChars(string, temp0, encoding); |
| 13570 | masm.addToCharPtr(temp0, begin, encoding); |
| 13571 | masm.storeNonInlineStringChars(temp0, output); |
| 13572 | }; |
| 13573 | |
| 13574 | Label isLatin1; |
| 13575 | masm.branchLatin1String(string, &isLatin1); |
| 13576 | initializeDependentString(CharEncoding::TwoByte); |
| 13577 | masm.jump(done); |
| 13578 | |
| 13579 | masm.bind(&isLatin1); |
| 13580 | initializeDependentString(CharEncoding::Latin1); |
| 13581 | } |
| 13582 | |
| 13583 | masm.bind(done); |
| 13584 | } |
| 13585 | |
| 13586 | JitCode* JitZone::generateStringConcatStub(JSContext* cx) { |
| 13587 | JitSpew(JitSpew_Codegen, "# Emitting StringConcat stub"); |
| 13588 | |
| 13589 | TempAllocator temp(&cx->tempLifoAlloc()); |
| 13590 | JitContext jcx(cx); |
| 13591 | StackMacroAssembler masm(cx, temp); |
| 13592 | AutoCreatedBy acb(masm, "JitZone::generateStringConcatStub"); |
| 13593 | |
| 13594 | Register lhs = CallTempReg0; |
| 13595 | Register rhs = CallTempReg1; |
| 13596 | Register temp1 = CallTempReg2; |
| 13597 | Register temp2 = CallTempReg3; |
| 13598 | Register temp3 = CallTempReg4; |
| 13599 | Register output = CallTempReg5; |
| 13600 | |
| 13601 | Label failure; |
| 13602 | #ifdef JS_USE_LINK_REGISTER |
| 13603 | masm.pushReturnAddress(); |
| 13604 | #endif |
| 13605 | masm.Push(FramePointer); |
| 13606 | masm.moveStackPtrTo(FramePointer); |
| 13607 | |
| 13608 | // If lhs is empty, return rhs. |
| 13609 | Label leftEmpty; |
| 13610 | masm.loadStringLength(lhs, temp1); |
| 13611 | masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty); |
| 13612 | |
| 13613 | // If rhs is empty, return lhs. |
| 13614 | Label rightEmpty; |
| 13615 | masm.loadStringLength(rhs, temp2); |
| 13616 | masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty); |
| 13617 | |
| 13618 | masm.add32(temp1, temp2); |
| 13619 | |
| 13620 | // Check if we can use a JSInlineString. The result is a Latin1 string if |
| 13621 | // lhs and rhs are both Latin1, so we AND the flags. |
| 13622 | Label isInlineTwoByte, isInlineLatin1; |
| 13623 | masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1); |
| 13624 | masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1); |
| 13625 | |
| 13626 | Label isLatin1, notInline; |
| 13627 | masm.branchTest32(Assembler::NonZero, temp1, |
| 13628 | Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1); |
| 13629 | { |
| 13630 | masm.branch32(Assembler::BelowOrEqual, temp2, |
| 13631 | Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), |
| 13632 | &isInlineTwoByte); |
| 13633 | masm.jump(¬Inline); |
| 13634 | } |
| 13635 | masm.bind(&isLatin1); |
| 13636 | { |
| 13637 | masm.branch32(Assembler::BelowOrEqual, temp2, |
| 13638 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1); |
| 13639 | } |
| 13640 | masm.bind(¬Inline); |
| 13641 | |
| 13642 | // Keep AND'ed flags in temp1. |
| 13643 | |
| 13644 | // Ensure result length <= JSString::MAX_LENGTH. |
| 13645 | masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure); |
| 13646 | |
| 13647 | // Allocate a new rope, guaranteed to be in the nursery if initialStringHeap |
| 13648 | // == gc::Heap::Default. (As a result, no post barriers are needed below.) |
| 13649 | masm.newGCString(output, temp3, initialStringHeap, &failure); |
| 13650 | |
| 13651 | // Store rope length and flags. temp1 still holds the result of AND'ing the |
| 13652 | // lhs and rhs flags, so we just have to clear the other flags to get our rope |
| 13653 | // flags (Latin1 if both lhs and rhs are Latin1). |
| 13654 | static_assert(JSString::INIT_ROPE_FLAGS == 0, |
| 13655 | "Rope type flags must have no bits set"); |
| 13656 | masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1); |
| 13657 | masm.store32(temp1, Address(output, JSString::offsetOfFlags())); |
| 13658 | masm.store32(temp2, Address(output, JSString::offsetOfLength())); |
| 13659 | |
| 13660 | // Store left and right nodes. |
| 13661 | masm.storeRopeChildren(lhs, rhs, output); |
| 13662 | masm.pop(FramePointer); |
| 13663 | masm.ret(); |
| 13664 | |
| 13665 | masm.bind(&leftEmpty); |
| 13666 | masm.mov(rhs, output); |
| 13667 | masm.pop(FramePointer); |
| 13668 | masm.ret(); |
| 13669 | |
| 13670 | masm.bind(&rightEmpty); |
| 13671 | masm.mov(lhs, output); |
| 13672 | masm.pop(FramePointer); |
| 13673 | masm.ret(); |
| 13674 | |
| 13675 | masm.bind(&isInlineTwoByte); |
| 13676 | ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, |
| 13677 | initialStringHeap, &failure, CharEncoding::TwoByte); |
| 13678 | masm.pop(FramePointer); |
| 13679 | masm.ret(); |
| 13680 | |
| 13681 | masm.bind(&isInlineLatin1); |
| 13682 | ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, |
| 13683 | initialStringHeap, &failure, CharEncoding::Latin1); |
| 13684 | masm.pop(FramePointer); |
| 13685 | masm.ret(); |
| 13686 | |
| 13687 | masm.bind(&failure); |
| 13688 | masm.movePtr(ImmPtr(nullptr), output); |
| 13689 | masm.pop(FramePointer); |
| 13690 | masm.ret(); |
| 13691 | |
| 13692 | Linker linker(masm); |
| 13693 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
| 13694 | |
| 13695 | CollectPerfSpewerJitCodeProfile(code, "StringConcatStub"); |
| 13696 | #ifdef MOZ_VTUNE1 |
| 13697 | vtune::MarkStub(code, "StringConcatStub"); |
| 13698 | #endif |
| 13699 | |
| 13700 | return code; |
| 13701 | } |
| 13702 | |
| 13703 | void JitRuntime::generateLazyLinkStub(MacroAssembler& masm) { |
| 13704 | AutoCreatedBy acb(masm, "JitRuntime::generateLazyLinkStub"); |
| 13705 | |
| 13706 | lazyLinkStubOffset_ = startTrampolineCode(masm); |
| 13707 | |
| 13708 | #ifdef JS_USE_LINK_REGISTER |
| 13709 | masm.pushReturnAddress(); |
| 13710 | #endif |
| 13711 | masm.Push(FramePointer); |
| 13712 | masm.moveStackPtrTo(FramePointer); |
| 13713 | |
| 13714 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 13715 | Register temp0 = regs.takeAny(); |
| 13716 | Register temp1 = regs.takeAny(); |
| 13717 | Register temp2 = regs.takeAny(); |
| 13718 | |
| 13719 | masm.loadJSContext(temp0); |
| 13720 | masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink); |
| 13721 | masm.moveStackPtrTo(temp1); |
| 13722 | |
| 13723 | using Fn = uint8_t* (*)(JSContext* cx, LazyLinkExitFrameLayout* frame); |
| 13724 | masm.setupUnalignedABICall(temp2); |
| 13725 | masm.passABIArg(temp0); |
| 13726 | masm.passABIArg(temp1); |
| 13727 | masm.callWithABI<Fn, LazyLinkTopActivation>( |
| 13728 | ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 13729 | |
| 13730 | // Discard exit frame and restore frame pointer. |
| 13731 | masm.leaveExitFrame(0); |
| 13732 | masm.pop(FramePointer); |
| 13733 | |
| 13734 | #ifdef JS_USE_LINK_REGISTER |
| 13735 | // Restore the return address such that the emitPrologue function of the |
| 13736 | // CodeGenerator can push it back on the stack with pushReturnAddress. |
| 13737 | masm.popReturnAddress(); |
| 13738 | #endif |
| 13739 | masm.jump(ReturnReg); |
| 13740 | } |
| 13741 | |
| 13742 | void JitRuntime::generateInterpreterStub(MacroAssembler& masm) { |
| 13743 | AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterStub"); |
| 13744 | |
| 13745 | interpreterStubOffset_ = startTrampolineCode(masm); |
| 13746 | |
| 13747 | #ifdef JS_USE_LINK_REGISTER |
| 13748 | masm.pushReturnAddress(); |
| 13749 | #endif |
| 13750 | masm.Push(FramePointer); |
| 13751 | masm.moveStackPtrTo(FramePointer); |
| 13752 | |
| 13753 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 13754 | Register temp0 = regs.takeAny(); |
| 13755 | Register temp1 = regs.takeAny(); |
| 13756 | Register temp2 = regs.takeAny(); |
| 13757 | |
| 13758 | masm.loadJSContext(temp0); |
| 13759 | masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub); |
| 13760 | masm.moveStackPtrTo(temp1); |
| 13761 | |
| 13762 | using Fn = bool (*)(JSContext* cx, InterpreterStubExitFrameLayout* frame); |
| 13763 | masm.setupUnalignedABICall(temp2); |
| 13764 | masm.passABIArg(temp0); |
| 13765 | masm.passABIArg(temp1); |
| 13766 | masm.callWithABI<Fn, InvokeFromInterpreterStub>( |
| 13767 | ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 13768 | |
| 13769 | masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); |
| 13770 | |
| 13771 | // Discard exit frame and restore frame pointer. |
| 13772 | masm.leaveExitFrame(0); |
| 13773 | masm.pop(FramePointer); |
| 13774 | |
| 13775 | // InvokeFromInterpreterStub stores the return value in argv[0], where the |
| 13776 | // caller stored |this|. Subtract |sizeof(void*)| for the frame pointer we |
| 13777 | // just popped. |
| 13778 | masm.loadValue(Address(masm.getStackPointer(), |
| 13779 | JitFrameLayout::offsetOfThis() - sizeof(void*)), |
| 13780 | JSReturnOperand); |
| 13781 | masm.ret(); |
| 13782 | } |
| 13783 | |
| 13784 | void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) { |
| 13785 | AutoCreatedBy acb(masm, "JitRuntime::generateDoubleToInt32ValueStub"); |
| 13786 | doubleToInt32ValueStubOffset_ = startTrampolineCode(masm); |
| 13787 | |
| 13788 | Label done; |
| 13789 | masm.branchTestDouble(Assembler::NotEqual, R0, &done); |
| 13790 | |
| 13791 | masm.unboxDouble(R0, FloatReg0); |
| 13792 | masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done, |
| 13793 | /* negativeZeroCheck = */ false); |
| 13794 | masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0); |
| 13795 | |
| 13796 | masm.bind(&done); |
| 13797 | masm.abiret(); |
| 13798 | } |
| 13799 | |
| 13800 | void CodeGenerator::visitLinearizeString(LLinearizeString* lir) { |
| 13801 | Register str = ToRegister(lir->str()); |
| 13802 | Register output = ToRegister(lir->output()); |
| 13803 | |
| 13804 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 13805 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
| 13806 | lir, ArgList(str), StoreRegisterTo(output)); |
| 13807 | |
| 13808 | masm.branchIfRope(str, ool->entry()); |
| 13809 | |
| 13810 | masm.movePtr(str, output); |
| 13811 | masm.bind(ool->rejoin()); |
| 13812 | } |
| 13813 | |
| 13814 | void CodeGenerator::visitLinearizeForCharAccess(LLinearizeForCharAccess* lir) { |
| 13815 | Register str = ToRegister(lir->str()); |
| 13816 | Register index = ToRegister(lir->index()); |
| 13817 | Register output = ToRegister(lir->output()); |
| 13818 | |
| 13819 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 13820 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
| 13821 | lir, ArgList(str), StoreRegisterTo(output)); |
| 13822 | |
| 13823 | masm.branchIfNotCanLoadStringChar(str, index, output, ool->entry()); |
| 13824 | |
| 13825 | masm.movePtr(str, output); |
| 13826 | masm.bind(ool->rejoin()); |
| 13827 | } |
| 13828 | |
| 13829 | void CodeGenerator::visitLinearizeForCodePointAccess( |
| 13830 | LLinearizeForCodePointAccess* lir) { |
| 13831 | Register str = ToRegister(lir->str()); |
| 13832 | Register index = ToRegister(lir->index()); |
| 13833 | Register output = ToRegister(lir->output()); |
| 13834 | Register temp = ToRegister(lir->temp0()); |
| 13835 | |
| 13836 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 13837 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
| 13838 | lir, ArgList(str), StoreRegisterTo(output)); |
| 13839 | |
| 13840 | masm.branchIfNotCanLoadStringCodePoint(str, index, output, temp, |
| 13841 | ool->entry()); |
| 13842 | |
| 13843 | masm.movePtr(str, output); |
| 13844 | masm.bind(ool->rejoin()); |
| 13845 | } |
| 13846 | |
| 13847 | void CodeGenerator::visitToRelativeStringIndex(LToRelativeStringIndex* lir) { |
| 13848 | Register index = ToRegister(lir->index()); |
| 13849 | Register length = ToRegister(lir->length()); |
| 13850 | Register output = ToRegister(lir->output()); |
| 13851 | |
| 13852 | masm.move32(Imm32(0), output); |
| 13853 | masm.cmp32Move32(Assembler::LessThan, index, Imm32(0), length, output); |
| 13854 | masm.add32(index, output); |
| 13855 | } |
| 13856 | |
| 13857 | void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) { |
| 13858 | Register str = ToRegister(lir->str()); |
| 13859 | Register output = ToRegister(lir->output()); |
| 13860 | Register temp0 = ToRegister(lir->temp0()); |
| 13861 | Register temp1 = ToRegister(lir->temp1()); |
| 13862 | |
| 13863 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
| 13864 | |
| 13865 | if (lir->index()->isBogus()) { |
| 13866 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)), |
| 13867 | StoreRegisterTo(output)); |
| 13868 | masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry()); |
| 13869 | masm.bind(ool->rejoin()); |
| 13870 | } else { |
| 13871 | Register index = ToRegister(lir->index()); |
| 13872 | |
| 13873 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index), |
| 13874 | StoreRegisterTo(output)); |
| 13875 | masm.loadStringChar(str, index, output, temp0, temp1, ool->entry()); |
| 13876 | masm.bind(ool->rejoin()); |
| 13877 | } |
| 13878 | } |
| 13879 | |
| 13880 | void CodeGenerator::visitCharCodeAtOrNegative(LCharCodeAtOrNegative* lir) { |
| 13881 | Register str = ToRegister(lir->str()); |
| 13882 | Register output = ToRegister(lir->output()); |
| 13883 | Register temp0 = ToRegister(lir->temp0()); |
| 13884 | Register temp1 = ToRegister(lir->temp1()); |
| 13885 | |
| 13886 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
| 13887 | |
| 13888 | // Return -1 for out-of-bounds access. |
| 13889 | masm.move32(Imm32(-1), output); |
| 13890 | |
| 13891 | if (lir->index()->isBogus()) { |
| 13892 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)), |
| 13893 | StoreRegisterTo(output)); |
| 13894 | |
| 13895 | masm.branch32(Assembler::Equal, Address(str, JSString::offsetOfLength()), |
| 13896 | Imm32(0), ool->rejoin()); |
| 13897 | masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry()); |
| 13898 | masm.bind(ool->rejoin()); |
| 13899 | } else { |
| 13900 | Register index = ToRegister(lir->index()); |
| 13901 | |
| 13902 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index), |
| 13903 | StoreRegisterTo(output)); |
| 13904 | |
| 13905 | masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), |
| 13906 | temp0, ool->rejoin()); |
| 13907 | masm.loadStringChar(str, index, output, temp0, temp1, ool->entry()); |
| 13908 | masm.bind(ool->rejoin()); |
| 13909 | } |
| 13910 | } |
| 13911 | |
| 13912 | void CodeGenerator::visitCodePointAt(LCodePointAt* lir) { |
| 13913 | Register str = ToRegister(lir->str()); |
| 13914 | Register index = ToRegister(lir->index()); |
| 13915 | Register output = ToRegister(lir->output()); |
| 13916 | Register temp0 = ToRegister(lir->temp0()); |
| 13917 | Register temp1 = ToRegister(lir->temp1()); |
| 13918 | |
| 13919 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
| 13920 | auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index), |
| 13921 | StoreRegisterTo(output)); |
| 13922 | |
| 13923 | masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry()); |
| 13924 | masm.bind(ool->rejoin()); |
| 13925 | } |
| 13926 | |
| 13927 | void CodeGenerator::visitCodePointAtOrNegative(LCodePointAtOrNegative* lir) { |
| 13928 | Register str = ToRegister(lir->str()); |
| 13929 | Register index = ToRegister(lir->index()); |
| 13930 | Register output = ToRegister(lir->output()); |
| 13931 | Register temp0 = ToRegister(lir->temp0()); |
| 13932 | Register temp1 = ToRegister(lir->temp1()); |
| 13933 | |
| 13934 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
| 13935 | auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index), |
| 13936 | StoreRegisterTo(output)); |
| 13937 | |
| 13938 | // Return -1 for out-of-bounds access. |
| 13939 | masm.move32(Imm32(-1), output); |
| 13940 | |
| 13941 | masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), |
| 13942 | temp0, ool->rejoin()); |
| 13943 | masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry()); |
| 13944 | masm.bind(ool->rejoin()); |
| 13945 | } |
| 13946 | |
| 13947 | void CodeGenerator::visitNegativeToNaN(LNegativeToNaN* lir) { |
| 13948 | Register input = ToRegister(lir->input()); |
| 13949 | ValueOperand output = ToOutValue(lir); |
| 13950 | |
| 13951 | masm.tagValue(JSVAL_TYPE_INT32, input, output); |
| 13952 | |
| 13953 | Label done; |
| 13954 | masm.branchTest32(Assembler::NotSigned, input, input, &done); |
| 13955 | masm.moveValue(JS::NaNValue(), output); |
| 13956 | masm.bind(&done); |
| 13957 | } |
| 13958 | |
| 13959 | void CodeGenerator::visitNegativeToUndefined(LNegativeToUndefined* lir) { |
| 13960 | Register input = ToRegister(lir->input()); |
| 13961 | ValueOperand output = ToOutValue(lir); |
| 13962 | |
| 13963 | masm.tagValue(JSVAL_TYPE_INT32, input, output); |
| 13964 | |
| 13965 | Label done; |
| 13966 | masm.branchTest32(Assembler::NotSigned, input, input, &done); |
| 13967 | masm.moveValue(JS::UndefinedValue(), output); |
| 13968 | masm.bind(&done); |
| 13969 | } |
| 13970 | |
| 13971 | void CodeGenerator::visitFromCharCode(LFromCharCode* lir) { |
| 13972 | Register code = ToRegister(lir->code()); |
| 13973 | Register output = ToRegister(lir->output()); |
| 13974 | |
| 13975 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
| 13976 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
| 13977 | StoreRegisterTo(output)); |
| 13978 | |
| 13979 | // OOL path if code >= UNIT_STATIC_LIMIT. |
| 13980 | masm.lookupStaticString(code, output, gen->runtime->staticStrings(), |
| 13981 | ool->entry()); |
| 13982 | |
| 13983 | masm.bind(ool->rejoin()); |
| 13984 | } |
| 13985 | |
| 13986 | void CodeGenerator::visitFromCharCodeEmptyIfNegative( |
| 13987 | LFromCharCodeEmptyIfNegative* lir) { |
| 13988 | Register code = ToRegister(lir->code()); |
| 13989 | Register output = ToRegister(lir->output()); |
| 13990 | |
| 13991 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
| 13992 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
| 13993 | StoreRegisterTo(output)); |
| 13994 | |
| 13995 | // Return the empty string for negative inputs. |
| 13996 | const JSAtomState& names = gen->runtime->names(); |
| 13997 | masm.movePtr(ImmGCPtr(names.empty_), output); |
| 13998 | masm.branchTest32(Assembler::Signed, code, code, ool->rejoin()); |
| 13999 | |
| 14000 | // OOL path if code >= UNIT_STATIC_LIMIT. |
| 14001 | masm.lookupStaticString(code, output, gen->runtime->staticStrings(), |
| 14002 | ool->entry()); |
| 14003 | |
| 14004 | masm.bind(ool->rejoin()); |
| 14005 | } |
| 14006 | |
| 14007 | void CodeGenerator::visitFromCharCodeUndefinedIfNegative( |
| 14008 | LFromCharCodeUndefinedIfNegative* lir) { |
| 14009 | Register code = ToRegister(lir->code()); |
| 14010 | ValueOperand output = ToOutValue(lir); |
| 14011 | Register temp = output.scratchReg(); |
| 14012 | |
| 14013 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
| 14014 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
| 14015 | StoreRegisterTo(temp)); |
| 14016 | |
| 14017 | // Return |undefined| for negative inputs. |
| 14018 | Label done; |
| 14019 | masm.moveValue(UndefinedValue(), output); |
| 14020 | masm.branchTest32(Assembler::Signed, code, code, &done); |
| 14021 | |
| 14022 | // OOL path if code >= UNIT_STATIC_LIMIT. |
| 14023 | masm.lookupStaticString(code, temp, gen->runtime->staticStrings(), |
| 14024 | ool->entry()); |
| 14025 | |
| 14026 | masm.bind(ool->rejoin()); |
| 14027 | masm.tagValue(JSVAL_TYPE_STRING, temp, output); |
| 14028 | |
| 14029 | masm.bind(&done); |
| 14030 | } |
| 14031 | |
| 14032 | void CodeGenerator::visitFromCodePoint(LFromCodePoint* lir) { |
| 14033 | Register codePoint = ToRegister(lir->codePoint()); |
| 14034 | Register output = ToRegister(lir->output()); |
| 14035 | Register temp0 = ToRegister(lir->temp0()); |
| 14036 | Register temp1 = ToRegister(lir->temp1()); |
| 14037 | LSnapshot* snapshot = lir->snapshot(); |
| 14038 | |
| 14039 | // The OOL path is only taken when we can't allocate the inline string. |
| 14040 | using Fn = JSLinearString* (*)(JSContext*, char32_t); |
| 14041 | auto* ool = oolCallVM<Fn, js::StringFromCodePoint>(lir, ArgList(codePoint), |
| 14042 | StoreRegisterTo(output)); |
| 14043 | |
| 14044 | Label isTwoByte; |
| 14045 | Label* done = ool->rejoin(); |
| 14046 | |
| 14047 | static_assert( |
| 14048 | StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR, |
| 14049 | "Latin-1 strings can be loaded from static strings"); |
| 14050 | |
| 14051 | { |
| 14052 | masm.lookupStaticString(codePoint, output, gen->runtime->staticStrings(), |
| 14053 | &isTwoByte); |
| 14054 | masm.jump(done); |
| 14055 | } |
| 14056 | masm.bind(&isTwoByte); |
| 14057 | { |
| 14058 | // Use a bailout if the input is not a valid code point, because |
| 14059 | // MFromCodePoint is movable and it'd be observable when a moved |
| 14060 | // fromCodePoint throws an exception before its actual call site. |
| 14061 | bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax), |
| 14062 | snapshot); |
| 14063 | |
| 14064 | // Allocate a JSThinInlineString. |
| 14065 | { |
| 14066 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE >= 2, |
| 14067 | "JSThinInlineString can hold a supplementary code point"); |
| 14068 | |
| 14069 | uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS; |
| 14070 | masm.newGCString(output, temp0, gen->initialStringHeap(), ool->entry()); |
| 14071 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
| 14072 | } |
| 14073 | |
| 14074 | Label isSupplementary; |
| 14075 | masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(unicode::NonBMPMin), |
| 14076 | &isSupplementary); |
| 14077 | { |
| 14078 | // Store length. |
| 14079 | masm.store32(Imm32(1), Address(output, JSString::offsetOfLength())); |
| 14080 | |
| 14081 | // Load chars pointer in temp0. |
| 14082 | masm.loadInlineStringCharsForStore(output, temp0); |
| 14083 | |
| 14084 | masm.store16(codePoint, Address(temp0, 0)); |
| 14085 | |
| 14086 | masm.jump(done); |
| 14087 | } |
| 14088 | masm.bind(&isSupplementary); |
| 14089 | { |
| 14090 | // Store length. |
| 14091 | masm.store32(Imm32(2), Address(output, JSString::offsetOfLength())); |
| 14092 | |
| 14093 | // Load chars pointer in temp0. |
| 14094 | masm.loadInlineStringCharsForStore(output, temp0); |
| 14095 | |
| 14096 | // Inlined unicode::LeadSurrogate(uint32_t). |
| 14097 | masm.move32(codePoint, temp1); |
| 14098 | masm.rshift32(Imm32(10), temp1); |
| 14099 | masm.add32(Imm32(unicode::LeadSurrogateMin - (unicode::NonBMPMin >> 10)), |
| 14100 | temp1); |
| 14101 | |
| 14102 | masm.store16(temp1, Address(temp0, 0)); |
| 14103 | |
| 14104 | // Inlined unicode::TrailSurrogate(uint32_t). |
| 14105 | masm.move32(codePoint, temp1); |
| 14106 | masm.and32(Imm32(0x3FF), temp1); |
| 14107 | masm.or32(Imm32(unicode::TrailSurrogateMin), temp1); |
| 14108 | |
| 14109 | masm.store16(temp1, Address(temp0, sizeof(char16_t))); |
| 14110 | } |
| 14111 | } |
| 14112 | |
| 14113 | masm.bind(done); |
| 14114 | } |
| 14115 | |
| 14116 | void CodeGenerator::visitStringIncludes(LStringIncludes* lir) { |
| 14117 | pushArg(ToRegister(lir->searchString())); |
| 14118 | pushArg(ToRegister(lir->string())); |
| 14119 | |
| 14120 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 14121 | callVM<Fn, js::StringIncludes>(lir); |
| 14122 | } |
| 14123 | |
| 14124 | template <typename LIns> |
| 14125 | static void CallStringMatch(MacroAssembler& masm, LIns* lir, OutOfLineCode* ool, |
| 14126 | LiveRegisterSet volatileRegs) { |
| 14127 | Register string = ToRegister(lir->string()); |
| 14128 | Register output = ToRegister(lir->output()); |
| 14129 | Register tempLength = ToRegister(lir->temp0()); |
| 14130 | Register tempChars = ToRegister(lir->temp1()); |
| 14131 | Register maybeTempPat = ToTempRegisterOrInvalid(lir->temp2()); |
| 14132 | |
| 14133 | const JSLinearString* searchString = lir->searchString(); |
| 14134 | size_t length = searchString->length(); |
| 14135 | MOZ_ASSERT(length == 1 || length == 2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length == 1 || length == 2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length == 1 || length == 2)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length == 1 || length == 2" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14135); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length == 1 || length == 2" ")"); do { *((volatile int*)__null) = 14135; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 14136 | |
| 14137 | // The additional temp register is only needed when searching for two |
| 14138 | // pattern characters. |
| 14139 | MOZ_ASSERT_IF(length == 2, maybeTempPat != InvalidReg)do { if (length == 2) { do { static_assert( mozilla::detail:: AssertionConditionType<decltype(maybeTempPat != InvalidReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(maybeTempPat != InvalidReg))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("maybeTempPat != InvalidReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14139); AnnotateMozCrashReason("MOZ_ASSERT" "(" "maybeTempPat != InvalidReg" ")"); do { *((volatile int*)__null) = 14139; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 14140 | |
| 14141 | if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) { |
| 14142 | masm.move32(Imm32(0), output); |
| 14143 | } else { |
| 14144 | masm.move32(Imm32(-1), output); |
| 14145 | } |
| 14146 | |
| 14147 | masm.loadStringLength(string, tempLength); |
| 14148 | |
| 14149 | // Can't be a substring when the string is smaller than the search string. |
| 14150 | Label done; |
| 14151 | masm.branch32(Assembler::Below, tempLength, Imm32(length), ool->rejoin()); |
| 14152 | |
| 14153 | bool searchStringIsPureTwoByte = false; |
| 14154 | if (searchString->hasTwoByteChars()) { |
| 14155 | JS::AutoCheckCannotGC nogc; |
| 14156 | searchStringIsPureTwoByte = |
| 14157 | !mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc)); |
| 14158 | } |
| 14159 | |
| 14160 | // Pure two-byte strings can't occur in a Latin-1 string. |
| 14161 | if (searchStringIsPureTwoByte) { |
| 14162 | masm.branchLatin1String(string, ool->rejoin()); |
| 14163 | } |
| 14164 | |
| 14165 | // Slow path when we need to linearize the string. |
| 14166 | masm.branchIfRope(string, ool->entry()); |
| 14167 | |
| 14168 | Label restoreVolatile; |
| 14169 | |
| 14170 | auto callMatcher = [&](CharEncoding encoding) { |
| 14171 | masm.loadStringChars(string, tempChars, encoding); |
| 14172 | |
| 14173 | LiveGeneralRegisterSet liveRegs; |
| 14174 | if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) { |
| 14175 | // Save |tempChars| to compute the result index. |
| 14176 | liveRegs.add(tempChars); |
| 14177 | |
| 14178 | #ifdef DEBUG1 |
| 14179 | // Save |tempLength| in debug-mode for assertions. |
| 14180 | liveRegs.add(tempLength); |
| 14181 | #endif |
| 14182 | |
| 14183 | // Exclude non-volatile registers. |
| 14184 | liveRegs.set() = GeneralRegisterSet::Intersect( |
| 14185 | liveRegs.set(), GeneralRegisterSet::Volatile()); |
| 14186 | |
| 14187 | masm.PushRegsInMask(liveRegs); |
| 14188 | } |
| 14189 | |
| 14190 | if (length == 1) { |
| 14191 | char16_t pat = searchString->latin1OrTwoByteChar(0); |
| 14192 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14193; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 14193 | pat <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14193); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14193; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 14194 | |
| 14195 | masm.move32(Imm32(pat), output); |
| 14196 | |
| 14197 | masm.setupAlignedABICall(); |
| 14198 | masm.passABIArg(tempChars); |
| 14199 | masm.passABIArg(output); |
| 14200 | masm.passABIArg(tempLength); |
| 14201 | if (encoding == CharEncoding::Latin1) { |
| 14202 | using Fn = const char* (*)(const char*, char, size_t); |
| 14203 | masm.callWithABI<Fn, mozilla::SIMD::memchr8>( |
| 14204 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 14205 | } else { |
| 14206 | using Fn = const char16_t* (*)(const char16_t*, char16_t, size_t); |
| 14207 | masm.callWithABI<Fn, mozilla::SIMD::memchr16>( |
| 14208 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 14209 | } |
| 14210 | } else { |
| 14211 | char16_t pat0 = searchString->latin1OrTwoByteChar(0); |
| 14212 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat0 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat0 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat0 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14213); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14213; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 14213 | pat0 <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat0 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat0 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat0 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14213); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14213; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 14214 | |
| 14215 | char16_t pat1 = searchString->latin1OrTwoByteChar(1); |
| 14216 | MOZ_ASSERT_IF(encoding == CharEncoding::Latin1,do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat1 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat1 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat1 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14217); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14217; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 14217 | pat1 <= JSString::MAX_LATIN1_CHAR)do { if (encoding == CharEncoding::Latin1) { do { static_assert ( mozilla::detail::AssertionConditionType<decltype(pat1 <= JSString::MAX_LATIN1_CHAR)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pat1 <= JSString::MAX_LATIN1_CHAR ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pat1 <= JSString::MAX_LATIN1_CHAR", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14217); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR" ")"); do { *((volatile int*)__null) = 14217; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 14218 | |
| 14219 | masm.move32(Imm32(pat0), output); |
| 14220 | masm.move32(Imm32(pat1), maybeTempPat); |
| 14221 | |
| 14222 | masm.setupAlignedABICall(); |
| 14223 | masm.passABIArg(tempChars); |
| 14224 | masm.passABIArg(output); |
| 14225 | masm.passABIArg(maybeTempPat); |
| 14226 | masm.passABIArg(tempLength); |
| 14227 | if (encoding == CharEncoding::Latin1) { |
| 14228 | using Fn = const char* (*)(const char*, char, char, size_t); |
| 14229 | masm.callWithABI<Fn, mozilla::SIMD::memchr2x8>( |
| 14230 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 14231 | } else { |
| 14232 | using Fn = |
| 14233 | const char16_t* (*)(const char16_t*, char16_t, char16_t, size_t); |
| 14234 | masm.callWithABI<Fn, mozilla::SIMD::memchr2x16>( |
| 14235 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 14236 | } |
| 14237 | } |
| 14238 | |
| 14239 | masm.storeCallPointerResult(output); |
| 14240 | |
| 14241 | // Convert to string index for `indexOf`. |
| 14242 | if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) { |
| 14243 | // Restore |tempChars|. (And in debug mode |tempLength|.) |
| 14244 | masm.PopRegsInMask(liveRegs); |
| 14245 | |
| 14246 | Label found; |
| 14247 | masm.branchPtr(Assembler::NotEqual, output, ImmPtr(nullptr), &found); |
| 14248 | { |
| 14249 | masm.move32(Imm32(-1), output); |
| 14250 | masm.jump(&restoreVolatile); |
| 14251 | } |
| 14252 | masm.bind(&found); |
| 14253 | |
| 14254 | #ifdef DEBUG1 |
| 14255 | // Check lower bound. |
| 14256 | Label lower; |
| 14257 | masm.branchPtr(Assembler::AboveOrEqual, output, tempChars, &lower); |
| 14258 | masm.assumeUnreachable("result pointer below string chars"); |
| 14259 | masm.bind(&lower); |
| 14260 | |
| 14261 | // Compute the end position of the characters. |
| 14262 | auto scale = encoding == CharEncoding::Latin1 ? TimesOne : TimesTwo; |
| 14263 | masm.computeEffectiveAddress(BaseIndex(tempChars, tempLength, scale), |
| 14264 | tempLength); |
| 14265 | |
| 14266 | // Check upper bound. |
| 14267 | Label upper; |
| 14268 | masm.branchPtr(Assembler::Below, output, tempLength, &upper); |
| 14269 | masm.assumeUnreachable("result pointer above string chars"); |
| 14270 | masm.bind(&upper); |
| 14271 | #endif |
| 14272 | |
| 14273 | masm.subPtr(tempChars, output); |
| 14274 | |
| 14275 | if (encoding == CharEncoding::TwoByte) { |
| 14276 | masm.rshiftPtr(Imm32(1), output); |
| 14277 | } |
| 14278 | } |
| 14279 | }; |
| 14280 | |
| 14281 | volatileRegs.takeUnchecked(output); |
| 14282 | volatileRegs.takeUnchecked(tempLength); |
| 14283 | volatileRegs.takeUnchecked(tempChars); |
| 14284 | if (maybeTempPat != InvalidReg) { |
| 14285 | volatileRegs.takeUnchecked(maybeTempPat); |
| 14286 | } |
| 14287 | masm.PushRegsInMask(volatileRegs); |
| 14288 | |
| 14289 | // Handle the case when the input is a Latin-1 string. |
| 14290 | if (!searchStringIsPureTwoByte) { |
| 14291 | Label twoByte; |
| 14292 | masm.branchTwoByteString(string, &twoByte); |
| 14293 | { |
| 14294 | callMatcher(CharEncoding::Latin1); |
| 14295 | masm.jump(&restoreVolatile); |
| 14296 | } |
| 14297 | masm.bind(&twoByte); |
| 14298 | } |
| 14299 | |
| 14300 | // Handle the case when the input is a two-byte string. |
| 14301 | callMatcher(CharEncoding::TwoByte); |
| 14302 | |
| 14303 | masm.bind(&restoreVolatile); |
| 14304 | masm.PopRegsInMask(volatileRegs); |
| 14305 | |
| 14306 | // Convert to bool for `includes`. |
| 14307 | if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) { |
| 14308 | masm.cmpPtrSet(Assembler::NotEqual, output, ImmPtr(nullptr), output); |
| 14309 | } |
| 14310 | |
| 14311 | masm.bind(ool->rejoin()); |
| 14312 | } |
| 14313 | |
| 14314 | void CodeGenerator::visitStringIncludesSIMD(LStringIncludesSIMD* lir) { |
| 14315 | Register string = ToRegister(lir->string()); |
| 14316 | Register output = ToRegister(lir->output()); |
| 14317 | const JSLinearString* searchString = lir->searchString(); |
| 14318 | |
| 14319 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 14320 | auto* ool = oolCallVM<Fn, js::StringIncludes>( |
| 14321 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
| 14322 | |
| 14323 | CallStringMatch(masm, lir, ool, liveVolatileRegs(lir)); |
| 14324 | } |
| 14325 | |
| 14326 | void CodeGenerator::visitStringIndexOf(LStringIndexOf* lir) { |
| 14327 | pushArg(ToRegister(lir->searchString())); |
| 14328 | pushArg(ToRegister(lir->string())); |
| 14329 | |
| 14330 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
| 14331 | callVM<Fn, js::StringIndexOf>(lir); |
| 14332 | } |
| 14333 | |
| 14334 | void CodeGenerator::visitStringIndexOfSIMD(LStringIndexOfSIMD* lir) { |
| 14335 | Register string = ToRegister(lir->string()); |
| 14336 | Register output = ToRegister(lir->output()); |
| 14337 | const JSLinearString* searchString = lir->searchString(); |
| 14338 | |
| 14339 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
| 14340 | auto* ool = oolCallVM<Fn, js::StringIndexOf>( |
| 14341 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
| 14342 | |
| 14343 | CallStringMatch(masm, lir, ool, liveVolatileRegs(lir)); |
| 14344 | } |
| 14345 | |
| 14346 | void CodeGenerator::visitStringLastIndexOf(LStringLastIndexOf* lir) { |
| 14347 | pushArg(ToRegister(lir->searchString())); |
| 14348 | pushArg(ToRegister(lir->string())); |
| 14349 | |
| 14350 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
| 14351 | callVM<Fn, js::StringLastIndexOf>(lir); |
| 14352 | } |
| 14353 | |
| 14354 | void CodeGenerator::visitStringStartsWith(LStringStartsWith* lir) { |
| 14355 | pushArg(ToRegister(lir->searchString())); |
| 14356 | pushArg(ToRegister(lir->string())); |
| 14357 | |
| 14358 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 14359 | callVM<Fn, js::StringStartsWith>(lir); |
| 14360 | } |
| 14361 | |
| 14362 | void CodeGenerator::visitStringStartsWithInline(LStringStartsWithInline* lir) { |
| 14363 | Register string = ToRegister(lir->string()); |
| 14364 | Register output = ToRegister(lir->output()); |
| 14365 | Register temp = ToRegister(lir->temp0()); |
| 14366 | |
| 14367 | const JSLinearString* searchString = lir->searchString(); |
| 14368 | |
| 14369 | size_t length = searchString->length(); |
| 14370 | MOZ_ASSERT(length > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14370); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0" ")"); do { *((volatile int*)__null) = 14370; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 14371 | |
| 14372 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 14373 | auto* ool = oolCallVM<Fn, js::StringStartsWith>( |
| 14374 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
| 14375 | |
| 14376 | masm.move32(Imm32(0), output); |
| 14377 | |
| 14378 | // Can't be a prefix when the string is smaller than the search string. |
| 14379 | masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()), |
| 14380 | Imm32(length), ool->rejoin()); |
| 14381 | |
| 14382 | // Unwind ropes at the start if possible. |
| 14383 | Label compare; |
| 14384 | masm.movePtr(string, temp); |
| 14385 | masm.branchIfNotRope(temp, &compare); |
| 14386 | |
| 14387 | Label unwindRope; |
| 14388 | masm.bind(&unwindRope); |
| 14389 | masm.loadRopeLeftChild(temp, output); |
| 14390 | masm.movePtr(output, temp); |
| 14391 | |
| 14392 | // If the left child is smaller than the search string, jump into the VM to |
| 14393 | // linearize the string. |
| 14394 | masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()), |
| 14395 | Imm32(length), ool->entry()); |
| 14396 | |
| 14397 | // Otherwise keep unwinding ropes. |
| 14398 | masm.branchIfRope(temp, &unwindRope); |
| 14399 | |
| 14400 | masm.bind(&compare); |
| 14401 | |
| 14402 | // If operands point to the same instance, it's trivially a prefix. |
| 14403 | Label notPointerEqual; |
| 14404 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString), |
| 14405 | ¬PointerEqual); |
| 14406 | masm.move32(Imm32(1), output); |
| 14407 | masm.jump(ool->rejoin()); |
| 14408 | masm.bind(¬PointerEqual); |
| 14409 | |
| 14410 | if (searchString->hasTwoByteChars()) { |
| 14411 | // Pure two-byte strings can't be a prefix of Latin-1 strings. |
| 14412 | JS::AutoCheckCannotGC nogc; |
| 14413 | if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) { |
| 14414 | Label compareChars; |
| 14415 | masm.branchTwoByteString(temp, &compareChars); |
| 14416 | masm.move32(Imm32(0), output); |
| 14417 | masm.jump(ool->rejoin()); |
| 14418 | masm.bind(&compareChars); |
| 14419 | } |
| 14420 | } |
| 14421 | |
| 14422 | // Load the input string's characters. |
| 14423 | Register stringChars = output; |
| 14424 | masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry()); |
| 14425 | |
| 14426 | // Start comparing character by character. |
| 14427 | masm.compareStringChars(JSOp::Eq, stringChars, searchString, output); |
| 14428 | |
| 14429 | masm.bind(ool->rejoin()); |
| 14430 | } |
| 14431 | |
| 14432 | void CodeGenerator::visitStringEndsWith(LStringEndsWith* lir) { |
| 14433 | pushArg(ToRegister(lir->searchString())); |
| 14434 | pushArg(ToRegister(lir->string())); |
| 14435 | |
| 14436 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 14437 | callVM<Fn, js::StringEndsWith>(lir); |
| 14438 | } |
| 14439 | |
| 14440 | void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) { |
| 14441 | Register string = ToRegister(lir->string()); |
| 14442 | Register output = ToRegister(lir->output()); |
| 14443 | Register temp = ToRegister(lir->temp0()); |
| 14444 | |
| 14445 | const JSLinearString* searchString = lir->searchString(); |
| 14446 | |
| 14447 | size_t length = searchString->length(); |
| 14448 | MOZ_ASSERT(length > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(length > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(length > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("length > 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "length > 0" ")"); do { *((volatile int*)__null) = 14448; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 14449 | |
| 14450 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 14451 | auto* ool = oolCallVM<Fn, js::StringEndsWith>( |
| 14452 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
| 14453 | |
| 14454 | masm.move32(Imm32(0), output); |
| 14455 | |
| 14456 | // Can't be a suffix when the string is smaller than the search string. |
| 14457 | masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()), |
| 14458 | Imm32(length), ool->rejoin()); |
| 14459 | |
| 14460 | // Unwind ropes at the end if possible. |
| 14461 | Label compare; |
| 14462 | masm.movePtr(string, temp); |
| 14463 | masm.branchIfNotRope(temp, &compare); |
| 14464 | |
| 14465 | Label unwindRope; |
| 14466 | masm.bind(&unwindRope); |
| 14467 | masm.loadRopeRightChild(temp, output); |
| 14468 | masm.movePtr(output, temp); |
| 14469 | |
| 14470 | // If the right child is smaller than the search string, jump into the VM to |
| 14471 | // linearize the string. |
| 14472 | masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()), |
| 14473 | Imm32(length), ool->entry()); |
| 14474 | |
| 14475 | // Otherwise keep unwinding ropes. |
| 14476 | masm.branchIfRope(temp, &unwindRope); |
| 14477 | |
| 14478 | masm.bind(&compare); |
| 14479 | |
| 14480 | // If operands point to the same instance, it's trivially a suffix. |
| 14481 | Label notPointerEqual; |
| 14482 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString), |
| 14483 | ¬PointerEqual); |
| 14484 | masm.move32(Imm32(1), output); |
| 14485 | masm.jump(ool->rejoin()); |
| 14486 | masm.bind(¬PointerEqual); |
| 14487 | |
| 14488 | CharEncoding encoding = searchString->hasLatin1Chars() |
| 14489 | ? CharEncoding::Latin1 |
| 14490 | : CharEncoding::TwoByte; |
| 14491 | if (encoding == CharEncoding::TwoByte) { |
| 14492 | // Pure two-byte strings can't be a suffix of Latin-1 strings. |
| 14493 | JS::AutoCheckCannotGC nogc; |
| 14494 | if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) { |
| 14495 | Label compareChars; |
| 14496 | masm.branchTwoByteString(temp, &compareChars); |
| 14497 | masm.move32(Imm32(0), output); |
| 14498 | masm.jump(ool->rejoin()); |
| 14499 | masm.bind(&compareChars); |
| 14500 | } |
| 14501 | } |
| 14502 | |
| 14503 | // Load the input string's characters. |
| 14504 | Register stringChars = output; |
| 14505 | masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry()); |
| 14506 | |
| 14507 | // Move string-char pointer to the suffix string. |
| 14508 | masm.loadStringLength(temp, temp); |
| 14509 | masm.sub32(Imm32(length), temp); |
| 14510 | masm.addToCharPtr(stringChars, temp, encoding); |
| 14511 | |
| 14512 | // Start comparing character by character. |
| 14513 | masm.compareStringChars(JSOp::Eq, stringChars, searchString, output); |
| 14514 | |
| 14515 | masm.bind(ool->rejoin()); |
| 14516 | } |
| 14517 | |
| 14518 | void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) { |
| 14519 | Register string = ToRegister(lir->string()); |
| 14520 | Register output = ToRegister(lir->output()); |
| 14521 | Register temp0 = ToRegister(lir->temp0()); |
| 14522 | Register temp1 = ToRegister(lir->temp1()); |
| 14523 | Register temp2 = ToRegister(lir->temp2()); |
| 14524 | |
| 14525 | // On x86 there are not enough registers. In that case reuse the string |
| 14526 | // register as a temporary. |
| 14527 | Register temp3 = |
| 14528 | lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3()); |
| 14529 | Register temp4 = ToRegister(lir->temp4()); |
| 14530 | |
| 14531 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 14532 | OutOfLineCode* ool = oolCallVM<Fn, js::StringToLowerCase>( |
| 14533 | lir, ArgList(string), StoreRegisterTo(output)); |
| 14534 | |
| 14535 | // Take the slow path if the string isn't a linear Latin-1 string. |
| 14536 | Imm32 linearLatin1Bits(JSString::LINEAR_BIT | JSString::LATIN1_CHARS_BIT); |
| 14537 | Register flags = temp0; |
| 14538 | masm.load32(Address(string, JSString::offsetOfFlags()), flags); |
| 14539 | masm.and32(linearLatin1Bits, flags); |
| 14540 | masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry()); |
| 14541 | |
| 14542 | Register length = temp0; |
| 14543 | masm.loadStringLength(string, length); |
| 14544 | |
| 14545 | // Return the input if it's the empty string. |
| 14546 | Label notEmptyString; |
| 14547 | masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬EmptyString); |
| 14548 | { |
| 14549 | masm.movePtr(string, output); |
| 14550 | masm.jump(ool->rejoin()); |
| 14551 | } |
| 14552 | masm.bind(¬EmptyString); |
| 14553 | |
| 14554 | Register inputChars = temp1; |
| 14555 | masm.loadStringChars(string, inputChars, CharEncoding::Latin1); |
| 14556 | |
| 14557 | Register toLowerCaseTable = temp2; |
| 14558 | masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable); |
| 14559 | |
| 14560 | // Single element strings can be directly retrieved from static strings cache. |
| 14561 | Label notSingleElementString; |
| 14562 | masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleElementString); |
| 14563 | { |
| 14564 | Register current = temp4; |
| 14565 | |
| 14566 | masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); |
| 14567 | masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), |
| 14568 | current); |
| 14569 | masm.lookupStaticString(current, output, gen->runtime->staticStrings()); |
| 14570 | |
| 14571 | masm.jump(ool->rejoin()); |
| 14572 | } |
| 14573 | masm.bind(¬SingleElementString); |
| 14574 | |
| 14575 | // Use the OOL-path when the string is too long. This prevents scanning long |
| 14576 | // strings which have upper case characters only near the end a second time in |
| 14577 | // the VM. |
| 14578 | constexpr int32_t MaxInlineLength = 64; |
| 14579 | masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry()); |
| 14580 | |
| 14581 | { |
| 14582 | // Check if there are any characters which need to be converted. |
| 14583 | // |
| 14584 | // This extra loop gives a small performance improvement for strings which |
| 14585 | // are already lower cased and lets us avoid calling into the runtime for |
| 14586 | // non-inline, all lower case strings. But more importantly it avoids |
| 14587 | // repeated inline allocation failures: |
| 14588 | // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the |
| 14589 | // |js::StringToLowerCase| runtime function when the result string can't be |
| 14590 | // allocated inline. And |js::StringToLowerCase| directly returns the input |
| 14591 | // string when no characters need to be converted. That means it won't |
| 14592 | // trigger GC to clear up the free nursery space, so the next toLowerCase() |
| 14593 | // call will again fail to inline allocate the result string. |
| 14594 | Label hasUpper; |
| 14595 | { |
| 14596 | Register checkInputChars = output; |
| 14597 | masm.movePtr(inputChars, checkInputChars); |
| 14598 | |
| 14599 | Register current = temp4; |
| 14600 | |
| 14601 | Label start; |
| 14602 | masm.bind(&start); |
| 14603 | masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1); |
| 14604 | masm.branch8(Assembler::NotEqual, |
| 14605 | BaseIndex(toLowerCaseTable, current, TimesOne), current, |
| 14606 | &hasUpper); |
| 14607 | masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars); |
| 14608 | masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); |
| 14609 | |
| 14610 | // Input is already in lower case. |
| 14611 | masm.movePtr(string, output); |
| 14612 | masm.jump(ool->rejoin()); |
| 14613 | } |
| 14614 | masm.bind(&hasUpper); |
| 14615 | |
| 14616 | // |length| was clobbered above, reload. |
| 14617 | masm.loadStringLength(string, length); |
| 14618 | |
| 14619 | // Call into the runtime when we can't create an inline string. |
| 14620 | masm.branch32(Assembler::Above, length, |
| 14621 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry()); |
| 14622 | |
| 14623 | AllocateThinOrFatInlineString(masm, output, length, temp4, |
| 14624 | initialStringHeap(), ool->entry(), |
| 14625 | CharEncoding::Latin1); |
| 14626 | |
| 14627 | if (temp3 == string) { |
| 14628 | masm.push(string); |
| 14629 | } |
| 14630 | |
| 14631 | Register outputChars = temp3; |
| 14632 | masm.loadInlineStringCharsForStore(output, outputChars); |
| 14633 | |
| 14634 | { |
| 14635 | Register current = temp4; |
| 14636 | |
| 14637 | Label start; |
| 14638 | masm.bind(&start); |
| 14639 | masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); |
| 14640 | masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), |
| 14641 | current); |
| 14642 | masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1); |
| 14643 | masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars); |
| 14644 | masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars); |
| 14645 | masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); |
| 14646 | } |
| 14647 | |
| 14648 | if (temp3 == string) { |
| 14649 | masm.pop(string); |
| 14650 | } |
| 14651 | } |
| 14652 | |
| 14653 | masm.bind(ool->rejoin()); |
| 14654 | } |
| 14655 | |
| 14656 | void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) { |
| 14657 | pushArg(ToRegister(lir->string())); |
| 14658 | |
| 14659 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 14660 | callVM<Fn, js::StringToUpperCase>(lir); |
| 14661 | } |
| 14662 | |
| 14663 | void CodeGenerator::visitCharCodeToLowerCase(LCharCodeToLowerCase* lir) { |
| 14664 | Register code = ToRegister(lir->code()); |
| 14665 | Register output = ToRegister(lir->output()); |
| 14666 | Register temp = ToRegister(lir->temp0()); |
| 14667 | |
| 14668 | using Fn = JSString* (*)(JSContext*, int32_t); |
| 14669 | auto* ool = oolCallVM<Fn, jit::CharCodeToLowerCase>(lir, ArgList(code), |
| 14670 | StoreRegisterTo(output)); |
| 14671 | |
| 14672 | constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1; |
| 14673 | |
| 14674 | // OOL path if code >= NonLatin1Min. |
| 14675 | masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry()); |
| 14676 | |
| 14677 | // Convert to lower case. |
| 14678 | masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), temp); |
| 14679 | masm.load8ZeroExtend(BaseIndex(temp, code, TimesOne), temp); |
| 14680 | |
| 14681 | // Load static string for lower case character. |
| 14682 | masm.lookupStaticString(temp, output, gen->runtime->staticStrings()); |
| 14683 | |
| 14684 | masm.bind(ool->rejoin()); |
| 14685 | } |
| 14686 | |
| 14687 | void CodeGenerator::visitCharCodeToUpperCase(LCharCodeToUpperCase* lir) { |
| 14688 | Register code = ToRegister(lir->code()); |
| 14689 | Register output = ToRegister(lir->output()); |
| 14690 | Register temp = ToRegister(lir->temp0()); |
| 14691 | |
| 14692 | using Fn = JSString* (*)(JSContext*, int32_t); |
| 14693 | auto* ool = oolCallVM<Fn, jit::CharCodeToUpperCase>(lir, ArgList(code), |
| 14694 | StoreRegisterTo(output)); |
| 14695 | |
| 14696 | constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1; |
| 14697 | |
| 14698 | // OOL path if code >= NonLatin1Min. |
| 14699 | masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry()); |
| 14700 | |
| 14701 | // Most one element Latin-1 strings can be directly retrieved from the |
| 14702 | // static strings cache, except the following three characters: |
| 14703 | // |
| 14704 | // 1. ToUpper(U+00B5) = 0+039C |
| 14705 | // 2. ToUpper(U+00FF) = 0+0178 |
| 14706 | // 3. ToUpper(U+00DF) = 0+0053 0+0053 |
| 14707 | masm.branch32(Assembler::Equal, code, Imm32(unicode::MICRO_SIGN), |
| 14708 | ool->entry()); |
| 14709 | masm.branch32(Assembler::Equal, code, |
| 14710 | Imm32(unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS), |
| 14711 | ool->entry()); |
| 14712 | masm.branch32(Assembler::Equal, code, |
| 14713 | Imm32(unicode::LATIN_SMALL_LETTER_SHARP_S), ool->entry()); |
| 14714 | |
| 14715 | // Inline unicode::ToUpperCase (without the special case for ASCII characters) |
| 14716 | |
| 14717 | constexpr size_t shift = unicode::CharInfoShift; |
| 14718 | |
| 14719 | // code >> shift |
| 14720 | masm.move32(code, temp); |
| 14721 | masm.rshift32(Imm32(shift), temp); |
| 14722 | |
| 14723 | // index = index1[code >> shift]; |
| 14724 | masm.movePtr(ImmPtr(unicode::index1), output); |
| 14725 | masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp); |
| 14726 | |
| 14727 | // (code & ((1 << shift) - 1) |
| 14728 | masm.move32(code, output); |
| 14729 | masm.and32(Imm32((1 << shift) - 1), output); |
| 14730 | |
| 14731 | // (index << shift) + (code & ((1 << shift) - 1)) |
| 14732 | masm.lshift32(Imm32(shift), temp); |
| 14733 | masm.add32(output, temp); |
| 14734 | |
| 14735 | // index = index2[(index << shift) + (code & ((1 << shift) - 1))] |
| 14736 | masm.movePtr(ImmPtr(unicode::index2), output); |
| 14737 | masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp); |
| 14738 | |
| 14739 | // Compute |index * 6| through |(index * 3) * TimesTwo|. |
| 14740 | static_assert(sizeof(unicode::CharacterInfo) == 6); |
| 14741 | masm.mulBy3(temp, temp); |
| 14742 | |
| 14743 | // upperCase = js_charinfo[index].upperCase |
| 14744 | masm.movePtr(ImmPtr(unicode::js_charinfo), output); |
| 14745 | masm.load16ZeroExtend(BaseIndex(output, temp, TimesTwo, |
| 14746 | offsetof(unicode::CharacterInfo, upperCase)__builtin_offsetof(unicode::CharacterInfo, upperCase)), |
| 14747 | temp); |
| 14748 | |
| 14749 | // uint16_t(ch) + upperCase |
| 14750 | masm.add32(code, temp); |
| 14751 | |
| 14752 | // Clear any high bits added when performing the unsigned 16-bit addition |
| 14753 | // through a signed 32-bit addition. |
| 14754 | masm.move8ZeroExtend(temp, temp); |
| 14755 | |
| 14756 | // Load static string for upper case character. |
| 14757 | masm.lookupStaticString(temp, output, gen->runtime->staticStrings()); |
| 14758 | |
| 14759 | masm.bind(ool->rejoin()); |
| 14760 | } |
| 14761 | |
| 14762 | void CodeGenerator::visitStringTrimStartIndex(LStringTrimStartIndex* lir) { |
| 14763 | Register string = ToRegister(lir->string()); |
| 14764 | Register output = ToRegister(lir->output()); |
| 14765 | |
| 14766 | auto volatileRegs = liveVolatileRegs(lir); |
| 14767 | volatileRegs.takeUnchecked(output); |
| 14768 | |
| 14769 | masm.PushRegsInMask(volatileRegs); |
| 14770 | |
| 14771 | using Fn = int32_t (*)(const JSString*); |
| 14772 | masm.setupAlignedABICall(); |
| 14773 | masm.passABIArg(string); |
| 14774 | masm.callWithABI<Fn, jit::StringTrimStartIndex>(); |
| 14775 | masm.storeCallInt32Result(output); |
| 14776 | |
| 14777 | masm.PopRegsInMask(volatileRegs); |
| 14778 | } |
| 14779 | |
| 14780 | void CodeGenerator::visitStringTrimEndIndex(LStringTrimEndIndex* lir) { |
| 14781 | Register string = ToRegister(lir->string()); |
| 14782 | Register start = ToRegister(lir->start()); |
| 14783 | Register output = ToRegister(lir->output()); |
| 14784 | |
| 14785 | auto volatileRegs = liveVolatileRegs(lir); |
| 14786 | volatileRegs.takeUnchecked(output); |
| 14787 | |
| 14788 | masm.PushRegsInMask(volatileRegs); |
| 14789 | |
| 14790 | using Fn = int32_t (*)(const JSString*, int32_t); |
| 14791 | masm.setupAlignedABICall(); |
| 14792 | masm.passABIArg(string); |
| 14793 | masm.passABIArg(start); |
| 14794 | masm.callWithABI<Fn, jit::StringTrimEndIndex>(); |
| 14795 | masm.storeCallInt32Result(output); |
| 14796 | |
| 14797 | masm.PopRegsInMask(volatileRegs); |
| 14798 | } |
| 14799 | |
| 14800 | void CodeGenerator::visitStringSplit(LStringSplit* lir) { |
| 14801 | pushArg(Imm32(INT32_MAX(2147483647))); |
| 14802 | pushArg(ToRegister(lir->separator())); |
| 14803 | pushArg(ToRegister(lir->string())); |
| 14804 | |
| 14805 | using Fn = ArrayObject* (*)(JSContext*, HandleString, HandleString, uint32_t); |
| 14806 | callVM<Fn, js::StringSplitString>(lir); |
| 14807 | } |
| 14808 | |
| 14809 | void CodeGenerator::visitInitializedLength(LInitializedLength* lir) { |
| 14810 | Address initLength(ToRegister(lir->elements()), |
| 14811 | ObjectElements::offsetOfInitializedLength()); |
| 14812 | masm.load32(initLength, ToRegister(lir->output())); |
| 14813 | } |
| 14814 | |
| 14815 | void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) { |
| 14816 | Address initLength(ToRegister(lir->elements()), |
| 14817 | ObjectElements::offsetOfInitializedLength()); |
| 14818 | SetLengthFromIndex(masm, lir->index(), initLength); |
| 14819 | } |
| 14820 | |
| 14821 | void CodeGenerator::visitNotI(LNotI* lir) { |
| 14822 | Register input = ToRegister(lir->input()); |
| 14823 | Register output = ToRegister(lir->output()); |
| 14824 | |
| 14825 | masm.cmp32Set(Assembler::Equal, input, Imm32(0), output); |
| 14826 | } |
| 14827 | |
| 14828 | void CodeGenerator::visitNotIPtr(LNotIPtr* lir) { |
| 14829 | Register input = ToRegister(lir->input()); |
| 14830 | Register output = ToRegister(lir->output()); |
| 14831 | |
| 14832 | masm.cmpPtrSet(Assembler::Equal, input, ImmWord(0), output); |
| 14833 | } |
| 14834 | |
| 14835 | void CodeGenerator::visitNotI64(LNotI64* lir) { |
| 14836 | Register64 input = ToRegister64(lir->inputI64()); |
| 14837 | Register output = ToRegister(lir->output()); |
| 14838 | |
| 14839 | masm.cmp64Set(Assembler::Equal, input, Imm64(0), output); |
| 14840 | } |
| 14841 | |
| 14842 | void CodeGenerator::visitNotBI(LNotBI* lir) { |
| 14843 | Register input = ToRegister(lir->input()); |
| 14844 | Register output = ToRegister(lir->output()); |
| 14845 | |
| 14846 | masm.cmp32Set(Assembler::Equal, Address(input, BigInt::offsetOfLength()), |
| 14847 | Imm32(0), output); |
| 14848 | } |
| 14849 | |
| 14850 | void CodeGenerator::visitNotO(LNotO* lir) { |
| 14851 | Register objreg = ToRegister(lir->input()); |
| 14852 | Register output = ToRegister(lir->output()); |
| 14853 | |
| 14854 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 14855 | if (intact) { |
| 14856 | // Bug 1874905: It would be fantastic if this could be optimized out. |
| 14857 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
| 14858 | masm.move32(Imm32(0), output); |
| 14859 | } else { |
| 14860 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 14861 | addOutOfLineCode(ool, lir->mir()); |
| 14862 | |
| 14863 | Label* ifEmulatesUndefined = ool->label1(); |
| 14864 | Label* ifDoesntEmulateUndefined = ool->label2(); |
| 14865 | |
| 14866 | branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined, |
| 14867 | ifDoesntEmulateUndefined, output, ool); |
| 14868 | // fall through |
| 14869 | |
| 14870 | Label join; |
| 14871 | |
| 14872 | masm.move32(Imm32(0), output); |
| 14873 | masm.jump(&join); |
| 14874 | |
| 14875 | masm.bind(ifEmulatesUndefined); |
| 14876 | masm.move32(Imm32(1), output); |
| 14877 | |
| 14878 | masm.bind(&join); |
| 14879 | } |
| 14880 | } |
| 14881 | |
| 14882 | void CodeGenerator::visitNotV(LNotV* lir) { |
| 14883 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 14884 | addOutOfLineCode(ool, lir->mir()); |
| 14885 | |
| 14886 | Label* ifTruthy = ool->label1(); |
| 14887 | Label* ifFalsy = ool->label2(); |
| 14888 | |
| 14889 | ValueOperand input = ToValue(lir, LNotV::InputIndex); |
| 14890 | Register tempToUnbox = ToTempUnboxRegister(lir->temp1()); |
| 14891 | FloatRegister floatTemp = ToFloatRegister(lir->temp0()); |
| 14892 | Register output = ToRegister(lir->output()); |
| 14893 | const TypeDataList& observedTypes = lir->mir()->observedTypes(); |
| 14894 | |
| 14895 | testValueTruthy(input, tempToUnbox, output, floatTemp, observedTypes, |
| 14896 | ifTruthy, ifFalsy, ool); |
| 14897 | |
| 14898 | Label join; |
| 14899 | |
| 14900 | // Note that the testValueTruthy call above may choose to fall through |
| 14901 | // to ifTruthy instead of branching there. |
| 14902 | masm.bind(ifTruthy); |
| 14903 | masm.move32(Imm32(0), output); |
| 14904 | masm.jump(&join); |
| 14905 | |
| 14906 | masm.bind(ifFalsy); |
| 14907 | masm.move32(Imm32(1), output); |
| 14908 | |
| 14909 | // both branches meet here. |
| 14910 | masm.bind(&join); |
| 14911 | } |
| 14912 | |
| 14913 | void CodeGenerator::visitBoundsCheck(LBoundsCheck* lir) { |
| 14914 | const LAllocation* index = lir->index(); |
| 14915 | const LAllocation* length = lir->length(); |
| 14916 | LSnapshot* snapshot = lir->snapshot(); |
| 14917 | |
| 14918 | MIRType type = lir->mir()->type(); |
| 14919 | |
| 14920 | auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) { |
| 14921 | if (type == MIRType::Int32) { |
| 14922 | bailoutCmp32(cond, lhs, rhs, snapshot); |
| 14923 | } else { |
| 14924 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14924); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14924; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 14925 | bailoutCmpPtr(cond, lhs, rhs, snapshot); |
| 14926 | } |
| 14927 | }; |
| 14928 | |
| 14929 | auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs, |
| 14930 | int32_t rhs) { |
| 14931 | if (type == MIRType::Int32) { |
| 14932 | bailoutCmp32(cond, lhs, Imm32(rhs), snapshot); |
| 14933 | } else { |
| 14934 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14934; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 14935 | bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot); |
| 14936 | } |
| 14937 | }; |
| 14938 | |
| 14939 | if (index->isConstant()) { |
| 14940 | // Use uint32 so that the comparison is unsigned. |
| 14941 | uint32_t idx = ToInt32(index); |
| 14942 | if (length->isConstant()) { |
| 14943 | uint32_t len = ToInt32(lir->length()); |
| 14944 | if (idx < len) { |
| 14945 | return; |
| 14946 | } |
| 14947 | bailout(snapshot); |
| 14948 | return; |
| 14949 | } |
| 14950 | |
| 14951 | if (length->isRegister()) { |
| 14952 | bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), idx); |
| 14953 | } else { |
| 14954 | bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), idx); |
| 14955 | } |
| 14956 | return; |
| 14957 | } |
| 14958 | |
| 14959 | Register indexReg = ToRegister(index); |
| 14960 | if (length->isConstant()) { |
| 14961 | bailoutCmpConstant(Assembler::AboveOrEqual, indexReg, ToInt32(length)); |
| 14962 | } else if (length->isRegister()) { |
| 14963 | bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), indexReg); |
| 14964 | } else { |
| 14965 | bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), indexReg); |
| 14966 | } |
| 14967 | } |
| 14968 | |
| 14969 | void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) { |
| 14970 | int32_t min = lir->mir()->minimum(); |
| 14971 | int32_t max = lir->mir()->maximum(); |
| 14972 | MOZ_ASSERT(max >= min)do { static_assert( mozilla::detail::AssertionConditionType< decltype(max >= min)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(max >= min))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("max >= min", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14972); AnnotateMozCrashReason("MOZ_ASSERT" "(" "max >= min" ")"); do { *((volatile int*)__null) = 14972; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 14973 | |
| 14974 | LSnapshot* snapshot = lir->snapshot(); |
| 14975 | MIRType type = lir->mir()->type(); |
| 14976 | |
| 14977 | const LAllocation* length = lir->length(); |
| 14978 | Register temp = ToRegister(lir->getTemp(0)); |
| 14979 | |
| 14980 | auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) { |
| 14981 | if (type == MIRType::Int32) { |
| 14982 | bailoutCmp32(cond, lhs, rhs, snapshot); |
| 14983 | } else { |
| 14984 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14984); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14984; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 14985 | bailoutCmpPtr(cond, lhs, rhs, snapshot); |
| 14986 | } |
| 14987 | }; |
| 14988 | |
| 14989 | auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs, |
| 14990 | int32_t rhs) { |
| 14991 | if (type == MIRType::Int32) { |
| 14992 | bailoutCmp32(cond, lhs, Imm32(rhs), snapshot); |
| 14993 | } else { |
| 14994 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 14994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 14994; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 14995 | bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot); |
| 14996 | } |
| 14997 | }; |
| 14998 | |
| 14999 | if (lir->index()->isConstant()) { |
| 15000 | int32_t nmin, nmax; |
| 15001 | int32_t index = ToInt32(lir->index()); |
| 15002 | if (SafeAdd(index, min, &nmin) && SafeAdd(index, max, &nmax) && nmin >= 0) { |
| 15003 | if (length->isRegister()) { |
| 15004 | bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax); |
| 15005 | } else { |
| 15006 | bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), nmax); |
| 15007 | } |
| 15008 | return; |
| 15009 | } |
| 15010 | masm.mov(ImmWord(index), temp); |
| 15011 | } else { |
| 15012 | masm.mov(ToRegister(lir->index()), temp); |
| 15013 | } |
| 15014 | |
| 15015 | // If the minimum and maximum differ then do an underflow check first. |
| 15016 | // If the two are the same then doing an unsigned comparison on the |
| 15017 | // length will also catch a negative index. |
| 15018 | if (min != max) { |
| 15019 | if (min != 0) { |
| 15020 | Label bail; |
| 15021 | if (type == MIRType::Int32) { |
| 15022 | masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail); |
| 15023 | } else { |
| 15024 | masm.branchAddPtr(Assembler::Overflow, Imm32(min), temp, &bail); |
| 15025 | } |
| 15026 | bailoutFrom(&bail, snapshot); |
| 15027 | } |
| 15028 | |
| 15029 | bailoutCmpConstant(Assembler::LessThan, temp, 0); |
| 15030 | |
| 15031 | if (min != 0) { |
| 15032 | int32_t diff; |
| 15033 | if (SafeSub(max, min, &diff)) { |
| 15034 | max = diff; |
| 15035 | } else { |
| 15036 | if (type == MIRType::Int32) { |
| 15037 | masm.sub32(Imm32(min), temp); |
| 15038 | } else { |
| 15039 | masm.subPtr(Imm32(min), temp); |
| 15040 | } |
| 15041 | } |
| 15042 | } |
| 15043 | } |
| 15044 | |
| 15045 | // Compute the maximum possible index. No overflow check is needed when |
| 15046 | // max > 0. We can only wraparound to a negative number, which will test as |
| 15047 | // larger than all nonnegative numbers in the unsigned comparison, and the |
| 15048 | // length is required to be nonnegative (else testing a negative length |
| 15049 | // would succeed on any nonnegative index). |
| 15050 | if (max != 0) { |
| 15051 | if (max < 0) { |
| 15052 | Label bail; |
| 15053 | if (type == MIRType::Int32) { |
| 15054 | masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail); |
| 15055 | } else { |
| 15056 | masm.branchAddPtr(Assembler::Overflow, Imm32(max), temp, &bail); |
| 15057 | } |
| 15058 | bailoutFrom(&bail, snapshot); |
| 15059 | } else { |
| 15060 | if (type == MIRType::Int32) { |
| 15061 | masm.add32(Imm32(max), temp); |
| 15062 | } else { |
| 15063 | masm.addPtr(Imm32(max), temp); |
| 15064 | } |
| 15065 | } |
| 15066 | } |
| 15067 | |
| 15068 | if (length->isRegister()) { |
| 15069 | bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), temp); |
| 15070 | } else { |
| 15071 | bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), temp); |
| 15072 | } |
| 15073 | } |
| 15074 | |
| 15075 | void CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir) { |
| 15076 | int32_t min = lir->mir()->minimum(); |
| 15077 | bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min), |
| 15078 | lir->snapshot()); |
| 15079 | } |
| 15080 | |
| 15081 | void CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir) { |
| 15082 | MOZ_ASSERT(JitOptions.spectreIndexMasking)do { static_assert( mozilla::detail::AssertionConditionType< decltype(JitOptions.spectreIndexMasking)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(JitOptions.spectreIndexMasking ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "JitOptions.spectreIndexMasking", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15082); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitOptions.spectreIndexMasking" ")"); do { *((volatile int*)__null) = 15082; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15083 | |
| 15084 | const LAllocation* length = lir->length(); |
| 15085 | Register index = ToRegister(lir->index()); |
| 15086 | Register output = ToRegister(lir->output()); |
| 15087 | |
| 15088 | if (lir->mir()->type() == MIRType::Int32) { |
| 15089 | if (length->isRegister()) { |
| 15090 | masm.spectreMaskIndex32(index, ToRegister(length), output); |
| 15091 | } else { |
| 15092 | masm.spectreMaskIndex32(index, ToAddress(length), output); |
| 15093 | } |
| 15094 | } else { |
| 15095 | MOZ_ASSERT(lir->mir()->type() == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::IntPtr)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::IntPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15095); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 15095; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15096 | if (length->isRegister()) { |
| 15097 | masm.spectreMaskIndexPtr(index, ToRegister(length), output); |
| 15098 | } else { |
| 15099 | masm.spectreMaskIndexPtr(index, ToAddress(length), output); |
| 15100 | } |
| 15101 | } |
| 15102 | } |
| 15103 | |
| 15104 | class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator> { |
| 15105 | LInstruction* ins_; |
| 15106 | |
| 15107 | public: |
| 15108 | explicit OutOfLineStoreElementHole(LInstruction* ins) : ins_(ins) { |
| 15109 | MOZ_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->isStoreElementHoleV() || ins->isStoreElementHoleT ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ins->isStoreElementHoleV() || ins->isStoreElementHoleT ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ins->isStoreElementHoleV() || ins->isStoreElementHoleT()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->isStoreElementHoleV() || ins->isStoreElementHoleT()" ")"); do { *((volatile int*)__null) = 15109; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15110 | } |
| 15111 | |
| 15112 | void accept(CodeGenerator* codegen) override { |
| 15113 | codegen->visitOutOfLineStoreElementHole(this); |
| 15114 | } |
| 15115 | |
| 15116 | MStoreElementHole* mir() const { |
| 15117 | return ins_->isStoreElementHoleV() ? ins_->toStoreElementHoleV()->mir() |
| 15118 | : ins_->toStoreElementHoleT()->mir(); |
| 15119 | } |
| 15120 | LInstruction* ins() const { return ins_; } |
| 15121 | }; |
| 15122 | |
| 15123 | void CodeGenerator::emitStoreHoleCheck(Register elements, |
| 15124 | const LAllocation* index, |
| 15125 | LSnapshot* snapshot) { |
| 15126 | Label bail; |
| 15127 | if (index->isConstant()) { |
| 15128 | Address dest(elements, ToInt32(index) * sizeof(js::Value)); |
| 15129 | masm.branchTestMagic(Assembler::Equal, dest, &bail); |
| 15130 | } else { |
| 15131 | BaseObjectElementIndex dest(elements, ToRegister(index)); |
| 15132 | masm.branchTestMagic(Assembler::Equal, dest, &bail); |
| 15133 | } |
| 15134 | bailoutFrom(&bail, snapshot); |
| 15135 | } |
| 15136 | |
| 15137 | void CodeGenerator::emitStoreElementTyped(const LAllocation* value, |
| 15138 | MIRType valueType, Register elements, |
| 15139 | const LAllocation* index) { |
| 15140 | MOZ_ASSERT(valueType != MIRType::MagicHole)do { static_assert( mozilla::detail::AssertionConditionType< decltype(valueType != MIRType::MagicHole)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(valueType != MIRType::MagicHole ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "valueType != MIRType::MagicHole", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15140); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole" ")"); do { *((volatile int*)__null) = 15140; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15141 | ConstantOrRegister v = ToConstantOrRegister(value, valueType); |
| 15142 | if (index->isConstant()) { |
| 15143 | Address dest(elements, ToInt32(index) * sizeof(js::Value)); |
| 15144 | masm.storeUnboxedValue(v, valueType, dest); |
| 15145 | } else { |
| 15146 | BaseObjectElementIndex dest(elements, ToRegister(index)); |
| 15147 | masm.storeUnboxedValue(v, valueType, dest); |
| 15148 | } |
| 15149 | } |
| 15150 | |
| 15151 | void CodeGenerator::visitStoreElementT(LStoreElementT* store) { |
| 15152 | Register elements = ToRegister(store->elements()); |
| 15153 | const LAllocation* index = store->index(); |
| 15154 | |
| 15155 | if (store->mir()->needsBarrier()) { |
| 15156 | emitPreBarrier(elements, index); |
| 15157 | } |
| 15158 | |
| 15159 | if (store->mir()->needsHoleCheck()) { |
| 15160 | emitStoreHoleCheck(elements, index, store->snapshot()); |
| 15161 | } |
| 15162 | |
| 15163 | emitStoreElementTyped(store->value(), store->mir()->value()->type(), elements, |
| 15164 | index); |
| 15165 | } |
| 15166 | |
| 15167 | void CodeGenerator::visitStoreElementV(LStoreElementV* lir) { |
| 15168 | const ValueOperand value = ToValue(lir, LStoreElementV::Value); |
| 15169 | Register elements = ToRegister(lir->elements()); |
| 15170 | const LAllocation* index = lir->index(); |
| 15171 | |
| 15172 | if (lir->mir()->needsBarrier()) { |
| 15173 | emitPreBarrier(elements, index); |
| 15174 | } |
| 15175 | |
| 15176 | if (lir->mir()->needsHoleCheck()) { |
| 15177 | emitStoreHoleCheck(elements, index, lir->snapshot()); |
| 15178 | } |
| 15179 | |
| 15180 | if (lir->index()->isConstant()) { |
| 15181 | Address dest(elements, ToInt32(lir->index()) * sizeof(js::Value)); |
| 15182 | masm.storeValue(value, dest); |
| 15183 | } else { |
| 15184 | BaseObjectElementIndex dest(elements, ToRegister(lir->index())); |
| 15185 | masm.storeValue(value, dest); |
| 15186 | } |
| 15187 | } |
| 15188 | |
| 15189 | void CodeGenerator::visitStoreHoleValueElement(LStoreHoleValueElement* lir) { |
| 15190 | Register elements = ToRegister(lir->elements()); |
| 15191 | Register index = ToRegister(lir->index()); |
| 15192 | |
| 15193 | Address elementsFlags(elements, ObjectElements::offsetOfFlags()); |
| 15194 | masm.or32(Imm32(ObjectElements::NON_PACKED), elementsFlags); |
| 15195 | |
| 15196 | BaseObjectElementIndex element(elements, index); |
| 15197 | masm.storeValue(MagicValue(JS_ELEMENTS_HOLE), element); |
| 15198 | } |
| 15199 | |
| 15200 | void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) { |
| 15201 | auto* ool = new (alloc()) OutOfLineStoreElementHole(lir); |
| 15202 | addOutOfLineCode(ool, lir->mir()); |
| 15203 | |
| 15204 | Register obj = ToRegister(lir->object()); |
| 15205 | Register elements = ToRegister(lir->elements()); |
| 15206 | Register index = ToRegister(lir->index()); |
| 15207 | Register temp = ToRegister(lir->temp0()); |
| 15208 | |
| 15209 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
| 15210 | masm.spectreBoundsCheck32(index, initLength, temp, ool->entry()); |
| 15211 | |
| 15212 | emitPreBarrier(elements, lir->index()); |
| 15213 | |
| 15214 | masm.bind(ool->rejoin()); |
| 15215 | emitStoreElementTyped(lir->value(), lir->mir()->value()->type(), elements, |
| 15216 | lir->index()); |
| 15217 | |
| 15218 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
| 15219 | LiveRegisterSet regs = liveVolatileRegs(lir); |
| 15220 | ConstantOrRegister val = |
| 15221 | ToConstantOrRegister(lir->value(), lir->mir()->value()->type()); |
| 15222 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp, val); |
| 15223 | } |
| 15224 | } |
| 15225 | |
| 15226 | void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) { |
| 15227 | auto* ool = new (alloc()) OutOfLineStoreElementHole(lir); |
| 15228 | addOutOfLineCode(ool, lir->mir()); |
| 15229 | |
| 15230 | Register obj = ToRegister(lir->object()); |
| 15231 | Register elements = ToRegister(lir->elements()); |
| 15232 | Register index = ToRegister(lir->index()); |
| 15233 | const ValueOperand value = ToValue(lir, LStoreElementHoleV::ValueIndex); |
| 15234 | Register temp = ToRegister(lir->temp0()); |
| 15235 | |
| 15236 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
| 15237 | masm.spectreBoundsCheck32(index, initLength, temp, ool->entry()); |
| 15238 | |
| 15239 | emitPreBarrier(elements, lir->index()); |
| 15240 | |
| 15241 | masm.bind(ool->rejoin()); |
| 15242 | masm.storeValue(value, BaseObjectElementIndex(elements, index)); |
| 15243 | |
| 15244 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
| 15245 | LiveRegisterSet regs = liveVolatileRegs(lir); |
| 15246 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->index(), temp, |
| 15247 | ConstantOrRegister(value)); |
| 15248 | } |
| 15249 | } |
| 15250 | |
| 15251 | void CodeGenerator::visitOutOfLineStoreElementHole( |
| 15252 | OutOfLineStoreElementHole* ool) { |
| 15253 | Register object, elements, index; |
| 15254 | LInstruction* ins = ool->ins(); |
| 15255 | mozilla::Maybe<ConstantOrRegister> value; |
| 15256 | Register temp; |
| 15257 | |
| 15258 | if (ins->isStoreElementHoleV()) { |
| 15259 | LStoreElementHoleV* store = ins->toStoreElementHoleV(); |
| 15260 | object = ToRegister(store->object()); |
| 15261 | elements = ToRegister(store->elements()); |
| 15262 | index = ToRegister(store->index()); |
| 15263 | value.emplace( |
| 15264 | TypedOrValueRegister(ToValue(store, LStoreElementHoleV::ValueIndex))); |
| 15265 | temp = ToRegister(store->temp0()); |
| 15266 | } else { |
| 15267 | LStoreElementHoleT* store = ins->toStoreElementHoleT(); |
| 15268 | object = ToRegister(store->object()); |
| 15269 | elements = ToRegister(store->elements()); |
| 15270 | index = ToRegister(store->index()); |
| 15271 | if (store->value()->isConstant()) { |
| 15272 | value.emplace( |
| 15273 | ConstantOrRegister(store->value()->toConstant()->toJSValue())); |
| 15274 | } else { |
| 15275 | MIRType valueType = store->mir()->value()->type(); |
| 15276 | value.emplace( |
| 15277 | TypedOrValueRegister(valueType, ToAnyRegister(store->value()))); |
| 15278 | } |
| 15279 | temp = ToRegister(store->temp0()); |
| 15280 | } |
| 15281 | |
| 15282 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
| 15283 | |
| 15284 | // We're out-of-bounds. We only handle the index == initlength case. |
| 15285 | // If index > initializedLength, bail out. Note that this relies on the |
| 15286 | // condition flags sticking from the incoming branch. |
| 15287 | // Also note: this branch does not need Spectre mitigations, doing that for |
| 15288 | // the capacity check below is sufficient. |
| 15289 | Label allocElement, addNewElement; |
| 15290 | #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ |
| 15291 | defined(JS_CODEGEN_LOONG64) || defined(JS_CODEGEN_RISCV64) |
| 15292 | // Had to reimplement for MIPS because there are no flags. |
| 15293 | bailoutCmp32(Assembler::NotEqual, initLength, index, ins->snapshot()); |
| 15294 | #else |
| 15295 | bailoutIf(Assembler::NotEqual, ins->snapshot()); |
| 15296 | #endif |
| 15297 | |
| 15298 | // If index < capacity, we can add a dense element inline. If not, we need |
| 15299 | // to allocate more elements first. |
| 15300 | masm.spectreBoundsCheck32( |
| 15301 | index, Address(elements, ObjectElements::offsetOfCapacity()), temp, |
| 15302 | &allocElement); |
| 15303 | masm.jump(&addNewElement); |
| 15304 | |
| 15305 | masm.bind(&allocElement); |
| 15306 | |
| 15307 | // Save all live volatile registers, except |temp|. |
| 15308 | LiveRegisterSet liveRegs = liveVolatileRegs(ins); |
| 15309 | liveRegs.takeUnchecked(temp); |
| 15310 | masm.PushRegsInMask(liveRegs); |
| 15311 | |
| 15312 | masm.setupAlignedABICall(); |
| 15313 | masm.loadJSContext(temp); |
| 15314 | masm.passABIArg(temp); |
| 15315 | masm.passABIArg(object); |
| 15316 | |
| 15317 | using Fn = bool (*)(JSContext*, NativeObject*); |
| 15318 | masm.callWithABI<Fn, NativeObject::addDenseElementPure>(); |
| 15319 | masm.storeCallPointerResult(temp); |
| 15320 | |
| 15321 | masm.PopRegsInMask(liveRegs); |
| 15322 | bailoutIfFalseBool(temp, ins->snapshot()); |
| 15323 | |
| 15324 | // Load the reallocated elements pointer. |
| 15325 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), elements); |
| 15326 | |
| 15327 | masm.bind(&addNewElement); |
| 15328 | |
| 15329 | // Increment initLength |
| 15330 | masm.add32(Imm32(1), initLength); |
| 15331 | |
| 15332 | // If length is now <= index, increment length too. |
| 15333 | Label skipIncrementLength; |
| 15334 | Address length(elements, ObjectElements::offsetOfLength()); |
| 15335 | masm.branch32(Assembler::Above, length, index, &skipIncrementLength); |
| 15336 | masm.add32(Imm32(1), length); |
| 15337 | masm.bind(&skipIncrementLength); |
| 15338 | |
| 15339 | // Jump to the inline path where we will store the value. |
| 15340 | // We rejoin after the prebarrier, because the memory is uninitialized. |
| 15341 | masm.jump(ool->rejoin()); |
| 15342 | } |
| 15343 | |
| 15344 | void CodeGenerator::visitArrayPopShift(LArrayPopShift* lir) { |
| 15345 | Register obj = ToRegister(lir->object()); |
| 15346 | Register temp1 = ToRegister(lir->temp0()); |
| 15347 | Register temp2 = ToRegister(lir->temp1()); |
| 15348 | ValueOperand out = ToOutValue(lir); |
| 15349 | |
| 15350 | Label bail; |
| 15351 | if (lir->mir()->mode() == MArrayPopShift::Pop) { |
| 15352 | masm.packedArrayPop(obj, out, temp1, temp2, &bail); |
| 15353 | } else { |
| 15354 | MOZ_ASSERT(lir->mir()->mode() == MArrayPopShift::Shift)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->mode() == MArrayPopShift::Shift)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->mode() == MArrayPopShift::Shift))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->mode() == MArrayPopShift::Shift" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->mode() == MArrayPopShift::Shift" ")"); do { *((volatile int*)__null) = 15354; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15355 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 15356 | masm.packedArrayShift(obj, out, temp1, temp2, volatileRegs, &bail); |
| 15357 | } |
| 15358 | bailoutFrom(&bail, lir->snapshot()); |
| 15359 | } |
| 15360 | |
| 15361 | class OutOfLineArrayPush : public OutOfLineCodeBase<CodeGenerator> { |
| 15362 | LArrayPush* ins_; |
| 15363 | |
| 15364 | public: |
| 15365 | explicit OutOfLineArrayPush(LArrayPush* ins) : ins_(ins) {} |
| 15366 | |
| 15367 | void accept(CodeGenerator* codegen) override { |
| 15368 | codegen->visitOutOfLineArrayPush(this); |
| 15369 | } |
| 15370 | |
| 15371 | LArrayPush* ins() const { return ins_; } |
| 15372 | }; |
| 15373 | |
| 15374 | void CodeGenerator::visitArrayPush(LArrayPush* lir) { |
| 15375 | Register obj = ToRegister(lir->object()); |
| 15376 | Register elementsTemp = ToRegister(lir->temp0()); |
| 15377 | Register length = ToRegister(lir->output()); |
| 15378 | ValueOperand value = ToValue(lir, LArrayPush::ValueIndex); |
| 15379 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
| 15380 | |
| 15381 | auto* ool = new (alloc()) OutOfLineArrayPush(lir); |
| 15382 | addOutOfLineCode(ool, lir->mir()); |
| 15383 | |
| 15384 | // Load obj->elements in elementsTemp. |
| 15385 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); |
| 15386 | |
| 15387 | Address initLengthAddr(elementsTemp, |
| 15388 | ObjectElements::offsetOfInitializedLength()); |
| 15389 | Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength()); |
| 15390 | Address capacityAddr(elementsTemp, ObjectElements::offsetOfCapacity()); |
| 15391 | |
| 15392 | // Bail out if length != initLength. |
| 15393 | masm.load32(lengthAddr, length); |
| 15394 | bailoutCmp32(Assembler::NotEqual, initLengthAddr, length, lir->snapshot()); |
| 15395 | |
| 15396 | // If length < capacity, we can add a dense element inline. If not, we |
| 15397 | // need to allocate more elements. |
| 15398 | masm.spectreBoundsCheck32(length, capacityAddr, spectreTemp, ool->entry()); |
| 15399 | masm.bind(ool->rejoin()); |
| 15400 | |
| 15401 | // Store the value. |
| 15402 | masm.storeValue(value, BaseObjectElementIndex(elementsTemp, length)); |
| 15403 | |
| 15404 | // Update length and initialized length. |
| 15405 | masm.add32(Imm32(1), length); |
| 15406 | masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); |
| 15407 | masm.store32(length, Address(elementsTemp, |
| 15408 | ObjectElements::offsetOfInitializedLength())); |
| 15409 | |
| 15410 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
| 15411 | LiveRegisterSet regs = liveVolatileRegs(lir); |
| 15412 | regs.addUnchecked(length); |
| 15413 | emitElementPostWriteBarrier(lir->mir(), regs, obj, lir->output()->output(), |
| 15414 | elementsTemp, ConstantOrRegister(value), |
| 15415 | /* indexDiff = */ -1); |
| 15416 | } |
| 15417 | } |
| 15418 | |
| 15419 | void CodeGenerator::visitOutOfLineArrayPush(OutOfLineArrayPush* ool) { |
| 15420 | LArrayPush* ins = ool->ins(); |
| 15421 | |
| 15422 | Register object = ToRegister(ins->object()); |
| 15423 | Register temp = ToRegister(ins->temp0()); |
| 15424 | |
| 15425 | LiveRegisterSet liveRegs = liveVolatileRegs(ins); |
| 15426 | liveRegs.takeUnchecked(temp); |
| 15427 | liveRegs.addUnchecked(ToRegister(ins->output())); |
| 15428 | liveRegs.addUnchecked(ToValue(ins, LArrayPush::ValueIndex)); |
| 15429 | |
| 15430 | masm.PushRegsInMask(liveRegs); |
| 15431 | |
| 15432 | masm.setupAlignedABICall(); |
| 15433 | masm.loadJSContext(temp); |
| 15434 | masm.passABIArg(temp); |
| 15435 | masm.passABIArg(object); |
| 15436 | |
| 15437 | using Fn = bool (*)(JSContext*, NativeObject* obj); |
| 15438 | masm.callWithABI<Fn, NativeObject::addDenseElementPure>(); |
| 15439 | masm.storeCallPointerResult(temp); |
| 15440 | |
| 15441 | masm.PopRegsInMask(liveRegs); |
| 15442 | bailoutIfFalseBool(temp, ins->snapshot()); |
| 15443 | |
| 15444 | // Load the reallocated elements pointer. |
| 15445 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
| 15446 | |
| 15447 | masm.jump(ool->rejoin()); |
| 15448 | } |
| 15449 | |
| 15450 | void CodeGenerator::visitArraySlice(LArraySlice* lir) { |
| 15451 | Register object = ToRegister(lir->object()); |
| 15452 | Register begin = ToRegister(lir->begin()); |
| 15453 | Register end = ToRegister(lir->end()); |
| 15454 | Register temp0 = ToRegister(lir->temp0()); |
| 15455 | Register temp1 = ToRegister(lir->temp1()); |
| 15456 | |
| 15457 | Label call, fail; |
| 15458 | |
| 15459 | Label bail; |
| 15460 | masm.branchArrayIsNotPacked(object, temp0, temp1, &bail); |
| 15461 | bailoutFrom(&bail, lir->snapshot()); |
| 15462 | |
| 15463 | // Try to allocate an object. |
| 15464 | TemplateObject templateObject(lir->mir()->templateObj()); |
| 15465 | masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(), |
| 15466 | &fail); |
| 15467 | |
| 15468 | masm.jump(&call); |
| 15469 | { |
| 15470 | masm.bind(&fail); |
| 15471 | masm.movePtr(ImmPtr(nullptr), temp0); |
| 15472 | } |
| 15473 | masm.bind(&call); |
| 15474 | |
| 15475 | pushArg(temp0); |
| 15476 | pushArg(end); |
| 15477 | pushArg(begin); |
| 15478 | pushArg(object); |
| 15479 | |
| 15480 | using Fn = |
| 15481 | JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); |
| 15482 | callVM<Fn, ArraySliceDense>(lir); |
| 15483 | } |
| 15484 | |
| 15485 | void CodeGenerator::visitArgumentsSlice(LArgumentsSlice* lir) { |
| 15486 | Register object = ToRegister(lir->object()); |
| 15487 | Register begin = ToRegister(lir->begin()); |
| 15488 | Register end = ToRegister(lir->end()); |
| 15489 | Register temp0 = ToRegister(lir->temp0()); |
| 15490 | Register temp1 = ToRegister(lir->temp1()); |
| 15491 | |
| 15492 | Label call, fail; |
| 15493 | |
| 15494 | // Try to allocate an object. |
| 15495 | TemplateObject templateObject(lir->mir()->templateObj()); |
| 15496 | masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(), |
| 15497 | &fail); |
| 15498 | |
| 15499 | masm.jump(&call); |
| 15500 | { |
| 15501 | masm.bind(&fail); |
| 15502 | masm.movePtr(ImmPtr(nullptr), temp0); |
| 15503 | } |
| 15504 | masm.bind(&call); |
| 15505 | |
| 15506 | pushArg(temp0); |
| 15507 | pushArg(end); |
| 15508 | pushArg(begin); |
| 15509 | pushArg(object); |
| 15510 | |
| 15511 | using Fn = |
| 15512 | JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); |
| 15513 | callVM<Fn, ArgumentsSliceDense>(lir); |
| 15514 | } |
| 15515 | |
| 15516 | #ifdef DEBUG1 |
| 15517 | void CodeGenerator::emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin, |
| 15518 | const RegisterOrInt32& count, |
| 15519 | Register numActualArgs) { |
| 15520 | // |begin| must be positive or zero. |
| 15521 | if (begin.is<Register>()) { |
| 15522 | Label beginOk; |
| 15523 | masm.branch32(Assembler::GreaterThanOrEqual, begin.as<Register>(), Imm32(0), |
| 15524 | &beginOk); |
| 15525 | masm.assumeUnreachable("begin < 0"); |
| 15526 | masm.bind(&beginOk); |
| 15527 | } else { |
| 15528 | MOZ_ASSERT(begin.as<int32_t>() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(begin.as<int32_t>() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(begin.as<int32_t>() >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("begin.as<int32_t>() >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15528); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() >= 0" ")"); do { *((volatile int*)__null) = 15528; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15529 | } |
| 15530 | |
| 15531 | // |count| must be positive or zero. |
| 15532 | if (count.is<Register>()) { |
| 15533 | Label countOk; |
| 15534 | masm.branch32(Assembler::GreaterThanOrEqual, count.as<Register>(), Imm32(0), |
| 15535 | &countOk); |
| 15536 | masm.assumeUnreachable("count < 0"); |
| 15537 | masm.bind(&countOk); |
| 15538 | } else { |
| 15539 | MOZ_ASSERT(count.as<int32_t>() >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(count.as<int32_t>() >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(count.as<int32_t>() >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("count.as<int32_t>() >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15539); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count.as<int32_t>() >= 0" ")"); do { *((volatile int*)__null) = 15539; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15540 | } |
| 15541 | |
| 15542 | // |begin| must be less-or-equal to |numActualArgs|. |
| 15543 | Label argsBeginOk; |
| 15544 | if (begin.is<Register>()) { |
| 15545 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(), |
| 15546 | &argsBeginOk); |
| 15547 | } else { |
| 15548 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
| 15549 | Imm32(begin.as<int32_t>()), &argsBeginOk); |
| 15550 | } |
| 15551 | masm.assumeUnreachable("begin <= numActualArgs"); |
| 15552 | masm.bind(&argsBeginOk); |
| 15553 | |
| 15554 | // |count| must be less-or-equal to |numActualArgs|. |
| 15555 | Label argsCountOk; |
| 15556 | if (count.is<Register>()) { |
| 15557 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, count.as<Register>(), |
| 15558 | &argsCountOk); |
| 15559 | } else { |
| 15560 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
| 15561 | Imm32(count.as<int32_t>()), &argsCountOk); |
| 15562 | } |
| 15563 | masm.assumeUnreachable("count <= numActualArgs"); |
| 15564 | masm.bind(&argsCountOk); |
| 15565 | |
| 15566 | // |begin| and |count| must be preserved, but |numActualArgs| can be changed. |
| 15567 | // |
| 15568 | // Pre-condition: |count| <= |numActualArgs| |
| 15569 | // Condition to test: |begin + count| <= |numActualArgs| |
| 15570 | // Transform to: |begin| <= |numActualArgs - count| |
| 15571 | if (count.is<Register>()) { |
| 15572 | masm.subPtr(count.as<Register>(), numActualArgs); |
| 15573 | } else { |
| 15574 | masm.subPtr(Imm32(count.as<int32_t>()), numActualArgs); |
| 15575 | } |
| 15576 | |
| 15577 | // |begin + count| must be less-or-equal to |numActualArgs|. |
| 15578 | Label argsBeginCountOk; |
| 15579 | if (begin.is<Register>()) { |
| 15580 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(), |
| 15581 | &argsBeginCountOk); |
| 15582 | } else { |
| 15583 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
| 15584 | Imm32(begin.as<int32_t>()), &argsBeginCountOk); |
| 15585 | } |
| 15586 | masm.assumeUnreachable("begin + count <= numActualArgs"); |
| 15587 | masm.bind(&argsBeginCountOk); |
| 15588 | } |
| 15589 | #endif |
| 15590 | |
| 15591 | template <class ArgumentsSlice> |
| 15592 | void CodeGenerator::emitNewArray(ArgumentsSlice* lir, |
| 15593 | const RegisterOrInt32& count, Register output, |
| 15594 | Register temp) { |
| 15595 | using Fn = ArrayObject* (*)(JSContext*, int32_t); |
| 15596 | auto* ool = count.match( |
| 15597 | [&](Register count) { |
| 15598 | return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>( |
| 15599 | lir, ArgList(count), StoreRegisterTo(output)); |
| 15600 | }, |
| 15601 | [&](int32_t count) { |
| 15602 | return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>( |
| 15603 | lir, ArgList(Imm32(count)), StoreRegisterTo(output)); |
| 15604 | }); |
| 15605 | |
| 15606 | TemplateObject templateObject(lir->mir()->templateObj()); |
| 15607 | MOZ_ASSERT(templateObject.isArrayObject())do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateObject.isArrayObject())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(templateObject.isArrayObject ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("templateObject.isArrayObject()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15607); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject.isArrayObject()" ")"); do { *((volatile int*)__null) = 15607; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15608 | |
| 15609 | auto templateNativeObj = templateObject.asTemplateNativeObject(); |
| 15610 | MOZ_ASSERT(templateNativeObj.getArrayLength() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateNativeObj.getArrayLength() == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(templateNativeObj.getArrayLength() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("templateNativeObj.getArrayLength() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15610); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getArrayLength() == 0" ")"); do { *((volatile int*)__null) = 15610; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15611 | MOZ_ASSERT(templateNativeObj.getDenseInitializedLength() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(templateNativeObj.getDenseInitializedLength() == 0)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(templateNativeObj.getDenseInitializedLength() == 0)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("templateNativeObj.getDenseInitializedLength() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15611); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateNativeObj.getDenseInitializedLength() == 0" ")"); do { *((volatile int*)__null) = 15611; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15612 | MOZ_ASSERT(!templateNativeObj.hasDynamicElements())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!templateNativeObj.hasDynamicElements())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(!templateNativeObj.hasDynamicElements()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!templateNativeObj.hasDynamicElements()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15612); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!templateNativeObj.hasDynamicElements()" ")"); do { *((volatile int*)__null) = 15612; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15613 | |
| 15614 | // Check array capacity. Call into the VM if the template object's capacity |
| 15615 | // is too small. |
| 15616 | bool tryAllocate = count.match( |
| 15617 | [&](Register count) { |
| 15618 | masm.branch32(Assembler::Above, count, |
| 15619 | Imm32(templateNativeObj.getDenseCapacity()), |
| 15620 | ool->entry()); |
| 15621 | return true; |
| 15622 | }, |
| 15623 | [&](int32_t count) { |
| 15624 | MOZ_ASSERT(count >= 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(count >= 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(count >= 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("count >= 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15624); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count >= 0" ")"); do { *((volatile int*)__null) = 15624; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15625 | if (uint32_t(count) > templateNativeObj.getDenseCapacity()) { |
| 15626 | masm.jump(ool->entry()); |
| 15627 | return false; |
| 15628 | } |
| 15629 | return true; |
| 15630 | }); |
| 15631 | |
| 15632 | if (tryAllocate) { |
| 15633 | // Try to allocate an object. |
| 15634 | masm.createGCObject(output, temp, templateObject, lir->mir()->initialHeap(), |
| 15635 | ool->entry()); |
| 15636 | |
| 15637 | auto setInitializedLengthAndLength = [&](auto count) { |
| 15638 | const int elementsOffset = NativeObject::offsetOfFixedElements(); |
| 15639 | |
| 15640 | // Update initialized length. |
| 15641 | Address initLength( |
| 15642 | output, elementsOffset + ObjectElements::offsetOfInitializedLength()); |
| 15643 | masm.store32(count, initLength); |
| 15644 | |
| 15645 | // Update length. |
| 15646 | Address length(output, elementsOffset + ObjectElements::offsetOfLength()); |
| 15647 | masm.store32(count, length); |
| 15648 | }; |
| 15649 | |
| 15650 | // The array object was successfully created. Set the length and initialized |
| 15651 | // length and then proceed to fill the elements. |
| 15652 | count.match([&](Register count) { setInitializedLengthAndLength(count); }, |
| 15653 | [&](int32_t count) { |
| 15654 | if (count > 0) { |
| 15655 | setInitializedLengthAndLength(Imm32(count)); |
| 15656 | } |
| 15657 | }); |
| 15658 | } |
| 15659 | |
| 15660 | masm.bind(ool->rejoin()); |
| 15661 | } |
| 15662 | |
| 15663 | void CodeGenerator::visitFrameArgumentsSlice(LFrameArgumentsSlice* lir) { |
| 15664 | Register begin = ToRegister(lir->begin()); |
| 15665 | Register count = ToRegister(lir->count()); |
| 15666 | Register temp = ToRegister(lir->temp0()); |
| 15667 | Register output = ToRegister(lir->output()); |
| 15668 | |
| 15669 | #ifdef DEBUG1 |
| 15670 | masm.loadNumActualArgs(FramePointer, temp); |
| 15671 | emitAssertArgumentsSliceBounds(RegisterOrInt32(begin), RegisterOrInt32(count), |
| 15672 | temp); |
| 15673 | #endif |
| 15674 | |
| 15675 | emitNewArray(lir, RegisterOrInt32(count), output, temp); |
| 15676 | |
| 15677 | Label done; |
| 15678 | masm.branch32(Assembler::Equal, count, Imm32(0), &done); |
| 15679 | { |
| 15680 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 15681 | allRegs.take(begin); |
| 15682 | allRegs.take(count); |
| 15683 | allRegs.take(temp); |
| 15684 | allRegs.take(output); |
| 15685 | |
| 15686 | ValueOperand value = allRegs.takeAnyValue(); |
| 15687 | |
| 15688 | LiveRegisterSet liveRegs; |
| 15689 | liveRegs.add(output); |
| 15690 | liveRegs.add(begin); |
| 15691 | liveRegs.add(value); |
| 15692 | |
| 15693 | masm.PushRegsInMask(liveRegs); |
| 15694 | |
| 15695 | // Initialize all elements. |
| 15696 | |
| 15697 | Register elements = output; |
| 15698 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
| 15699 | |
| 15700 | Register argIndex = begin; |
| 15701 | |
| 15702 | Register index = temp; |
| 15703 | masm.move32(Imm32(0), index); |
| 15704 | |
| 15705 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
| 15706 | BaseValueIndex argPtr(FramePointer, argIndex, argvOffset); |
| 15707 | |
| 15708 | Label loop; |
| 15709 | masm.bind(&loop); |
| 15710 | |
| 15711 | masm.loadValue(argPtr, value); |
| 15712 | |
| 15713 | // We don't need a pre-barrier, because the element at |index| is guaranteed |
| 15714 | // to be a non-GC thing (either uninitialized memory or the magic hole |
| 15715 | // value). |
| 15716 | masm.storeValue(value, BaseObjectElementIndex(elements, index)); |
| 15717 | |
| 15718 | masm.add32(Imm32(1), index); |
| 15719 | masm.add32(Imm32(1), argIndex); |
| 15720 | |
| 15721 | masm.branch32(Assembler::LessThan, index, count, &loop); |
| 15722 | |
| 15723 | masm.PopRegsInMask(liveRegs); |
| 15724 | |
| 15725 | // Emit a post-write barrier if |output| is tenured. |
| 15726 | // |
| 15727 | // We expect that |output| is nursery allocated, so it isn't worth the |
| 15728 | // trouble to check if no frame argument is a nursery thing, which would |
| 15729 | // allow to omit the post-write barrier. |
| 15730 | masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done); |
| 15731 | |
| 15732 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 15733 | volatileRegs.takeUnchecked(temp); |
| 15734 | if (output.volatile_()) { |
| 15735 | volatileRegs.addUnchecked(output); |
| 15736 | } |
| 15737 | |
| 15738 | masm.PushRegsInMask(volatileRegs); |
| 15739 | emitPostWriteBarrier(output); |
| 15740 | masm.PopRegsInMask(volatileRegs); |
| 15741 | } |
| 15742 | masm.bind(&done); |
| 15743 | } |
| 15744 | |
| 15745 | CodeGenerator::RegisterOrInt32 CodeGenerator::ToRegisterOrInt32( |
| 15746 | const LAllocation* allocation) { |
| 15747 | if (allocation->isConstant()) { |
| 15748 | return RegisterOrInt32(allocation->toConstant()->toInt32()); |
| 15749 | } |
| 15750 | return RegisterOrInt32(ToRegister(allocation)); |
| 15751 | } |
| 15752 | |
| 15753 | void CodeGenerator::visitInlineArgumentsSlice(LInlineArgumentsSlice* lir) { |
| 15754 | RegisterOrInt32 begin = ToRegisterOrInt32(lir->begin()); |
| 15755 | RegisterOrInt32 count = ToRegisterOrInt32(lir->count()); |
| 15756 | Register temp = ToRegister(lir->temp()); |
| 15757 | Register output = ToRegister(lir->output()); |
| 15758 | |
| 15759 | uint32_t numActuals = lir->mir()->numActuals(); |
| 15760 | |
| 15761 | #ifdef DEBUG1 |
| 15762 | masm.move32(Imm32(numActuals), temp); |
| 15763 | |
| 15764 | emitAssertArgumentsSliceBounds(begin, count, temp); |
| 15765 | #endif |
| 15766 | |
| 15767 | emitNewArray(lir, count, output, temp); |
| 15768 | |
| 15769 | // We're done if there are no actual arguments. |
| 15770 | if (numActuals == 0) { |
| 15771 | return; |
| 15772 | } |
| 15773 | |
| 15774 | // Check if any arguments have to be copied. |
| 15775 | Label done; |
| 15776 | if (count.is<Register>()) { |
| 15777 | masm.branch32(Assembler::Equal, count.as<Register>(), Imm32(0), &done); |
| 15778 | } else if (count.as<int32_t>() == 0) { |
| 15779 | return; |
| 15780 | } |
| 15781 | |
| 15782 | auto getArg = [&](uint32_t i) { |
| 15783 | return toConstantOrRegister(lir, LInlineArgumentsSlice::ArgIndex(i), |
| 15784 | lir->mir()->getArg(i)->type()); |
| 15785 | }; |
| 15786 | |
| 15787 | auto storeArg = [&](uint32_t i, auto dest) { |
| 15788 | // We don't need a pre-barrier because the element at |index| is guaranteed |
| 15789 | // to be a non-GC thing (either uninitialized memory or the magic hole |
| 15790 | // value). |
| 15791 | masm.storeConstantOrRegister(getArg(i), dest); |
| 15792 | }; |
| 15793 | |
| 15794 | // Initialize all elements. |
| 15795 | if (numActuals == 1) { |
| 15796 | // There's exactly one argument. We've checked that |count| is non-zero, |
| 15797 | // which implies that |begin| must be zero. |
| 15798 | MOZ_ASSERT_IF(begin.is<int32_t>(), begin.as<int32_t>() == 0)do { if (begin.is<int32_t>()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(begin.as<int32_t >() == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(begin.as<int32_t>() == 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("begin.as<int32_t>() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 15798); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() == 0" ")"); do { *((volatile int*)__null) = 15798; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 15799 | |
| 15800 | Register elements = temp; |
| 15801 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
| 15802 | |
| 15803 | storeArg(0, Address(elements, 0)); |
| 15804 | } else if (begin.is<Register>()) { |
| 15805 | // There is more than one argument and |begin| isn't a compile-time |
| 15806 | // constant. Iterate through 0..numActuals to search for |begin| and then |
| 15807 | // start copying |count| arguments from that index. |
| 15808 | |
| 15809 | LiveGeneralRegisterSet liveRegs; |
| 15810 | liveRegs.add(output); |
| 15811 | liveRegs.add(begin.as<Register>()); |
| 15812 | |
| 15813 | masm.PushRegsInMask(liveRegs); |
| 15814 | |
| 15815 | Register elements = output; |
| 15816 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
| 15817 | |
| 15818 | Register argIndex = begin.as<Register>(); |
| 15819 | |
| 15820 | Register index = temp; |
| 15821 | masm.move32(Imm32(0), index); |
| 15822 | |
| 15823 | Label doneLoop; |
| 15824 | for (uint32_t i = 0; i < numActuals; ++i) { |
| 15825 | Label next; |
| 15826 | masm.branch32(Assembler::NotEqual, argIndex, Imm32(i), &next); |
| 15827 | |
| 15828 | storeArg(i, BaseObjectElementIndex(elements, index)); |
| 15829 | |
| 15830 | masm.add32(Imm32(1), index); |
| 15831 | masm.add32(Imm32(1), argIndex); |
| 15832 | |
| 15833 | if (count.is<Register>()) { |
| 15834 | masm.branch32(Assembler::GreaterThanOrEqual, index, |
| 15835 | count.as<Register>(), &doneLoop); |
| 15836 | } else { |
| 15837 | masm.branch32(Assembler::GreaterThanOrEqual, index, |
| 15838 | Imm32(count.as<int32_t>()), &doneLoop); |
| 15839 | } |
| 15840 | |
| 15841 | masm.bind(&next); |
| 15842 | } |
| 15843 | masm.bind(&doneLoop); |
| 15844 | |
| 15845 | masm.PopRegsInMask(liveRegs); |
| 15846 | } else { |
| 15847 | // There is more than one argument and |begin| is a compile-time constant. |
| 15848 | |
| 15849 | Register elements = temp; |
| 15850 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
| 15851 | |
| 15852 | int32_t argIndex = begin.as<int32_t>(); |
| 15853 | |
| 15854 | int32_t index = 0; |
| 15855 | |
| 15856 | Label doneLoop; |
| 15857 | for (uint32_t i = argIndex; i < numActuals; ++i) { |
| 15858 | storeArg(i, Address(elements, index * sizeof(Value))); |
| 15859 | |
| 15860 | index += 1; |
| 15861 | |
| 15862 | if (count.is<Register>()) { |
| 15863 | masm.branch32(Assembler::LessThanOrEqual, count.as<Register>(), |
| 15864 | Imm32(index), &doneLoop); |
| 15865 | } else { |
| 15866 | if (index >= count.as<int32_t>()) { |
| 15867 | break; |
| 15868 | } |
| 15869 | } |
| 15870 | } |
| 15871 | masm.bind(&doneLoop); |
| 15872 | } |
| 15873 | |
| 15874 | // Determine if we have to emit post-write barrier. |
| 15875 | // |
| 15876 | // If either |begin| or |count| is a constant, use their value directly. |
| 15877 | // Otherwise assume we copy all inline arguments from 0..numActuals. |
| 15878 | bool postWriteBarrier = false; |
| 15879 | uint32_t actualBegin = begin.match([](Register) { return 0; }, |
| 15880 | [](int32_t value) { return value; }); |
| 15881 | uint32_t actualCount = |
| 15882 | count.match([=](Register) { return numActuals; }, |
| 15883 | [](int32_t value) -> uint32_t { return value; }); |
| 15884 | for (uint32_t i = 0; i < actualCount; ++i) { |
| 15885 | ConstantOrRegister arg = getArg(actualBegin + i); |
| 15886 | if (arg.constant()) { |
| 15887 | Value v = arg.value(); |
| 15888 | if (v.isGCThing() && IsInsideNursery(v.toGCThing())) { |
| 15889 | postWriteBarrier = true; |
| 15890 | } |
| 15891 | } else { |
| 15892 | MIRType type = arg.reg().type(); |
| 15893 | if (type == MIRType::Value || NeedsPostBarrier(type)) { |
| 15894 | postWriteBarrier = true; |
| 15895 | } |
| 15896 | } |
| 15897 | } |
| 15898 | |
| 15899 | // Emit a post-write barrier if |output| is tenured and we couldn't |
| 15900 | // determine at compile-time that no barrier is needed. |
| 15901 | if (postWriteBarrier) { |
| 15902 | masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done); |
| 15903 | |
| 15904 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 15905 | volatileRegs.takeUnchecked(temp); |
| 15906 | if (output.volatile_()) { |
| 15907 | volatileRegs.addUnchecked(output); |
| 15908 | } |
| 15909 | |
| 15910 | masm.PushRegsInMask(volatileRegs); |
| 15911 | emitPostWriteBarrier(output); |
| 15912 | masm.PopRegsInMask(volatileRegs); |
| 15913 | } |
| 15914 | |
| 15915 | masm.bind(&done); |
| 15916 | } |
| 15917 | |
| 15918 | void CodeGenerator::visitNormalizeSliceTerm(LNormalizeSliceTerm* lir) { |
| 15919 | Register value = ToRegister(lir->value()); |
| 15920 | Register length = ToRegister(lir->length()); |
| 15921 | Register output = ToRegister(lir->output()); |
| 15922 | |
| 15923 | masm.move32(value, output); |
| 15924 | |
| 15925 | Label positive; |
| 15926 | masm.branch32(Assembler::GreaterThanOrEqual, value, Imm32(0), &positive); |
| 15927 | |
| 15928 | Label done; |
| 15929 | masm.add32(length, output); |
| 15930 | masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(0), &done); |
| 15931 | masm.move32(Imm32(0), output); |
| 15932 | masm.jump(&done); |
| 15933 | |
| 15934 | masm.bind(&positive); |
| 15935 | masm.cmp32Move32(Assembler::LessThan, length, value, length, output); |
| 15936 | |
| 15937 | masm.bind(&done); |
| 15938 | } |
| 15939 | |
| 15940 | void CodeGenerator::visitArrayJoin(LArrayJoin* lir) { |
| 15941 | Label skipCall; |
| 15942 | |
| 15943 | Register output = ToRegister(lir->output()); |
| 15944 | Register sep = ToRegister(lir->separator()); |
| 15945 | Register array = ToRegister(lir->array()); |
| 15946 | Register temp = ToRegister(lir->temp0()); |
| 15947 | |
| 15948 | // Fast path for simple length <= 1 cases. |
| 15949 | { |
| 15950 | masm.loadPtr(Address(array, NativeObject::offsetOfElements()), temp); |
| 15951 | Address length(temp, ObjectElements::offsetOfLength()); |
| 15952 | Address initLength(temp, ObjectElements::offsetOfInitializedLength()); |
| 15953 | |
| 15954 | // Check for length == 0 |
| 15955 | Label notEmpty; |
| 15956 | masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬Empty); |
| 15957 | const JSAtomState& names = gen->runtime->names(); |
| 15958 | masm.movePtr(ImmGCPtr(names.empty_), output); |
| 15959 | masm.jump(&skipCall); |
| 15960 | |
| 15961 | masm.bind(¬Empty); |
| 15962 | Label notSingleString; |
| 15963 | // Check for length == 1, initializedLength >= 1, arr[0].isString() |
| 15964 | masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleString); |
| 15965 | masm.branch32(Assembler::LessThan, initLength, Imm32(1), ¬SingleString); |
| 15966 | |
| 15967 | Address elem0(temp, 0); |
| 15968 | masm.branchTestString(Assembler::NotEqual, elem0, ¬SingleString); |
| 15969 | |
| 15970 | // At this point, 'output' can be used as a scratch register, since we're |
| 15971 | // guaranteed to succeed. |
| 15972 | masm.unboxString(elem0, output); |
| 15973 | masm.jump(&skipCall); |
| 15974 | masm.bind(¬SingleString); |
| 15975 | } |
| 15976 | |
| 15977 | pushArg(sep); |
| 15978 | pushArg(array); |
| 15979 | |
| 15980 | using Fn = JSString* (*)(JSContext*, HandleObject, HandleString); |
| 15981 | callVM<Fn, jit::ArrayJoin>(lir); |
| 15982 | masm.bind(&skipCall); |
| 15983 | } |
| 15984 | |
| 15985 | void CodeGenerator::visitObjectKeys(LObjectKeys* lir) { |
| 15986 | Register object = ToRegister(lir->object()); |
| 15987 | |
| 15988 | pushArg(object); |
| 15989 | |
| 15990 | using Fn = JSObject* (*)(JSContext*, HandleObject); |
| 15991 | callVM<Fn, jit::ObjectKeys>(lir); |
| 15992 | } |
| 15993 | |
| 15994 | void CodeGenerator::visitObjectKeysLength(LObjectKeysLength* lir) { |
| 15995 | Register object = ToRegister(lir->object()); |
| 15996 | |
| 15997 | pushArg(object); |
| 15998 | |
| 15999 | using Fn = bool (*)(JSContext*, HandleObject, int32_t*); |
| 16000 | callVM<Fn, jit::ObjectKeysLength>(lir); |
| 16001 | } |
| 16002 | |
| 16003 | void CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir) { |
| 16004 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 16005 | TypedOrValueRegister val = |
| 16006 | toConstantOrRegister(lir, LGetIteratorCache::ValueIndex, |
| 16007 | lir->mir()->value()->type()) |
| 16008 | .reg(); |
| 16009 | Register output = ToRegister(lir->output()); |
| 16010 | Register temp0 = ToRegister(lir->temp0()); |
| 16011 | Register temp1 = ToRegister(lir->temp1()); |
| 16012 | |
| 16013 | IonGetIteratorIC ic(liveRegs, val, output, temp0, temp1); |
| 16014 | addIC(lir, allocateIC(ic)); |
| 16015 | } |
| 16016 | |
| 16017 | void CodeGenerator::visitOptimizeSpreadCallCache( |
| 16018 | LOptimizeSpreadCallCache* lir) { |
| 16019 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 16020 | ValueOperand val = ToValue(lir, LOptimizeSpreadCallCache::ValueIndex); |
| 16021 | ValueOperand output = ToOutValue(lir); |
| 16022 | Register temp = ToRegister(lir->temp0()); |
| 16023 | |
| 16024 | IonOptimizeSpreadCallIC ic(liveRegs, val, output, temp); |
| 16025 | addIC(lir, allocateIC(ic)); |
| 16026 | } |
| 16027 | |
| 16028 | void CodeGenerator::visitCloseIterCache(LCloseIterCache* lir) { |
| 16029 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 16030 | Register iter = ToRegister(lir->iter()); |
| 16031 | Register temp = ToRegister(lir->temp0()); |
| 16032 | CompletionKind kind = CompletionKind(lir->mir()->completionKind()); |
| 16033 | |
| 16034 | IonCloseIterIC ic(liveRegs, iter, temp, kind); |
| 16035 | addIC(lir, allocateIC(ic)); |
| 16036 | } |
| 16037 | |
| 16038 | void CodeGenerator::visitOptimizeGetIteratorCache( |
| 16039 | LOptimizeGetIteratorCache* lir) { |
| 16040 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 16041 | ValueOperand val = ToValue(lir, LOptimizeGetIteratorCache::ValueIndex); |
| 16042 | Register output = ToRegister(lir->output()); |
| 16043 | Register temp = ToRegister(lir->temp0()); |
| 16044 | |
| 16045 | IonOptimizeGetIteratorIC ic(liveRegs, val, output, temp); |
| 16046 | addIC(lir, allocateIC(ic)); |
| 16047 | } |
| 16048 | |
| 16049 | void CodeGenerator::visitIteratorMore(LIteratorMore* lir) { |
| 16050 | const Register obj = ToRegister(lir->iterator()); |
| 16051 | const ValueOperand output = ToOutValue(lir); |
| 16052 | const Register temp = ToRegister(lir->temp0()); |
| 16053 | |
| 16054 | masm.iteratorMore(obj, output, temp); |
| 16055 | } |
| 16056 | |
| 16057 | void CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch* lir) { |
| 16058 | ValueOperand input = ToValue(lir, LIsNoIterAndBranch::Input); |
| 16059 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
| 16060 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
| 16061 | |
| 16062 | masm.branchTestMagic(Assembler::Equal, input, ifTrue); |
| 16063 | |
| 16064 | if (!isNextBlock(lir->ifFalse()->lir())) { |
| 16065 | masm.jump(ifFalse); |
| 16066 | } |
| 16067 | } |
| 16068 | |
| 16069 | void CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) { |
| 16070 | const Register obj = ToRegister(lir->object()); |
| 16071 | const Register temp0 = ToRegister(lir->temp0()); |
| 16072 | const Register temp1 = ToRegister(lir->temp1()); |
| 16073 | const Register temp2 = ToRegister(lir->temp2()); |
| 16074 | |
| 16075 | masm.iteratorClose(obj, temp0, temp1, temp2); |
| 16076 | } |
| 16077 | |
| 16078 | void CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) { |
| 16079 | // read number of actual arguments from the JS frame. |
| 16080 | Register argc = ToRegister(lir->output()); |
| 16081 | masm.loadNumActualArgs(FramePointer, argc); |
| 16082 | } |
| 16083 | |
| 16084 | void CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) { |
| 16085 | ValueOperand result = ToOutValue(lir); |
| 16086 | const LAllocation* index = lir->index(); |
| 16087 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
| 16088 | |
| 16089 | // This instruction is used to access actual arguments and formal arguments. |
| 16090 | // The number of Values on the stack is |max(numFormals, numActuals)|, so we |
| 16091 | // assert |index < numFormals || index < numActuals| in debug builds. |
| 16092 | DebugOnly<size_t> numFormals = gen->outerInfo().script()->function()->nargs(); |
| 16093 | |
| 16094 | if (index->isConstant()) { |
| 16095 | int32_t i = index->toConstant()->toInt32(); |
| 16096 | #ifdef DEBUG1 |
| 16097 | if (uint32_t(i) >= numFormals) { |
| 16098 | Label ok; |
| 16099 | Register argc = result.scratchReg(); |
| 16100 | masm.loadNumActualArgs(FramePointer, argc); |
| 16101 | masm.branch32(Assembler::Above, argc, Imm32(i), &ok); |
| 16102 | masm.assumeUnreachable("Invalid argument index"); |
| 16103 | masm.bind(&ok); |
| 16104 | } |
| 16105 | #endif |
| 16106 | Address argPtr(FramePointer, sizeof(Value) * i + argvOffset); |
| 16107 | masm.loadValue(argPtr, result); |
| 16108 | } else { |
| 16109 | Register i = ToRegister(index); |
| 16110 | #ifdef DEBUG1 |
| 16111 | Label ok; |
| 16112 | Register argc = result.scratchReg(); |
| 16113 | masm.branch32(Assembler::Below, i, Imm32(numFormals), &ok); |
| 16114 | masm.loadNumActualArgs(FramePointer, argc); |
| 16115 | masm.branch32(Assembler::Above, argc, i, &ok); |
| 16116 | masm.assumeUnreachable("Invalid argument index"); |
| 16117 | masm.bind(&ok); |
| 16118 | #endif |
| 16119 | BaseValueIndex argPtr(FramePointer, i, argvOffset); |
| 16120 | masm.loadValue(argPtr, result); |
| 16121 | } |
| 16122 | } |
| 16123 | |
| 16124 | void CodeGenerator::visitGetFrameArgumentHole(LGetFrameArgumentHole* lir) { |
| 16125 | ValueOperand result = ToOutValue(lir); |
| 16126 | Register index = ToRegister(lir->index()); |
| 16127 | Register length = ToRegister(lir->length()); |
| 16128 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0()); |
| 16129 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
| 16130 | |
| 16131 | Label outOfBounds, done; |
| 16132 | masm.spectreBoundsCheck32(index, length, spectreTemp, &outOfBounds); |
| 16133 | |
| 16134 | BaseValueIndex argPtr(FramePointer, index, argvOffset); |
| 16135 | masm.loadValue(argPtr, result); |
| 16136 | masm.jump(&done); |
| 16137 | |
| 16138 | masm.bind(&outOfBounds); |
| 16139 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 16140 | masm.moveValue(UndefinedValue(), result); |
| 16141 | |
| 16142 | masm.bind(&done); |
| 16143 | } |
| 16144 | |
| 16145 | void CodeGenerator::visitRest(LRest* lir) { |
| 16146 | Register numActuals = ToRegister(lir->numActuals()); |
| 16147 | Register temp0 = ToRegister(lir->temp0()); |
| 16148 | Register temp1 = ToRegister(lir->temp1()); |
| 16149 | Register temp2 = ToRegister(lir->temp2()); |
| 16150 | Register temp3 = ToRegister(lir->temp3()); |
| 16151 | unsigned numFormals = lir->mir()->numFormals(); |
| 16152 | |
| 16153 | constexpr uint32_t arrayCapacity = 2; |
| 16154 | |
| 16155 | if (Shape* shape = lir->mir()->shape()) { |
| 16156 | uint32_t arrayLength = 0; |
| 16157 | gc::AllocKind allocKind = GuessArrayGCKind(arrayCapacity); |
| 16158 | MOZ_ASSERT(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_))do { static_assert( mozilla::detail::AssertionConditionType< decltype(CanChangeToBackgroundAllocKind(allocKind, &ArrayObject ::class_))>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(CanChangeToBackgroundAllocKind(allocKind , &ArrayObject::class_)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16158); AnnotateMozCrashReason("MOZ_ASSERT" "(" "CanChangeToBackgroundAllocKind(allocKind, &ArrayObject::class_)" ")"); do { *((volatile int*)__null) = 16158; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16159 | allocKind = ForegroundToBackgroundAllocKind(allocKind); |
| 16160 | MOZ_ASSERT(GetGCKindSlots(allocKind) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements ::VALUES_PER_HEADER)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 16161; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 16161 | arrayCapacity + ObjectElements::VALUES_PER_HEADER)do { static_assert( mozilla::detail::AssertionConditionType< decltype(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements ::VALUES_PER_HEADER)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16161); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" ")"); do { *((volatile int*)__null) = 16161; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16162 | |
| 16163 | Label joinAlloc, failAlloc; |
| 16164 | masm.movePtr(ImmGCPtr(shape), temp0); |
| 16165 | masm.createArrayWithFixedElements(temp2, temp0, temp1, InvalidReg, |
| 16166 | arrayLength, arrayCapacity, 0, 0, |
| 16167 | allocKind, gc::Heap::Default, &failAlloc); |
| 16168 | masm.jump(&joinAlloc); |
| 16169 | { |
| 16170 | masm.bind(&failAlloc); |
| 16171 | masm.movePtr(ImmPtr(nullptr), temp2); |
| 16172 | } |
| 16173 | masm.bind(&joinAlloc); |
| 16174 | } else { |
| 16175 | masm.movePtr(ImmPtr(nullptr), temp2); |
| 16176 | } |
| 16177 | |
| 16178 | // Set temp1 to the address of the first actual argument. |
| 16179 | size_t actualsOffset = JitFrameLayout::offsetOfActualArgs(); |
| 16180 | masm.computeEffectiveAddress(Address(FramePointer, actualsOffset), temp1); |
| 16181 | |
| 16182 | // Compute array length: max(numActuals - numFormals, 0). |
| 16183 | Register lengthReg; |
| 16184 | if (numFormals) { |
| 16185 | lengthReg = temp0; |
| 16186 | Label emptyLength, joinLength; |
| 16187 | masm.branch32(Assembler::LessThanOrEqual, numActuals, Imm32(numFormals), |
| 16188 | &emptyLength); |
| 16189 | { |
| 16190 | masm.move32(numActuals, lengthReg); |
| 16191 | masm.sub32(Imm32(numFormals), lengthReg); |
| 16192 | |
| 16193 | // Skip formal arguments. |
| 16194 | masm.addPtr(Imm32(sizeof(Value) * numFormals), temp1); |
| 16195 | |
| 16196 | masm.jump(&joinLength); |
| 16197 | } |
| 16198 | masm.bind(&emptyLength); |
| 16199 | { |
| 16200 | masm.move32(Imm32(0), lengthReg); |
| 16201 | |
| 16202 | // Leave temp1 pointed to the start of actuals() when the rest-array |
| 16203 | // length is zero. We don't use |actuals() + numFormals| because |
| 16204 | // |numFormals| can be any non-negative int32 value when this MRest was |
| 16205 | // created from scalar replacement optimizations. And it seems |
| 16206 | // questionable to compute a Value* pointer which points to who knows |
| 16207 | // where. |
| 16208 | } |
| 16209 | masm.bind(&joinLength); |
| 16210 | } else { |
| 16211 | // Use numActuals directly when there are no formals. |
| 16212 | lengthReg = numActuals; |
| 16213 | } |
| 16214 | |
| 16215 | // Try to initialize the array elements. |
| 16216 | Label vmCall, done; |
| 16217 | if (lir->mir()->shape()) { |
| 16218 | // Call into C++ if we failed to allocate an array or there are more than |
| 16219 | // |arrayCapacity| elements. |
| 16220 | masm.branchTestPtr(Assembler::Zero, temp2, temp2, &vmCall); |
| 16221 | masm.branch32(Assembler::Above, lengthReg, Imm32(arrayCapacity), &vmCall); |
| 16222 | |
| 16223 | // The array must be nursery allocated so no post barrier is needed. |
| 16224 | #ifdef DEBUG1 |
| 16225 | Label ok; |
| 16226 | masm.branchPtrInNurseryChunk(Assembler::Equal, temp2, temp3, &ok); |
| 16227 | masm.assumeUnreachable("Unexpected tenured object for LRest"); |
| 16228 | masm.bind(&ok); |
| 16229 | #endif |
| 16230 | |
| 16231 | Label initialized; |
| 16232 | masm.branch32(Assembler::Equal, lengthReg, Imm32(0), &initialized); |
| 16233 | |
| 16234 | // Store length and initializedLength. |
| 16235 | Register elements = temp3; |
| 16236 | masm.loadPtr(Address(temp2, NativeObject::offsetOfElements()), elements); |
| 16237 | Address lengthAddr(elements, ObjectElements::offsetOfLength()); |
| 16238 | Address initLengthAddr(elements, |
| 16239 | ObjectElements::offsetOfInitializedLength()); |
| 16240 | masm.store32(lengthReg, lengthAddr); |
| 16241 | masm.store32(lengthReg, initLengthAddr); |
| 16242 | |
| 16243 | // Store either one or two elements. This may clobber lengthReg (temp0). |
| 16244 | static_assert(arrayCapacity == 2, "code handles 1 or 2 elements"); |
| 16245 | Label storeFirst; |
| 16246 | masm.branch32(Assembler::Equal, lengthReg, Imm32(1), &storeFirst); |
| 16247 | masm.storeValue(Address(temp1, sizeof(Value)), |
| 16248 | Address(elements, sizeof(Value)), temp0); |
| 16249 | masm.bind(&storeFirst); |
| 16250 | masm.storeValue(Address(temp1, 0), Address(elements, 0), temp0); |
| 16251 | |
| 16252 | // Done. |
| 16253 | masm.bind(&initialized); |
| 16254 | masm.movePtr(temp2, ReturnReg); |
| 16255 | masm.jump(&done); |
| 16256 | } |
| 16257 | |
| 16258 | masm.bind(&vmCall); |
| 16259 | |
| 16260 | pushArg(temp2); |
| 16261 | pushArg(temp1); |
| 16262 | pushArg(lengthReg); |
| 16263 | |
| 16264 | using Fn = |
| 16265 | ArrayObject* (*)(JSContext*, uint32_t, Value*, Handle<ArrayObject*>); |
| 16266 | callVM<Fn, InitRestParameter>(lir); |
| 16267 | |
| 16268 | masm.bind(&done); |
| 16269 | } |
| 16270 | |
| 16271 | // Create a stackmap from the given safepoint, with the structure: |
| 16272 | // |
| 16273 | // <reg dump, if any> |
| 16274 | // | ++ <body (general spill)> |
| 16275 | // | | ++ <space for Frame> |
| 16276 | // | | ++ <inbound args> |
| 16277 | // | | | |
| 16278 | // Lowest Addr Highest Addr |
| 16279 | // | |
| 16280 | // framePushedAtStackMapBase |
| 16281 | // |
| 16282 | // The caller owns the resulting stackmap. This assumes a grow-down stack. |
| 16283 | // |
| 16284 | // For non-debug builds, if the stackmap would contain no pointers, no |
| 16285 | // stackmap is created, and nullptr is returned. For a debug build, a |
| 16286 | // stackmap is always created and returned. |
| 16287 | // |
| 16288 | // Depending on the type of safepoint, the stackmap may need to account for |
| 16289 | // spilled registers. WasmSafepointKind::LirCall corresponds to LIR nodes where |
| 16290 | // isCall() == true, for which the register allocator will spill/restore all |
| 16291 | // live registers at the LIR level - in this case, the LSafepoint sees only live |
| 16292 | // values on the stack, never in registers. WasmSafepointKind::CodegenCall, on |
| 16293 | // the other hand, is for LIR nodes which may manually spill/restore live |
| 16294 | // registers in codegen, in which case the stackmap must account for this. Traps |
| 16295 | // also require tracking of live registers, but spilling is handled by the trap |
| 16296 | // mechanism. |
| 16297 | static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint, |
| 16298 | const RegisterOffsets& trapExitLayout, |
| 16299 | size_t trapExitLayoutNumWords, |
| 16300 | size_t nInboundStackArgBytes, |
| 16301 | wasm::StackMap** result) { |
| 16302 | // Ensure this is defined on all return paths. |
| 16303 | *result = nullptr; |
| 16304 | |
| 16305 | // The size of the wasm::Frame itself. |
| 16306 | const size_t nFrameBytes = sizeof(wasm::Frame); |
| 16307 | |
| 16308 | // This is the number of bytes spilled for live registers, outside of a trap. |
| 16309 | // For traps, trapExitLayout and trapExitLayoutNumWords will be used. |
| 16310 | const size_t nRegisterDumpBytes = |
| 16311 | MacroAssembler::PushRegsInMaskSizeInBytes(safepoint.liveRegs()); |
| 16312 | |
| 16313 | // As mentioned above, for WasmSafepointKind::LirCall, register spills and |
| 16314 | // restores are handled at the LIR level and there should therefore be no live |
| 16315 | // registers to handle here. |
| 16316 | MOZ_ASSERT_IF(safepoint.wasmSafepointKind() == WasmSafepointKind::LirCall,do { if (safepoint.wasmSafepointKind() == WasmSafepointKind:: LirCall) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(nRegisterDumpBytes == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(nRegisterDumpBytes == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { *((volatile int*)__null) = 16317; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 16317 | nRegisterDumpBytes == 0)do { if (safepoint.wasmSafepointKind() == WasmSafepointKind:: LirCall) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(nRegisterDumpBytes == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(nRegisterDumpBytes == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { *((volatile int*)__null) = 16317; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 16318 | MOZ_ASSERT(nRegisterDumpBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nRegisterDumpBytes % sizeof(void*) == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nRegisterDumpBytes % sizeof(void*) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nRegisterDumpBytes % sizeof(void*) == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16318); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nRegisterDumpBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16318; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16319 | |
| 16320 | // This is the number of bytes in the general spill area, below the Frame. |
| 16321 | const size_t nBodyBytes = safepoint.framePushedAtStackMapBase(); |
| 16322 | |
| 16323 | // The stack map owns any alignment padding around inbound stack args. |
| 16324 | const size_t nInboundStackArgBytesAligned = |
| 16325 | wasm::AlignStackArgAreaSize(nInboundStackArgBytes); |
| 16326 | |
| 16327 | // This is the number of bytes in the general spill area, the Frame, and the |
| 16328 | // incoming args, but not including any register dump area. |
| 16329 | const size_t nNonRegisterBytes = |
| 16330 | nBodyBytes + nFrameBytes + nInboundStackArgBytesAligned; |
| 16331 | MOZ_ASSERT(nNonRegisterBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(nNonRegisterBytes % sizeof(void*) == 0)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(nNonRegisterBytes % sizeof(void*) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("nNonRegisterBytes % sizeof(void*) == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16331); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nNonRegisterBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16331; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16332 | |
| 16333 | // This is the number of bytes in the register dump area, if any, below the |
| 16334 | // general spill area. |
| 16335 | const size_t nRegisterBytes = |
| 16336 | (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) |
| 16337 | ? (trapExitLayoutNumWords * sizeof(void*)) |
| 16338 | : nRegisterDumpBytes; |
| 16339 | |
| 16340 | // This is the total number of bytes covered by the map. |
| 16341 | const size_t nTotalBytes = nNonRegisterBytes + nRegisterBytes; |
| 16342 | |
| 16343 | #ifndef DEBUG1 |
| 16344 | bool needStackMap = !(safepoint.wasmAnyRefRegs().empty() && |
| 16345 | safepoint.wasmAnyRefSlots().empty() && |
| 16346 | safepoint.slotsOrElementsSlots().empty()); |
| 16347 | |
| 16348 | // There are no references, and this is a non-debug build, so don't bother |
| 16349 | // building the stackmap. |
| 16350 | if (!needStackMap) { |
| 16351 | return true; |
| 16352 | } |
| 16353 | #endif |
| 16354 | |
| 16355 | wasm::StackMap* stackMap = |
| 16356 | wasm::StackMap::create(nTotalBytes / sizeof(void*)); |
| 16357 | if (!stackMap) { |
| 16358 | return false; |
| 16359 | } |
| 16360 | if (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) { |
| 16361 | stackMap->setExitStubWords(trapExitLayoutNumWords); |
| 16362 | } |
| 16363 | |
| 16364 | // REG DUMP AREA, if any. |
| 16365 | size_t regDumpWords = 0; |
| 16366 | const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs(); |
| 16367 | const LiveGeneralRegisterSet slotsOrElementsRegs = |
| 16368 | safepoint.slotsOrElementsRegs(); |
| 16369 | const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union( |
| 16370 | wasmAnyRefRegs.set(), slotsOrElementsRegs.set())); |
| 16371 | GeneralRegisterForwardIterator refRegsIter(refRegs); |
| 16372 | switch (safepoint.wasmSafepointKind()) { |
| 16373 | case WasmSafepointKind::LirCall: |
| 16374 | case WasmSafepointKind::StackSwitch: |
| 16375 | case WasmSafepointKind::CodegenCall: { |
| 16376 | size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*); |
| 16377 | regDumpWords += spilledNumWords; |
| 16378 | |
| 16379 | for (; refRegsIter.more(); ++refRegsIter) { |
| 16380 | Register reg = *refRegsIter; |
| 16381 | size_t offsetFromSpillBase = |
| 16382 | safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) / |
| 16383 | sizeof(void*); |
| 16384 | MOZ_ASSERT(0 < offsetFromSpillBase &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16385); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" ")"); do { *((volatile int*)__null) = 16385; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 16385 | offsetFromSpillBase <= spilledNumWords)do { static_assert( mozilla::detail::AssertionConditionType< decltype(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16385); AnnotateMozCrashReason("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" ")"); do { *((volatile int*)__null) = 16385; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16386 | size_t index = spilledNumWords - offsetFromSpillBase; |
| 16387 | |
| 16388 | if (wasmAnyRefRegs.has(reg)) { |
| 16389 | stackMap->set(index, wasm::StackMap::AnyRef); |
| 16390 | } else { |
| 16391 | MOZ_ASSERT(slotsOrElementsRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotsOrElementsRegs.has(reg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slotsOrElementsRegs.has(reg) ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "slotsOrElementsRegs.has(reg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16391); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)" ")"); do { *((volatile int*)__null) = 16391; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16392 | stackMap->set(index, wasm::StackMap::ArrayDataPointer); |
| 16393 | } |
| 16394 | } |
| 16395 | // Float and vector registers do not have to be handled; they cannot |
| 16396 | // contain wasm anyrefs, and they are spilled after general-purpose |
| 16397 | // registers. Gprs are therefore closest to the spill base and thus their |
| 16398 | // offset calculation does not need to account for other spills. |
| 16399 | } break; |
| 16400 | case WasmSafepointKind::Trap: { |
| 16401 | regDumpWords += trapExitLayoutNumWords; |
| 16402 | |
| 16403 | for (; refRegsIter.more(); ++refRegsIter) { |
| 16404 | Register reg = *refRegsIter; |
| 16405 | size_t offsetFromTop = trapExitLayout.getOffset(reg); |
| 16406 | |
| 16407 | // If this doesn't hold, the associated register wasn't saved by |
| 16408 | // the trap exit stub. Better to crash now than much later, in |
| 16409 | // some obscure place, and possibly with security consequences. |
| 16410 | MOZ_RELEASE_ASSERT(offsetFromTop < trapExitLayoutNumWords)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetFromTop < trapExitLayoutNumWords)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(offsetFromTop < trapExitLayoutNumWords))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("offsetFromTop < trapExitLayoutNumWords" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16410); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "offsetFromTop < trapExitLayoutNumWords" ")"); do { *((volatile int*)__null) = 16410; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16411 | |
| 16412 | // offsetFromTop is an offset in words down from the highest |
| 16413 | // address in the exit stub save area. Switch it around to be an |
| 16414 | // offset up from the bottom of the (integer register) save area. |
| 16415 | size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop; |
| 16416 | |
| 16417 | if (wasmAnyRefRegs.has(reg)) { |
| 16418 | stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef); |
| 16419 | } else { |
| 16420 | MOZ_ASSERT(slotsOrElementsRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(slotsOrElementsRegs.has(reg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slotsOrElementsRegs.has(reg) ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "slotsOrElementsRegs.has(reg)", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16420); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slotsOrElementsRegs.has(reg)" ")"); do { *((volatile int*)__null) = 16420; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16421 | stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer); |
| 16422 | } |
| 16423 | } |
| 16424 | } break; |
| 16425 | default: |
| 16426 | MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16426); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")" ); do { *((volatile int*)__null) = 16426; __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 16427 | } |
| 16428 | |
| 16429 | // Ensure other reg/slot collections on LSafepoint are empty. |
| 16430 | MOZ_ASSERT(safepoint.gcRegs().empty() && safepoint.gcSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.gcRegs().empty() && safepoint.gcSlots ().empty())>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(safepoint.gcRegs().empty() && safepoint.gcSlots().empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("safepoint.gcRegs().empty() && safepoint.gcSlots().empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16430); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.gcRegs().empty() && safepoint.gcSlots().empty()" ")"); do { *((volatile int*)__null) = 16430; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16431 | #ifdef JS_NUNBOX32 |
| 16432 | MOZ_ASSERT(safepoint.nunboxParts().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.nunboxParts().empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(safepoint.nunboxParts().empty ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("safepoint.nunboxParts().empty()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16432); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.nunboxParts().empty()" ")"); do { *((volatile int*)__null) = 16432; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16433 | #elif JS_PUNBOX641 |
| 16434 | MOZ_ASSERT(safepoint.valueRegs().empty() && safepoint.valueSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.valueRegs().empty() && safepoint.valueSlots ().empty())>::isValid, "invalid assertion condition"); if ( (__builtin_expect(!!(!(!!(safepoint.valueRegs().empty() && safepoint.valueSlots().empty()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("safepoint.valueRegs().empty() && safepoint.valueSlots().empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16434); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.valueRegs().empty() && safepoint.valueSlots().empty()" ")"); do { *((volatile int*)__null) = 16434; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16435 | #endif |
| 16436 | |
| 16437 | // BODY (GENERAL SPILL) AREA and FRAME and INCOMING ARGS |
| 16438 | // Deal with roots on the stack. |
| 16439 | const LSafepoint::SlotList& wasmAnyRefSlots = safepoint.wasmAnyRefSlots(); |
| 16440 | for (SafepointSlotEntry wasmAnyRefSlot : wasmAnyRefSlots) { |
| 16441 | // The following needs to correspond with JitFrameLayout::slotRef |
| 16442 | // wasmAnyRefSlot.stack == 0 means the slot is in the args area |
| 16443 | if (wasmAnyRefSlot.stack) { |
| 16444 | // It's a slot in the body allocation, so .slot is interpreted |
| 16445 | // as an index downwards from the Frame* |
| 16446 | MOZ_ASSERT(wasmAnyRefSlot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasmAnyRefSlot.slot <= nBodyBytes)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wasmAnyRefSlot.slot <= nBodyBytes ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "wasmAnyRefSlot.slot <= nBodyBytes", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16446); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot <= nBodyBytes" ")"); do { *((volatile int*)__null) = 16446; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16447 | uint32_t offsetInBytes = nBodyBytes - wasmAnyRefSlot.slot; |
| 16448 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16448; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16449 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
| 16450 | wasm::StackMap::AnyRef); |
| 16451 | } else { |
| 16452 | // It's an argument slot |
| 16453 | MOZ_ASSERT(wasmAnyRefSlot.slot < nInboundStackArgBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasmAnyRefSlot.slot < nInboundStackArgBytes)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(wasmAnyRefSlot.slot < nInboundStackArgBytes))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("wasmAnyRefSlot.slot < nInboundStackArgBytes" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16453); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot < nInboundStackArgBytes" ")"); do { *((volatile int*)__null) = 16453; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16454 | uint32_t offsetInBytes = nBodyBytes + nFrameBytes + wasmAnyRefSlot.slot; |
| 16455 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16456 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
| 16457 | wasm::StackMap::AnyRef); |
| 16458 | } |
| 16459 | } |
| 16460 | |
| 16461 | // Track array data pointers on the stack |
| 16462 | const LSafepoint::SlotList& slots = safepoint.slotsOrElementsSlots(); |
| 16463 | for (SafepointSlotEntry slot : slots) { |
| 16464 | MOZ_ASSERT(slot.stack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot.stack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot.stack))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot.stack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16464); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack" ")"); do { *((volatile int*)__null) = 16464; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16465 | |
| 16466 | // It's a slot in the body allocation, so .slot is interpreted |
| 16467 | // as an index downwards from the Frame* |
| 16468 | MOZ_ASSERT(slot.slot <= nBodyBytes)do { static_assert( mozilla::detail::AssertionConditionType< decltype(slot.slot <= nBodyBytes)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(slot.slot <= nBodyBytes)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("slot.slot <= nBodyBytes" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16468); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { *((volatile int*)__null) = 16468; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16469 | uint32_t offsetInBytes = nBodyBytes - slot.slot; |
| 16470 | MOZ_ASSERT(offsetInBytes % sizeof(void*) == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(offsetInBytes % sizeof(void*) == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(offsetInBytes % sizeof(void* ) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("offsetInBytes % sizeof(void*) == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { *((volatile int*)__null) = 16470; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16471 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
| 16472 | wasm::StackMap::Kind::ArrayDataPointer); |
| 16473 | } |
| 16474 | |
| 16475 | // Record in the map, how far down from the highest address the Frame* is. |
| 16476 | // Take the opportunity to check that we haven't marked any part of the |
| 16477 | // Frame itself as a pointer. |
| 16478 | stackMap->setFrameOffsetFromTop((nInboundStackArgBytesAligned + nFrameBytes) / |
| 16479 | sizeof(void*)); |
| 16480 | #ifdef DEBUG1 |
| 16481 | for (uint32_t i = 0; i < nFrameBytes / sizeof(void*); i++) { |
| 16482 | MOZ_ASSERT(stackMap->get(stackMap->header.numMappedWords -do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16484; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 16483 | stackMap->header.frameOffsetFromTop + i) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16484; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 16484 | wasm::StackMap::Kind::POD)do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap ::Kind::POD)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(stackMap->get(stackMap->header .numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD))), 0))) { do { } while (false) ; MOZ_ReportAssertionFailure("stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { *((volatile int*)__null) = 16484; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16485 | } |
| 16486 | #endif |
| 16487 | |
| 16488 | *result = stackMap; |
| 16489 | return true; |
| 16490 | } |
| 16491 | |
| 16492 | bool CodeGenerator::generateWasm(wasm::CallIndirectId callIndirectId, |
| 16493 | const wasm::TrapSiteDesc& entryTrapSiteDesc, |
| 16494 | const wasm::ArgTypeVector& argTypes, |
| 16495 | const RegisterOffsets& trapExitLayout, |
| 16496 | size_t trapExitLayoutNumWords, |
| 16497 | wasm::FuncOffsets* offsets, |
| 16498 | wasm::StackMaps* stackMaps, |
| 16499 | wasm::Decoder* decoder) { |
| 16500 | AutoCreatedBy acb(masm, "CodeGenerator::generateWasm"); |
| 16501 | |
| 16502 | JitSpew(JitSpew_Codegen, "# Emitting wasm code"); |
| 16503 | |
| 16504 | size_t nInboundStackArgBytes = StackArgAreaSizeUnaligned(argTypes); |
| 16505 | inboundStackArgBytes_ = nInboundStackArgBytes; |
| 16506 | |
| 16507 | wasm::GenerateFunctionPrologue(masm, callIndirectId, mozilla::Nothing(), |
| 16508 | offsets); |
| 16509 | |
| 16510 | MOZ_ASSERT(masm.framePushed() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("masm.framePushed() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16510); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { *((volatile int*)__null) = 16510; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16511 | |
| 16512 | // Very large frames are implausible, probably an attack. |
| 16513 | if (frameSize() > wasm::MaxFrameSize) { |
| 16514 | return decoder->fail(decoder->beginOffset(), "stack frame is too large"); |
| 16515 | } |
| 16516 | |
| 16517 | if (omitOverRecursedCheck()) { |
| 16518 | masm.reserveStack(frameSize()); |
| 16519 | } else { |
| 16520 | std::pair<CodeOffset, uint32_t> pair = |
| 16521 | masm.wasmReserveStackChecked(frameSize(), entryTrapSiteDesc); |
| 16522 | CodeOffset trapInsnOffset = pair.first; |
| 16523 | size_t nBytesReservedBeforeTrap = pair.second; |
| 16524 | |
| 16525 | wasm::StackMap* functionEntryStackMap = nullptr; |
| 16526 | if (!CreateStackMapForFunctionEntryTrap( |
| 16527 | argTypes, trapExitLayout, trapExitLayoutNumWords, |
| 16528 | nBytesReservedBeforeTrap, nInboundStackArgBytes, |
| 16529 | &functionEntryStackMap)) { |
| 16530 | return false; |
| 16531 | } |
| 16532 | |
| 16533 | // In debug builds, we'll always have a stack map, even if there are no |
| 16534 | // refs to track. |
| 16535 | MOZ_ASSERT(functionEntryStackMap)do { static_assert( mozilla::detail::AssertionConditionType< decltype(functionEntryStackMap)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(functionEntryStackMap))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("functionEntryStackMap" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "functionEntryStackMap" ")"); do { *((volatile int*)__null) = 16535; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16536 | |
| 16537 | if (functionEntryStackMap && |
| 16538 | !stackMaps->add((uint8_t*)(uintptr_t)trapInsnOffset.offset(), |
| 16539 | functionEntryStackMap)) { |
| 16540 | functionEntryStackMap->destroy(); |
| 16541 | return false; |
| 16542 | } |
| 16543 | } |
| 16544 | |
| 16545 | MOZ_ASSERT(masm.framePushed() == frameSize())do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == frameSize())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == frameSize ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("masm.framePushed() == frameSize()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16545); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { *((volatile int*)__null) = 16545; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16546 | |
| 16547 | if (!generateBody()) { |
| 16548 | return false; |
| 16549 | } |
| 16550 | |
| 16551 | masm.bind(&returnLabel_); |
| 16552 | wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets); |
| 16553 | |
| 16554 | if (!generateOutOfLineCode()) { |
| 16555 | return false; |
| 16556 | } |
| 16557 | |
| 16558 | masm.flush(); |
| 16559 | if (masm.oom()) { |
| 16560 | return false; |
| 16561 | } |
| 16562 | |
| 16563 | offsets->end = masm.currentOffset(); |
| 16564 | |
| 16565 | MOZ_ASSERT(!masm.failureLabel()->used())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!masm.failureLabel()->used())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!masm.failureLabel()->used ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!masm.failureLabel()->used()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16565); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!masm.failureLabel()->used()" ")"); do { *((volatile int*)__null) = 16565; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16566 | MOZ_ASSERT(snapshots_.listSize() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(snapshots_.listSize() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(snapshots_.listSize() == 0)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("snapshots_.listSize() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.listSize() == 0" ")"); do { *((volatile int*)__null) = 16566; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16567 | MOZ_ASSERT(snapshots_.RVATableSize() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(snapshots_.RVATableSize() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(snapshots_.RVATableSize() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("snapshots_.RVATableSize() == 0", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.RVATableSize() == 0" ")"); do { *((volatile int*)__null) = 16567; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16568 | MOZ_ASSERT(recovers_.size() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(recovers_.size() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(recovers_.size() == 0))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("recovers_.size() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size() == 0" ")"); do { *((volatile int*)__null) = 16568; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16569 | MOZ_ASSERT(graph.numConstants() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(graph.numConstants() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(graph.numConstants() == 0))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("graph.numConstants() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16569); AnnotateMozCrashReason("MOZ_ASSERT" "(" "graph.numConstants() == 0" ")"); do { *((volatile int*)__null) = 16569; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16570 | MOZ_ASSERT(osiIndices_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(osiIndices_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(osiIndices_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("osiIndices_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osiIndices_.empty()" ")"); do { *((volatile int*)__null) = 16570; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16571 | MOZ_ASSERT(icList_.empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(icList_.empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(icList_.empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("icList_.empty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16571); AnnotateMozCrashReason("MOZ_ASSERT" "(" "icList_.empty()" ")"); do { *((volatile int*)__null) = 16571; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16572 | MOZ_ASSERT(safepoints_.size() == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoints_.size() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(safepoints_.size() == 0))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("safepoints_.size() == 0" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16572); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoints_.size() == 0" ")"); do { *((volatile int*)__null) = 16572; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16573 | MOZ_ASSERT(!scriptCounts_)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!scriptCounts_)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!scriptCounts_))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!scriptCounts_" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16573); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!scriptCounts_" ")"); do { *((volatile int*)__null) = 16573; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16574 | |
| 16575 | // Convert the safepoints to stackmaps and add them to our running |
| 16576 | // collection thereof. |
| 16577 | for (CodegenSafepointIndex& index : safepointIndices_) { |
| 16578 | wasm::StackMap* stackMap = nullptr; |
| 16579 | if (!CreateStackMapFromLSafepoint(*index.safepoint(), trapExitLayout, |
| 16580 | trapExitLayoutNumWords, |
| 16581 | nInboundStackArgBytes, &stackMap)) { |
| 16582 | return false; |
| 16583 | } |
| 16584 | |
| 16585 | // In debug builds, we'll always have a stack map. |
| 16586 | MOZ_ASSERT(stackMap)do { static_assert( mozilla::detail::AssertionConditionType< decltype(stackMap)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(stackMap))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("stackMap", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16586); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap" ")" ); do { *((volatile int*)__null) = 16586; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 16587 | if (!stackMap) { |
| 16588 | continue; |
| 16589 | } |
| 16590 | |
| 16591 | if (!stackMaps->add((uint8_t*)(uintptr_t)index.displacement(), stackMap)) { |
| 16592 | stackMap->destroy(); |
| 16593 | return false; |
| 16594 | } |
| 16595 | } |
| 16596 | |
| 16597 | return true; |
| 16598 | } |
| 16599 | |
| 16600 | bool CodeGenerator::generate(const WarpSnapshot* snapshot) { |
| 16601 | AutoCreatedBy acb(masm, "CodeGenerator::generate"); |
| 16602 | |
| 16603 | MOZ_ASSERT(snapshot)do { static_assert( mozilla::detail::AssertionConditionType< decltype(snapshot)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(snapshot))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("snapshot", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshot" ")" ); do { *((volatile int*)__null) = 16603; __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 16604 | snapshot_ = snapshot; |
| 16605 | |
| 16606 | JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%u:%u", |
| 16607 | gen->outerInfo().script()->filename(), |
| 16608 | gen->outerInfo().script()->lineno(), |
| 16609 | gen->outerInfo().script()->column().oneOriginValue()); |
| 16610 | |
| 16611 | // Initialize native code table with an entry to the start of |
| 16612 | // top-level script. |
| 16613 | InlineScriptTree* tree = gen->outerInfo().inlineScriptTree(); |
| 16614 | jsbytecode* startPC = tree->script()->code(); |
| 16615 | BytecodeSite* startSite = new (gen->alloc()) BytecodeSite(tree, startPC); |
| 16616 | if (!addNativeToBytecodeEntry(startSite)) { |
| 16617 | return false; |
| 16618 | } |
| 16619 | |
| 16620 | if (!safepoints_.init(gen->alloc())) { |
| 16621 | return false; |
| 16622 | } |
| 16623 | |
| 16624 | size_t maxSafepointIndices = |
| 16625 | graph.numSafepoints() + graph.extraSafepointUses(); |
| 16626 | if (!safepointIndices_.reserve(maxSafepointIndices)) { |
| 16627 | return false; |
| 16628 | } |
| 16629 | if (!osiIndices_.reserve(graph.numSafepoints())) { |
| 16630 | return false; |
| 16631 | } |
| 16632 | |
| 16633 | perfSpewer_.recordOffset(masm, "Prologue"); |
| 16634 | if (!generatePrologue()) { |
| 16635 | return false; |
| 16636 | } |
| 16637 | |
| 16638 | // Reset native => bytecode map table with top-level script and startPc. |
| 16639 | if (!addNativeToBytecodeEntry(startSite)) { |
| 16640 | return false; |
| 16641 | } |
| 16642 | |
| 16643 | if (!generateBody()) { |
| 16644 | return false; |
| 16645 | } |
| 16646 | |
| 16647 | // Reset native => bytecode map table with top-level script and startPc. |
| 16648 | if (!addNativeToBytecodeEntry(startSite)) { |
| 16649 | return false; |
| 16650 | } |
| 16651 | |
| 16652 | perfSpewer_.recordOffset(masm, "Epilogue"); |
| 16653 | if (!generateEpilogue()) { |
| 16654 | return false; |
| 16655 | } |
| 16656 | |
| 16657 | // Reset native => bytecode map table with top-level script and startPc. |
| 16658 | if (!addNativeToBytecodeEntry(startSite)) { |
| 16659 | return false; |
| 16660 | } |
| 16661 | |
| 16662 | perfSpewer_.recordOffset(masm, "InvalidateEpilogue"); |
| 16663 | generateInvalidateEpilogue(); |
| 16664 | |
| 16665 | // native => bytecode entries for OOL code will be added |
| 16666 | // by CodeGeneratorShared::generateOutOfLineCode |
| 16667 | perfSpewer_.recordOffset(masm, "OOLCode"); |
| 16668 | if (!generateOutOfLineCode()) { |
| 16669 | return false; |
| 16670 | } |
| 16671 | |
| 16672 | // Add terminal entry. |
| 16673 | if (!addNativeToBytecodeEntry(startSite)) { |
| 16674 | return false; |
| 16675 | } |
| 16676 | |
| 16677 | // Dump Native to bytecode entries to spew. |
| 16678 | dumpNativeToBytecodeEntries(); |
| 16679 | |
| 16680 | // We encode safepoints after the OSI-point offsets have been determined. |
| 16681 | if (!encodeSafepoints()) { |
| 16682 | return false; |
| 16683 | } |
| 16684 | |
| 16685 | // If this assertion trips, then you have multiple things to do: |
| 16686 | // |
| 16687 | // This assertion will report if a safepoint is used multiple times for the |
| 16688 | // same instruction. To fix this assertion make sure to call |
| 16689 | // `lirGraph_.addExtraSafepointUses(..);` in the Lowering phase. |
| 16690 | // |
| 16691 | // However, this non-worrying issue might hide a more dramatic security issue, |
| 16692 | // which is that having multiple encoding of a safepoint in a single LIR |
| 16693 | // instruction is not safe, unless: |
| 16694 | // |
| 16695 | // - The multiple uses of the safepoints are in different code path. i-e |
| 16696 | // there should be not single execution trace making use of multiple |
| 16697 | // calls within a single instruction. |
| 16698 | // |
| 16699 | // - There is enough space to encode data in-place of the call instruction. |
| 16700 | // Such that a patched-call site does not corrupt the code path on another |
| 16701 | // execution trace. |
| 16702 | // |
| 16703 | // This issue is caused by the way invalidation works, to keep the code alive |
| 16704 | // when invalidated code is only referenced by the stack. This works by |
| 16705 | // storing data in-place of the calling code, which thus becomes unsafe to |
| 16706 | // execute. |
| 16707 | MOZ_ASSERT(safepointIndices_.length() <= maxSafepointIndices)do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepointIndices_.length() <= maxSafepointIndices )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(safepointIndices_.length() <= maxSafepointIndices ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "safepointIndices_.length() <= maxSafepointIndices", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16707); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepointIndices_.length() <= maxSafepointIndices" ")"); do { *((volatile int*)__null) = 16707; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16708 | |
| 16709 | // For each instruction with a safepoint, we have an OSI point inserted after |
| 16710 | // which handles bailouts in case of invalidation of the code. |
| 16711 | MOZ_ASSERT(osiIndices_.length() == graph.numSafepoints())do { static_assert( mozilla::detail::AssertionConditionType< decltype(osiIndices_.length() == graph.numSafepoints())>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(osiIndices_.length() == graph.numSafepoints()))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("osiIndices_.length() == graph.numSafepoints()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osiIndices_.length() == graph.numSafepoints()" ")"); do { *((volatile int*)__null) = 16711; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16712 | |
| 16713 | return !masm.oom(); |
| 16714 | } |
| 16715 | |
| 16716 | static bool AddInlinedCompilations(JSContext* cx, HandleScript script, |
| 16717 | IonCompilationId compilationId, |
| 16718 | const WarpSnapshot* snapshot, |
| 16719 | bool* isValid) { |
| 16720 | MOZ_ASSERT(!*isValid)do { static_assert( mozilla::detail::AssertionConditionType< decltype(!*isValid)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!*isValid))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!*isValid", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16720); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*isValid" ")"); do { *((volatile int*)__null) = 16720; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16721 | RecompileInfo recompileInfo(script, compilationId); |
| 16722 | |
| 16723 | JitZone* jitZone = cx->zone()->jitZone(); |
| 16724 | |
| 16725 | for (const auto* scriptSnapshot : snapshot->scripts()) { |
| 16726 | JSScript* inlinedScript = scriptSnapshot->script(); |
| 16727 | if (inlinedScript == script) { |
| 16728 | continue; |
| 16729 | } |
| 16730 | |
| 16731 | // TODO(post-Warp): This matches FinishCompilation and is necessary to |
| 16732 | // ensure in-progress compilations are canceled when an inlined functon |
| 16733 | // becomes a debuggee. See the breakpoint-14.js jit-test. |
| 16734 | // When TI is gone, try to clean this up by moving AddInlinedCompilations to |
| 16735 | // WarpOracle so that we can handle this as part of addPendingRecompile |
| 16736 | // instead of requiring this separate check. |
| 16737 | if (inlinedScript->isDebuggee()) { |
| 16738 | *isValid = false; |
| 16739 | return true; |
| 16740 | } |
| 16741 | |
| 16742 | if (!jitZone->addInlinedCompilation(recompileInfo, inlinedScript)) { |
| 16743 | return false; |
| 16744 | } |
| 16745 | } |
| 16746 | |
| 16747 | *isValid = true; |
| 16748 | return true; |
| 16749 | } |
| 16750 | |
| 16751 | struct EmulatesUndefinedDependency final : public CompilationDependency { |
| 16752 | explicit EmulatesUndefinedDependency() |
| 16753 | : CompilationDependency(CompilationDependency::Type::EmulatesUndefined) { |
| 16754 | }; |
| 16755 | |
| 16756 | virtual bool operator==(CompilationDependency& dep) { |
| 16757 | // Since the emulates undefined fuse is runtime wide, they are all equal |
| 16758 | return dep.type == type; |
| 16759 | } |
| 16760 | |
| 16761 | virtual bool checkDependency(JSContext* cx) { |
| 16762 | return cx->runtime()->hasSeenObjectEmulateUndefinedFuse.ref().intact(); |
| 16763 | } |
| 16764 | |
| 16765 | virtual bool registerDependency(JSContext* cx, HandleScript script) { |
| 16766 | MOZ_ASSERT(checkDependency(cx))do { static_assert( mozilla::detail::AssertionConditionType< decltype(checkDependency(cx))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(checkDependency(cx)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("checkDependency(cx)" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16766); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checkDependency(cx)" ")"); do { *((volatile int*)__null) = 16766; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16767 | return cx->runtime() |
| 16768 | ->hasSeenObjectEmulateUndefinedFuse.ref() |
| 16769 | .addFuseDependency(cx, script); |
| 16770 | } |
| 16771 | |
| 16772 | virtual UniquePtr<CompilationDependency> clone() { |
| 16773 | return MakeUnique<EmulatesUndefinedDependency>(); |
| 16774 | } |
| 16775 | }; |
| 16776 | |
| 16777 | bool CodeGenerator::addHasSeenObjectEmulateUndefinedFuseDependency() { |
| 16778 | EmulatesUndefinedDependency dep; |
| 16779 | return mirGen().tracker.addDependency(dep); |
| 16780 | } |
| 16781 | |
| 16782 | bool CodeGenerator::link(JSContext* cx) { |
| 16783 | AutoCreatedBy acb(masm, "CodeGenerator::link"); |
| 16784 | |
| 16785 | // We cancel off-thread Ion compilations in a few places during GC, but if |
| 16786 | // this compilation was performed off-thread it will already have been |
| 16787 | // removed from the relevant lists by this point. Don't allow GC here. |
| 16788 | JS::AutoAssertNoGC nogc(cx); |
| 16789 | |
| 16790 | RootedScript script(cx, gen->outerInfo().script()); |
| 16791 | MOZ_ASSERT(!script->hasIonScript())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!script->hasIonScript())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!script->hasIonScript())) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!script->hasIonScript()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16791); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!script->hasIonScript()" ")"); do { *((volatile int*)__null) = 16791; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16792 | |
| 16793 | if (scriptCounts_ && !script->hasScriptCounts() && |
| 16794 | !script->initScriptCounts(cx)) { |
| 16795 | return false; |
| 16796 | } |
| 16797 | |
| 16798 | JitZone* jitZone = cx->zone()->jitZone(); |
| 16799 | |
| 16800 | IonCompilationId compilationId = |
| 16801 | cx->runtime()->jitRuntime()->nextCompilationId(); |
| 16802 | jitZone->currentCompilationIdRef().emplace(compilationId); |
| 16803 | auto resetCurrentId = mozilla::MakeScopeExit( |
| 16804 | [jitZone] { jitZone->currentCompilationIdRef().reset(); }); |
| 16805 | |
| 16806 | // Record constraints. If an error occured, returns false and potentially |
| 16807 | // prevent future compilations. Otherwise, if an invalidation occured, then |
| 16808 | // skip the current compilation. |
| 16809 | bool isValid = false; |
| 16810 | |
| 16811 | // If an inlined script is invalidated (for example, by attaching |
| 16812 | // a debugger), we must also invalidate the parent IonScript. |
| 16813 | if (!AddInlinedCompilations(cx, script, compilationId, snapshot_, &isValid)) { |
| 16814 | return false; |
| 16815 | } |
| 16816 | |
| 16817 | // This compilation is no longer valid; don't proceed, but return true as this |
| 16818 | // isn't an error case either. |
| 16819 | if (!isValid) { |
| 16820 | return true; |
| 16821 | } |
| 16822 | |
| 16823 | CompilationDependencyTracker& tracker = mirGen().tracker; |
| 16824 | // Make sure we're using the same realm as this context. |
| 16825 | MOZ_ASSERT(mirGen().realm->realmPtr() == cx->realm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mirGen().realm->realmPtr() == cx->realm())> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mirGen().realm->realmPtr() == cx->realm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("mirGen().realm->realmPtr() == cx->realm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16825); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mirGen().realm->realmPtr() == cx->realm()" ")"); do { *((volatile int*)__null) = 16825; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16826 | if (!tracker.checkDependencies(cx)) { |
| 16827 | return true; |
| 16828 | } |
| 16829 | |
| 16830 | for (auto& dep : tracker.dependencies) { |
| 16831 | if (!dep->registerDependency(cx, script)) { |
| 16832 | return false; // Should we make sure we only return false on OOM and then |
| 16833 | // eat the OOM here? |
| 16834 | } |
| 16835 | } |
| 16836 | |
| 16837 | uint32_t argumentSlots = (gen->outerInfo().nargs() + 1) * sizeof(Value); |
| 16838 | |
| 16839 | size_t numNurseryObjects = snapshot_->nurseryObjects().length(); |
| 16840 | |
| 16841 | IonScript* ionScript = IonScript::New( |
| 16842 | cx, compilationId, graph.localSlotsSize(), argumentSlots, frameDepth_, |
| 16843 | snapshots_.listSize(), snapshots_.RVATableSize(), recovers_.size(), |
| 16844 | graph.numConstants(), numNurseryObjects, safepointIndices_.length(), |
| 16845 | osiIndices_.length(), icList_.length(), runtimeData_.length(), |
| 16846 | safepoints_.size()); |
| 16847 | if (!ionScript) { |
| 16848 | return false; |
| 16849 | } |
| 16850 | #ifdef DEBUG1 |
| 16851 | ionScript->setICHash(snapshot_->icHash()); |
| 16852 | #endif |
| 16853 | |
| 16854 | auto freeIonScript = mozilla::MakeScopeExit([&ionScript] { |
| 16855 | // Use js_free instead of IonScript::Destroy: the cache list is still |
| 16856 | // uninitialized. |
| 16857 | js_free(ionScript); |
| 16858 | }); |
| 16859 | |
| 16860 | Linker linker(masm); |
| 16861 | JitCode* code = linker.newCode(cx, CodeKind::Ion); |
| 16862 | if (!code) { |
| 16863 | return false; |
| 16864 | } |
| 16865 | |
| 16866 | // Encode native to bytecode map if profiling is enabled. |
| 16867 | if (isProfilerInstrumentationEnabled()) { |
| 16868 | // Generate native-to-bytecode main table. |
| 16869 | IonEntry::ScriptList scriptList; |
| 16870 | if (!generateCompactNativeToBytecodeMap(cx, code, scriptList)) { |
| 16871 | return false; |
| 16872 | } |
| 16873 | |
| 16874 | uint8_t* ionTableAddr = |
| 16875 | ((uint8_t*)nativeToBytecodeMap_.get()) + nativeToBytecodeTableOffset_; |
| 16876 | JitcodeIonTable* ionTable = (JitcodeIonTable*)ionTableAddr; |
| 16877 | |
| 16878 | // Construct the IonEntry that will go into the global table. |
| 16879 | auto entry = MakeJitcodeGlobalEntry<IonEntry>( |
| 16880 | cx, code, code->raw(), code->rawEnd(), std::move(scriptList), ionTable); |
| 16881 | if (!entry) { |
| 16882 | return false; |
| 16883 | } |
| 16884 | (void)nativeToBytecodeMap_.release(); // Table is now owned by |entry|. |
| 16885 | |
| 16886 | // Add entry to the global table. |
| 16887 | JitcodeGlobalTable* globalTable = |
| 16888 | cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); |
| 16889 | if (!globalTable->addEntry(std::move(entry))) { |
| 16890 | return false; |
| 16891 | } |
| 16892 | |
| 16893 | // Mark the jitcode as having a bytecode map. |
| 16894 | code->setHasBytecodeMap(); |
| 16895 | } else { |
| 16896 | // Add a dumy jitcodeGlobalTable entry. |
| 16897 | auto entry = MakeJitcodeGlobalEntry<DummyEntry>(cx, code, code->raw(), |
| 16898 | code->rawEnd()); |
| 16899 | if (!entry) { |
| 16900 | return false; |
| 16901 | } |
| 16902 | |
| 16903 | // Add entry to the global table. |
| 16904 | JitcodeGlobalTable* globalTable = |
| 16905 | cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); |
| 16906 | if (!globalTable->addEntry(std::move(entry))) { |
| 16907 | return false; |
| 16908 | } |
| 16909 | |
| 16910 | // Mark the jitcode as having a bytecode map. |
| 16911 | code->setHasBytecodeMap(); |
| 16912 | } |
| 16913 | |
| 16914 | ionScript->setMethod(code); |
| 16915 | |
| 16916 | // If the Gecko Profiler is enabled, mark IonScript as having been |
| 16917 | // instrumented accordingly. |
| 16918 | if (isProfilerInstrumentationEnabled()) { |
| 16919 | ionScript->setHasProfilingInstrumentation(); |
| 16920 | } |
| 16921 | |
| 16922 | Assembler::PatchDataWithValueCheck( |
| 16923 | CodeLocationLabel(code, invalidateEpilogueData_), ImmPtr(ionScript), |
| 16924 | ImmPtr((void*)-1)); |
| 16925 | |
| 16926 | for (CodeOffset offset : ionScriptLabels_) { |
| 16927 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, offset), |
| 16928 | ImmPtr(ionScript), ImmPtr((void*)-1)); |
| 16929 | } |
| 16930 | |
| 16931 | for (NurseryObjectLabel label : ionNurseryObjectLabels_) { |
| 16932 | void* entry = ionScript->addressOfNurseryObject(label.nurseryIndex); |
| 16933 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset), |
| 16934 | ImmPtr(entry), ImmPtr((void*)-1)); |
| 16935 | } |
| 16936 | |
| 16937 | // for generating inline caches during the execution. |
| 16938 | if (runtimeData_.length()) { |
| 16939 | ionScript->copyRuntimeData(&runtimeData_[0]); |
| 16940 | } |
| 16941 | if (icList_.length()) { |
| 16942 | ionScript->copyICEntries(&icList_[0]); |
| 16943 | } |
| 16944 | |
| 16945 | for (size_t i = 0; i < icInfo_.length(); i++) { |
| 16946 | IonIC& ic = ionScript->getICFromIndex(i); |
| 16947 | Assembler::PatchDataWithValueCheck( |
| 16948 | CodeLocationLabel(code, icInfo_[i].icOffsetForJump), |
| 16949 | ImmPtr(ic.codeRawPtr()), ImmPtr((void*)-1)); |
| 16950 | Assembler::PatchDataWithValueCheck( |
| 16951 | CodeLocationLabel(code, icInfo_[i].icOffsetForPush), ImmPtr(&ic), |
| 16952 | ImmPtr((void*)-1)); |
| 16953 | } |
| 16954 | |
| 16955 | JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)", (void*)ionScript, |
| 16956 | (void*)code->raw()); |
| 16957 | |
| 16958 | ionScript->setInvalidationEpilogueDataOffset( |
| 16959 | invalidateEpilogueData_.offset()); |
| 16960 | if (jsbytecode* osrPc = gen->outerInfo().osrPc()) { |
| 16961 | ionScript->setOsrPc(osrPc); |
| 16962 | ionScript->setOsrEntryOffset(getOsrEntryOffset()); |
| 16963 | } |
| 16964 | ionScript->setInvalidationEpilogueOffset(invalidate_.offset()); |
| 16965 | |
| 16966 | perfSpewer_.saveProfile(cx, script, code); |
| 16967 | |
| 16968 | #ifdef MOZ_VTUNE1 |
| 16969 | vtune::MarkScript(code, script, "ion"); |
| 16970 | #endif |
| 16971 | |
| 16972 | // Set a Ion counter hint for this script. |
| 16973 | if (cx->runtime()->jitRuntime()->hasJitHintsMap()) { |
| 16974 | JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap(); |
| 16975 | jitHints->recordIonCompilation(script); |
| 16976 | } |
| 16977 | |
| 16978 | // for marking during GC. |
| 16979 | if (safepointIndices_.length()) { |
| 16980 | ionScript->copySafepointIndices(&safepointIndices_[0]); |
| 16981 | } |
| 16982 | if (safepoints_.size()) { |
| 16983 | ionScript->copySafepoints(&safepoints_); |
| 16984 | } |
| 16985 | |
| 16986 | // for recovering from an Ion Frame. |
| 16987 | if (osiIndices_.length()) { |
| 16988 | ionScript->copyOsiIndices(&osiIndices_[0]); |
| 16989 | } |
| 16990 | if (snapshots_.listSize()) { |
| 16991 | ionScript->copySnapshots(&snapshots_); |
| 16992 | } |
| 16993 | MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size())do { if (snapshots_.listSize()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(recovers_.size() )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(recovers_.size()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("recovers_.size()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 16993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size()" ")"); do { *((volatile int*)__null) = 16993; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 16994 | if (recovers_.size()) { |
| 16995 | ionScript->copyRecovers(&recovers_); |
| 16996 | } |
| 16997 | if (graph.numConstants()) { |
| 16998 | const Value* vp = graph.constantPool(); |
| 16999 | ionScript->copyConstants(vp); |
| 17000 | for (size_t i = 0; i < graph.numConstants(); i++) { |
| 17001 | const Value& v = vp[i]; |
| 17002 | if (v.isGCThing()) { |
| 17003 | if (gc::StoreBuffer* sb = v.toGCThing()->storeBuffer()) { |
| 17004 | sb->putWholeCell(script); |
| 17005 | break; |
| 17006 | } |
| 17007 | } |
| 17008 | } |
| 17009 | } |
| 17010 | |
| 17011 | // Attach any generated script counts to the script. |
| 17012 | if (IonScriptCounts* counts = extractScriptCounts()) { |
| 17013 | script->addIonCounts(counts); |
| 17014 | } |
| 17015 | // WARNING: Code after this point must be infallible! |
| 17016 | |
| 17017 | // Copy the list of nursery objects. Note that the store buffer can add |
| 17018 | // HeapPtr edges that must be cleared in IonScript::Destroy. See the |
| 17019 | // infallibility warning above. |
| 17020 | const auto& nurseryObjects = snapshot_->nurseryObjects(); |
| 17021 | for (size_t i = 0; i < nurseryObjects.length(); i++) { |
| 17022 | ionScript->nurseryObjects()[i].init(nurseryObjects[i]); |
| 17023 | } |
| 17024 | |
| 17025 | // Transfer ownership of the IonScript to the JitScript. At this point enough |
| 17026 | // of the IonScript must be initialized for IonScript::Destroy to work. |
| 17027 | freeIonScript.release(); |
| 17028 | script->jitScript()->setIonScript(script, ionScript); |
| 17029 | |
| 17030 | return true; |
| 17031 | } |
| 17032 | |
| 17033 | // An out-of-line path to convert a boxed int32 to either a float or double. |
| 17034 | class OutOfLineUnboxFloatingPoint : public OutOfLineCodeBase<CodeGenerator> { |
| 17035 | LUnboxFloatingPoint* unboxFloatingPoint_; |
| 17036 | |
| 17037 | public: |
| 17038 | explicit OutOfLineUnboxFloatingPoint(LUnboxFloatingPoint* unboxFloatingPoint) |
| 17039 | : unboxFloatingPoint_(unboxFloatingPoint) {} |
| 17040 | |
| 17041 | void accept(CodeGenerator* codegen) override { |
| 17042 | codegen->visitOutOfLineUnboxFloatingPoint(this); |
| 17043 | } |
| 17044 | |
| 17045 | LUnboxFloatingPoint* unboxFloatingPoint() const { |
| 17046 | return unboxFloatingPoint_; |
| 17047 | } |
| 17048 | }; |
| 17049 | |
| 17050 | void CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint* lir) { |
| 17051 | const ValueOperand box = ToValue(lir, LUnboxFloatingPoint::Input); |
| 17052 | const LDefinition* result = lir->output(); |
| 17053 | |
| 17054 | // Out-of-line path to convert int32 to double or bailout |
| 17055 | // if this instruction is fallible. |
| 17056 | OutOfLineUnboxFloatingPoint* ool = |
| 17057 | new (alloc()) OutOfLineUnboxFloatingPoint(lir); |
| 17058 | addOutOfLineCode(ool, lir->mir()); |
| 17059 | |
| 17060 | FloatRegister resultReg = ToFloatRegister(result); |
| 17061 | masm.branchTestDouble(Assembler::NotEqual, box, ool->entry()); |
| 17062 | masm.unboxDouble(box, resultReg); |
| 17063 | if (lir->type() == MIRType::Float32) { |
| 17064 | masm.convertDoubleToFloat32(resultReg, resultReg); |
| 17065 | } |
| 17066 | masm.bind(ool->rejoin()); |
| 17067 | } |
| 17068 | |
| 17069 | void CodeGenerator::visitOutOfLineUnboxFloatingPoint( |
| 17070 | OutOfLineUnboxFloatingPoint* ool) { |
| 17071 | LUnboxFloatingPoint* ins = ool->unboxFloatingPoint(); |
| 17072 | const ValueOperand value = ToValue(ins, LUnboxFloatingPoint::Input); |
| 17073 | |
| 17074 | if (ins->mir()->fallible()) { |
| 17075 | Label bail; |
| 17076 | masm.branchTestInt32(Assembler::NotEqual, value, &bail); |
| 17077 | bailoutFrom(&bail, ins->snapshot()); |
| 17078 | } |
| 17079 | if (ins->type() == MIRType::Float32) { |
| 17080 | masm.convertInt32ToFloat32(value.payloadOrValueReg(), |
| 17081 | ToFloatRegister(ins->output())); |
| 17082 | } else { |
| 17083 | masm.convertInt32ToDouble(value.payloadOrValueReg(), |
| 17084 | ToFloatRegister(ins->output())); |
| 17085 | } |
| 17086 | masm.jump(ool->rejoin()); |
| 17087 | } |
| 17088 | |
| 17089 | void CodeGenerator::visitCallBindVar(LCallBindVar* lir) { |
| 17090 | pushArg(ToRegister(lir->environmentChain())); |
| 17091 | |
| 17092 | using Fn = JSObject* (*)(JSContext*, JSObject*); |
| 17093 | callVM<Fn, BindVarOperation>(lir); |
| 17094 | } |
| 17095 | |
| 17096 | void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) { |
| 17097 | Register obj = ToRegister(lir->getOperand(0)); |
| 17098 | ValueOperand idVal = ToValue(lir, LMegamorphicSetElement::IndexIndex); |
| 17099 | ValueOperand value = ToValue(lir, LMegamorphicSetElement::ValueIndex); |
| 17100 | |
| 17101 | Register temp0 = ToRegister(lir->temp0()); |
| 17102 | // See comment in LIROps.yaml (x86 is short on registers) |
| 17103 | #ifndef JS_CODEGEN_X86 |
| 17104 | Register temp1 = ToRegister(lir->temp1()); |
| 17105 | Register temp2 = ToRegister(lir->temp2()); |
| 17106 | #endif |
| 17107 | |
| 17108 | // The instruction is marked as call-instruction so only these registers are |
| 17109 | // live. |
| 17110 | LiveRegisterSet liveRegs; |
| 17111 | liveRegs.addUnchecked(obj); |
| 17112 | liveRegs.addUnchecked(idVal); |
| 17113 | liveRegs.addUnchecked(value); |
| 17114 | liveRegs.addUnchecked(temp0); |
| 17115 | #ifndef JS_CODEGEN_X86 |
| 17116 | liveRegs.addUnchecked(temp1); |
| 17117 | liveRegs.addUnchecked(temp2); |
| 17118 | #endif |
| 17119 | |
| 17120 | Label cacheHit, done; |
| 17121 | #ifdef JS_CODEGEN_X86 |
| 17122 | masm.emitMegamorphicCachedSetSlot( |
| 17123 | idVal, obj, temp0, value, liveRegs, &cacheHit, |
| 17124 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
| 17125 | EmitPreBarrier(masm, addr, mirType); |
| 17126 | }); |
| 17127 | #else |
| 17128 | masm.emitMegamorphicCachedSetSlot( |
| 17129 | idVal, obj, temp0, temp1, temp2, value, liveRegs, &cacheHit, |
| 17130 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
| 17131 | EmitPreBarrier(masm, addr, mirType); |
| 17132 | }); |
| 17133 | #endif |
| 17134 | |
| 17135 | pushArg(Imm32(lir->mir()->strict())); |
| 17136 | pushArg(ToValue(lir, LMegamorphicSetElement::ValueIndex)); |
| 17137 | pushArg(ToValue(lir, LMegamorphicSetElement::IndexIndex)); |
| 17138 | pushArg(obj); |
| 17139 | |
| 17140 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool); |
| 17141 | callVM<Fn, js::jit::SetElementMegamorphic<true>>(lir); |
| 17142 | |
| 17143 | masm.jump(&done); |
| 17144 | masm.bind(&cacheHit); |
| 17145 | |
| 17146 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done); |
| 17147 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done); |
| 17148 | |
| 17149 | // Note: because this is a call-instruction, no registers need to be saved. |
| 17150 | MOZ_ASSERT(lir->isCall())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->isCall())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->isCall()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->isCall()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17150); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { *((volatile int*)__null) = 17150; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17151 | emitPostWriteBarrier(obj); |
| 17152 | |
| 17153 | masm.bind(&done); |
| 17154 | } |
| 17155 | |
| 17156 | void CodeGenerator::visitLoadScriptedProxyHandler( |
| 17157 | LLoadScriptedProxyHandler* ins) { |
| 17158 | Register obj = ToRegister(ins->getOperand(0)); |
| 17159 | Register output = ToRegister(ins->output()); |
| 17160 | |
| 17161 | masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), output); |
| 17162 | |
| 17163 | Label bail; |
| 17164 | Address handlerAddr(output, js::detail::ProxyReservedSlots::offsetOfSlot( |
| 17165 | ScriptedProxyHandler::HANDLER_EXTRA)); |
| 17166 | masm.fallibleUnboxObject(handlerAddr, output, &bail); |
| 17167 | bailoutFrom(&bail, ins->snapshot()); |
| 17168 | } |
| 17169 | |
| 17170 | #ifdef JS_PUNBOX641 |
| 17171 | void CodeGenerator::visitCheckScriptedProxyGetResult( |
| 17172 | LCheckScriptedProxyGetResult* ins) { |
| 17173 | ValueOperand target = ToValue(ins, LCheckScriptedProxyGetResult::TargetIndex); |
| 17174 | ValueOperand value = ToValue(ins, LCheckScriptedProxyGetResult::ValueIndex); |
| 17175 | ValueOperand id = ToValue(ins, LCheckScriptedProxyGetResult::IdIndex); |
| 17176 | Register scratch = ToRegister(ins->temp0()); |
| 17177 | Register scratch2 = ToRegister(ins->temp1()); |
| 17178 | |
| 17179 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, |
| 17180 | MutableHandleValue); |
| 17181 | OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>( |
| 17182 | ins, ArgList(scratch, id, value), StoreValueTo(value)); |
| 17183 | |
| 17184 | masm.unboxObject(target, scratch); |
| 17185 | masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch, |
| 17186 | scratch2, ool->entry()); |
| 17187 | masm.bind(ool->rejoin()); |
| 17188 | } |
| 17189 | #endif |
| 17190 | |
| 17191 | void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) { |
| 17192 | ValueOperand id = ToValue(ins, LIdToStringOrSymbol::IdIndex); |
| 17193 | ValueOperand output = ToOutValue(ins); |
| 17194 | Register scratch = ToRegister(ins->temp0()); |
| 17195 | |
| 17196 | masm.moveValue(id, output); |
| 17197 | |
| 17198 | Label done, callVM; |
| 17199 | Label bail; |
| 17200 | { |
| 17201 | ScratchTagScope tag(masm, output); |
| 17202 | masm.splitTagForTest(output, tag); |
| 17203 | masm.branchTestString(Assembler::Equal, tag, &done); |
| 17204 | masm.branchTestSymbol(Assembler::Equal, tag, &done); |
| 17205 | masm.branchTestInt32(Assembler::NotEqual, tag, &bail); |
| 17206 | } |
| 17207 | |
| 17208 | masm.unboxInt32(output, scratch); |
| 17209 | |
| 17210 | using Fn = JSLinearString* (*)(JSContext*, int); |
| 17211 | OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>( |
| 17212 | ins, ArgList(scratch), StoreRegisterTo(output.scratchReg())); |
| 17213 | |
| 17214 | masm.lookupStaticIntString(scratch, output.scratchReg(), |
| 17215 | gen->runtime->staticStrings(), ool->entry()); |
| 17216 | |
| 17217 | masm.bind(ool->rejoin()); |
| 17218 | masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output); |
| 17219 | masm.bind(&done); |
| 17220 | |
| 17221 | bailoutFrom(&bail, ins->snapshot()); |
| 17222 | } |
| 17223 | |
| 17224 | void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) { |
| 17225 | const Register obj = ToRegister(ins->getOperand(0)); |
| 17226 | size_t slot = ins->mir()->slot(); |
| 17227 | ValueOperand result = ToOutValue(ins); |
| 17228 | |
| 17229 | masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result); |
| 17230 | } |
| 17231 | |
| 17232 | void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) { |
| 17233 | const Register obj = ToRegister(ins->getOperand(0)); |
| 17234 | size_t slot = ins->mir()->slot(); |
| 17235 | AnyRegister result = ToAnyRegister(ins->getDef(0)); |
| 17236 | MIRType type = ins->mir()->type(); |
| 17237 | |
| 17238 | masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), |
| 17239 | type, result); |
| 17240 | } |
| 17241 | |
| 17242 | template <typename T> |
| 17243 | static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type, |
| 17244 | bool fallible, AnyRegister dest, Label* fail) { |
| 17245 | if (type == MIRType::Double) { |
| 17246 | MOZ_ASSERT(dest.isFloat())do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.isFloat())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.isFloat()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("dest.isFloat()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17246); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.isFloat()" ")"); do { *((volatile int*)__null) = 17246; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17247 | masm.ensureDouble(src, dest.fpu(), fail); |
| 17248 | return; |
| 17249 | } |
| 17250 | if (fallible) { |
| 17251 | switch (type) { |
| 17252 | case MIRType::Int32: |
| 17253 | masm.fallibleUnboxInt32(src, dest.gpr(), fail); |
| 17254 | break; |
| 17255 | case MIRType::Boolean: |
| 17256 | masm.fallibleUnboxBoolean(src, dest.gpr(), fail); |
| 17257 | break; |
| 17258 | case MIRType::Object: |
| 17259 | masm.fallibleUnboxObject(src, dest.gpr(), fail); |
| 17260 | break; |
| 17261 | case MIRType::String: |
| 17262 | masm.fallibleUnboxString(src, dest.gpr(), fail); |
| 17263 | break; |
| 17264 | case MIRType::Symbol: |
| 17265 | masm.fallibleUnboxSymbol(src, dest.gpr(), fail); |
| 17266 | break; |
| 17267 | case MIRType::BigInt: |
| 17268 | masm.fallibleUnboxBigInt(src, dest.gpr(), fail); |
| 17269 | break; |
| 17270 | default: |
| 17271 | MOZ_CRASH("Unexpected MIRType")do { do { } while (false); MOZ_ReportCrash("" "Unexpected MIRType" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17271); AnnotateMozCrashReason("MOZ_CRASH(" "Unexpected MIRType" ")"); do { *((volatile int*)__null) = 17271; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 17272 | } |
| 17273 | return; |
| 17274 | } |
| 17275 | masm.loadUnboxedValue(src, type, dest); |
| 17276 | } |
| 17277 | |
| 17278 | void CodeGenerator::visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* ins) { |
| 17279 | const MLoadFixedSlotAndUnbox* mir = ins->mir(); |
| 17280 | MIRType type = mir->type(); |
| 17281 | Register input = ToRegister(ins->object()); |
| 17282 | AnyRegister result = ToAnyRegister(ins->output()); |
| 17283 | size_t slot = mir->slot(); |
| 17284 | |
| 17285 | Address address(input, NativeObject::getFixedSlotOffset(slot)); |
| 17286 | |
| 17287 | Label bail; |
| 17288 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
| 17289 | if (mir->fallible()) { |
| 17290 | bailoutFrom(&bail, ins->snapshot()); |
| 17291 | } |
| 17292 | } |
| 17293 | |
| 17294 | void CodeGenerator::visitLoadDynamicSlotAndUnbox( |
| 17295 | LLoadDynamicSlotAndUnbox* ins) { |
| 17296 | const MLoadDynamicSlotAndUnbox* mir = ins->mir(); |
| 17297 | MIRType type = mir->type(); |
| 17298 | Register input = ToRegister(ins->slots()); |
| 17299 | AnyRegister result = ToAnyRegister(ins->output()); |
| 17300 | size_t slot = mir->slot(); |
| 17301 | |
| 17302 | Address address(input, slot * sizeof(JS::Value)); |
| 17303 | |
| 17304 | Label bail; |
| 17305 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
| 17306 | if (mir->fallible()) { |
| 17307 | bailoutFrom(&bail, ins->snapshot()); |
| 17308 | } |
| 17309 | } |
| 17310 | |
| 17311 | void CodeGenerator::visitLoadElementAndUnbox(LLoadElementAndUnbox* ins) { |
| 17312 | const MLoadElementAndUnbox* mir = ins->mir(); |
| 17313 | MIRType type = mir->type(); |
| 17314 | Register elements = ToRegister(ins->elements()); |
| 17315 | AnyRegister result = ToAnyRegister(ins->output()); |
| 17316 | |
| 17317 | Label bail; |
| 17318 | if (ins->index()->isConstant()) { |
| 17319 | NativeObject::elementsSizeMustNotOverflow(); |
| 17320 | int32_t offset = ToInt32(ins->index()) * sizeof(Value); |
| 17321 | Address address(elements, offset); |
| 17322 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
| 17323 | } else { |
| 17324 | BaseObjectElementIndex address(elements, ToRegister(ins->index())); |
| 17325 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, &bail); |
| 17326 | } |
| 17327 | |
| 17328 | if (mir->fallible()) { |
| 17329 | bailoutFrom(&bail, ins->snapshot()); |
| 17330 | } |
| 17331 | } |
| 17332 | |
| 17333 | class OutOfLineAtomizeSlot : public OutOfLineCodeBase<CodeGenerator> { |
| 17334 | LInstruction* lir_; |
| 17335 | Register stringReg_; |
| 17336 | Address slotAddr_; |
| 17337 | TypedOrValueRegister dest_; |
| 17338 | |
| 17339 | public: |
| 17340 | OutOfLineAtomizeSlot(LInstruction* lir, Register stringReg, Address slotAddr, |
| 17341 | TypedOrValueRegister dest) |
| 17342 | : lir_(lir), stringReg_(stringReg), slotAddr_(slotAddr), dest_(dest) {} |
| 17343 | |
| 17344 | void accept(CodeGenerator* codegen) final { |
| 17345 | codegen->visitOutOfLineAtomizeSlot(this); |
| 17346 | } |
| 17347 | LInstruction* lir() const { return lir_; } |
| 17348 | Register stringReg() const { return stringReg_; } |
| 17349 | Address slotAddr() const { return slotAddr_; } |
| 17350 | TypedOrValueRegister dest() const { return dest_; } |
| 17351 | }; |
| 17352 | |
| 17353 | void CodeGenerator::visitOutOfLineAtomizeSlot(OutOfLineAtomizeSlot* ool) { |
| 17354 | LInstruction* lir = ool->lir(); |
| 17355 | Register stringReg = ool->stringReg(); |
| 17356 | Address slotAddr = ool->slotAddr(); |
| 17357 | TypedOrValueRegister dest = ool->dest(); |
| 17358 | |
| 17359 | // This code is called with a non-atomic string in |stringReg|. |
| 17360 | // When it returns, |stringReg| contains an unboxed pointer to an |
| 17361 | // atomized version of that string, and |slotAddr| contains a |
| 17362 | // StringValue pointing to that atom. If |dest| is a ValueOperand, |
| 17363 | // it contains the same StringValue; otherwise we assert that |dest| |
| 17364 | // is |stringReg|. |
| 17365 | |
| 17366 | saveLive(lir); |
| 17367 | pushArg(stringReg); |
| 17368 | |
| 17369 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
| 17370 | callVM<Fn, js::AtomizeString>(lir); |
| 17371 | StoreRegisterTo(stringReg).generate(this); |
| 17372 | restoreLiveIgnore(lir, StoreRegisterTo(stringReg).clobbered()); |
| 17373 | |
| 17374 | if (dest.hasValue()) { |
| 17375 | masm.moveValue( |
| 17376 | TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)), |
| 17377 | dest.valueReg()); |
| 17378 | } else { |
| 17379 | MOZ_ASSERT(dest.typedReg().gpr() == stringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.typedReg().gpr() == stringReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.typedReg().gpr() == stringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "dest.typedReg().gpr() == stringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17379); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg" ")"); do { *((volatile int*)__null) = 17379; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17380 | } |
| 17381 | |
| 17382 | emitPreBarrier(slotAddr); |
| 17383 | masm.storeTypedOrValue(dest, slotAddr); |
| 17384 | |
| 17385 | // We don't need a post-barrier because atoms aren't nursery-allocated. |
| 17386 | #ifdef DEBUG1 |
| 17387 | // We need a temp register for the nursery check. Spill something. |
| 17388 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 17389 | allRegs.take(stringReg); |
| 17390 | Register temp = allRegs.takeAny(); |
| 17391 | masm.push(temp); |
| 17392 | |
| 17393 | Label tenured; |
| 17394 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, stringReg, temp, &tenured); |
| 17395 | masm.assumeUnreachable("AtomizeString returned a nursery pointer"); |
| 17396 | masm.bind(&tenured); |
| 17397 | |
| 17398 | masm.pop(temp); |
| 17399 | #endif |
| 17400 | |
| 17401 | masm.jump(ool->rejoin()); |
| 17402 | } |
| 17403 | |
| 17404 | void CodeGenerator::emitMaybeAtomizeSlot(LInstruction* ins, Register stringReg, |
| 17405 | Address slotAddr, |
| 17406 | TypedOrValueRegister dest) { |
| 17407 | OutOfLineAtomizeSlot* ool = |
| 17408 | new (alloc()) OutOfLineAtomizeSlot(ins, stringReg, slotAddr, dest); |
| 17409 | addOutOfLineCode(ool, ins->mirRaw()->toInstruction()); |
| 17410 | masm.branchTest32(Assembler::NonZero, |
| 17411 | Address(stringReg, JSString::offsetOfFlags()), |
| 17412 | Imm32(JSString::ATOM_BIT), ool->rejoin()); |
| 17413 | |
| 17414 | masm.branchTest32(Assembler::Zero, |
| 17415 | Address(stringReg, JSString::offsetOfFlags()), |
| 17416 | Imm32(JSString::ATOM_REF_BIT), ool->entry()); |
| 17417 | masm.loadPtr(Address(stringReg, JSAtomRefString::offsetOfAtom()), stringReg); |
| 17418 | |
| 17419 | if (dest.hasValue()) { |
| 17420 | masm.moveValue( |
| 17421 | TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)), |
| 17422 | dest.valueReg()); |
| 17423 | } else { |
| 17424 | MOZ_ASSERT(dest.typedReg().gpr() == stringReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(dest.typedReg().gpr() == stringReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(dest.typedReg().gpr() == stringReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "dest.typedReg().gpr() == stringReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17424); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg" ")"); do { *((volatile int*)__null) = 17424; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17425 | } |
| 17426 | |
| 17427 | emitPreBarrier(slotAddr); |
| 17428 | masm.storeTypedOrValue(dest, slotAddr); |
| 17429 | |
| 17430 | masm.bind(ool->rejoin()); |
| 17431 | } |
| 17432 | |
| 17433 | void CodeGenerator::visitLoadFixedSlotAndAtomize( |
| 17434 | LLoadFixedSlotAndAtomize* ins) { |
| 17435 | Register obj = ToRegister(ins->getOperand(0)); |
| 17436 | Register temp = ToRegister(ins->temp0()); |
| 17437 | size_t slot = ins->mir()->slot(); |
| 17438 | ValueOperand result = ToOutValue(ins); |
| 17439 | |
| 17440 | Address slotAddr(obj, NativeObject::getFixedSlotOffset(slot)); |
| 17441 | masm.loadValue(slotAddr, result); |
| 17442 | |
| 17443 | Label notString; |
| 17444 | masm.branchTestString(Assembler::NotEqual, result, ¬String); |
| 17445 | masm.unboxString(result, temp); |
| 17446 | emitMaybeAtomizeSlot(ins, temp, slotAddr, result); |
| 17447 | masm.bind(¬String); |
| 17448 | } |
| 17449 | |
| 17450 | void CodeGenerator::visitLoadDynamicSlotAndAtomize( |
| 17451 | LLoadDynamicSlotAndAtomize* ins) { |
| 17452 | ValueOperand result = ToOutValue(ins); |
| 17453 | Register temp = ToRegister(ins->temp0()); |
| 17454 | Register base = ToRegister(ins->input()); |
| 17455 | int32_t offset = ins->mir()->slot() * sizeof(js::Value); |
| 17456 | |
| 17457 | Address slotAddr(base, offset); |
| 17458 | masm.loadValue(slotAddr, result); |
| 17459 | |
| 17460 | Label notString; |
| 17461 | masm.branchTestString(Assembler::NotEqual, result, ¬String); |
| 17462 | masm.unboxString(result, temp); |
| 17463 | emitMaybeAtomizeSlot(ins, temp, slotAddr, result); |
| 17464 | masm.bind(¬String); |
| 17465 | } |
| 17466 | |
| 17467 | void CodeGenerator::visitLoadFixedSlotUnboxAndAtomize( |
| 17468 | LLoadFixedSlotUnboxAndAtomize* ins) { |
| 17469 | const MLoadFixedSlotAndUnbox* mir = ins->mir(); |
| 17470 | MOZ_ASSERT(mir->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->type() == MIRType::String)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mir->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 17470; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17471 | Register input = ToRegister(ins->object()); |
| 17472 | AnyRegister result = ToAnyRegister(ins->output()); |
| 17473 | size_t slot = mir->slot(); |
| 17474 | |
| 17475 | Address slotAddr(input, NativeObject::getFixedSlotOffset(slot)); |
| 17476 | |
| 17477 | Label bail; |
| 17478 | EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result, |
| 17479 | &bail); |
| 17480 | emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr, |
| 17481 | TypedOrValueRegister(MIRType::String, result)); |
| 17482 | |
| 17483 | if (mir->fallible()) { |
| 17484 | bailoutFrom(&bail, ins->snapshot()); |
| 17485 | } |
| 17486 | } |
| 17487 | |
| 17488 | void CodeGenerator::visitLoadDynamicSlotUnboxAndAtomize( |
| 17489 | LLoadDynamicSlotUnboxAndAtomize* ins) { |
| 17490 | const MLoadDynamicSlotAndUnbox* mir = ins->mir(); |
| 17491 | MOZ_ASSERT(mir->type() == MIRType::String)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->type() == MIRType::String)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->type() == MIRType::String ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "mir->type() == MIRType::String", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 17491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String" ")"); do { *((volatile int*)__null) = 17491; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17492 | Register input = ToRegister(ins->slots()); |
| 17493 | AnyRegister result = ToAnyRegister(ins->output()); |
| 17494 | size_t slot = mir->slot(); |
| 17495 | |
| 17496 | Address slotAddr(input, slot * sizeof(JS::Value)); |
| 17497 | |
| 17498 | Label bail; |
| 17499 | EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result, |
| 17500 | &bail); |
| 17501 | emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr, |
| 17502 | TypedOrValueRegister(MIRType::String, result)); |
| 17503 | |
| 17504 | if (mir->fallible()) { |
| 17505 | bailoutFrom(&bail, ins->snapshot()); |
| 17506 | } |
| 17507 | } |
| 17508 | |
| 17509 | void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) { |
| 17510 | const Register obj = ToRegister(ins->getOperand(0)); |
| 17511 | const ValueOperand value = ToValue(ins, LAddAndStoreSlot::ValueIndex); |
| 17512 | const Register maybeTemp = ToTempRegisterOrInvalid(ins->temp0()); |
| 17513 | |
| 17514 | Shape* shape = ins->mir()->shape(); |
| 17515 | masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) { |
| 17516 | EmitPreBarrier(masm, addr, MIRType::Shape); |
| 17517 | }); |
| 17518 | |
| 17519 | // Perform the store. No pre-barrier required since this is a new |
| 17520 | // initialization. |
| 17521 | |
| 17522 | uint32_t offset = ins->mir()->slotOffset(); |
| 17523 | if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) { |
| 17524 | Address slot(obj, offset); |
| 17525 | masm.storeValue(value, slot); |
| 17526 | } else { |
| 17527 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), maybeTemp); |
| 17528 | Address slot(maybeTemp, offset); |
| 17529 | masm.storeValue(value, slot); |
| 17530 | } |
| 17531 | } |
| 17532 | |
| 17533 | void CodeGenerator::visitAllocateAndStoreSlot(LAllocateAndStoreSlot* ins) { |
| 17534 | const Register obj = ToRegister(ins->getOperand(0)); |
| 17535 | const ValueOperand value = ToValue(ins, LAllocateAndStoreSlot::ValueIndex); |
| 17536 | const Register temp0 = ToRegister(ins->temp0()); |
| 17537 | const Register temp1 = ToRegister(ins->temp1()); |
| 17538 | |
| 17539 | masm.Push(obj); |
| 17540 | masm.Push(value); |
| 17541 | |
| 17542 | using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount); |
| 17543 | masm.setupAlignedABICall(); |
| 17544 | masm.loadJSContext(temp0); |
| 17545 | masm.passABIArg(temp0); |
| 17546 | masm.passABIArg(obj); |
| 17547 | masm.move32(Imm32(ins->mir()->numNewSlots()), temp1); |
| 17548 | masm.passABIArg(temp1); |
| 17549 | masm.callWithABI<Fn, NativeObject::growSlotsPure>(); |
| 17550 | masm.storeCallPointerResult(temp0); |
| 17551 | |
| 17552 | masm.Pop(value); |
| 17553 | masm.Pop(obj); |
| 17554 | |
| 17555 | bailoutIfFalseBool(temp0, ins->snapshot()); |
| 17556 | |
| 17557 | masm.storeObjShape(ins->mir()->shape(), obj, |
| 17558 | [](MacroAssembler& masm, const Address& addr) { |
| 17559 | EmitPreBarrier(masm, addr, MIRType::Shape); |
| 17560 | }); |
| 17561 | |
| 17562 | // Perform the store. No pre-barrier required since this is a new |
| 17563 | // initialization. |
| 17564 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0); |
| 17565 | Address slot(temp0, ins->mir()->slotOffset()); |
| 17566 | masm.storeValue(value, slot); |
| 17567 | } |
| 17568 | |
| 17569 | void CodeGenerator::visitAddSlotAndCallAddPropHook( |
| 17570 | LAddSlotAndCallAddPropHook* ins) { |
| 17571 | const Register obj = ToRegister(ins->object()); |
| 17572 | const ValueOperand value = |
| 17573 | ToValue(ins, LAddSlotAndCallAddPropHook::ValueIndex); |
| 17574 | |
| 17575 | pushArg(ImmGCPtr(ins->mir()->shape())); |
| 17576 | pushArg(value); |
| 17577 | pushArg(obj); |
| 17578 | |
| 17579 | using Fn = |
| 17580 | bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, Handle<Shape*>); |
| 17581 | callVM<Fn, AddSlotAndCallAddPropHook>(ins); |
| 17582 | } |
| 17583 | |
| 17584 | void CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV* ins) { |
| 17585 | const Register obj = ToRegister(ins->getOperand(0)); |
| 17586 | size_t slot = ins->mir()->slot(); |
| 17587 | |
| 17588 | const ValueOperand value = ToValue(ins, LStoreFixedSlotV::ValueIndex); |
| 17589 | |
| 17590 | Address address(obj, NativeObject::getFixedSlotOffset(slot)); |
| 17591 | if (ins->mir()->needsBarrier()) { |
| 17592 | emitPreBarrier(address); |
| 17593 | } |
| 17594 | |
| 17595 | masm.storeValue(value, address); |
| 17596 | } |
| 17597 | |
| 17598 | void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) { |
| 17599 | const Register obj = ToRegister(ins->getOperand(0)); |
| 17600 | size_t slot = ins->mir()->slot(); |
| 17601 | |
| 17602 | const LAllocation* value = ins->value(); |
| 17603 | MIRType valueType = ins->mir()->value()->type(); |
| 17604 | |
| 17605 | Address address(obj, NativeObject::getFixedSlotOffset(slot)); |
| 17606 | if (ins->mir()->needsBarrier()) { |
| 17607 | emitPreBarrier(address); |
| 17608 | } |
| 17609 | |
| 17610 | ConstantOrRegister nvalue = |
| 17611 | value->isConstant() |
| 17612 | ? ConstantOrRegister(value->toConstant()->toJSValue()) |
| 17613 | : TypedOrValueRegister(valueType, ToAnyRegister(value)); |
| 17614 | masm.storeConstantOrRegister(nvalue, address); |
| 17615 | } |
| 17616 | |
| 17617 | void CodeGenerator::visitGetNameCache(LGetNameCache* ins) { |
| 17618 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 17619 | Register envChain = ToRegister(ins->envObj()); |
| 17620 | ValueOperand output = ToOutValue(ins); |
| 17621 | Register temp = ToRegister(ins->temp0()); |
| 17622 | |
| 17623 | IonGetNameIC ic(liveRegs, envChain, output, temp); |
| 17624 | addIC(ins, allocateIC(ic)); |
| 17625 | } |
| 17626 | |
| 17627 | void CodeGenerator::addGetPropertyCache(LInstruction* ins, |
| 17628 | LiveRegisterSet liveRegs, |
| 17629 | TypedOrValueRegister value, |
| 17630 | const ConstantOrRegister& id, |
| 17631 | ValueOperand output) { |
| 17632 | CacheKind kind = CacheKind::GetElem; |
| 17633 | if (id.constant() && id.value().isString()) { |
| 17634 | JSString* idString = id.value().toString(); |
| 17635 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
| 17636 | kind = CacheKind::GetProp; |
| 17637 | } |
| 17638 | } |
| 17639 | IonGetPropertyIC cache(kind, liveRegs, value, id, output); |
| 17640 | addIC(ins, allocateIC(cache)); |
| 17641 | } |
| 17642 | |
| 17643 | void CodeGenerator::addSetPropertyCache(LInstruction* ins, |
| 17644 | LiveRegisterSet liveRegs, |
| 17645 | Register objReg, Register temp, |
| 17646 | const ConstantOrRegister& id, |
| 17647 | const ConstantOrRegister& value, |
| 17648 | bool strict) { |
| 17649 | CacheKind kind = CacheKind::SetElem; |
| 17650 | if (id.constant() && id.value().isString()) { |
| 17651 | JSString* idString = id.value().toString(); |
| 17652 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
| 17653 | kind = CacheKind::SetProp; |
| 17654 | } |
| 17655 | } |
| 17656 | IonSetPropertyIC cache(kind, liveRegs, objReg, temp, id, value, strict); |
| 17657 | addIC(ins, allocateIC(cache)); |
| 17658 | } |
| 17659 | |
| 17660 | ConstantOrRegister CodeGenerator::toConstantOrRegister(LInstruction* lir, |
| 17661 | size_t n, MIRType type) { |
| 17662 | if (type == MIRType::Value) { |
| 17663 | return TypedOrValueRegister(ToValue(lir, n)); |
| 17664 | } |
| 17665 | |
| 17666 | const LAllocation* value = lir->getOperand(n); |
| 17667 | if (value->isConstant()) { |
| 17668 | return ConstantOrRegister(value->toConstant()->toJSValue()); |
| 17669 | } |
| 17670 | |
| 17671 | return TypedOrValueRegister(type, ToAnyRegister(value)); |
| 17672 | } |
| 17673 | |
| 17674 | void CodeGenerator::visitGetPropertyCache(LGetPropertyCache* ins) { |
| 17675 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 17676 | TypedOrValueRegister value = |
| 17677 | toConstantOrRegister(ins, LGetPropertyCache::ValueIndex, |
| 17678 | ins->mir()->value()->type()) |
| 17679 | .reg(); |
| 17680 | ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCache::IdIndex, |
| 17681 | ins->mir()->idval()->type()); |
| 17682 | ValueOperand output = ToOutValue(ins); |
| 17683 | addGetPropertyCache(ins, liveRegs, value, id, output); |
| 17684 | } |
| 17685 | |
| 17686 | void CodeGenerator::visitGetPropSuperCache(LGetPropSuperCache* ins) { |
| 17687 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 17688 | Register obj = ToRegister(ins->obj()); |
| 17689 | TypedOrValueRegister receiver = |
| 17690 | toConstantOrRegister(ins, LGetPropSuperCache::ReceiverIndex, |
| 17691 | ins->mir()->receiver()->type()) |
| 17692 | .reg(); |
| 17693 | ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCache::IdIndex, |
| 17694 | ins->mir()->idval()->type()); |
| 17695 | ValueOperand output = ToOutValue(ins); |
| 17696 | |
| 17697 | CacheKind kind = CacheKind::GetElemSuper; |
| 17698 | if (id.constant() && id.value().isString()) { |
| 17699 | JSString* idString = id.value().toString(); |
| 17700 | if (idString->isAtom() && !idString->asAtom().isIndex()) { |
| 17701 | kind = CacheKind::GetPropSuper; |
| 17702 | } |
| 17703 | } |
| 17704 | |
| 17705 | IonGetPropSuperIC cache(kind, liveRegs, obj, receiver, id, output); |
| 17706 | addIC(ins, allocateIC(cache)); |
| 17707 | } |
| 17708 | |
| 17709 | void CodeGenerator::visitBindNameCache(LBindNameCache* ins) { |
| 17710 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 17711 | Register envChain = ToRegister(ins->environmentChain()); |
| 17712 | Register output = ToRegister(ins->output()); |
| 17713 | Register temp = ToRegister(ins->temp0()); |
| 17714 | |
| 17715 | IonBindNameIC ic(liveRegs, envChain, output, temp); |
| 17716 | addIC(ins, allocateIC(ic)); |
| 17717 | } |
| 17718 | |
| 17719 | void CodeGenerator::visitHasOwnCache(LHasOwnCache* ins) { |
| 17720 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 17721 | TypedOrValueRegister value = |
| 17722 | toConstantOrRegister(ins, LHasOwnCache::ValueIndex, |
| 17723 | ins->mir()->value()->type()) |
| 17724 | .reg(); |
| 17725 | TypedOrValueRegister id = toConstantOrRegister(ins, LHasOwnCache::IdIndex, |
| 17726 | ins->mir()->idval()->type()) |
| 17727 | .reg(); |
| 17728 | Register output = ToRegister(ins->output()); |
| 17729 | |
| 17730 | IonHasOwnIC cache(liveRegs, value, id, output); |
| 17731 | addIC(ins, allocateIC(cache)); |
| 17732 | } |
| 17733 | |
| 17734 | void CodeGenerator::visitCheckPrivateFieldCache(LCheckPrivateFieldCache* ins) { |
| 17735 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 17736 | TypedOrValueRegister value = |
| 17737 | toConstantOrRegister(ins, LCheckPrivateFieldCache::ValueIndex, |
| 17738 | ins->mir()->value()->type()) |
| 17739 | .reg(); |
| 17740 | TypedOrValueRegister id = |
| 17741 | toConstantOrRegister(ins, LCheckPrivateFieldCache::IdIndex, |
| 17742 | ins->mir()->idval()->type()) |
| 17743 | .reg(); |
| 17744 | Register output = ToRegister(ins->output()); |
| 17745 | |
| 17746 | IonCheckPrivateFieldIC cache(liveRegs, value, id, output); |
| 17747 | addIC(ins, allocateIC(cache)); |
| 17748 | } |
| 17749 | |
| 17750 | void CodeGenerator::visitNewPrivateName(LNewPrivateName* ins) { |
| 17751 | pushArg(ImmGCPtr(ins->mir()->name())); |
| 17752 | |
| 17753 | using Fn = JS::Symbol* (*)(JSContext*, Handle<JSAtom*>); |
| 17754 | callVM<Fn, NewPrivateName>(ins); |
| 17755 | } |
| 17756 | |
| 17757 | void CodeGenerator::visitCallDeleteProperty(LCallDeleteProperty* lir) { |
| 17758 | pushArg(ImmGCPtr(lir->mir()->name())); |
| 17759 | pushArg(ToValue(lir, LCallDeleteProperty::ValueIndex)); |
| 17760 | |
| 17761 | using Fn = bool (*)(JSContext*, HandleValue, Handle<PropertyName*>, bool*); |
| 17762 | if (lir->mir()->strict()) { |
| 17763 | callVM<Fn, DelPropOperation<true>>(lir); |
| 17764 | } else { |
| 17765 | callVM<Fn, DelPropOperation<false>>(lir); |
| 17766 | } |
| 17767 | } |
| 17768 | |
| 17769 | void CodeGenerator::visitCallDeleteElement(LCallDeleteElement* lir) { |
| 17770 | pushArg(ToValue(lir, LCallDeleteElement::IndexIndex)); |
| 17771 | pushArg(ToValue(lir, LCallDeleteElement::ValueIndex)); |
| 17772 | |
| 17773 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*); |
| 17774 | if (lir->mir()->strict()) { |
| 17775 | callVM<Fn, DelElemOperation<true>>(lir); |
| 17776 | } else { |
| 17777 | callVM<Fn, DelElemOperation<false>>(lir); |
| 17778 | } |
| 17779 | } |
| 17780 | |
| 17781 | void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) { |
| 17782 | Register obj = ToRegister(lir->object()); |
| 17783 | Register iterObj = ToRegister(lir->output()); |
| 17784 | Register temp = ToRegister(lir->temp0()); |
| 17785 | Register temp2 = ToRegister(lir->temp1()); |
| 17786 | Register temp3 = ToRegister(lir->temp2()); |
| 17787 | |
| 17788 | using Fn = PropertyIteratorObject* (*)(JSContext*, HandleObject); |
| 17789 | OutOfLineCode* ool = (lir->mir()->wantsIndices()) |
| 17790 | ? oolCallVM<Fn, GetIteratorWithIndices>( |
| 17791 | lir, ArgList(obj), StoreRegisterTo(iterObj)) |
| 17792 | : oolCallVM<Fn, GetIterator>( |
| 17793 | lir, ArgList(obj), StoreRegisterTo(iterObj)); |
| 17794 | |
| 17795 | masm.maybeLoadIteratorFromShape(obj, iterObj, temp, temp2, temp3, |
| 17796 | ool->entry()); |
| 17797 | |
| 17798 | Register nativeIter = temp; |
| 17799 | masm.loadPrivate( |
| 17800 | Address(iterObj, PropertyIteratorObject::offsetOfIteratorSlot()), |
| 17801 | nativeIter); |
| 17802 | |
| 17803 | if (lir->mir()->wantsIndices()) { |
| 17804 | // At least one consumer of the output of this iterator has been optimized |
| 17805 | // to use iterator indices. If the cached iterator doesn't include indices, |
| 17806 | // but it was marked to indicate that we can create them if needed, then we |
| 17807 | // do a VM call to replace the cached iterator with a fresh iterator |
| 17808 | // including indices. |
| 17809 | masm.branchNativeIteratorIndices(Assembler::Equal, nativeIter, temp2, |
| 17810 | NativeIteratorIndices::AvailableOnRequest, |
| 17811 | ool->entry()); |
| 17812 | } |
| 17813 | |
| 17814 | Address iterFlagsAddr(nativeIter, NativeIterator::offsetOfFlagsAndCount()); |
| 17815 | masm.storePtr( |
| 17816 | obj, Address(nativeIter, NativeIterator::offsetOfObjectBeingIterated())); |
| 17817 | masm.or32(Imm32(NativeIterator::Flags::Active), iterFlagsAddr); |
| 17818 | |
| 17819 | Register enumeratorsAddr = temp2; |
| 17820 | masm.movePtr(ImmPtr(lir->mir()->enumeratorsAddr()), enumeratorsAddr); |
| 17821 | masm.registerIterator(enumeratorsAddr, nativeIter, temp3); |
| 17822 | |
| 17823 | // Generate post-write barrier for storing to |iterObj->objectBeingIterated_|. |
| 17824 | // We already know that |iterObj| is tenured, so we only have to check |obj|. |
| 17825 | Label skipBarrier; |
| 17826 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp2, &skipBarrier); |
| 17827 | { |
| 17828 | LiveRegisterSet save = liveVolatileRegs(lir); |
| 17829 | save.takeUnchecked(temp); |
| 17830 | save.takeUnchecked(temp2); |
| 17831 | save.takeUnchecked(temp3); |
| 17832 | if (iterObj.volatile_()) { |
| 17833 | save.addUnchecked(iterObj); |
| 17834 | } |
| 17835 | |
| 17836 | masm.PushRegsInMask(save); |
| 17837 | emitPostWriteBarrier(iterObj); |
| 17838 | masm.PopRegsInMask(save); |
| 17839 | } |
| 17840 | masm.bind(&skipBarrier); |
| 17841 | |
| 17842 | masm.bind(ool->rejoin()); |
| 17843 | } |
| 17844 | |
| 17845 | void CodeGenerator::visitValueToIterator(LValueToIterator* lir) { |
| 17846 | pushArg(ToValue(lir, LValueToIterator::ValueIndex)); |
| 17847 | |
| 17848 | using Fn = PropertyIteratorObject* (*)(JSContext*, HandleValue); |
| 17849 | callVM<Fn, ValueToIterator>(lir); |
| 17850 | } |
| 17851 | |
| 17852 | void CodeGenerator::visitIteratorHasIndicesAndBranch( |
| 17853 | LIteratorHasIndicesAndBranch* lir) { |
| 17854 | Register iterator = ToRegister(lir->iterator()); |
| 17855 | Register object = ToRegister(lir->object()); |
| 17856 | Register temp = ToRegister(lir->temp()); |
| 17857 | Register temp2 = ToRegister(lir->temp2()); |
| 17858 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
| 17859 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
| 17860 | |
| 17861 | // Check that the iterator has indices available. |
| 17862 | Address nativeIterAddr(iterator, |
| 17863 | PropertyIteratorObject::offsetOfIteratorSlot()); |
| 17864 | masm.loadPrivate(nativeIterAddr, temp); |
| 17865 | masm.branchNativeIteratorIndices(Assembler::NotEqual, temp, temp2, |
| 17866 | NativeIteratorIndices::Valid, ifFalse); |
| 17867 | |
| 17868 | // Guard that the first shape stored in the iterator matches the current |
| 17869 | // shape of the iterated object. |
| 17870 | Address firstShapeAddr(temp, NativeIterator::offsetOfFirstShape()); |
| 17871 | masm.loadPtr(firstShapeAddr, temp); |
| 17872 | masm.branchTestObjShape(Assembler::NotEqual, object, temp, temp2, object, |
| 17873 | ifFalse); |
| 17874 | |
| 17875 | if (!isNextBlock(lir->ifTrue()->lir())) { |
| 17876 | masm.jump(ifTrue); |
| 17877 | } |
| 17878 | } |
| 17879 | |
| 17880 | void CodeGenerator::visitLoadSlotByIteratorIndex( |
| 17881 | LLoadSlotByIteratorIndex* lir) { |
| 17882 | Register object = ToRegister(lir->object()); |
| 17883 | Register iterator = ToRegister(lir->iterator()); |
| 17884 | Register temp = ToRegister(lir->temp0()); |
| 17885 | Register temp2 = ToRegister(lir->temp1()); |
| 17886 | ValueOperand result = ToOutValue(lir); |
| 17887 | |
| 17888 | masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2); |
| 17889 | |
| 17890 | Label notDynamicSlot, notFixedSlot, done; |
| 17891 | masm.branch32(Assembler::NotEqual, temp2, |
| 17892 | Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), |
| 17893 | ¬DynamicSlot); |
| 17894 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
| 17895 | masm.loadValue(BaseValueIndex(temp2, temp), result); |
| 17896 | masm.jump(&done); |
| 17897 | |
| 17898 | masm.bind(¬DynamicSlot); |
| 17899 | masm.branch32(Assembler::NotEqual, temp2, |
| 17900 | Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), ¬FixedSlot); |
| 17901 | // Fixed slot |
| 17902 | masm.loadValue(BaseValueIndex(object, temp, sizeof(NativeObject)), result); |
| 17903 | masm.jump(&done); |
| 17904 | masm.bind(¬FixedSlot); |
| 17905 | |
| 17906 | #ifdef DEBUG1 |
| 17907 | Label kindOkay; |
| 17908 | masm.branch32(Assembler::Equal, temp2, |
| 17909 | Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); |
| 17910 | masm.assumeUnreachable("Invalid PropertyIndex::Kind"); |
| 17911 | masm.bind(&kindOkay); |
| 17912 | #endif |
| 17913 | |
| 17914 | // Dense element |
| 17915 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2); |
| 17916 | Label indexOkay; |
| 17917 | Address initLength(temp2, ObjectElements::offsetOfInitializedLength()); |
| 17918 | masm.branch32(Assembler::Above, initLength, temp, &indexOkay); |
| 17919 | masm.assumeUnreachable("Dense element out of bounds"); |
| 17920 | masm.bind(&indexOkay); |
| 17921 | |
| 17922 | masm.loadValue(BaseObjectElementIndex(temp2, temp), result); |
| 17923 | masm.bind(&done); |
| 17924 | } |
| 17925 | |
| 17926 | void CodeGenerator::visitStoreSlotByIteratorIndex( |
| 17927 | LStoreSlotByIteratorIndex* lir) { |
| 17928 | Register object = ToRegister(lir->object()); |
| 17929 | Register iterator = ToRegister(lir->iterator()); |
| 17930 | ValueOperand value = ToValue(lir, LStoreSlotByIteratorIndex::ValueIndex); |
| 17931 | Register temp = ToRegister(lir->temp0()); |
| 17932 | Register temp2 = ToRegister(lir->temp1()); |
| 17933 | |
| 17934 | masm.extractCurrentIndexAndKindFromIterator(iterator, temp, temp2); |
| 17935 | |
| 17936 | Label notDynamicSlot, notFixedSlot, done, doStore; |
| 17937 | masm.branch32(Assembler::NotEqual, temp2, |
| 17938 | Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), |
| 17939 | ¬DynamicSlot); |
| 17940 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
| 17941 | masm.computeEffectiveAddress(BaseValueIndex(temp2, temp), temp); |
| 17942 | masm.jump(&doStore); |
| 17943 | |
| 17944 | masm.bind(¬DynamicSlot); |
| 17945 | masm.branch32(Assembler::NotEqual, temp2, |
| 17946 | Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), ¬FixedSlot); |
| 17947 | // Fixed slot |
| 17948 | masm.computeEffectiveAddress( |
| 17949 | BaseValueIndex(object, temp, sizeof(NativeObject)), temp); |
| 17950 | masm.jump(&doStore); |
| 17951 | masm.bind(¬FixedSlot); |
| 17952 | |
| 17953 | #ifdef DEBUG1 |
| 17954 | Label kindOkay; |
| 17955 | masm.branch32(Assembler::Equal, temp2, |
| 17956 | Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); |
| 17957 | masm.assumeUnreachable("Invalid PropertyIndex::Kind"); |
| 17958 | masm.bind(&kindOkay); |
| 17959 | #endif |
| 17960 | |
| 17961 | // Dense element |
| 17962 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp2); |
| 17963 | Label indexOkay; |
| 17964 | Address initLength(temp2, ObjectElements::offsetOfInitializedLength()); |
| 17965 | masm.branch32(Assembler::Above, initLength, temp, &indexOkay); |
| 17966 | masm.assumeUnreachable("Dense element out of bounds"); |
| 17967 | masm.bind(&indexOkay); |
| 17968 | |
| 17969 | BaseObjectElementIndex elementAddress(temp2, temp); |
| 17970 | masm.computeEffectiveAddress(elementAddress, temp); |
| 17971 | |
| 17972 | masm.bind(&doStore); |
| 17973 | Address storeAddress(temp, 0); |
| 17974 | emitPreBarrier(storeAddress); |
| 17975 | masm.storeValue(value, storeAddress); |
| 17976 | |
| 17977 | masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp2, &done); |
| 17978 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp2, &done); |
| 17979 | |
| 17980 | saveVolatile(temp2); |
| 17981 | emitPostWriteBarrier(object); |
| 17982 | restoreVolatile(temp2); |
| 17983 | |
| 17984 | masm.bind(&done); |
| 17985 | } |
| 17986 | |
| 17987 | void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) { |
| 17988 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 17989 | Register objReg = ToRegister(ins->object()); |
| 17990 | Register temp = ToRegister(ins->temp0()); |
| 17991 | |
| 17992 | ConstantOrRegister id = toConstantOrRegister(ins, LSetPropertyCache::IdIndex, |
| 17993 | ins->mir()->idval()->type()); |
| 17994 | ConstantOrRegister value = toConstantOrRegister( |
| 17995 | ins, LSetPropertyCache::ValueIndex, ins->mir()->value()->type()); |
| 17996 | |
| 17997 | addSetPropertyCache(ins, liveRegs, objReg, temp, id, value, |
| 17998 | ins->mir()->strict()); |
| 17999 | } |
| 18000 | |
| 18001 | void CodeGenerator::visitThrow(LThrow* lir) { |
| 18002 | pushArg(ToValue(lir, LThrow::ValueIndex)); |
| 18003 | |
| 18004 | using Fn = bool (*)(JSContext*, HandleValue); |
| 18005 | callVM<Fn, js::ThrowOperation>(lir); |
| 18006 | } |
| 18007 | |
| 18008 | void CodeGenerator::visitThrowWithStack(LThrowWithStack* lir) { |
| 18009 | pushArg(ToValue(lir, LThrowWithStack::StackIndex)); |
| 18010 | pushArg(ToValue(lir, LThrowWithStack::ValueIndex)); |
| 18011 | |
| 18012 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue); |
| 18013 | callVM<Fn, js::ThrowWithStackOperation>(lir); |
| 18014 | } |
| 18015 | |
| 18016 | class OutOfLineTypeOfV : public OutOfLineCodeBase<CodeGenerator> { |
| 18017 | LTypeOfV* ins_; |
| 18018 | |
| 18019 | public: |
| 18020 | explicit OutOfLineTypeOfV(LTypeOfV* ins) : ins_(ins) {} |
| 18021 | |
| 18022 | void accept(CodeGenerator* codegen) override { |
| 18023 | codegen->visitOutOfLineTypeOfV(this); |
| 18024 | } |
| 18025 | LTypeOfV* ins() const { return ins_; } |
| 18026 | }; |
| 18027 | |
| 18028 | void CodeGenerator::emitTypeOfJSType(JSValueType type, Register output) { |
| 18029 | switch (type) { |
| 18030 | case JSVAL_TYPE_OBJECT: |
| 18031 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
| 18032 | break; |
| 18033 | case JSVAL_TYPE_DOUBLE: |
| 18034 | case JSVAL_TYPE_INT32: |
| 18035 | masm.move32(Imm32(JSTYPE_NUMBER), output); |
| 18036 | break; |
| 18037 | case JSVAL_TYPE_BOOLEAN: |
| 18038 | masm.move32(Imm32(JSTYPE_BOOLEAN), output); |
| 18039 | break; |
| 18040 | case JSVAL_TYPE_UNDEFINED: |
| 18041 | masm.move32(Imm32(JSTYPE_UNDEFINED), output); |
| 18042 | break; |
| 18043 | case JSVAL_TYPE_NULL: |
| 18044 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
| 18045 | break; |
| 18046 | case JSVAL_TYPE_STRING: |
| 18047 | masm.move32(Imm32(JSTYPE_STRING), output); |
| 18048 | break; |
| 18049 | case JSVAL_TYPE_SYMBOL: |
| 18050 | masm.move32(Imm32(JSTYPE_SYMBOL), output); |
| 18051 | break; |
| 18052 | case JSVAL_TYPE_BIGINT: |
| 18053 | masm.move32(Imm32(JSTYPE_BIGINT), output); |
| 18054 | break; |
| 18055 | default: |
| 18056 | MOZ_CRASH("Unsupported JSValueType")do { do { } while (false); MOZ_ReportCrash("" "Unsupported JSValueType" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18056); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported JSValueType" ")"); do { *((volatile int*)__null) = 18056; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18057 | } |
| 18058 | } |
| 18059 | |
| 18060 | void CodeGenerator::emitTypeOfCheck(JSValueType type, Register tag, |
| 18061 | Register output, Label* done, |
| 18062 | Label* oolObject) { |
| 18063 | Label notMatch; |
| 18064 | switch (type) { |
| 18065 | case JSVAL_TYPE_OBJECT: |
| 18066 | // The input may be a callable object (result is "function") or |
| 18067 | // may emulate undefined (result is "undefined"). Use an OOL path. |
| 18068 | masm.branchTestObject(Assembler::Equal, tag, oolObject); |
| 18069 | return; |
| 18070 | case JSVAL_TYPE_DOUBLE: |
| 18071 | case JSVAL_TYPE_INT32: |
| 18072 | masm.branchTestNumber(Assembler::NotEqual, tag, ¬Match); |
| 18073 | break; |
| 18074 | default: |
| 18075 | masm.branchTestType(Assembler::NotEqual, tag, type, ¬Match); |
| 18076 | break; |
| 18077 | } |
| 18078 | |
| 18079 | emitTypeOfJSType(type, output); |
| 18080 | masm.jump(done); |
| 18081 | masm.bind(¬Match); |
| 18082 | } |
| 18083 | |
| 18084 | void CodeGenerator::visitTypeOfV(LTypeOfV* lir) { |
| 18085 | const ValueOperand value = ToValue(lir, LTypeOfV::InputIndex); |
| 18086 | Register output = ToRegister(lir->output()); |
| 18087 | Register tag = masm.extractTag(value, output); |
| 18088 | |
| 18089 | Label done; |
| 18090 | |
| 18091 | auto* ool = new (alloc()) OutOfLineTypeOfV(lir); |
| 18092 | addOutOfLineCode(ool, lir->mir()); |
| 18093 | |
| 18094 | const std::initializer_list<JSValueType> defaultOrder = { |
| 18095 | JSVAL_TYPE_OBJECT, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_UNDEFINED, |
| 18096 | JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_STRING, |
| 18097 | JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT}; |
| 18098 | |
| 18099 | mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder); |
| 18100 | |
| 18101 | // Generate checks for previously observed types first. |
| 18102 | // The TypeDataList is sorted by descending frequency. |
| 18103 | for (auto& observed : lir->mir()->observedTypes()) { |
| 18104 | JSValueType type = observed.type(); |
| 18105 | |
| 18106 | // Unify number types. |
| 18107 | if (type == JSVAL_TYPE_INT32) { |
| 18108 | type = JSVAL_TYPE_DOUBLE; |
| 18109 | } |
| 18110 | |
| 18111 | remaining -= type; |
| 18112 | |
| 18113 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
| 18114 | } |
| 18115 | |
| 18116 | // Generate checks for remaining types. |
| 18117 | for (auto type : defaultOrder) { |
| 18118 | if (!remaining.contains(type)) { |
| 18119 | continue; |
| 18120 | } |
| 18121 | remaining -= type; |
| 18122 | |
| 18123 | if (remaining.isEmpty() && type != JSVAL_TYPE_OBJECT) { |
| 18124 | // We can skip the check for the last remaining type, unless the type is |
| 18125 | // JSVAL_TYPE_OBJECT, which may have to go through the OOL path. |
| 18126 | #ifdef DEBUG1 |
| 18127 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
| 18128 | masm.assumeUnreachable("Unexpected Value type in visitTypeOfV"); |
| 18129 | #else |
| 18130 | emitTypeOfJSType(type, output); |
| 18131 | #endif |
| 18132 | } else { |
| 18133 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
| 18134 | } |
| 18135 | } |
| 18136 | MOZ_ASSERT(remaining.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(remaining.isEmpty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(remaining.isEmpty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("remaining.isEmpty()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18136); AnnotateMozCrashReason("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { *((volatile int*)__null) = 18136; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18137 | |
| 18138 | masm.bind(&done); |
| 18139 | masm.bind(ool->rejoin()); |
| 18140 | } |
| 18141 | |
| 18142 | void CodeGenerator::emitTypeOfObject(Register obj, Register output, |
| 18143 | Label* done) { |
| 18144 | Label slowCheck, isObject, isCallable, isUndefined; |
| 18145 | masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable, |
| 18146 | &isUndefined); |
| 18147 | |
| 18148 | masm.bind(&isCallable); |
| 18149 | masm.move32(Imm32(JSTYPE_FUNCTION), output); |
| 18150 | masm.jump(done); |
| 18151 | |
| 18152 | masm.bind(&isUndefined); |
| 18153 | masm.move32(Imm32(JSTYPE_UNDEFINED), output); |
| 18154 | masm.jump(done); |
| 18155 | |
| 18156 | masm.bind(&isObject); |
| 18157 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
| 18158 | masm.jump(done); |
| 18159 | |
| 18160 | masm.bind(&slowCheck); |
| 18161 | |
| 18162 | saveVolatile(output); |
| 18163 | using Fn = JSType (*)(JSObject*); |
| 18164 | masm.setupAlignedABICall(); |
| 18165 | masm.passABIArg(obj); |
| 18166 | masm.callWithABI<Fn, js::TypeOfObject>(); |
| 18167 | masm.storeCallInt32Result(output); |
| 18168 | restoreVolatile(output); |
| 18169 | } |
| 18170 | |
| 18171 | void CodeGenerator::visitOutOfLineTypeOfV(OutOfLineTypeOfV* ool) { |
| 18172 | LTypeOfV* ins = ool->ins(); |
| 18173 | |
| 18174 | ValueOperand input = ToValue(ins, LTypeOfV::InputIndex); |
| 18175 | Register temp = ToTempUnboxRegister(ins->temp0()); |
| 18176 | Register output = ToRegister(ins->output()); |
| 18177 | |
| 18178 | Register obj = masm.extractObject(input, temp); |
| 18179 | emitTypeOfObject(obj, output, ool->rejoin()); |
| 18180 | masm.jump(ool->rejoin()); |
| 18181 | } |
| 18182 | |
| 18183 | void CodeGenerator::visitTypeOfO(LTypeOfO* lir) { |
| 18184 | Register obj = ToRegister(lir->object()); |
| 18185 | Register output = ToRegister(lir->output()); |
| 18186 | |
| 18187 | Label done; |
| 18188 | emitTypeOfObject(obj, output, &done); |
| 18189 | masm.bind(&done); |
| 18190 | } |
| 18191 | |
| 18192 | void CodeGenerator::visitTypeOfName(LTypeOfName* lir) { |
| 18193 | Register input = ToRegister(lir->input()); |
| 18194 | Register output = ToRegister(lir->output()); |
| 18195 | |
| 18196 | #ifdef DEBUG1 |
| 18197 | Label ok; |
| 18198 | masm.branch32(Assembler::Below, input, Imm32(JSTYPE_LIMIT), &ok); |
| 18199 | masm.assumeUnreachable("bad JSType"); |
| 18200 | masm.bind(&ok); |
| 18201 | #endif |
| 18202 | |
| 18203 | static_assert(JSTYPE_UNDEFINED == 0); |
| 18204 | |
| 18205 | masm.movePtr(ImmPtr(&gen->runtime->names().undefined), output); |
| 18206 | masm.loadPtr(BaseIndex(output, input, ScalePointer), output); |
| 18207 | } |
| 18208 | |
| 18209 | class OutOfLineTypeOfIsNonPrimitiveV : public OutOfLineCodeBase<CodeGenerator> { |
| 18210 | LTypeOfIsNonPrimitiveV* ins_; |
| 18211 | |
| 18212 | public: |
| 18213 | explicit OutOfLineTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* ins) |
| 18214 | : ins_(ins) {} |
| 18215 | |
| 18216 | void accept(CodeGenerator* codegen) override { |
| 18217 | codegen->visitOutOfLineTypeOfIsNonPrimitiveV(this); |
| 18218 | } |
| 18219 | auto* ins() const { return ins_; } |
| 18220 | }; |
| 18221 | |
| 18222 | class OutOfLineTypeOfIsNonPrimitiveO : public OutOfLineCodeBase<CodeGenerator> { |
| 18223 | LTypeOfIsNonPrimitiveO* ins_; |
| 18224 | |
| 18225 | public: |
| 18226 | explicit OutOfLineTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* ins) |
| 18227 | : ins_(ins) {} |
| 18228 | |
| 18229 | void accept(CodeGenerator* codegen) override { |
| 18230 | codegen->visitOutOfLineTypeOfIsNonPrimitiveO(this); |
| 18231 | } |
| 18232 | auto* ins() const { return ins_; } |
| 18233 | }; |
| 18234 | |
| 18235 | void CodeGenerator::emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj, |
| 18236 | Register output) { |
| 18237 | saveVolatile(output); |
| 18238 | using Fn = JSType (*)(JSObject*); |
| 18239 | masm.setupAlignedABICall(); |
| 18240 | masm.passABIArg(obj); |
| 18241 | masm.callWithABI<Fn, js::TypeOfObject>(); |
| 18242 | masm.storeCallInt32Result(output); |
| 18243 | restoreVolatile(output); |
| 18244 | |
| 18245 | auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false); |
| 18246 | masm.cmp32Set(cond, output, Imm32(mir->jstype()), output); |
| 18247 | } |
| 18248 | |
| 18249 | void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveV( |
| 18250 | OutOfLineTypeOfIsNonPrimitiveV* ool) { |
| 18251 | auto* ins = ool->ins(); |
| 18252 | ValueOperand input = ToValue(ins, LTypeOfIsNonPrimitiveV::InputIndex); |
| 18253 | Register output = ToRegister(ins->output()); |
| 18254 | Register temp = ToTempUnboxRegister(ins->temp0()); |
| 18255 | |
| 18256 | Register obj = masm.extractObject(input, temp); |
| 18257 | |
| 18258 | emitTypeOfIsObjectOOL(ins->mir(), obj, output); |
| 18259 | |
| 18260 | masm.jump(ool->rejoin()); |
| 18261 | } |
| 18262 | |
| 18263 | void CodeGenerator::visitOutOfLineTypeOfIsNonPrimitiveO( |
| 18264 | OutOfLineTypeOfIsNonPrimitiveO* ool) { |
| 18265 | auto* ins = ool->ins(); |
| 18266 | Register input = ToRegister(ins->input()); |
| 18267 | Register output = ToRegister(ins->output()); |
| 18268 | |
| 18269 | emitTypeOfIsObjectOOL(ins->mir(), input, output); |
| 18270 | |
| 18271 | masm.jump(ool->rejoin()); |
| 18272 | } |
| 18273 | |
| 18274 | void CodeGenerator::emitTypeOfIsObject(MTypeOfIs* mir, Register obj, |
| 18275 | Register output, Label* success, |
| 18276 | Label* fail, Label* slowCheck) { |
| 18277 | Label* isObject = fail; |
| 18278 | Label* isFunction = fail; |
| 18279 | Label* isUndefined = fail; |
| 18280 | |
| 18281 | switch (mir->jstype()) { |
| 18282 | case JSTYPE_UNDEFINED: |
| 18283 | isUndefined = success; |
| 18284 | break; |
| 18285 | |
| 18286 | case JSTYPE_OBJECT: |
| 18287 | isObject = success; |
| 18288 | break; |
| 18289 | |
| 18290 | case JSTYPE_FUNCTION: |
| 18291 | isFunction = success; |
| 18292 | break; |
| 18293 | |
| 18294 | case JSTYPE_STRING: |
| 18295 | case JSTYPE_NUMBER: |
| 18296 | case JSTYPE_BOOLEAN: |
| 18297 | case JSTYPE_SYMBOL: |
| 18298 | case JSTYPE_BIGINT: |
| 18299 | #ifdef ENABLE_RECORD_TUPLE |
| 18300 | case JSTYPE_RECORD: |
| 18301 | case JSTYPE_TUPLE: |
| 18302 | #endif |
| 18303 | case JSTYPE_LIMIT: |
| 18304 | MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18304); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type" ")"); do { *((volatile int*)__null) = 18304; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18305 | } |
| 18306 | |
| 18307 | masm.typeOfObject(obj, output, slowCheck, isObject, isFunction, isUndefined); |
| 18308 | |
| 18309 | auto op = mir->jsop(); |
| 18310 | |
| 18311 | Label done; |
| 18312 | masm.bind(fail); |
| 18313 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
| 18314 | masm.jump(&done); |
| 18315 | masm.bind(success); |
| 18316 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
| 18317 | masm.bind(&done); |
| 18318 | } |
| 18319 | |
| 18320 | void CodeGenerator::visitTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* lir) { |
| 18321 | ValueOperand input = ToValue(lir, LTypeOfIsNonPrimitiveV::InputIndex); |
| 18322 | Register output = ToRegister(lir->output()); |
| 18323 | Register temp = ToTempUnboxRegister(lir->temp0()); |
| 18324 | |
| 18325 | auto* mir = lir->mir(); |
| 18326 | |
| 18327 | auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveV(lir); |
| 18328 | addOutOfLineCode(ool, mir); |
| 18329 | |
| 18330 | Label success, fail; |
| 18331 | |
| 18332 | switch (mir->jstype()) { |
| 18333 | case JSTYPE_UNDEFINED: { |
| 18334 | ScratchTagScope tag(masm, input); |
| 18335 | masm.splitTagForTest(input, tag); |
| 18336 | |
| 18337 | masm.branchTestUndefined(Assembler::Equal, tag, &success); |
| 18338 | masm.branchTestObject(Assembler::NotEqual, tag, &fail); |
| 18339 | break; |
| 18340 | } |
| 18341 | |
| 18342 | case JSTYPE_OBJECT: { |
| 18343 | ScratchTagScope tag(masm, input); |
| 18344 | masm.splitTagForTest(input, tag); |
| 18345 | |
| 18346 | masm.branchTestNull(Assembler::Equal, tag, &success); |
| 18347 | masm.branchTestObject(Assembler::NotEqual, tag, &fail); |
| 18348 | break; |
| 18349 | } |
| 18350 | |
| 18351 | case JSTYPE_FUNCTION: { |
| 18352 | masm.branchTestObject(Assembler::NotEqual, input, &fail); |
| 18353 | break; |
| 18354 | } |
| 18355 | |
| 18356 | case JSTYPE_STRING: |
| 18357 | case JSTYPE_NUMBER: |
| 18358 | case JSTYPE_BOOLEAN: |
| 18359 | case JSTYPE_SYMBOL: |
| 18360 | case JSTYPE_BIGINT: |
| 18361 | #ifdef ENABLE_RECORD_TUPLE |
| 18362 | case JSTYPE_RECORD: |
| 18363 | case JSTYPE_TUPLE: |
| 18364 | #endif |
| 18365 | case JSTYPE_LIMIT: |
| 18366 | MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18366); AnnotateMozCrashReason("MOZ_CRASH(" "Primitive type" ")"); do { *((volatile int*)__null) = 18366; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18367 | } |
| 18368 | |
| 18369 | Register obj = masm.extractObject(input, temp); |
| 18370 | |
| 18371 | emitTypeOfIsObject(mir, obj, output, &success, &fail, ool->entry()); |
| 18372 | |
| 18373 | masm.bind(ool->rejoin()); |
| 18374 | } |
| 18375 | |
| 18376 | void CodeGenerator::visitTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* lir) { |
| 18377 | Register input = ToRegister(lir->input()); |
| 18378 | Register output = ToRegister(lir->output()); |
| 18379 | |
| 18380 | auto* mir = lir->mir(); |
| 18381 | |
| 18382 | auto* ool = new (alloc()) OutOfLineTypeOfIsNonPrimitiveO(lir); |
| 18383 | addOutOfLineCode(ool, mir); |
| 18384 | |
| 18385 | Label success, fail; |
| 18386 | emitTypeOfIsObject(mir, input, output, &success, &fail, ool->entry()); |
| 18387 | |
| 18388 | masm.bind(ool->rejoin()); |
| 18389 | } |
| 18390 | |
| 18391 | void CodeGenerator::visitTypeOfIsPrimitive(LTypeOfIsPrimitive* lir) { |
| 18392 | ValueOperand input = ToValue(lir, LTypeOfIsPrimitive::InputIndex); |
| 18393 | Register output = ToRegister(lir->output()); |
| 18394 | |
| 18395 | auto* mir = lir->mir(); |
| 18396 | auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false); |
| 18397 | |
| 18398 | switch (mir->jstype()) { |
| 18399 | case JSTYPE_STRING: |
| 18400 | masm.testStringSet(cond, input, output); |
| 18401 | break; |
| 18402 | case JSTYPE_NUMBER: |
| 18403 | masm.testNumberSet(cond, input, output); |
| 18404 | break; |
| 18405 | case JSTYPE_BOOLEAN: |
| 18406 | masm.testBooleanSet(cond, input, output); |
| 18407 | break; |
| 18408 | case JSTYPE_SYMBOL: |
| 18409 | masm.testSymbolSet(cond, input, output); |
| 18410 | break; |
| 18411 | case JSTYPE_BIGINT: |
| 18412 | masm.testBigIntSet(cond, input, output); |
| 18413 | break; |
| 18414 | |
| 18415 | case JSTYPE_UNDEFINED: |
| 18416 | case JSTYPE_OBJECT: |
| 18417 | case JSTYPE_FUNCTION: |
| 18418 | #ifdef ENABLE_RECORD_TUPLE |
| 18419 | case JSTYPE_RECORD: |
| 18420 | case JSTYPE_TUPLE: |
| 18421 | #endif |
| 18422 | case JSTYPE_LIMIT: |
| 18423 | MOZ_CRASH("Non-primitive type")do { do { } while (false); MOZ_ReportCrash("" "Non-primitive type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18423); AnnotateMozCrashReason("MOZ_CRASH(" "Non-primitive type" ")"); do { *((volatile int*)__null) = 18423; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18424 | } |
| 18425 | } |
| 18426 | |
| 18427 | void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) { |
| 18428 | pushArg(ToValue(lir, LToAsyncIter::NextMethodIndex)); |
| 18429 | pushArg(ToRegister(lir->iterator())); |
| 18430 | |
| 18431 | using Fn = JSObject* (*)(JSContext*, HandleObject, HandleValue); |
| 18432 | callVM<Fn, js::CreateAsyncFromSyncIterator>(lir); |
| 18433 | } |
| 18434 | |
| 18435 | void CodeGenerator::visitToPropertyKeyCache(LToPropertyKeyCache* lir) { |
| 18436 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 18437 | ValueOperand input = ToValue(lir, LToPropertyKeyCache::InputIndex); |
| 18438 | ValueOperand output = ToOutValue(lir); |
| 18439 | |
| 18440 | IonToPropertyKeyIC ic(liveRegs, input, output); |
| 18441 | addIC(lir, allocateIC(ic)); |
| 18442 | } |
| 18443 | |
| 18444 | void CodeGenerator::visitLoadElementV(LLoadElementV* load) { |
| 18445 | Register elements = ToRegister(load->elements()); |
| 18446 | const ValueOperand out = ToOutValue(load); |
| 18447 | |
| 18448 | if (load->index()->isConstant()) { |
| 18449 | NativeObject::elementsSizeMustNotOverflow(); |
| 18450 | int32_t offset = ToInt32(load->index()) * sizeof(Value); |
| 18451 | masm.loadValue(Address(elements, offset), out); |
| 18452 | } else { |
| 18453 | masm.loadValue(BaseObjectElementIndex(elements, ToRegister(load->index())), |
| 18454 | out); |
| 18455 | } |
| 18456 | |
| 18457 | Label testMagic; |
| 18458 | masm.branchTestMagic(Assembler::Equal, out, &testMagic); |
| 18459 | bailoutFrom(&testMagic, load->snapshot()); |
| 18460 | } |
| 18461 | |
| 18462 | void CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) { |
| 18463 | Register elements = ToRegister(lir->elements()); |
| 18464 | Register index = ToRegister(lir->index()); |
| 18465 | Register initLength = ToRegister(lir->initLength()); |
| 18466 | const ValueOperand out = ToOutValue(lir); |
| 18467 | |
| 18468 | const MLoadElementHole* mir = lir->mir(); |
| 18469 | |
| 18470 | // If the index is out of bounds, load |undefined|. Otherwise, load the |
| 18471 | // value. |
| 18472 | Label outOfBounds, done; |
| 18473 | masm.spectreBoundsCheck32(index, initLength, out.scratchReg(), &outOfBounds); |
| 18474 | |
| 18475 | masm.loadValue(BaseObjectElementIndex(elements, index), out); |
| 18476 | |
| 18477 | // If the value wasn't a hole, we're done. Otherwise, we'll load undefined. |
| 18478 | masm.branchTestMagic(Assembler::NotEqual, out, &done); |
| 18479 | |
| 18480 | if (mir->needsNegativeIntCheck()) { |
| 18481 | Label loadUndefined; |
| 18482 | masm.jump(&loadUndefined); |
| 18483 | |
| 18484 | masm.bind(&outOfBounds); |
| 18485 | |
| 18486 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 18487 | |
| 18488 | masm.bind(&loadUndefined); |
| 18489 | } else { |
| 18490 | masm.bind(&outOfBounds); |
| 18491 | } |
| 18492 | masm.moveValue(UndefinedValue(), out); |
| 18493 | |
| 18494 | masm.bind(&done); |
| 18495 | } |
| 18496 | |
| 18497 | void CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) { |
| 18498 | Register elements = ToRegister(lir->elements()); |
| 18499 | Register temp0 = ToTempRegisterOrInvalid(lir->temp0()); |
| 18500 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
| 18501 | AnyRegister out = ToAnyRegister(lir->output()); |
| 18502 | |
| 18503 | const MLoadUnboxedScalar* mir = lir->mir(); |
| 18504 | |
| 18505 | Scalar::Type storageType = mir->storageType(); |
| 18506 | |
| 18507 | LiveRegisterSet volatileRegs; |
| 18508 | if (MacroAssembler::LoadRequiresCall(storageType)) { |
| 18509 | volatileRegs = liveVolatileRegs(lir); |
| 18510 | } |
| 18511 | |
| 18512 | Label fail; |
| 18513 | if (lir->index()->isConstant()) { |
| 18514 | Address source = |
| 18515 | ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment()); |
| 18516 | masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail, |
| 18517 | volatileRegs); |
| 18518 | } else { |
| 18519 | BaseIndex source(elements, ToRegister(lir->index()), |
| 18520 | ScaleFromScalarType(storageType), mir->offsetAdjustment()); |
| 18521 | masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail, |
| 18522 | volatileRegs); |
| 18523 | } |
| 18524 | |
| 18525 | if (fail.used()) { |
| 18526 | bailoutFrom(&fail, lir->snapshot()); |
| 18527 | } |
| 18528 | } |
| 18529 | |
| 18530 | void CodeGenerator::visitLoadUnboxedInt64(LLoadUnboxedInt64* lir) { |
| 18531 | Register elements = ToRegister(lir->elements()); |
| 18532 | Register64 out = ToOutRegister64(lir); |
| 18533 | |
| 18534 | const MLoadUnboxedScalar* mir = lir->mir(); |
| 18535 | |
| 18536 | Scalar::Type storageType = mir->storageType(); |
| 18537 | |
| 18538 | if (lir->index()->isConstant()) { |
| 18539 | Address source = |
| 18540 | ToAddress(elements, lir->index(), storageType, mir->offsetAdjustment()); |
| 18541 | masm.load64(source, out); |
| 18542 | } else { |
| 18543 | BaseIndex source(elements, ToRegister(lir->index()), |
| 18544 | ScaleFromScalarType(storageType), mir->offsetAdjustment()); |
| 18545 | masm.load64(source, out); |
| 18546 | } |
| 18547 | } |
| 18548 | |
| 18549 | void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) { |
| 18550 | Register elements = ToRegister(lir->elements()); |
| 18551 | const LAllocation* littleEndian = lir->littleEndian(); |
| 18552 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
| 18553 | Register temp2 = ToTempRegisterOrInvalid(lir->temp2()); |
| 18554 | Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64()); |
| 18555 | AnyRegister out = ToAnyRegister(lir->output()); |
| 18556 | |
| 18557 | const MLoadDataViewElement* mir = lir->mir(); |
| 18558 | Scalar::Type storageType = mir->storageType(); |
| 18559 | |
| 18560 | LiveRegisterSet volatileRegs; |
| 18561 | if (MacroAssembler::LoadRequiresCall(storageType)) { |
| 18562 | volatileRegs = liveVolatileRegs(lir); |
| 18563 | } |
| 18564 | |
| 18565 | BaseIndex source(elements, ToRegister(lir->index()), TimesOne); |
| 18566 | |
| 18567 | bool noSwap = littleEndian->isConstant() && |
| 18568 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
| 18569 | |
| 18570 | // Directly load if no byte swap is needed and the platform supports unaligned |
| 18571 | // accesses for the access. (Such support is assumed for integer types.) |
| 18572 | if (noSwap && (!Scalar::isFloatingType(storageType) || |
| 18573 | MacroAssembler::SupportsFastUnalignedFPAccesses())) { |
| 18574 | Label fail; |
| 18575 | masm.loadFromTypedArray(storageType, source, out, temp1, temp2, &fail, |
| 18576 | volatileRegs); |
| 18577 | |
| 18578 | if (fail.used()) { |
| 18579 | bailoutFrom(&fail, lir->snapshot()); |
| 18580 | } |
| 18581 | return; |
| 18582 | } |
| 18583 | |
| 18584 | // Load the value into a gpr register. |
| 18585 | switch (storageType) { |
| 18586 | case Scalar::Int16: |
| 18587 | masm.load16UnalignedSignExtend(source, out.gpr()); |
| 18588 | break; |
| 18589 | case Scalar::Uint16: |
| 18590 | masm.load16UnalignedZeroExtend(source, out.gpr()); |
| 18591 | break; |
| 18592 | case Scalar::Int32: |
| 18593 | masm.load32Unaligned(source, out.gpr()); |
| 18594 | break; |
| 18595 | case Scalar::Uint32: |
| 18596 | masm.load32Unaligned(source, out.isFloat() ? temp1 : out.gpr()); |
| 18597 | break; |
| 18598 | case Scalar::Float16: |
| 18599 | masm.load16UnalignedZeroExtend(source, temp1); |
| 18600 | break; |
| 18601 | case Scalar::Float32: |
| 18602 | masm.load32Unaligned(source, temp1); |
| 18603 | break; |
| 18604 | case Scalar::Float64: |
| 18605 | masm.load64Unaligned(source, temp64); |
| 18606 | break; |
| 18607 | case Scalar::Int8: |
| 18608 | case Scalar::Uint8: |
| 18609 | case Scalar::Uint8Clamped: |
| 18610 | case Scalar::BigInt64: |
| 18611 | case Scalar::BigUint64: |
| 18612 | default: |
| 18613 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18613); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18613; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18614 | } |
| 18615 | |
| 18616 | if (!noSwap) { |
| 18617 | // Swap the bytes in the loaded value. |
| 18618 | Label skip; |
| 18619 | if (!littleEndian->isConstant()) { |
| 18620 | masm.branch32( |
| 18621 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
| 18622 | ToRegister(littleEndian), Imm32(0), &skip); |
| 18623 | } |
| 18624 | |
| 18625 | switch (storageType) { |
| 18626 | case Scalar::Int16: |
| 18627 | masm.byteSwap16SignExtend(out.gpr()); |
| 18628 | break; |
| 18629 | case Scalar::Uint16: |
| 18630 | masm.byteSwap16ZeroExtend(out.gpr()); |
| 18631 | break; |
| 18632 | case Scalar::Int32: |
| 18633 | masm.byteSwap32(out.gpr()); |
| 18634 | break; |
| 18635 | case Scalar::Uint32: |
| 18636 | masm.byteSwap32(out.isFloat() ? temp1 : out.gpr()); |
| 18637 | break; |
| 18638 | case Scalar::Float16: |
| 18639 | masm.byteSwap16ZeroExtend(temp1); |
| 18640 | break; |
| 18641 | case Scalar::Float32: |
| 18642 | masm.byteSwap32(temp1); |
| 18643 | break; |
| 18644 | case Scalar::Float64: |
| 18645 | masm.byteSwap64(temp64); |
| 18646 | break; |
| 18647 | case Scalar::Int8: |
| 18648 | case Scalar::Uint8: |
| 18649 | case Scalar::Uint8Clamped: |
| 18650 | case Scalar::BigInt64: |
| 18651 | case Scalar::BigUint64: |
| 18652 | default: |
| 18653 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18653); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18653; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18654 | } |
| 18655 | |
| 18656 | if (skip.used()) { |
| 18657 | masm.bind(&skip); |
| 18658 | } |
| 18659 | } |
| 18660 | |
| 18661 | // Move the value into the output register. |
| 18662 | switch (storageType) { |
| 18663 | case Scalar::Int16: |
| 18664 | case Scalar::Uint16: |
| 18665 | case Scalar::Int32: |
| 18666 | break; |
| 18667 | case Scalar::Uint32: |
| 18668 | if (out.isFloat()) { |
| 18669 | masm.convertUInt32ToDouble(temp1, out.fpu()); |
| 18670 | } else { |
| 18671 | // Bail out if the value doesn't fit into a signed int32 value. This |
| 18672 | // is what allows MLoadDataViewElement to have a type() of |
| 18673 | // MIRType::Int32 for UInt32 array loads. |
| 18674 | bailoutTest32(Assembler::Signed, out.gpr(), out.gpr(), lir->snapshot()); |
| 18675 | } |
| 18676 | break; |
| 18677 | case Scalar::Float16: |
| 18678 | masm.moveGPRToFloat16(temp1, out.fpu(), temp2, volatileRegs); |
| 18679 | masm.canonicalizeFloat(out.fpu()); |
| 18680 | break; |
| 18681 | case Scalar::Float32: |
| 18682 | masm.moveGPRToFloat32(temp1, out.fpu()); |
| 18683 | masm.canonicalizeFloat(out.fpu()); |
| 18684 | break; |
| 18685 | case Scalar::Float64: |
| 18686 | masm.moveGPR64ToDouble(temp64, out.fpu()); |
| 18687 | masm.canonicalizeDouble(out.fpu()); |
| 18688 | break; |
| 18689 | case Scalar::Int8: |
| 18690 | case Scalar::Uint8: |
| 18691 | case Scalar::Uint8Clamped: |
| 18692 | case Scalar::BigInt64: |
| 18693 | case Scalar::BigUint64: |
| 18694 | default: |
| 18695 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18695); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 18695; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18696 | } |
| 18697 | } |
| 18698 | |
| 18699 | void CodeGenerator::visitLoadDataViewElement64(LLoadDataViewElement64* lir) { |
| 18700 | Register elements = ToRegister(lir->elements()); |
| 18701 | const LAllocation* littleEndian = lir->littleEndian(); |
| 18702 | Register64 out = ToOutRegister64(lir); |
| 18703 | |
| 18704 | MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->storageType()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Scalar::isBigIntType(lir->mir()->storageType() ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(Scalar::isBigIntType(lir->mir()->storageType() )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("Scalar::isBigIntType(lir->mir()->storageType())", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18704); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->storageType())" ")"); do { *((volatile int*)__null) = 18704; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18705 | |
| 18706 | BaseIndex source(elements, ToRegister(lir->index()), TimesOne); |
| 18707 | |
| 18708 | bool noSwap = littleEndian->isConstant() && |
| 18709 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
| 18710 | |
| 18711 | // Load the value into a register. |
| 18712 | masm.load64Unaligned(source, out); |
| 18713 | |
| 18714 | if (!noSwap) { |
| 18715 | // Swap the bytes in the loaded value. |
| 18716 | Label skip; |
| 18717 | if (!littleEndian->isConstant()) { |
| 18718 | masm.branch32( |
| 18719 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
| 18720 | ToRegister(littleEndian), Imm32(0), &skip); |
| 18721 | } |
| 18722 | |
| 18723 | masm.byteSwap64(out); |
| 18724 | |
| 18725 | if (skip.used()) { |
| 18726 | masm.bind(&skip); |
| 18727 | } |
| 18728 | } |
| 18729 | } |
| 18730 | |
| 18731 | void CodeGenerator::visitLoadTypedArrayElementHole( |
| 18732 | LLoadTypedArrayElementHole* lir) { |
| 18733 | Register elements = ToRegister(lir->elements()); |
| 18734 | Register index = ToRegister(lir->index()); |
| 18735 | Register length = ToRegister(lir->length()); |
| 18736 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 18737 | const ValueOperand out = ToOutValue(lir); |
| 18738 | |
| 18739 | Register scratch = out.scratchReg(); |
| 18740 | |
| 18741 | // Load undefined if index >= length. |
| 18742 | Label outOfBounds, done; |
| 18743 | masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds); |
| 18744 | |
| 18745 | Scalar::Type arrayType = lir->mir()->arrayType(); |
| 18746 | |
| 18747 | LiveRegisterSet volatileRegs; |
| 18748 | if (MacroAssembler::LoadRequiresCall(arrayType)) { |
| 18749 | volatileRegs = liveVolatileRegs(lir); |
| 18750 | } |
| 18751 | |
| 18752 | Label fail; |
| 18753 | BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); |
| 18754 | MacroAssembler::Uint32Mode uint32Mode = |
| 18755 | lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble |
| 18756 | : MacroAssembler::Uint32Mode::FailOnDouble; |
| 18757 | masm.loadFromTypedArray(arrayType, source, out, uint32Mode, temp, &fail, |
| 18758 | volatileRegs); |
| 18759 | masm.jump(&done); |
| 18760 | |
| 18761 | masm.bind(&outOfBounds); |
| 18762 | masm.moveValue(UndefinedValue(), out); |
| 18763 | |
| 18764 | if (fail.used()) { |
| 18765 | bailoutFrom(&fail, lir->snapshot()); |
| 18766 | } |
| 18767 | |
| 18768 | masm.bind(&done); |
| 18769 | } |
| 18770 | |
| 18771 | void CodeGenerator::visitLoadTypedArrayElementHoleBigInt( |
| 18772 | LLoadTypedArrayElementHoleBigInt* lir) { |
| 18773 | Register elements = ToRegister(lir->elements()); |
| 18774 | Register index = ToRegister(lir->index()); |
| 18775 | Register length = ToRegister(lir->length()); |
| 18776 | const ValueOperand out = ToOutValue(lir); |
| 18777 | |
| 18778 | Register temp = ToRegister(lir->temp()); |
| 18779 | |
| 18780 | // On x86 there are not enough registers. In that case reuse the output |
| 18781 | // registers as temporaries. |
| 18782 | #ifdef JS_CODEGEN_X86 |
| 18783 | MOZ_ASSERT(lir->temp64().isBogusTemp())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->temp64().isBogusTemp())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->temp64().isBogusTemp ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->temp64().isBogusTemp()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18783); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->temp64().isBogusTemp()" ")"); do { *((volatile int*)__null) = 18783; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18784 | Register64 temp64 = out.toRegister64(); |
| 18785 | #else |
| 18786 | Register64 temp64 = ToRegister64(lir->temp64()); |
| 18787 | #endif |
| 18788 | |
| 18789 | // Load undefined if index >= length. |
| 18790 | Label outOfBounds, done; |
| 18791 | masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds); |
| 18792 | |
| 18793 | Scalar::Type arrayType = lir->mir()->arrayType(); |
| 18794 | BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); |
| 18795 | masm.load64(source, temp64); |
| 18796 | |
| 18797 | #ifdef JS_CODEGEN_X86 |
| 18798 | Register bigInt = temp; |
| 18799 | Register maybeTemp = InvalidReg; |
| 18800 | #else |
| 18801 | Register bigInt = out.scratchReg(); |
| 18802 | Register maybeTemp = temp; |
| 18803 | #endif |
| 18804 | emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp); |
| 18805 | |
| 18806 | masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out); |
| 18807 | masm.jump(&done); |
| 18808 | |
| 18809 | masm.bind(&outOfBounds); |
| 18810 | masm.moveValue(UndefinedValue(), out); |
| 18811 | |
| 18812 | masm.bind(&done); |
| 18813 | } |
| 18814 | |
| 18815 | template <SwitchTableType tableType> |
| 18816 | class OutOfLineSwitch : public OutOfLineCodeBase<CodeGenerator> { |
| 18817 | using LabelsVector = Vector<Label, 0, JitAllocPolicy>; |
| 18818 | using CodeLabelsVector = Vector<CodeLabel, 0, JitAllocPolicy>; |
| 18819 | LabelsVector labels_; |
| 18820 | CodeLabelsVector codeLabels_; |
| 18821 | CodeLabel start_; |
| 18822 | bool isOutOfLine_; |
| 18823 | |
| 18824 | void accept(CodeGenerator* codegen) override { |
| 18825 | codegen->visitOutOfLineSwitch(this); |
| 18826 | } |
| 18827 | |
| 18828 | public: |
| 18829 | explicit OutOfLineSwitch(TempAllocator& alloc) |
| 18830 | : labels_(alloc), codeLabels_(alloc), isOutOfLine_(false) {} |
| 18831 | |
| 18832 | CodeLabel* start() { return &start_; } |
| 18833 | |
| 18834 | CodeLabelsVector& codeLabels() { return codeLabels_; } |
| 18835 | LabelsVector& labels() { return labels_; } |
| 18836 | |
| 18837 | void jumpToCodeEntries(MacroAssembler& masm, Register index, Register temp) { |
| 18838 | Register base; |
| 18839 | if (tableType == SwitchTableType::Inline) { |
| 18840 | #if defined(JS_CODEGEN_ARM) |
| 18841 | base = ::js::jit::pc; |
| 18842 | #else |
| 18843 | MOZ_CRASH("NYI: SwitchTableType::Inline")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::Inline" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18843); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::Inline" ")"); do { *((volatile int*)__null) = 18843; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18844 | #endif |
| 18845 | } else { |
| 18846 | #if defined(JS_CODEGEN_ARM) |
| 18847 | MOZ_CRASH("NYI: SwitchTableType::OutOfLine")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::OutOfLine" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18847); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine" ")"); do { *((volatile int*)__null) = 18847; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18848 | #else |
| 18849 | masm.mov(start(), temp); |
| 18850 | base = temp; |
| 18851 | #endif |
| 18852 | } |
| 18853 | BaseIndex jumpTarget(base, index, ScalePointer); |
| 18854 | masm.branchToComputedAddress(jumpTarget); |
| 18855 | } |
| 18856 | |
| 18857 | // Register an entry in the switch table. |
| 18858 | void addTableEntry(MacroAssembler& masm) { |
| 18859 | if ((!isOutOfLine_ && tableType == SwitchTableType::Inline) || |
| 18860 | (isOutOfLine_ && tableType == SwitchTableType::OutOfLine)) { |
| 18861 | CodeLabel cl; |
| 18862 | masm.writeCodePointer(&cl); |
| 18863 | masm.propagateOOM(codeLabels_.append(std::move(cl))); |
| 18864 | } |
| 18865 | } |
| 18866 | // Register the code, to which the table will jump to. |
| 18867 | void addCodeEntry(MacroAssembler& masm) { |
| 18868 | Label entry; |
| 18869 | masm.bind(&entry); |
| 18870 | masm.propagateOOM(labels_.append(std::move(entry))); |
| 18871 | } |
| 18872 | |
| 18873 | void setOutOfLine() { isOutOfLine_ = true; } |
| 18874 | }; |
| 18875 | |
| 18876 | template <SwitchTableType tableType> |
| 18877 | void CodeGenerator::visitOutOfLineSwitch( |
| 18878 | OutOfLineSwitch<tableType>* jumpTable) { |
| 18879 | jumpTable->setOutOfLine(); |
| 18880 | auto& labels = jumpTable->labels(); |
| 18881 | |
| 18882 | if (tableType == SwitchTableType::OutOfLine) { |
| 18883 | #if defined(JS_CODEGEN_ARM) |
| 18884 | MOZ_CRASH("NYI: SwitchTableType::OutOfLine")do { do { } while (false); MOZ_ReportCrash("" "NYI: SwitchTableType::OutOfLine" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18884); AnnotateMozCrashReason("MOZ_CRASH(" "NYI: SwitchTableType::OutOfLine" ")"); do { *((volatile int*)__null) = 18884; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 18885 | #elif defined(JS_CODEGEN_NONE) |
| 18886 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 18886); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 18886; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
| 18887 | #else |
| 18888 | |
| 18889 | # if defined(JS_CODEGEN_ARM64) |
| 18890 | AutoForbidPoolsAndNops afp( |
| 18891 | &masm, |
| 18892 | (labels.length() + 1) * (sizeof(void*) / vixl::kInstructionSize)); |
| 18893 | # endif |
| 18894 | |
| 18895 | masm.haltingAlign(sizeof(void*)); |
| 18896 | |
| 18897 | // Bind the address of the jump table and reserve the space for code |
| 18898 | // pointers to jump in the newly generated code. |
| 18899 | masm.bind(jumpTable->start()); |
| 18900 | masm.addCodeLabel(*jumpTable->start()); |
| 18901 | for (size_t i = 0, e = labels.length(); i < e; i++) { |
| 18902 | jumpTable->addTableEntry(masm); |
| 18903 | } |
| 18904 | #endif |
| 18905 | } |
| 18906 | |
| 18907 | // Register all reserved pointers of the jump table to target labels. The |
| 18908 | // entries of the jump table need to be absolute addresses and thus must be |
| 18909 | // patched after codegen is finished. |
| 18910 | auto& codeLabels = jumpTable->codeLabels(); |
| 18911 | for (size_t i = 0, e = codeLabels.length(); i < e; i++) { |
| 18912 | auto& cl = codeLabels[i]; |
| 18913 | cl.target()->bind(labels[i].offset()); |
| 18914 | masm.addCodeLabel(cl); |
| 18915 | } |
| 18916 | } |
| 18917 | |
| 18918 | template void CodeGenerator::visitOutOfLineSwitch( |
| 18919 | OutOfLineSwitch<SwitchTableType::Inline>* jumpTable); |
| 18920 | template void CodeGenerator::visitOutOfLineSwitch( |
| 18921 | OutOfLineSwitch<SwitchTableType::OutOfLine>* jumpTable); |
| 18922 | |
| 18923 | template <typename T> |
| 18924 | static inline void StoreToTypedArray(MacroAssembler& masm, |
| 18925 | Scalar::Type writeType, |
| 18926 | const LAllocation* value, const T& dest, |
| 18927 | Register temp, |
| 18928 | LiveRegisterSet volatileRegs) { |
| 18929 | if (Scalar::isFloatingType(writeType)) { |
| 18930 | masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, temp, |
| 18931 | volatileRegs); |
| 18932 | } else { |
| 18933 | if (value->isConstant()) { |
| 18934 | masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest); |
| 18935 | } else { |
| 18936 | masm.storeToTypedIntArray(writeType, ToRegister(value), dest); |
| 18937 | } |
| 18938 | } |
| 18939 | } |
| 18940 | |
| 18941 | void CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) { |
| 18942 | Register elements = ToRegister(lir->elements()); |
| 18943 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 18944 | const LAllocation* value = lir->value(); |
| 18945 | |
| 18946 | const MStoreUnboxedScalar* mir = lir->mir(); |
| 18947 | |
| 18948 | Scalar::Type writeType = mir->writeType(); |
| 18949 | |
| 18950 | LiveRegisterSet volatileRegs; |
| 18951 | if (MacroAssembler::StoreRequiresCall(writeType)) { |
| 18952 | volatileRegs = liveVolatileRegs(lir); |
| 18953 | } |
| 18954 | |
| 18955 | if (lir->index()->isConstant()) { |
| 18956 | Address dest = ToAddress(elements, lir->index(), writeType); |
| 18957 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
| 18958 | } else { |
| 18959 | BaseIndex dest(elements, ToRegister(lir->index()), |
| 18960 | ScaleFromScalarType(writeType)); |
| 18961 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
| 18962 | } |
| 18963 | } |
| 18964 | |
| 18965 | void CodeGenerator::visitStoreUnboxedInt64(LStoreUnboxedInt64* lir) { |
| 18966 | Register elements = ToRegister(lir->elements()); |
| 18967 | Register64 value = ToRegister64(lir->value()); |
| 18968 | |
| 18969 | Scalar::Type writeType = lir->mir()->writeType(); |
| 18970 | |
| 18971 | if (lir->index()->isConstant()) { |
| 18972 | Address dest = ToAddress(elements, lir->index(), writeType); |
| 18973 | masm.storeToTypedBigIntArray(writeType, value, dest); |
| 18974 | } else { |
| 18975 | BaseIndex dest(elements, ToRegister(lir->index()), |
| 18976 | ScaleFromScalarType(writeType)); |
| 18977 | masm.storeToTypedBigIntArray(writeType, value, dest); |
| 18978 | } |
| 18979 | } |
| 18980 | |
| 18981 | void CodeGenerator::visitStoreDataViewElement(LStoreDataViewElement* lir) { |
| 18982 | Register elements = ToRegister(lir->elements()); |
| 18983 | const LAllocation* value = lir->value(); |
| 18984 | const LAllocation* littleEndian = lir->littleEndian(); |
| 18985 | Register temp = ToTempRegisterOrInvalid(lir->temp()); |
| 18986 | Register64 temp64 = ToTempRegister64OrInvalid(lir->temp64()); |
| 18987 | |
| 18988 | const MStoreDataViewElement* mir = lir->mir(); |
| 18989 | Scalar::Type writeType = mir->writeType(); |
| 18990 | |
| 18991 | LiveRegisterSet volatileRegs; |
| 18992 | if (MacroAssembler::StoreRequiresCall(writeType)) { |
| 18993 | volatileRegs = liveVolatileRegs(lir); |
| 18994 | } |
| 18995 | |
| 18996 | BaseIndex dest(elements, ToRegister(lir->index()), TimesOne); |
| 18997 | |
| 18998 | bool noSwap = littleEndian->isConstant() && |
| 18999 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
| 19000 | |
| 19001 | // Directly store if no byte swap is needed and the platform supports |
| 19002 | // unaligned accesses for the access. (Such support is assumed for integer |
| 19003 | // types.) |
| 19004 | if (noSwap && (!Scalar::isFloatingType(writeType) || |
| 19005 | MacroAssembler::SupportsFastUnalignedFPAccesses())) { |
| 19006 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
| 19007 | return; |
| 19008 | } |
| 19009 | |
| 19010 | // Load the value into a gpr register. |
| 19011 | switch (writeType) { |
| 19012 | case Scalar::Int16: |
| 19013 | case Scalar::Uint16: |
| 19014 | case Scalar::Int32: |
| 19015 | case Scalar::Uint32: |
| 19016 | if (value->isConstant()) { |
| 19017 | masm.move32(Imm32(ToInt32(value)), temp); |
| 19018 | } else { |
| 19019 | masm.move32(ToRegister(value), temp); |
| 19020 | } |
| 19021 | break; |
| 19022 | case Scalar::Float16: { |
| 19023 | FloatRegister fvalue = ToFloatRegister(value); |
| 19024 | masm.canonicalizeFloatIfDeterministic(fvalue); |
| 19025 | masm.moveFloat16ToGPR(fvalue, temp, volatileRegs); |
| 19026 | break; |
| 19027 | } |
| 19028 | case Scalar::Float32: { |
| 19029 | FloatRegister fvalue = ToFloatRegister(value); |
| 19030 | masm.canonicalizeFloatIfDeterministic(fvalue); |
| 19031 | masm.moveFloat32ToGPR(fvalue, temp); |
| 19032 | break; |
| 19033 | } |
| 19034 | case Scalar::Float64: { |
| 19035 | FloatRegister fvalue = ToFloatRegister(value); |
| 19036 | masm.canonicalizeDoubleIfDeterministic(fvalue); |
| 19037 | masm.moveDoubleToGPR64(fvalue, temp64); |
| 19038 | break; |
| 19039 | } |
| 19040 | case Scalar::Int8: |
| 19041 | case Scalar::Uint8: |
| 19042 | case Scalar::Uint8Clamped: |
| 19043 | case Scalar::BigInt64: |
| 19044 | case Scalar::BigUint64: |
| 19045 | default: |
| 19046 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19046); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 19046; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 19047 | } |
| 19048 | |
| 19049 | if (!noSwap) { |
| 19050 | // Swap the bytes in the loaded value. |
| 19051 | Label skip; |
| 19052 | if (!littleEndian->isConstant()) { |
| 19053 | masm.branch32( |
| 19054 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
| 19055 | ToRegister(littleEndian), Imm32(0), &skip); |
| 19056 | } |
| 19057 | |
| 19058 | switch (writeType) { |
| 19059 | case Scalar::Int16: |
| 19060 | masm.byteSwap16SignExtend(temp); |
| 19061 | break; |
| 19062 | case Scalar::Uint16: |
| 19063 | case Scalar::Float16: |
| 19064 | masm.byteSwap16ZeroExtend(temp); |
| 19065 | break; |
| 19066 | case Scalar::Int32: |
| 19067 | case Scalar::Uint32: |
| 19068 | case Scalar::Float32: |
| 19069 | masm.byteSwap32(temp); |
| 19070 | break; |
| 19071 | case Scalar::Float64: |
| 19072 | masm.byteSwap64(temp64); |
| 19073 | break; |
| 19074 | case Scalar::Int8: |
| 19075 | case Scalar::Uint8: |
| 19076 | case Scalar::Uint8Clamped: |
| 19077 | case Scalar::BigInt64: |
| 19078 | case Scalar::BigUint64: |
| 19079 | default: |
| 19080 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19080); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 19080; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 19081 | } |
| 19082 | |
| 19083 | if (skip.used()) { |
| 19084 | masm.bind(&skip); |
| 19085 | } |
| 19086 | } |
| 19087 | |
| 19088 | // Store the value into the destination. |
| 19089 | switch (writeType) { |
| 19090 | case Scalar::Int16: |
| 19091 | case Scalar::Uint16: |
| 19092 | case Scalar::Float16: |
| 19093 | masm.store16Unaligned(temp, dest); |
| 19094 | break; |
| 19095 | case Scalar::Int32: |
| 19096 | case Scalar::Uint32: |
| 19097 | case Scalar::Float32: |
| 19098 | masm.store32Unaligned(temp, dest); |
| 19099 | break; |
| 19100 | case Scalar::Float64: |
| 19101 | masm.store64Unaligned(temp64, dest); |
| 19102 | break; |
| 19103 | case Scalar::Int8: |
| 19104 | case Scalar::Uint8: |
| 19105 | case Scalar::Uint8Clamped: |
| 19106 | case Scalar::BigInt64: |
| 19107 | case Scalar::BigUint64: |
| 19108 | default: |
| 19109 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19109); AnnotateMozCrashReason("MOZ_CRASH(" "Invalid typed array type" ")"); do { *((volatile int*)__null) = 19109; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 19110 | } |
| 19111 | } |
| 19112 | |
| 19113 | void CodeGenerator::visitStoreDataViewElement64(LStoreDataViewElement64* lir) { |
| 19114 | Register elements = ToRegister(lir->elements()); |
| 19115 | Register64 value = ToRegister64(lir->value()); |
| 19116 | const LAllocation* littleEndian = lir->littleEndian(); |
| 19117 | Register64 temp = ToTempRegister64OrInvalid(lir->temp()); |
| 19118 | |
| 19119 | MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->writeType()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Scalar::isBigIntType(lir->mir()->writeType())) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(Scalar::isBigIntType(lir->mir()->writeType())) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Scalar::isBigIntType(lir->mir()->writeType())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->writeType())" ")"); do { *((volatile int*)__null) = 19119; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19120 | |
| 19121 | BaseIndex dest(elements, ToRegister(lir->index()), TimesOne); |
| 19122 | |
| 19123 | bool noSwap = littleEndian->isConstant() && |
| 19124 | ToBoolean(littleEndian) == MOZ_LITTLE_ENDIAN()1; |
| 19125 | |
| 19126 | if (!noSwap) { |
| 19127 | // Preserve the input value. |
| 19128 | if (temp != Register64::Invalid()) { |
| 19129 | masm.move64(value, temp); |
| 19130 | value = temp; |
| 19131 | } else { |
| 19132 | masm.Push(value); |
| 19133 | } |
| 19134 | |
| 19135 | // Swap the bytes in the loaded value. |
| 19136 | Label skip; |
| 19137 | if (!littleEndian->isConstant()) { |
| 19138 | masm.branch32( |
| 19139 | MOZ_LITTLE_ENDIAN()1 ? Assembler::NotEqual : Assembler::Equal, |
| 19140 | ToRegister(littleEndian), Imm32(0), &skip); |
| 19141 | } |
| 19142 | |
| 19143 | masm.byteSwap64(value); |
| 19144 | |
| 19145 | if (skip.used()) { |
| 19146 | masm.bind(&skip); |
| 19147 | } |
| 19148 | } |
| 19149 | |
| 19150 | // Store the value into the destination. |
| 19151 | masm.store64Unaligned(value, dest); |
| 19152 | |
| 19153 | // Restore |value| if it was modified. |
| 19154 | if (!noSwap && temp == Register64::Invalid()) { |
| 19155 | masm.Pop(value); |
| 19156 | } |
| 19157 | } |
| 19158 | |
| 19159 | void CodeGenerator::visitStoreTypedArrayElementHole( |
| 19160 | LStoreTypedArrayElementHole* lir) { |
| 19161 | Register elements = ToRegister(lir->elements()); |
| 19162 | const LAllocation* value = lir->value(); |
| 19163 | |
| 19164 | Scalar::Type arrayType = lir->mir()->arrayType(); |
| 19165 | |
| 19166 | Register index = ToRegister(lir->index()); |
| 19167 | const LAllocation* length = lir->length(); |
| 19168 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 19169 | |
| 19170 | LiveRegisterSet volatileRegs; |
| 19171 | if (MacroAssembler::StoreRequiresCall(arrayType)) { |
| 19172 | volatileRegs = liveVolatileRegs(lir); |
| 19173 | } |
| 19174 | |
| 19175 | Label skip; |
| 19176 | if (length->isRegister()) { |
| 19177 | masm.spectreBoundsCheckPtr(index, ToRegister(length), temp, &skip); |
| 19178 | } else { |
| 19179 | masm.spectreBoundsCheckPtr(index, ToAddress(length), temp, &skip); |
| 19180 | } |
| 19181 | |
| 19182 | BaseIndex dest(elements, index, ScaleFromScalarType(arrayType)); |
| 19183 | StoreToTypedArray(masm, arrayType, value, dest, temp, volatileRegs); |
| 19184 | |
| 19185 | masm.bind(&skip); |
| 19186 | } |
| 19187 | |
| 19188 | void CodeGenerator::visitStoreTypedArrayElementHoleInt64( |
| 19189 | LStoreTypedArrayElementHoleInt64* lir) { |
| 19190 | Register elements = ToRegister(lir->elements()); |
| 19191 | Register64 value = ToRegister64(lir->value()); |
| 19192 | |
| 19193 | Scalar::Type arrayType = lir->mir()->arrayType(); |
| 19194 | |
| 19195 | Register index = ToRegister(lir->index()); |
| 19196 | const LAllocation* length = lir->length(); |
| 19197 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp()); |
| 19198 | |
| 19199 | Label skip; |
| 19200 | if (length->isRegister()) { |
| 19201 | masm.spectreBoundsCheckPtr(index, ToRegister(length), spectreTemp, &skip); |
| 19202 | } else { |
| 19203 | masm.spectreBoundsCheckPtr(index, ToAddress(length), spectreTemp, &skip); |
| 19204 | } |
| 19205 | |
| 19206 | BaseIndex dest(elements, index, ScaleFromScalarType(arrayType)); |
| 19207 | masm.storeToTypedBigIntArray(arrayType, value, dest); |
| 19208 | |
| 19209 | masm.bind(&skip); |
| 19210 | } |
| 19211 | |
| 19212 | void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { |
| 19213 | masm.memoryBarrier(ins->type()); |
| 19214 | } |
| 19215 | |
| 19216 | void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) { |
| 19217 | Register value = ToRegister(lir->value()); |
| 19218 | Register output = ToRegister(lir->output()); |
| 19219 | |
| 19220 | masm.atomicIsLockFreeJS(value, output); |
| 19221 | } |
| 19222 | |
| 19223 | void CodeGenerator::visitAtomicPause(LAtomicPause* lir) { masm.atomicPause(); } |
| 19224 | |
| 19225 | void CodeGenerator::visitClampIToUint8(LClampIToUint8* lir) { |
| 19226 | Register output = ToRegister(lir->output()); |
| 19227 | MOZ_ASSERT(output == ToRegister(lir->input()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(output == ToRegister(lir->input()))>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(output == ToRegister(lir->input())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output == ToRegister(lir->input())" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19227); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output == ToRegister(lir->input())" ")"); do { *((volatile int*)__null) = 19227; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19228 | masm.clampIntToUint8(output); |
| 19229 | } |
| 19230 | |
| 19231 | void CodeGenerator::visitClampDToUint8(LClampDToUint8* lir) { |
| 19232 | FloatRegister input = ToFloatRegister(lir->input()); |
| 19233 | Register output = ToRegister(lir->output()); |
| 19234 | masm.clampDoubleToUint8(input, output); |
| 19235 | } |
| 19236 | |
| 19237 | void CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) { |
| 19238 | ValueOperand operand = ToValue(lir, LClampVToUint8::InputIndex); |
| 19239 | FloatRegister tempFloat = ToFloatRegister(lir->temp0()); |
| 19240 | Register output = ToRegister(lir->output()); |
| 19241 | |
| 19242 | using Fn = bool (*)(JSContext*, JSString*, double*); |
| 19243 | OutOfLineCode* oolString = oolCallVM<Fn, StringToNumber>( |
| 19244 | lir, ArgList(output), StoreFloatRegisterTo(tempFloat)); |
| 19245 | Label* stringEntry = oolString->entry(); |
| 19246 | Label* stringRejoin = oolString->rejoin(); |
| 19247 | |
| 19248 | Label fails; |
| 19249 | masm.clampValueToUint8(operand, stringEntry, stringRejoin, output, tempFloat, |
| 19250 | output, &fails); |
| 19251 | |
| 19252 | bailoutFrom(&fails, lir->snapshot()); |
| 19253 | } |
| 19254 | |
| 19255 | void CodeGenerator::visitInCache(LInCache* ins) { |
| 19256 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 19257 | |
| 19258 | ConstantOrRegister key = |
| 19259 | toConstantOrRegister(ins, LInCache::LhsIndex, ins->mir()->key()->type()); |
| 19260 | Register object = ToRegister(ins->rhs()); |
| 19261 | Register output = ToRegister(ins->output()); |
| 19262 | Register temp = ToRegister(ins->temp0()); |
| 19263 | |
| 19264 | IonInIC cache(liveRegs, key, object, output, temp); |
| 19265 | addIC(ins, allocateIC(cache)); |
| 19266 | } |
| 19267 | |
| 19268 | void CodeGenerator::visitInArray(LInArray* lir) { |
| 19269 | const MInArray* mir = lir->mir(); |
| 19270 | Register elements = ToRegister(lir->elements()); |
| 19271 | Register initLength = ToRegister(lir->initLength()); |
| 19272 | Register output = ToRegister(lir->output()); |
| 19273 | |
| 19274 | Label falseBranch, done, trueBranch; |
| 19275 | |
| 19276 | if (lir->index()->isConstant()) { |
| 19277 | int32_t index = ToInt32(lir->index()); |
| 19278 | |
| 19279 | if (index < 0) { |
| 19280 | MOZ_ASSERT(mir->needsNegativeIntCheck())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->needsNegativeIntCheck())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->needsNegativeIntCheck ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mir->needsNegativeIntCheck()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19280); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->needsNegativeIntCheck()" ")"); do { *((volatile int*)__null) = 19280; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19281 | bailout(lir->snapshot()); |
| 19282 | return; |
| 19283 | } |
| 19284 | |
| 19285 | masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), |
| 19286 | &falseBranch); |
| 19287 | |
| 19288 | NativeObject::elementsSizeMustNotOverflow(); |
| 19289 | Address address = Address(elements, index * sizeof(Value)); |
| 19290 | masm.branchTestMagic(Assembler::Equal, address, &falseBranch); |
| 19291 | } else { |
| 19292 | Register index = ToRegister(lir->index()); |
| 19293 | |
| 19294 | Label negativeIntCheck; |
| 19295 | Label* failedInitLength = &falseBranch; |
| 19296 | if (mir->needsNegativeIntCheck()) { |
| 19297 | failedInitLength = &negativeIntCheck; |
| 19298 | } |
| 19299 | |
| 19300 | masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength); |
| 19301 | |
| 19302 | BaseObjectElementIndex address(elements, index); |
| 19303 | masm.branchTestMagic(Assembler::Equal, address, &falseBranch); |
| 19304 | |
| 19305 | if (mir->needsNegativeIntCheck()) { |
| 19306 | masm.jump(&trueBranch); |
| 19307 | masm.bind(&negativeIntCheck); |
| 19308 | |
| 19309 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 19310 | |
| 19311 | masm.jump(&falseBranch); |
| 19312 | } |
| 19313 | } |
| 19314 | |
| 19315 | masm.bind(&trueBranch); |
| 19316 | masm.move32(Imm32(1), output); |
| 19317 | masm.jump(&done); |
| 19318 | |
| 19319 | masm.bind(&falseBranch); |
| 19320 | masm.move32(Imm32(0), output); |
| 19321 | masm.bind(&done); |
| 19322 | } |
| 19323 | |
| 19324 | void CodeGenerator::visitGuardElementNotHole(LGuardElementNotHole* lir) { |
| 19325 | Register elements = ToRegister(lir->elements()); |
| 19326 | const LAllocation* index = lir->index(); |
| 19327 | |
| 19328 | Label testMagic; |
| 19329 | if (index->isConstant()) { |
| 19330 | Address address(elements, ToInt32(index) * sizeof(js::Value)); |
| 19331 | masm.branchTestMagic(Assembler::Equal, address, &testMagic); |
| 19332 | } else { |
| 19333 | BaseObjectElementIndex address(elements, ToRegister(index)); |
| 19334 | masm.branchTestMagic(Assembler::Equal, address, &testMagic); |
| 19335 | } |
| 19336 | bailoutFrom(&testMagic, lir->snapshot()); |
| 19337 | } |
| 19338 | |
| 19339 | void CodeGenerator::visitInstanceOfO(LInstanceOfO* ins) { |
| 19340 | Register protoReg = ToRegister(ins->rhs()); |
| 19341 | emitInstanceOf(ins, protoReg); |
| 19342 | } |
| 19343 | |
| 19344 | void CodeGenerator::visitInstanceOfV(LInstanceOfV* ins) { |
| 19345 | Register protoReg = ToRegister(ins->rhs()); |
| 19346 | emitInstanceOf(ins, protoReg); |
| 19347 | } |
| 19348 | |
| 19349 | void CodeGenerator::emitInstanceOf(LInstruction* ins, Register protoReg) { |
| 19350 | // This path implements fun_hasInstance when the function's prototype is |
| 19351 | // known to be the object in protoReg |
| 19352 | |
| 19353 | Label done; |
| 19354 | Register output = ToRegister(ins->getDef(0)); |
| 19355 | |
| 19356 | // If the lhs is a primitive, the result is false. |
| 19357 | Register objReg; |
| 19358 | if (ins->isInstanceOfV()) { |
| 19359 | Label isObject; |
| 19360 | ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex); |
| 19361 | masm.branchTestObject(Assembler::Equal, lhsValue, &isObject); |
| 19362 | masm.mov(ImmWord(0), output); |
| 19363 | masm.jump(&done); |
| 19364 | masm.bind(&isObject); |
| 19365 | objReg = masm.extractObject(lhsValue, output); |
| 19366 | } else { |
| 19367 | objReg = ToRegister(ins->toInstanceOfO()->lhs()); |
| 19368 | } |
| 19369 | |
| 19370 | // Crawl the lhs's prototype chain in a loop to search for prototypeObject. |
| 19371 | // This follows the main loop of js::IsPrototypeOf, though additionally breaks |
| 19372 | // out of the loop on Proxy::LazyProto. |
| 19373 | |
| 19374 | // Load the lhs's prototype. |
| 19375 | masm.loadObjProto(objReg, output); |
| 19376 | |
| 19377 | Label testLazy; |
| 19378 | { |
| 19379 | Label loopPrototypeChain; |
| 19380 | masm.bind(&loopPrototypeChain); |
| 19381 | |
| 19382 | // Test for the target prototype object. |
| 19383 | Label notPrototypeObject; |
| 19384 | masm.branchPtr(Assembler::NotEqual, output, protoReg, ¬PrototypeObject); |
| 19385 | masm.mov(ImmWord(1), output); |
| 19386 | masm.jump(&done); |
| 19387 | masm.bind(¬PrototypeObject); |
| 19388 | |
| 19389 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19389); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 19389; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19390 | |
| 19391 | // Test for nullptr or Proxy::LazyProto |
| 19392 | masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy); |
| 19393 | |
| 19394 | // Load the current object's prototype. |
| 19395 | masm.loadObjProto(output, output); |
| 19396 | |
| 19397 | masm.jump(&loopPrototypeChain); |
| 19398 | } |
| 19399 | |
| 19400 | // Make a VM call if an object with a lazy proto was found on the prototype |
| 19401 | // chain. This currently occurs only for cross compartment wrappers, which |
| 19402 | // we do not expect to be compared with non-wrapper functions from this |
| 19403 | // compartment. Otherwise, we stopped on a nullptr prototype and the output |
| 19404 | // register is already correct. |
| 19405 | |
| 19406 | using Fn = bool (*)(JSContext*, HandleObject, JSObject*, bool*); |
| 19407 | auto* ool = oolCallVM<Fn, IsPrototypeOf>(ins, ArgList(protoReg, objReg), |
| 19408 | StoreRegisterTo(output)); |
| 19409 | |
| 19410 | // Regenerate the original lhs object for the VM call. |
| 19411 | Label regenerate, *lazyEntry; |
| 19412 | if (objReg != output) { |
| 19413 | lazyEntry = ool->entry(); |
| 19414 | } else { |
| 19415 | masm.bind(®enerate); |
| 19416 | lazyEntry = ®enerate; |
| 19417 | if (ins->isInstanceOfV()) { |
| 19418 | ValueOperand lhsValue = ToValue(ins, LInstanceOfV::LhsIndex); |
| 19419 | objReg = masm.extractObject(lhsValue, output); |
| 19420 | } else { |
| 19421 | objReg = ToRegister(ins->toInstanceOfO()->lhs()); |
| 19422 | } |
| 19423 | MOZ_ASSERT(objReg == output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(objReg == output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(objReg == output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("objReg == output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "objReg == output" ")"); do { *((volatile int*)__null) = 19423; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19424 | masm.jump(ool->entry()); |
| 19425 | } |
| 19426 | |
| 19427 | masm.bind(&testLazy); |
| 19428 | masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry); |
| 19429 | |
| 19430 | masm.bind(&done); |
| 19431 | masm.bind(ool->rejoin()); |
| 19432 | } |
| 19433 | |
| 19434 | void CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins) { |
| 19435 | // The Lowering ensures that RHS is an object, and that LHS is a value. |
| 19436 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 19437 | TypedOrValueRegister lhs = |
| 19438 | TypedOrValueRegister(ToValue(ins, LInstanceOfCache::LHS)); |
| 19439 | Register rhs = ToRegister(ins->rhs()); |
| 19440 | Register output = ToRegister(ins->output()); |
| 19441 | |
| 19442 | IonInstanceOfIC ic(liveRegs, lhs, rhs, output); |
| 19443 | addIC(ins, allocateIC(ic)); |
| 19444 | } |
| 19445 | |
| 19446 | void CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) { |
| 19447 | const Register JSContextReg = ToRegister(ins->getJSContextReg()); |
| 19448 | const Register ObjectReg = ToRegister(ins->getObjectReg()); |
| 19449 | const Register PrivateReg = ToRegister(ins->getPrivReg()); |
| 19450 | const Register ValueReg = ToRegister(ins->getValueReg()); |
| 19451 | |
| 19452 | Label haveValue; |
| 19453 | if (ins->mir()->valueMayBeInSlot()) { |
| 19454 | size_t slot = ins->mir()->domMemberSlotIndex(); |
| 19455 | // It's a bit annoying to redo these slot calculations, which duplcate |
| 19456 | // LSlots and a few other things like that, but I'm not sure there's a |
| 19457 | // way to reuse those here. |
| 19458 | // |
| 19459 | // If this ever gets fixed to work with proxies (by not assuming that |
| 19460 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
| 19461 | // match fixed slot indices), we can reenable MGetDOMProperty for |
| 19462 | // proxies in IonBuilder. |
| 19463 | if (slot < NativeObject::MAX_FIXED_SLOTS) { |
| 19464 | masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)), |
| 19465 | JSReturnOperand); |
| 19466 | } else { |
| 19467 | // It's a dynamic slot. |
| 19468 | slot -= NativeObject::MAX_FIXED_SLOTS; |
| 19469 | // Use PrivateReg as a scratch register for the slots pointer. |
| 19470 | masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()), |
| 19471 | PrivateReg); |
| 19472 | masm.loadValue(Address(PrivateReg, slot * sizeof(js::Value)), |
| 19473 | JSReturnOperand); |
| 19474 | } |
| 19475 | masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue); |
| 19476 | } |
| 19477 | |
| 19478 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
| 19479 | |
| 19480 | masm.checkStackAlignment(); |
| 19481 | |
| 19482 | // Make space for the outparam. Pre-initialize it to UndefinedValue so we |
| 19483 | // can trace it at GC time. |
| 19484 | masm.Push(UndefinedValue()); |
| 19485 | // We pass the pointer to our out param as an instance of |
| 19486 | // JSJitGetterCallArgs, since on the binary level it's the same thing. |
| 19487 | static_assert(sizeof(JSJitGetterCallArgs) == sizeof(Value*)); |
| 19488 | masm.moveStackPtrTo(ValueReg); |
| 19489 | |
| 19490 | masm.Push(ObjectReg); |
| 19491 | |
| 19492 | LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind()); |
| 19493 | |
| 19494 | // Rooting will happen at GC time. |
| 19495 | masm.moveStackPtrTo(ObjectReg); |
| 19496 | |
| 19497 | Realm* getterRealm = ins->mir()->getterRealm(); |
| 19498 | if (gen->realm->realmPtr() != getterRealm) { |
| 19499 | // We use JSContextReg as scratch register here. |
| 19500 | masm.switchToRealm(getterRealm, JSContextReg); |
| 19501 | } |
| 19502 | |
| 19503 | uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); |
| 19504 | masm.loadJSContext(JSContextReg); |
| 19505 | masm.enterFakeExitFrame(JSContextReg, JSContextReg, |
| 19506 | ExitFrameType::IonDOMGetter); |
| 19507 | |
| 19508 | markSafepointAt(safepointOffset, ins); |
| 19509 | |
| 19510 | masm.setupAlignedABICall(); |
| 19511 | masm.loadJSContext(JSContextReg); |
| 19512 | masm.passABIArg(JSContextReg); |
| 19513 | masm.passABIArg(ObjectReg); |
| 19514 | masm.passABIArg(PrivateReg); |
| 19515 | masm.passABIArg(ValueReg); |
| 19516 | ensureOsiSpace(); |
| 19517 | masm.callWithABI(DynamicFunction<JSJitGetterOp>(ins->mir()->fun()), |
| 19518 | ABIType::General, |
| 19519 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 19520 | |
| 19521 | if (ins->mir()->isInfallible()) { |
| 19522 | masm.loadValue(Address(masm.getStackPointer(), |
| 19523 | IonDOMExitFrameLayout::offsetOfResult()), |
| 19524 | JSReturnOperand); |
| 19525 | } else { |
| 19526 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
| 19527 | |
| 19528 | masm.loadValue(Address(masm.getStackPointer(), |
| 19529 | IonDOMExitFrameLayout::offsetOfResult()), |
| 19530 | JSReturnOperand); |
| 19531 | } |
| 19532 | |
| 19533 | // Switch back to the current realm if needed. Note: if the getter threw an |
| 19534 | // exception, the exception handler will do this. |
| 19535 | if (gen->realm->realmPtr() != getterRealm) { |
| 19536 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 19537 | "Clobbering ReturnReg should not affect the return value"); |
| 19538 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 19539 | } |
| 19540 | |
| 19541 | // Until C++ code is instrumented against Spectre, prevent speculative |
| 19542 | // execution from returning any private data. |
| 19543 | if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) { |
| 19544 | masm.speculationBarrier(); |
| 19545 | } |
| 19546 | |
| 19547 | masm.adjustStack(IonDOMExitFrameLayout::Size()); |
| 19548 | |
| 19549 | masm.bind(&haveValue); |
| 19550 | |
| 19551 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19551); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 19551; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19552 | } |
| 19553 | |
| 19554 | void CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins) { |
| 19555 | // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to |
| 19556 | // use an LLoadFixedSlotV or some subclass of it for this case: that would |
| 19557 | // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then |
| 19558 | // we'd have to duplicate a bunch of stuff we now get for free from |
| 19559 | // MGetDOMProperty. |
| 19560 | // |
| 19561 | // If this ever gets fixed to work with proxies (by not assuming that |
| 19562 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
| 19563 | // match fixed slot indices), we can reenable MGetDOMMember for |
| 19564 | // proxies in IonBuilder. |
| 19565 | Register object = ToRegister(ins->object()); |
| 19566 | size_t slot = ins->mir()->domMemberSlotIndex(); |
| 19567 | ValueOperand result = ToOutValue(ins); |
| 19568 | |
| 19569 | masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)), |
| 19570 | result); |
| 19571 | } |
| 19572 | |
| 19573 | void CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT* ins) { |
| 19574 | // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to |
| 19575 | // use an LLoadFixedSlotT or some subclass of it for this case: that would |
| 19576 | // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then |
| 19577 | // we'd have to duplicate a bunch of stuff we now get for free from |
| 19578 | // MGetDOMProperty. |
| 19579 | // |
| 19580 | // If this ever gets fixed to work with proxies (by not assuming that |
| 19581 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
| 19582 | // match fixed slot indices), we can reenable MGetDOMMember for |
| 19583 | // proxies in IonBuilder. |
| 19584 | Register object = ToRegister(ins->object()); |
| 19585 | size_t slot = ins->mir()->domMemberSlotIndex(); |
| 19586 | AnyRegister result = ToAnyRegister(ins->getDef(0)); |
| 19587 | MIRType type = ins->mir()->type(); |
| 19588 | |
| 19589 | masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)), |
| 19590 | type, result); |
| 19591 | } |
| 19592 | |
| 19593 | void CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) { |
| 19594 | const Register JSContextReg = ToRegister(ins->getJSContextReg()); |
| 19595 | const Register ObjectReg = ToRegister(ins->getObjectReg()); |
| 19596 | const Register PrivateReg = ToRegister(ins->getPrivReg()); |
| 19597 | const Register ValueReg = ToRegister(ins->getValueReg()); |
| 19598 | |
| 19599 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
| 19600 | |
| 19601 | masm.checkStackAlignment(); |
| 19602 | |
| 19603 | // Push the argument. Rooting will happen at GC time. |
| 19604 | ValueOperand argVal = ToValue(ins, LSetDOMProperty::Value); |
| 19605 | masm.Push(argVal); |
| 19606 | // We pass the pointer to our out param as an instance of |
| 19607 | // JSJitGetterCallArgs, since on the binary level it's the same thing. |
| 19608 | static_assert(sizeof(JSJitSetterCallArgs) == sizeof(Value*)); |
| 19609 | masm.moveStackPtrTo(ValueReg); |
| 19610 | |
| 19611 | masm.Push(ObjectReg); |
| 19612 | |
| 19613 | LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind()); |
| 19614 | |
| 19615 | // Rooting will happen at GC time. |
| 19616 | masm.moveStackPtrTo(ObjectReg); |
| 19617 | |
| 19618 | Realm* setterRealm = ins->mir()->setterRealm(); |
| 19619 | if (gen->realm->realmPtr() != setterRealm) { |
| 19620 | // We use JSContextReg as scratch register here. |
| 19621 | masm.switchToRealm(setterRealm, JSContextReg); |
| 19622 | } |
| 19623 | |
| 19624 | uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); |
| 19625 | masm.loadJSContext(JSContextReg); |
| 19626 | masm.enterFakeExitFrame(JSContextReg, JSContextReg, |
| 19627 | ExitFrameType::IonDOMSetter); |
| 19628 | |
| 19629 | markSafepointAt(safepointOffset, ins); |
| 19630 | |
| 19631 | masm.setupAlignedABICall(); |
| 19632 | masm.loadJSContext(JSContextReg); |
| 19633 | masm.passABIArg(JSContextReg); |
| 19634 | masm.passABIArg(ObjectReg); |
| 19635 | masm.passABIArg(PrivateReg); |
| 19636 | masm.passABIArg(ValueReg); |
| 19637 | ensureOsiSpace(); |
| 19638 | masm.callWithABI(DynamicFunction<JSJitSetterOp>(ins->mir()->fun()), |
| 19639 | ABIType::General, |
| 19640 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 19641 | |
| 19642 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
| 19643 | |
| 19644 | // Switch back to the current realm if needed. Note: if the setter threw an |
| 19645 | // exception, the exception handler will do this. |
| 19646 | if (gen->realm->realmPtr() != setterRealm) { |
| 19647 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 19648 | } |
| 19649 | |
| 19650 | masm.adjustStack(IonDOMExitFrameLayout::Size()); |
| 19651 | |
| 19652 | MOZ_ASSERT(masm.framePushed() == initialStack)do { static_assert( mozilla::detail::AssertionConditionType< decltype(masm.framePushed() == initialStack)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(masm.framePushed() == initialStack ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "masm.framePushed() == initialStack", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 19652); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { *((volatile int*)__null) = 19652; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19653 | } |
| 19654 | |
| 19655 | void CodeGenerator::visitLoadDOMExpandoValue(LLoadDOMExpandoValue* ins) { |
| 19656 | Register proxy = ToRegister(ins->proxy()); |
| 19657 | ValueOperand out = ToOutValue(ins); |
| 19658 | |
| 19659 | masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()), |
| 19660 | out.scratchReg()); |
| 19661 | masm.loadValue(Address(out.scratchReg(), |
| 19662 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()), |
| 19663 | out); |
| 19664 | } |
| 19665 | |
| 19666 | void CodeGenerator::visitLoadDOMExpandoValueGuardGeneration( |
| 19667 | LLoadDOMExpandoValueGuardGeneration* ins) { |
| 19668 | Register proxy = ToRegister(ins->proxy()); |
| 19669 | ValueOperand out = ToOutValue(ins); |
| 19670 | |
| 19671 | Label bail; |
| 19672 | masm.loadDOMExpandoValueGuardGeneration(proxy, out, |
| 19673 | ins->mir()->expandoAndGeneration(), |
| 19674 | ins->mir()->generation(), &bail); |
| 19675 | bailoutFrom(&bail, ins->snapshot()); |
| 19676 | } |
| 19677 | |
| 19678 | void CodeGenerator::visitLoadDOMExpandoValueIgnoreGeneration( |
| 19679 | LLoadDOMExpandoValueIgnoreGeneration* ins) { |
| 19680 | Register proxy = ToRegister(ins->proxy()); |
| 19681 | ValueOperand out = ToOutValue(ins); |
| 19682 | |
| 19683 | masm.loadPtr(Address(proxy, ProxyObject::offsetOfReservedSlots()), |
| 19684 | out.scratchReg()); |
| 19685 | |
| 19686 | // Load the ExpandoAndGeneration* from the PrivateValue. |
| 19687 | masm.loadPrivate( |
| 19688 | Address(out.scratchReg(), |
| 19689 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()), |
| 19690 | out.scratchReg()); |
| 19691 | |
| 19692 | // Load expandoAndGeneration->expando into the output Value register. |
| 19693 | masm.loadValue( |
| 19694 | Address(out.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), out); |
| 19695 | } |
| 19696 | |
| 19697 | void CodeGenerator::visitGuardDOMExpandoMissingOrGuardShape( |
| 19698 | LGuardDOMExpandoMissingOrGuardShape* ins) { |
| 19699 | Register temp = ToRegister(ins->temp0()); |
| 19700 | ValueOperand input = |
| 19701 | ToValue(ins, LGuardDOMExpandoMissingOrGuardShape::InputIndex); |
| 19702 | |
| 19703 | Label done; |
| 19704 | masm.branchTestUndefined(Assembler::Equal, input, &done); |
| 19705 | |
| 19706 | masm.debugAssertIsObject(input); |
| 19707 | masm.unboxObject(input, temp); |
| 19708 | // The expando object is not used in this case, so we don't need Spectre |
| 19709 | // mitigations. |
| 19710 | Label bail; |
| 19711 | masm.branchTestObjShapeNoSpectreMitigations(Assembler::NotEqual, temp, |
| 19712 | ins->mir()->shape(), &bail); |
| 19713 | bailoutFrom(&bail, ins->snapshot()); |
| 19714 | |
| 19715 | masm.bind(&done); |
| 19716 | } |
| 19717 | |
| 19718 | class OutOfLineIsCallable : public OutOfLineCodeBase<CodeGenerator> { |
| 19719 | Register object_; |
| 19720 | Register output_; |
| 19721 | |
| 19722 | public: |
| 19723 | OutOfLineIsCallable(Register object, Register output) |
| 19724 | : object_(object), output_(output) {} |
| 19725 | |
| 19726 | void accept(CodeGenerator* codegen) override { |
| 19727 | codegen->visitOutOfLineIsCallable(this); |
| 19728 | } |
| 19729 | Register object() const { return object_; } |
| 19730 | Register output() const { return output_; } |
| 19731 | }; |
| 19732 | |
| 19733 | void CodeGenerator::visitIsCallableO(LIsCallableO* ins) { |
| 19734 | Register object = ToRegister(ins->object()); |
| 19735 | Register output = ToRegister(ins->output()); |
| 19736 | |
| 19737 | OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(object, output); |
| 19738 | addOutOfLineCode(ool, ins->mir()); |
| 19739 | |
| 19740 | masm.isCallable(object, output, ool->entry()); |
| 19741 | |
| 19742 | masm.bind(ool->rejoin()); |
| 19743 | } |
| 19744 | |
| 19745 | void CodeGenerator::visitIsCallableV(LIsCallableV* ins) { |
| 19746 | ValueOperand val = ToValue(ins, LIsCallableV::ObjectIndex); |
| 19747 | Register output = ToRegister(ins->output()); |
| 19748 | Register temp = ToRegister(ins->temp0()); |
| 19749 | |
| 19750 | Label notObject; |
| 19751 | masm.fallibleUnboxObject(val, temp, ¬Object); |
| 19752 | |
| 19753 | OutOfLineIsCallable* ool = new (alloc()) OutOfLineIsCallable(temp, output); |
| 19754 | addOutOfLineCode(ool, ins->mir()); |
| 19755 | |
| 19756 | masm.isCallable(temp, output, ool->entry()); |
| 19757 | masm.jump(ool->rejoin()); |
| 19758 | |
| 19759 | masm.bind(¬Object); |
| 19760 | masm.move32(Imm32(0), output); |
| 19761 | |
| 19762 | masm.bind(ool->rejoin()); |
| 19763 | } |
| 19764 | |
| 19765 | void CodeGenerator::visitOutOfLineIsCallable(OutOfLineIsCallable* ool) { |
| 19766 | Register object = ool->object(); |
| 19767 | Register output = ool->output(); |
| 19768 | |
| 19769 | saveVolatile(output); |
| 19770 | using Fn = bool (*)(JSObject* obj); |
| 19771 | masm.setupAlignedABICall(); |
| 19772 | masm.passABIArg(object); |
| 19773 | masm.callWithABI<Fn, ObjectIsCallable>(); |
| 19774 | masm.storeCallBoolResult(output); |
| 19775 | restoreVolatile(output); |
| 19776 | masm.jump(ool->rejoin()); |
| 19777 | } |
| 19778 | |
| 19779 | class OutOfLineIsConstructor : public OutOfLineCodeBase<CodeGenerator> { |
| 19780 | LIsConstructor* ins_; |
| 19781 | |
| 19782 | public: |
| 19783 | explicit OutOfLineIsConstructor(LIsConstructor* ins) : ins_(ins) {} |
| 19784 | |
| 19785 | void accept(CodeGenerator* codegen) override { |
| 19786 | codegen->visitOutOfLineIsConstructor(this); |
| 19787 | } |
| 19788 | LIsConstructor* ins() const { return ins_; } |
| 19789 | }; |
| 19790 | |
| 19791 | void CodeGenerator::visitIsConstructor(LIsConstructor* ins) { |
| 19792 | Register object = ToRegister(ins->object()); |
| 19793 | Register output = ToRegister(ins->output()); |
| 19794 | |
| 19795 | OutOfLineIsConstructor* ool = new (alloc()) OutOfLineIsConstructor(ins); |
| 19796 | addOutOfLineCode(ool, ins->mir()); |
| 19797 | |
| 19798 | masm.isConstructor(object, output, ool->entry()); |
| 19799 | |
| 19800 | masm.bind(ool->rejoin()); |
| 19801 | } |
| 19802 | |
| 19803 | void CodeGenerator::visitOutOfLineIsConstructor(OutOfLineIsConstructor* ool) { |
| 19804 | LIsConstructor* ins = ool->ins(); |
| 19805 | Register object = ToRegister(ins->object()); |
| 19806 | Register output = ToRegister(ins->output()); |
| 19807 | |
| 19808 | saveVolatile(output); |
| 19809 | using Fn = bool (*)(JSObject* obj); |
| 19810 | masm.setupAlignedABICall(); |
| 19811 | masm.passABIArg(object); |
| 19812 | masm.callWithABI<Fn, ObjectIsConstructor>(); |
| 19813 | masm.storeCallBoolResult(output); |
| 19814 | restoreVolatile(output); |
| 19815 | masm.jump(ool->rejoin()); |
| 19816 | } |
| 19817 | |
| 19818 | void CodeGenerator::visitIsCrossRealmArrayConstructor( |
| 19819 | LIsCrossRealmArrayConstructor* ins) { |
| 19820 | Register object = ToRegister(ins->object()); |
| 19821 | Register output = ToRegister(ins->output()); |
| 19822 | |
| 19823 | masm.setIsCrossRealmArrayConstructor(object, output); |
| 19824 | } |
| 19825 | |
| 19826 | static void EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool, |
| 19827 | Register obj, Register output, |
| 19828 | Label* notArray = nullptr) { |
| 19829 | masm.loadObjClassUnsafe(obj, output); |
| 19830 | |
| 19831 | Label isArray; |
| 19832 | masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_), |
| 19833 | &isArray); |
| 19834 | |
| 19835 | // Branch to OOL path if it's a proxy. |
| 19836 | masm.branchTestClassIsProxy(true, output, ool->entry()); |
| 19837 | |
| 19838 | if (notArray) { |
| 19839 | masm.bind(notArray); |
| 19840 | } |
| 19841 | masm.move32(Imm32(0), output); |
| 19842 | masm.jump(ool->rejoin()); |
| 19843 | |
| 19844 | masm.bind(&isArray); |
| 19845 | masm.move32(Imm32(1), output); |
| 19846 | |
| 19847 | masm.bind(ool->rejoin()); |
| 19848 | } |
| 19849 | |
| 19850 | void CodeGenerator::visitIsArrayO(LIsArrayO* lir) { |
| 19851 | Register object = ToRegister(lir->object()); |
| 19852 | Register output = ToRegister(lir->output()); |
| 19853 | |
| 19854 | using Fn = bool (*)(JSContext*, HandleObject, bool*); |
| 19855 | OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>( |
| 19856 | lir, ArgList(object), StoreRegisterTo(output)); |
| 19857 | EmitObjectIsArray(masm, ool, object, output); |
| 19858 | } |
| 19859 | |
| 19860 | void CodeGenerator::visitIsArrayV(LIsArrayV* lir) { |
| 19861 | ValueOperand val = ToValue(lir, LIsArrayV::ValueIndex); |
| 19862 | Register output = ToRegister(lir->output()); |
| 19863 | Register temp = ToRegister(lir->temp0()); |
| 19864 | |
| 19865 | Label notArray; |
| 19866 | masm.fallibleUnboxObject(val, temp, ¬Array); |
| 19867 | |
| 19868 | using Fn = bool (*)(JSContext*, HandleObject, bool*); |
| 19869 | OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>( |
| 19870 | lir, ArgList(temp), StoreRegisterTo(output)); |
| 19871 | EmitObjectIsArray(masm, ool, temp, output, ¬Array); |
| 19872 | } |
| 19873 | |
| 19874 | void CodeGenerator::visitIsTypedArray(LIsTypedArray* lir) { |
| 19875 | Register object = ToRegister(lir->object()); |
| 19876 | Register output = ToRegister(lir->output()); |
| 19877 | |
| 19878 | OutOfLineCode* ool = nullptr; |
| 19879 | if (lir->mir()->isPossiblyWrapped()) { |
| 19880 | using Fn = bool (*)(JSContext*, JSObject*, bool*); |
| 19881 | ool = oolCallVM<Fn, jit::IsPossiblyWrappedTypedArray>( |
| 19882 | lir, ArgList(object), StoreRegisterTo(output)); |
| 19883 | } |
| 19884 | |
| 19885 | Label notTypedArray; |
| 19886 | Label done; |
| 19887 | |
| 19888 | masm.loadObjClassUnsafe(object, output); |
| 19889 | masm.branchIfClassIsNotTypedArray(output, ¬TypedArray); |
| 19890 | |
| 19891 | masm.move32(Imm32(1), output); |
| 19892 | masm.jump(&done); |
| 19893 | masm.bind(¬TypedArray); |
| 19894 | if (ool) { |
| 19895 | Label notProxy; |
| 19896 | masm.branchTestClassIsProxy(false, output, ¬Proxy); |
| 19897 | masm.branchTestProxyHandlerFamily(Assembler::Equal, object, output, |
| 19898 | &Wrapper::family, ool->entry()); |
| 19899 | masm.bind(¬Proxy); |
| 19900 | } |
| 19901 | masm.move32(Imm32(0), output); |
| 19902 | masm.bind(&done); |
| 19903 | if (ool) { |
| 19904 | masm.bind(ool->rejoin()); |
| 19905 | } |
| 19906 | } |
| 19907 | |
| 19908 | void CodeGenerator::visitIsObject(LIsObject* ins) { |
| 19909 | Register output = ToRegister(ins->output()); |
| 19910 | ValueOperand value = ToValue(ins, LIsObject::ObjectIndex); |
| 19911 | masm.testObjectSet(Assembler::Equal, value, output); |
| 19912 | } |
| 19913 | |
| 19914 | void CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins) { |
| 19915 | ValueOperand value = ToValue(ins, LIsObjectAndBranch::Input); |
| 19916 | |
| 19917 | MBasicBlock* ifTrue = ins->ifTrue(); |
| 19918 | MBasicBlock* ifFalse = ins->ifFalse(); |
| 19919 | |
| 19920 | if (isNextBlock(ifFalse->lir())) { |
| 19921 | masm.branchTestObject(Assembler::Equal, value, |
| 19922 | getJumpLabelForBranch(ifTrue)); |
| 19923 | } else { |
| 19924 | masm.branchTestObject(Assembler::NotEqual, value, |
| 19925 | getJumpLabelForBranch(ifFalse)); |
| 19926 | jumpToBlock(ifTrue); |
| 19927 | } |
| 19928 | } |
| 19929 | |
| 19930 | void CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins) { |
| 19931 | Register output = ToRegister(ins->output()); |
| 19932 | ValueOperand value = ToValue(ins, LIsNullOrUndefined::InputIndex); |
| 19933 | |
| 19934 | Label isNotNull, done; |
| 19935 | masm.branchTestNull(Assembler::NotEqual, value, &isNotNull); |
| 19936 | |
| 19937 | masm.move32(Imm32(1), output); |
| 19938 | masm.jump(&done); |
| 19939 | |
| 19940 | masm.bind(&isNotNull); |
| 19941 | masm.testUndefinedSet(Assembler::Equal, value, output); |
| 19942 | |
| 19943 | masm.bind(&done); |
| 19944 | } |
| 19945 | |
| 19946 | void CodeGenerator::visitIsNullOrUndefinedAndBranch( |
| 19947 | LIsNullOrUndefinedAndBranch* ins) { |
| 19948 | Label* ifTrue = getJumpLabelForBranch(ins->ifTrue()); |
| 19949 | Label* ifFalse = getJumpLabelForBranch(ins->ifFalse()); |
| 19950 | ValueOperand value = ToValue(ins, LIsNullOrUndefinedAndBranch::Input); |
| 19951 | |
| 19952 | ScratchTagScope tag(masm, value); |
| 19953 | masm.splitTagForTest(value, tag); |
| 19954 | |
| 19955 | masm.branchTestNull(Assembler::Equal, tag, ifTrue); |
| 19956 | masm.branchTestUndefined(Assembler::Equal, tag, ifTrue); |
| 19957 | |
| 19958 | if (!isNextBlock(ins->ifFalse()->lir())) { |
| 19959 | masm.jump(ifFalse); |
| 19960 | } |
| 19961 | } |
| 19962 | |
| 19963 | void CodeGenerator::loadOutermostJSScript(Register reg) { |
| 19964 | // The "outermost" JSScript means the script that we are compiling |
| 19965 | // basically; this is not always the script associated with the |
| 19966 | // current basic block, which might be an inlined script. |
| 19967 | |
| 19968 | MIRGraph& graph = current->mir()->graph(); |
| 19969 | MBasicBlock* entryBlock = graph.entryBlock(); |
| 19970 | masm.movePtr(ImmGCPtr(entryBlock->info().script()), reg); |
| 19971 | } |
| 19972 | |
| 19973 | void CodeGenerator::loadJSScriptForBlock(MBasicBlock* block, Register reg) { |
| 19974 | // The current JSScript means the script for the current |
| 19975 | // basic block. This may be an inlined script. |
| 19976 | |
| 19977 | JSScript* script = block->info().script(); |
| 19978 | masm.movePtr(ImmGCPtr(script), reg); |
| 19979 | } |
| 19980 | |
| 19981 | void CodeGenerator::visitHasClass(LHasClass* ins) { |
| 19982 | Register lhs = ToRegister(ins->lhs()); |
| 19983 | Register output = ToRegister(ins->output()); |
| 19984 | |
| 19985 | masm.loadObjClassUnsafe(lhs, output); |
| 19986 | masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), |
| 19987 | output); |
| 19988 | } |
| 19989 | |
| 19990 | void CodeGenerator::visitGuardToClass(LGuardToClass* ins) { |
| 19991 | Register lhs = ToRegister(ins->lhs()); |
| 19992 | Register temp = ToRegister(ins->temp0()); |
| 19993 | |
| 19994 | // branchTestObjClass may zero the object register on speculative paths |
| 19995 | // (we should have a defineReuseInput allocation in this case). |
| 19996 | Register spectreRegToZero = lhs; |
| 19997 | |
| 19998 | Label notEqual; |
| 19999 | |
| 20000 | masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(), |
| 20001 | temp, spectreRegToZero, ¬Equal); |
| 20002 | |
| 20003 | // Can't return null-return here, so bail. |
| 20004 | bailoutFrom(¬Equal, ins->snapshot()); |
| 20005 | } |
| 20006 | |
| 20007 | void CodeGenerator::visitGuardToEitherClass(LGuardToEitherClass* ins) { |
| 20008 | Register lhs = ToRegister(ins->lhs()); |
| 20009 | Register temp = ToRegister(ins->temp0()); |
| 20010 | |
| 20011 | // branchTestObjClass may zero the object register on speculative paths |
| 20012 | // (we should have a defineReuseInput allocation in this case). |
| 20013 | Register spectreRegToZero = lhs; |
| 20014 | |
| 20015 | Label notEqual; |
| 20016 | |
| 20017 | masm.branchTestObjClass(Assembler::NotEqual, lhs, |
| 20018 | {ins->mir()->getClass1(), ins->mir()->getClass2()}, |
| 20019 | temp, spectreRegToZero, ¬Equal); |
| 20020 | |
| 20021 | // Can't return null-return here, so bail. |
| 20022 | bailoutFrom(¬Equal, ins->snapshot()); |
| 20023 | } |
| 20024 | |
| 20025 | void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) { |
| 20026 | Register lhs = ToRegister(ins->lhs()); |
| 20027 | Register temp = ToRegister(ins->temp0()); |
| 20028 | |
| 20029 | // branchTestObjClass may zero the object register on speculative paths |
| 20030 | // (we should have a defineReuseInput allocation in this case). |
| 20031 | Register spectreRegToZero = lhs; |
| 20032 | |
| 20033 | Label notEqual; |
| 20034 | |
| 20035 | masm.branchTestObjIsFunction(Assembler::NotEqual, lhs, temp, spectreRegToZero, |
| 20036 | ¬Equal); |
| 20037 | |
| 20038 | // Can't return null-return here, so bail. |
| 20039 | bailoutFrom(¬Equal, ins->snapshot()); |
| 20040 | } |
| 20041 | |
| 20042 | void CodeGenerator::visitObjectClassToString(LObjectClassToString* lir) { |
| 20043 | Register obj = ToRegister(lir->lhs()); |
| 20044 | Register temp = ToRegister(lir->temp0()); |
| 20045 | |
| 20046 | using Fn = JSString* (*)(JSContext*, JSObject*); |
| 20047 | masm.setupAlignedABICall(); |
| 20048 | masm.loadJSContext(temp); |
| 20049 | masm.passABIArg(temp); |
| 20050 | masm.passABIArg(obj); |
| 20051 | masm.callWithABI<Fn, js::ObjectClassToString>(); |
| 20052 | |
| 20053 | bailoutCmpPtr(Assembler::Equal, ReturnReg, ImmWord(0), lir->snapshot()); |
| 20054 | } |
| 20055 | |
| 20056 | void CodeGenerator::visitWasmParameter(LWasmParameter* lir) {} |
| 20057 | |
| 20058 | void CodeGenerator::visitWasmParameterI64(LWasmParameterI64* lir) {} |
| 20059 | |
| 20060 | void CodeGenerator::visitWasmReturn(LWasmReturn* lir) { |
| 20061 | // Don't emit a jump to the return label if this is the last block. |
| 20062 | if (current->mir() != *gen->graph().poBegin()) { |
| 20063 | masm.jump(&returnLabel_); |
| 20064 | } |
| 20065 | } |
| 20066 | |
| 20067 | void CodeGenerator::visitWasmReturnI64(LWasmReturnI64* lir) { |
| 20068 | // Don't emit a jump to the return label if this is the last block. |
| 20069 | if (current->mir() != *gen->graph().poBegin()) { |
| 20070 | masm.jump(&returnLabel_); |
| 20071 | } |
| 20072 | } |
| 20073 | |
| 20074 | void CodeGenerator::visitWasmReturnVoid(LWasmReturnVoid* lir) { |
| 20075 | // Don't emit a jump to the return label if this is the last block. |
| 20076 | if (current->mir() != *gen->graph().poBegin()) { |
| 20077 | masm.jump(&returnLabel_); |
| 20078 | } |
| 20079 | } |
| 20080 | |
| 20081 | void CodeGenerator::emitAssertRangeI(MIRType type, const Range* r, |
| 20082 | Register input) { |
| 20083 | // Check the lower bound. |
| 20084 | if (r->hasInt32LowerBound() && r->lower() > INT32_MIN(-2147483647-1)) { |
| 20085 | Label success; |
| 20086 | if (type == MIRType::Int32 || type == MIRType::Boolean) { |
| 20087 | masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), |
| 20088 | &success); |
| 20089 | } else { |
| 20090 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20090); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 20090; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20091 | masm.branchPtr(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), |
| 20092 | &success); |
| 20093 | } |
| 20094 | masm.assumeUnreachable( |
| 20095 | "Integer input should be equal or higher than Lowerbound."); |
| 20096 | masm.bind(&success); |
| 20097 | } |
| 20098 | |
| 20099 | // Check the upper bound. |
| 20100 | if (r->hasInt32UpperBound() && r->upper() < INT32_MAX(2147483647)) { |
| 20101 | Label success; |
| 20102 | if (type == MIRType::Int32 || type == MIRType::Boolean) { |
| 20103 | masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), |
| 20104 | &success); |
| 20105 | } else { |
| 20106 | MOZ_ASSERT(type == MIRType::IntPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(type == MIRType::IntPtr)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(type == MIRType::IntPtr))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("type == MIRType::IntPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20106); AnnotateMozCrashReason("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { *((volatile int*)__null) = 20106; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20107 | masm.branchPtr(Assembler::LessThanOrEqual, input, Imm32(r->upper()), |
| 20108 | &success); |
| 20109 | } |
| 20110 | masm.assumeUnreachable( |
| 20111 | "Integer input should be lower or equal than Upperbound."); |
| 20112 | masm.bind(&success); |
| 20113 | } |
| 20114 | |
| 20115 | // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and |
| 20116 | // r->exponent(), there's nothing to check, because if we ended up in the |
| 20117 | // integer range checking code, the value is already in an integer register |
| 20118 | // in the integer range. |
| 20119 | } |
| 20120 | |
| 20121 | void CodeGenerator::emitAssertRangeD(const Range* r, FloatRegister input, |
| 20122 | FloatRegister temp) { |
| 20123 | // Check the lower bound. |
| 20124 | if (r->hasInt32LowerBound()) { |
| 20125 | Label success; |
| 20126 | masm.loadConstantDouble(r->lower(), temp); |
| 20127 | if (r->canBeNaN()) { |
| 20128 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &success); |
| 20129 | } |
| 20130 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, |
| 20131 | &success); |
| 20132 | masm.assumeUnreachable( |
| 20133 | "Double input should be equal or higher than Lowerbound."); |
| 20134 | masm.bind(&success); |
| 20135 | } |
| 20136 | // Check the upper bound. |
| 20137 | if (r->hasInt32UpperBound()) { |
| 20138 | Label success; |
| 20139 | masm.loadConstantDouble(r->upper(), temp); |
| 20140 | if (r->canBeNaN()) { |
| 20141 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &success); |
| 20142 | } |
| 20143 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success); |
| 20144 | masm.assumeUnreachable( |
| 20145 | "Double input should be lower or equal than Upperbound."); |
| 20146 | masm.bind(&success); |
| 20147 | } |
| 20148 | |
| 20149 | // This code does not yet check r->canHaveFractionalPart(). This would require |
| 20150 | // new assembler interfaces to make rounding instructions available. |
| 20151 | |
| 20152 | if (!r->canBeNegativeZero()) { |
| 20153 | Label success; |
| 20154 | |
| 20155 | // First, test for being equal to 0.0, which also includes -0.0. |
| 20156 | masm.loadConstantDouble(0.0, temp); |
| 20157 | masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp, |
| 20158 | &success); |
| 20159 | |
| 20160 | // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is |
| 20161 | // -Infinity instead of Infinity. |
| 20162 | masm.loadConstantDouble(1.0, temp); |
| 20163 | masm.divDouble(input, temp); |
| 20164 | masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success); |
| 20165 | |
| 20166 | masm.assumeUnreachable("Input shouldn't be negative zero."); |
| 20167 | |
| 20168 | masm.bind(&success); |
| 20169 | } |
| 20170 | |
| 20171 | if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() && |
| 20172 | r->exponent() < FloatingPoint<double>::kExponentBias) { |
| 20173 | // Check the bounds implied by the maximum exponent. |
| 20174 | Label exponentLoOk; |
| 20175 | masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp); |
| 20176 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk); |
| 20177 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, |
| 20178 | &exponentLoOk); |
| 20179 | masm.assumeUnreachable("Check for exponent failed."); |
| 20180 | masm.bind(&exponentLoOk); |
| 20181 | |
| 20182 | Label exponentHiOk; |
| 20183 | masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp); |
| 20184 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk); |
| 20185 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, |
| 20186 | &exponentHiOk); |
| 20187 | masm.assumeUnreachable("Check for exponent failed."); |
| 20188 | masm.bind(&exponentHiOk); |
| 20189 | } else if (!r->hasInt32Bounds() && !r->canBeNaN()) { |
| 20190 | // If we think the value can't be NaN, check that it isn't. |
| 20191 | Label notnan; |
| 20192 | masm.branchDouble(Assembler::DoubleOrdered, input, input, ¬nan); |
| 20193 | masm.assumeUnreachable("Input shouldn't be NaN."); |
| 20194 | masm.bind(¬nan); |
| 20195 | |
| 20196 | // If we think the value also can't be an infinity, check that it isn't. |
| 20197 | if (!r->canBeInfiniteOrNaN()) { |
| 20198 | Label notposinf; |
| 20199 | masm.loadConstantDouble(PositiveInfinity<double>(), temp); |
| 20200 | masm.branchDouble(Assembler::DoubleLessThan, input, temp, ¬posinf); |
| 20201 | masm.assumeUnreachable("Input shouldn't be +Inf."); |
| 20202 | masm.bind(¬posinf); |
| 20203 | |
| 20204 | Label notneginf; |
| 20205 | masm.loadConstantDouble(NegativeInfinity<double>(), temp); |
| 20206 | masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, ¬neginf); |
| 20207 | masm.assumeUnreachable("Input shouldn't be -Inf."); |
| 20208 | masm.bind(¬neginf); |
| 20209 | } |
| 20210 | } |
| 20211 | } |
| 20212 | |
| 20213 | void CodeGenerator::visitAssertClass(LAssertClass* ins) { |
| 20214 | Register obj = ToRegister(ins->input()); |
| 20215 | Register temp = ToRegister(ins->getTemp(0)); |
| 20216 | |
| 20217 | Label success; |
| 20218 | if (ins->mir()->getClass() == &FunctionClass) { |
| 20219 | // Allow both possible function classes here. |
| 20220 | masm.branchTestObjIsFunctionNoSpectreMitigations(Assembler::Equal, obj, |
| 20221 | temp, &success); |
| 20222 | } else { |
| 20223 | masm.branchTestObjClassNoSpectreMitigations( |
| 20224 | Assembler::Equal, obj, ins->mir()->getClass(), temp, &success); |
| 20225 | } |
| 20226 | masm.assumeUnreachable("Wrong KnownClass during run-time"); |
| 20227 | masm.bind(&success); |
| 20228 | } |
| 20229 | |
| 20230 | void CodeGenerator::visitAssertShape(LAssertShape* ins) { |
| 20231 | Register obj = ToRegister(ins->input()); |
| 20232 | |
| 20233 | Label success; |
| 20234 | masm.branchTestObjShapeNoSpectreMitigations(Assembler::Equal, obj, |
| 20235 | ins->mir()->shape(), &success); |
| 20236 | masm.assumeUnreachable("Wrong Shape during run-time"); |
| 20237 | masm.bind(&success); |
| 20238 | } |
| 20239 | |
| 20240 | void CodeGenerator::visitAssertRangeI(LAssertRangeI* ins) { |
| 20241 | Register input = ToRegister(ins->input()); |
| 20242 | const Range* r = ins->range(); |
| 20243 | |
| 20244 | emitAssertRangeI(ins->mir()->input()->type(), r, input); |
| 20245 | } |
| 20246 | |
| 20247 | void CodeGenerator::visitAssertRangeD(LAssertRangeD* ins) { |
| 20248 | FloatRegister input = ToFloatRegister(ins->input()); |
| 20249 | FloatRegister temp = ToFloatRegister(ins->temp()); |
| 20250 | const Range* r = ins->range(); |
| 20251 | |
| 20252 | emitAssertRangeD(r, input, temp); |
| 20253 | } |
| 20254 | |
| 20255 | void CodeGenerator::visitAssertRangeF(LAssertRangeF* ins) { |
| 20256 | FloatRegister input = ToFloatRegister(ins->input()); |
| 20257 | FloatRegister temp = ToFloatRegister(ins->temp()); |
| 20258 | FloatRegister temp2 = ToFloatRegister(ins->temp2()); |
| 20259 | |
| 20260 | const Range* r = ins->range(); |
| 20261 | |
| 20262 | masm.convertFloat32ToDouble(input, temp); |
| 20263 | emitAssertRangeD(r, temp, temp2); |
| 20264 | } |
| 20265 | |
| 20266 | void CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) { |
| 20267 | const Range* r = ins->range(); |
| 20268 | const ValueOperand value = ToValue(ins, LAssertRangeV::Input); |
| 20269 | Label done; |
| 20270 | |
| 20271 | { |
| 20272 | ScratchTagScope tag(masm, value); |
| 20273 | masm.splitTagForTest(value, tag); |
| 20274 | |
| 20275 | { |
| 20276 | Label isNotInt32; |
| 20277 | masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32); |
| 20278 | { |
| 20279 | ScratchTagScopeRelease _(&tag); |
| 20280 | Register unboxInt32 = ToTempUnboxRegister(ins->temp()); |
| 20281 | Register input = masm.extractInt32(value, unboxInt32); |
| 20282 | emitAssertRangeI(MIRType::Int32, r, input); |
| 20283 | masm.jump(&done); |
| 20284 | } |
| 20285 | masm.bind(&isNotInt32); |
| 20286 | } |
| 20287 | |
| 20288 | { |
| 20289 | Label isNotDouble; |
| 20290 | masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble); |
| 20291 | { |
| 20292 | ScratchTagScopeRelease _(&tag); |
| 20293 | FloatRegister input = ToFloatRegister(ins->floatTemp1()); |
| 20294 | FloatRegister temp = ToFloatRegister(ins->floatTemp2()); |
| 20295 | masm.unboxDouble(value, input); |
| 20296 | emitAssertRangeD(r, input, temp); |
| 20297 | masm.jump(&done); |
| 20298 | } |
| 20299 | masm.bind(&isNotDouble); |
| 20300 | } |
| 20301 | } |
| 20302 | |
| 20303 | masm.assumeUnreachable("Incorrect range for Value."); |
| 20304 | masm.bind(&done); |
| 20305 | } |
| 20306 | |
| 20307 | void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) { |
| 20308 | using Fn = bool (*)(JSContext*); |
| 20309 | OutOfLineCode* ool = |
| 20310 | oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing()); |
| 20311 | |
| 20312 | const void* interruptAddr = gen->runtime->addressOfInterruptBits(); |
| 20313 | masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0), |
| 20314 | ool->entry()); |
| 20315 | masm.bind(ool->rejoin()); |
| 20316 | } |
| 20317 | |
| 20318 | void CodeGenerator::visitOutOfLineResumableWasmTrap( |
| 20319 | OutOfLineResumableWasmTrap* ool) { |
| 20320 | LInstruction* lir = ool->lir(); |
| 20321 | masm.wasmTrap(ool->trap(), ool->trapSiteDesc()); |
| 20322 | |
| 20323 | markSafepointAt(masm.currentOffset(), lir); |
| 20324 | |
| 20325 | // Note that masm.framePushed() doesn't include the register dump area. |
| 20326 | // That will be taken into account when the StackMap is created from the |
| 20327 | // LSafepoint. |
| 20328 | lir->safepoint()->setFramePushedAtStackMapBase(ool->framePushed()); |
| 20329 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::Trap); |
| 20330 | |
| 20331 | masm.jump(ool->rejoin()); |
| 20332 | } |
| 20333 | |
| 20334 | void CodeGenerator::visitOutOfLineAbortingWasmTrap( |
| 20335 | OutOfLineAbortingWasmTrap* ool) { |
| 20336 | masm.wasmTrap(ool->trap(), ool->trapSiteDesc()); |
| 20337 | } |
| 20338 | |
| 20339 | void CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir) { |
| 20340 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20340; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20341 | |
| 20342 | OutOfLineResumableWasmTrap* ool = new (alloc()) OutOfLineResumableWasmTrap( |
| 20343 | lir, masm.framePushed(), lir->mir()->trapSiteDesc(), |
| 20344 | wasm::Trap::CheckInterrupt); |
| 20345 | addOutOfLineCode(ool, lir->mir()); |
| 20346 | masm.branch32( |
| 20347 | Assembler::NotEqual, |
| 20348 | Address(ToRegister(lir->instance()), wasm::Instance::offsetOfInterrupt()), |
| 20349 | Imm32(0), ool->entry()); |
| 20350 | masm.bind(ool->rejoin()); |
| 20351 | } |
| 20352 | |
| 20353 | void CodeGenerator::visitWasmTrap(LWasmTrap* lir) { |
| 20354 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20354; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20355 | const MWasmTrap* mir = lir->mir(); |
| 20356 | |
| 20357 | masm.wasmTrap(mir->trap(), mir->trapSiteDesc()); |
| 20358 | } |
| 20359 | |
| 20360 | void CodeGenerator::visitWasmTrapIfNull(LWasmTrapIfNull* lir) { |
| 20361 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20361); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20361; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20362 | const MWasmTrapIfNull* mir = lir->mir(); |
| 20363 | Label nonNull; |
| 20364 | Register ref = ToRegister(lir->ref()); |
| 20365 | |
| 20366 | masm.branchWasmAnyRefIsNull(false, ref, &nonNull); |
| 20367 | masm.wasmTrap(mir->trap(), mir->trapSiteDesc()); |
| 20368 | masm.bind(&nonNull); |
| 20369 | } |
| 20370 | |
| 20371 | void CodeGenerator::visitWasmRefIsSubtypeOfAbstract( |
| 20372 | LWasmRefIsSubtypeOfAbstract* ins) { |
| 20373 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20373); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20373; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20374 | |
| 20375 | const MWasmRefIsSubtypeOfAbstract* mir = ins->mir(); |
| 20376 | MOZ_ASSERT(!mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!mir->destType().isTypeRef())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!mir->destType().isTypeRef ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!mir->destType().isTypeRef()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()" ")"); do { *((volatile int*)__null) = 20376; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20377 | |
| 20378 | Register ref = ToRegister(ins->ref()); |
| 20379 | Register superSTV = Register::Invalid(); |
| 20380 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
| 20381 | Register scratch2 = Register::Invalid(); |
| 20382 | Register result = ToRegister(ins->output()); |
| 20383 | Label onSuccess; |
| 20384 | Label onFail; |
| 20385 | Label join; |
| 20386 | masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(), |
| 20387 | &onSuccess, /*onSuccess=*/true, superSTV, |
| 20388 | scratch1, scratch2); |
| 20389 | masm.bind(&onFail); |
| 20390 | masm.xor32(result, result); |
| 20391 | masm.jump(&join); |
| 20392 | masm.bind(&onSuccess); |
| 20393 | masm.move32(Imm32(1), result); |
| 20394 | masm.bind(&join); |
| 20395 | } |
| 20396 | |
| 20397 | void CodeGenerator::visitWasmRefIsSubtypeOfConcrete( |
| 20398 | LWasmRefIsSubtypeOfConcrete* ins) { |
| 20399 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20399); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20399; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20400 | |
| 20401 | const MWasmRefIsSubtypeOfConcrete* mir = ins->mir(); |
| 20402 | MOZ_ASSERT(mir->destType().isTypeRef())do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->destType().isTypeRef())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mir->destType().isTypeRef ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("mir->destType().isTypeRef()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20402); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()" ")"); do { *((volatile int*)__null) = 20402; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20403 | |
| 20404 | Register ref = ToRegister(ins->ref()); |
| 20405 | Register superSTV = ToRegister(ins->superSTV()); |
| 20406 | Register scratch1 = ToRegister(ins->temp0()); |
| 20407 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
| 20408 | Register result = ToRegister(ins->output()); |
| 20409 | Label onSuccess; |
| 20410 | Label join; |
| 20411 | masm.branchWasmRefIsSubtype(ref, mir->sourceType(), mir->destType(), |
| 20412 | &onSuccess, /*onSuccess=*/true, superSTV, |
| 20413 | scratch1, scratch2); |
| 20414 | masm.move32(Imm32(0), result); |
| 20415 | masm.jump(&join); |
| 20416 | masm.bind(&onSuccess); |
| 20417 | masm.move32(Imm32(1), result); |
| 20418 | masm.bind(&join); |
| 20419 | } |
| 20420 | |
| 20421 | void CodeGenerator::visitWasmRefIsSubtypeOfAbstractAndBranch( |
| 20422 | LWasmRefIsSubtypeOfAbstractAndBranch* ins) { |
| 20423 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20423; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20424 | Register ref = ToRegister(ins->ref()); |
| 20425 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
| 20426 | Label* onSuccess = getJumpLabelForBranch(ins->ifTrue()); |
| 20427 | Label* onFail = getJumpLabelForBranch(ins->ifFalse()); |
| 20428 | masm.branchWasmRefIsSubtype( |
| 20429 | ref, ins->sourceType(), ins->destType(), onSuccess, /*onSuccess=*/true, |
| 20430 | Register::Invalid(), scratch1, Register::Invalid()); |
| 20431 | masm.jump(onFail); |
| 20432 | } |
| 20433 | |
| 20434 | void CodeGenerator::visitWasmRefIsSubtypeOfConcreteAndBranch( |
| 20435 | LWasmRefIsSubtypeOfConcreteAndBranch* ins) { |
| 20436 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20436); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20436; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20437 | Register ref = ToRegister(ins->ref()); |
| 20438 | Register superSTV = ToRegister(ins->superSTV()); |
| 20439 | Register scratch1 = ToRegister(ins->temp0()); |
| 20440 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
| 20441 | Label* onSuccess = getJumpLabelForBranch(ins->ifTrue()); |
| 20442 | Label* onFail = getJumpLabelForBranch(ins->ifFalse()); |
| 20443 | masm.branchWasmRefIsSubtype(ref, ins->sourceType(), ins->destType(), |
| 20444 | onSuccess, /*onSuccess=*/true, superSTV, scratch1, |
| 20445 | scratch2); |
| 20446 | masm.jump(onFail); |
| 20447 | } |
| 20448 | |
| 20449 | void CodeGenerator::callWasmStructAllocFun( |
| 20450 | LInstruction* lir, wasm::SymbolicAddress fun, Register typeDefData, |
| 20451 | Register output, const wasm::TrapSiteDesc& trapSiteDesc) { |
| 20452 | MOZ_ASSERT(fun == wasm::SymbolicAddress::StructNewIL_true ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm:: SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress ::StructNewOOL_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { *((volatile int*)__null) = 20455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20453 | fun == wasm::SymbolicAddress::StructNewIL_false ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm:: SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress ::StructNewOOL_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { *((volatile int*)__null) = 20455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20454 | fun == wasm::SymbolicAddress::StructNewOOL_true ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm:: SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress ::StructNewOOL_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { *((volatile int*)__null) = 20455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20455 | fun == wasm::SymbolicAddress::StructNewOOL_false)do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm:: SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress ::StructNewOOL_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20455); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { *((volatile int*)__null) = 20455; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20456 | MOZ_ASSERT(wasm::SASigStructNewIL_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20457; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20457 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20457); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20457; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20458 | MOZ_ASSERT(wasm::SASigStructNewIL_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_false. failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20459); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20459; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20459 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewIL_false. failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20459); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20459; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20460 | MOZ_ASSERT(wasm::SASigStructNewOOL_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_true. failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20461); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20461; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20461 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_true. failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20461); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20461; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20462 | MOZ_ASSERT(wasm::SASigStructNewOOL_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_false .failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20463); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20463; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20463 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigStructNewOOL_false .failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20463); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20463; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20464 | |
| 20465 | masm.Push(InstanceReg); |
| 20466 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 20467 | saveLive(lir); |
| 20468 | |
| 20469 | masm.setupWasmABICall(); |
| 20470 | masm.passABIArg(InstanceReg); |
| 20471 | masm.passABIArg(typeDefData); |
| 20472 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 20473 | CodeOffset offset = |
| 20474 | masm.callWithABI(trapSiteDesc.bytecodeOffset, fun, |
| 20475 | mozilla::Some(instanceOffset), ABIType::General); |
| 20476 | masm.storeCallPointerResult(output); |
| 20477 | |
| 20478 | markSafepointAt(offset.offset(), lir); |
| 20479 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance); |
| 20480 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall); |
| 20481 | |
| 20482 | restoreLive(lir); |
| 20483 | masm.Pop(InstanceReg); |
| 20484 | #if JS_CODEGEN_ARM64 |
| 20485 | masm.syncStackPtr(); |
| 20486 | #endif |
| 20487 | |
| 20488 | masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr, |
| 20489 | trapSiteDesc); |
| 20490 | } |
| 20491 | |
| 20492 | // Out-of-line path to allocate wasm GC structs |
| 20493 | class OutOfLineWasmNewStruct : public OutOfLineCodeBase<CodeGenerator> { |
| 20494 | LInstruction* lir_; |
| 20495 | wasm::SymbolicAddress fun_; |
| 20496 | Register typeDefData_; |
| 20497 | Register output_; |
| 20498 | wasm::TrapSiteDesc trapSiteDesc_; |
| 20499 | |
| 20500 | public: |
| 20501 | OutOfLineWasmNewStruct(LInstruction* lir, wasm::SymbolicAddress fun, |
| 20502 | Register typeDefData, Register output, |
| 20503 | const wasm::TrapSiteDesc& trapSiteDesc) |
| 20504 | : lir_(lir), |
| 20505 | fun_(fun), |
| 20506 | typeDefData_(typeDefData), |
| 20507 | output_(output), |
| 20508 | trapSiteDesc_(trapSiteDesc) {} |
| 20509 | |
| 20510 | void accept(CodeGenerator* codegen) override { |
| 20511 | codegen->visitOutOfLineWasmNewStruct(this); |
| 20512 | } |
| 20513 | |
| 20514 | LInstruction* lir() const { return lir_; } |
| 20515 | wasm::SymbolicAddress fun() const { return fun_; } |
| 20516 | Register typeDefData() const { return typeDefData_; } |
| 20517 | Register output() const { return output_; } |
| 20518 | const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } |
| 20519 | }; |
| 20520 | |
| 20521 | void CodeGenerator::visitOutOfLineWasmNewStruct(OutOfLineWasmNewStruct* ool) { |
| 20522 | callWasmStructAllocFun(ool->lir(), ool->fun(), ool->typeDefData(), |
| 20523 | ool->output(), ool->trapSiteDesc()); |
| 20524 | masm.jump(ool->rejoin()); |
| 20525 | } |
| 20526 | |
| 20527 | void CodeGenerator::visitWasmNewStructObject(LWasmNewStructObject* lir) { |
| 20528 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20528); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20528; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20529 | |
| 20530 | MWasmNewStructObject* mir = lir->mir(); |
| 20531 | |
| 20532 | Register typeDefData = ToRegister(lir->typeDefData()); |
| 20533 | Register output = ToRegister(lir->output()); |
| 20534 | |
| 20535 | if (mir->isOutline()) { |
| 20536 | wasm::SymbolicAddress fun = mir->zeroFields() |
| 20537 | ? wasm::SymbolicAddress::StructNewOOL_true |
| 20538 | : wasm::SymbolicAddress::StructNewOOL_false; |
| 20539 | callWasmStructAllocFun(lir, fun, typeDefData, output, mir->trapSiteDesc()); |
| 20540 | } else { |
| 20541 | wasm::SymbolicAddress fun = mir->zeroFields() |
| 20542 | ? wasm::SymbolicAddress::StructNewIL_true |
| 20543 | : wasm::SymbolicAddress::StructNewIL_false; |
| 20544 | |
| 20545 | Register instance = ToRegister(lir->instance()); |
| 20546 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20546); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20546; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20547 | |
| 20548 | auto* ool = new (alloc()) OutOfLineWasmNewStruct( |
| 20549 | lir, fun, typeDefData, output, mir->trapSiteDesc()); |
| 20550 | addOutOfLineCode(ool, lir->mir()); |
| 20551 | |
| 20552 | Register temp1 = ToRegister(lir->temp0()); |
| 20553 | Register temp2 = ToRegister(lir->temp1()); |
| 20554 | masm.wasmNewStructObject(instance, output, typeDefData, temp1, temp2, |
| 20555 | ool->entry(), mir->allocKind(), mir->zeroFields()); |
| 20556 | |
| 20557 | masm.bind(ool->rejoin()); |
| 20558 | } |
| 20559 | } |
| 20560 | |
| 20561 | void CodeGenerator::callWasmArrayAllocFun( |
| 20562 | LInstruction* lir, wasm::SymbolicAddress fun, Register numElements, |
| 20563 | Register typeDefData, Register output, |
| 20564 | const wasm::TrapSiteDesc& trapSiteDesc) { |
| 20565 | MOZ_ASSERT(fun == wasm::SymbolicAddress::ArrayNew_true ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" ")"); do { *((volatile int*)__null) = 20566; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20566 | fun == wasm::SymbolicAddress::ArrayNew_false)do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun == wasm::SymbolicAddress ::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20566); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" ")"); do { *((volatile int*)__null) = 20566; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20567 | MOZ_ASSERT(wasm::SASigArrayNew_true.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20568; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20568 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20568); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20568; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20569 | MOZ_ASSERT(wasm::SASigArrayNew_false.failureMode ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20570; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 20570 | wasm::FailureMode::FailOnNullPtr)do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode ::FailOnNullPtr)>::isValid, "invalid assertion condition") ; if ((__builtin_expect(!!(!(!!(wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20570); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { *((volatile int*)__null) = 20570; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20571 | |
| 20572 | masm.Push(InstanceReg); |
| 20573 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 20574 | saveLive(lir); |
| 20575 | |
| 20576 | masm.setupWasmABICall(); |
| 20577 | masm.passABIArg(InstanceReg); |
| 20578 | masm.passABIArg(numElements); |
| 20579 | masm.passABIArg(typeDefData); |
| 20580 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 20581 | CodeOffset offset = |
| 20582 | masm.callWithABI(trapSiteDesc.bytecodeOffset, fun, |
| 20583 | mozilla::Some(instanceOffset), ABIType::General); |
| 20584 | masm.storeCallPointerResult(output); |
| 20585 | |
| 20586 | markSafepointAt(offset.offset(), lir); |
| 20587 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance); |
| 20588 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall); |
| 20589 | |
| 20590 | restoreLive(lir); |
| 20591 | masm.Pop(InstanceReg); |
| 20592 | #if JS_CODEGEN_ARM64 |
| 20593 | masm.syncStackPtr(); |
| 20594 | #endif |
| 20595 | |
| 20596 | masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr, |
| 20597 | trapSiteDesc); |
| 20598 | } |
| 20599 | |
| 20600 | // Out-of-line path to allocate wasm GC arrays |
| 20601 | class OutOfLineWasmNewArray : public OutOfLineCodeBase<CodeGenerator> { |
| 20602 | LInstruction* lir_; |
| 20603 | wasm::SymbolicAddress fun_; |
| 20604 | Register numElementsReg_; |
| 20605 | mozilla::Maybe<uint32_t> numElements_; |
| 20606 | Register typeDefData_; |
| 20607 | Register output_; |
| 20608 | wasm::TrapSiteDesc trapSiteDesc_; |
| 20609 | |
| 20610 | public: |
| 20611 | OutOfLineWasmNewArray(LInstruction* lir, wasm::SymbolicAddress fun, |
| 20612 | Register numElementsReg, |
| 20613 | mozilla::Maybe<uint32_t> numElements, |
| 20614 | Register typeDefData, Register output, |
| 20615 | const wasm::TrapSiteDesc& trapSiteDesc) |
| 20616 | : lir_(lir), |
| 20617 | fun_(fun), |
| 20618 | numElementsReg_(numElementsReg), |
| 20619 | numElements_(numElements), |
| 20620 | typeDefData_(typeDefData), |
| 20621 | output_(output), |
| 20622 | trapSiteDesc_(trapSiteDesc) {} |
| 20623 | |
| 20624 | void accept(CodeGenerator* codegen) override { |
| 20625 | codegen->visitOutOfLineWasmNewArray(this); |
| 20626 | } |
| 20627 | |
| 20628 | LInstruction* lir() const { return lir_; } |
| 20629 | wasm::SymbolicAddress fun() const { return fun_; } |
| 20630 | Register numElementsReg() const { return numElementsReg_; } |
| 20631 | mozilla::Maybe<uint32_t> numElements() const { return numElements_; } |
| 20632 | Register typeDefData() const { return typeDefData_; } |
| 20633 | Register output() const { return output_; } |
| 20634 | const wasm::TrapSiteDesc& trapSiteDesc() const { return trapSiteDesc_; } |
| 20635 | }; |
| 20636 | |
| 20637 | void CodeGenerator::visitOutOfLineWasmNewArray(OutOfLineWasmNewArray* ool) { |
| 20638 | if (ool->numElements().isSome()) { |
| 20639 | masm.move32(Imm32(ool->numElements().value()), ool->numElementsReg()); |
| 20640 | } |
| 20641 | callWasmArrayAllocFun(ool->lir(), ool->fun(), ool->numElementsReg(), |
| 20642 | ool->typeDefData(), ool->output(), ool->trapSiteDesc()); |
| 20643 | masm.jump(ool->rejoin()); |
| 20644 | } |
| 20645 | |
| 20646 | void CodeGenerator::visitWasmNewArrayObject(LWasmNewArrayObject* lir) { |
| 20647 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20647); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 20647; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20648 | |
| 20649 | MWasmNewArrayObject* mir = lir->mir(); |
| 20650 | |
| 20651 | Register typeDefData = ToRegister(lir->typeDefData()); |
| 20652 | Register output = ToRegister(lir->output()); |
| 20653 | Register temp1 = ToRegister(lir->temp0()); |
| 20654 | Register temp2 = ToRegister(lir->temp1()); |
| 20655 | |
| 20656 | wasm::SymbolicAddress fun = mir->zeroFields() |
| 20657 | ? wasm::SymbolicAddress::ArrayNew_true |
| 20658 | : wasm::SymbolicAddress::ArrayNew_false; |
| 20659 | |
| 20660 | if (lir->numElements()->isConstant()) { |
| 20661 | // numElements is constant, so we can do optimized code generation. |
| 20662 | uint32_t numElements = lir->numElements()->toConstant()->toInt32(); |
| 20663 | CheckedUint32 storageBytes = |
| 20664 | WasmArrayObject::calcStorageBytesChecked(mir->elemSize(), numElements); |
| 20665 | if (!storageBytes.isValid() || |
| 20666 | storageBytes.value() > WasmArrayObject_MaxInlineBytes) { |
| 20667 | // Too much array data to store inline. Immediately perform an instance |
| 20668 | // call to handle the out-of-line storage (or the trap). |
| 20669 | masm.move32(Imm32(numElements), temp1); |
| 20670 | callWasmArrayAllocFun(lir, fun, temp1, typeDefData, output, |
| 20671 | mir->trapSiteDesc()); |
| 20672 | } else { |
| 20673 | // storageBytes is small enough to be stored inline in WasmArrayObject. |
| 20674 | // Attempt a nursery allocation and fall back to an instance call if it |
| 20675 | // fails. |
| 20676 | Register instance = ToRegister(lir->instance()); |
| 20677 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20677); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20677; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20678 | |
| 20679 | auto ool = new (alloc()) |
| 20680 | OutOfLineWasmNewArray(lir, fun, temp1, mozilla::Some(numElements), |
| 20681 | typeDefData, output, mir->trapSiteDesc()); |
| 20682 | addOutOfLineCode(ool, lir->mir()); |
| 20683 | |
| 20684 | masm.wasmNewArrayObjectFixed(instance, output, typeDefData, temp1, temp2, |
| 20685 | ool->entry(), numElements, |
| 20686 | storageBytes.value(), mir->zeroFields()); |
| 20687 | |
| 20688 | masm.bind(ool->rejoin()); |
| 20689 | } |
| 20690 | } else { |
| 20691 | // numElements is dynamic. Attempt a dynamic inline-storage nursery |
| 20692 | // allocation and fall back to an instance call if it fails. |
| 20693 | Register instance = ToRegister(lir->instance()); |
| 20694 | MOZ_ASSERT(instance == InstanceReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(instance == InstanceReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(instance == InstanceReg))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("instance == InstanceReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20694); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { *((volatile int*)__null) = 20694; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20695 | Register numElements = ToRegister(lir->numElements()); |
| 20696 | |
| 20697 | auto ool = new (alloc()) |
| 20698 | OutOfLineWasmNewArray(lir, fun, numElements, mozilla::Nothing(), |
| 20699 | typeDefData, output, mir->trapSiteDesc()); |
| 20700 | addOutOfLineCode(ool, lir->mir()); |
| 20701 | |
| 20702 | masm.wasmNewArrayObject(instance, output, numElements, typeDefData, temp1, |
| 20703 | ool->entry(), mir->elemSize(), mir->zeroFields()); |
| 20704 | |
| 20705 | masm.bind(ool->rejoin()); |
| 20706 | } |
| 20707 | } |
| 20708 | |
| 20709 | void CodeGenerator::visitWasmHeapReg(LWasmHeapReg* ins) { |
| 20710 | #ifdef WASM_HAS_HEAPREG1 |
| 20711 | masm.movePtr(HeapReg, ToRegister(ins->output())); |
| 20712 | #else |
| 20713 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20713); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile int*)__null) = 20713; __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
| 20714 | #endif |
| 20715 | } |
| 20716 | |
| 20717 | void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) { |
| 20718 | const MWasmBoundsCheck* mir = ins->mir(); |
| 20719 | Register ptr = ToRegister(ins->ptr()); |
| 20720 | Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit()); |
| 20721 | // When there are no spectre mitigations in place, branching out-of-line to |
| 20722 | // the trap is a big performance win, but with mitigations it's trickier. See |
| 20723 | // bug 1680243. |
| 20724 | if (JitOptions.spectreIndexMasking) { |
| 20725 | Label ok; |
| 20726 | masm.wasmBoundsCheck32(Assembler::Below, ptr, boundsCheckLimit, &ok); |
| 20727 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 20728 | masm.bind(&ok); |
| 20729 | } else { |
| 20730 | OutOfLineAbortingWasmTrap* ool = new (alloc()) |
| 20731 | OutOfLineAbortingWasmTrap(mir->trapSiteDesc(), wasm::Trap::OutOfBounds); |
| 20732 | addOutOfLineCode(ool, mir); |
| 20733 | masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptr, boundsCheckLimit, |
| 20734 | ool->entry()); |
| 20735 | } |
| 20736 | } |
| 20737 | |
| 20738 | void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) { |
| 20739 | const MWasmBoundsCheck* mir = ins->mir(); |
| 20740 | Register64 ptr = ToRegister64(ins->ptr()); |
| 20741 | Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit()); |
| 20742 | // See above. |
| 20743 | if (JitOptions.spectreIndexMasking) { |
| 20744 | Label ok; |
| 20745 | masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok); |
| 20746 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 20747 | masm.bind(&ok); |
| 20748 | } else { |
| 20749 | OutOfLineAbortingWasmTrap* ool = new (alloc()) |
| 20750 | OutOfLineAbortingWasmTrap(mir->trapSiteDesc(), wasm::Trap::OutOfBounds); |
| 20751 | addOutOfLineCode(ool, mir); |
| 20752 | masm.wasmBoundsCheck64(Assembler::AboveOrEqual, ptr, boundsCheckLimit, |
| 20753 | ool->entry()); |
| 20754 | } |
| 20755 | } |
| 20756 | |
| 20757 | void CodeGenerator::visitWasmBoundsCheckRange32(LWasmBoundsCheckRange32* ins) { |
| 20758 | const MWasmBoundsCheckRange32* mir = ins->mir(); |
| 20759 | Register index = ToRegister(ins->index()); |
| 20760 | Register length = ToRegister(ins->length()); |
| 20761 | Register limit = ToRegister(ins->limit()); |
| 20762 | Register tmp = ToRegister(ins->temp0()); |
| 20763 | |
| 20764 | masm.wasmBoundsCheckRange32(index, length, limit, tmp, mir->trapSiteDesc()); |
| 20765 | } |
| 20766 | |
| 20767 | void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) { |
| 20768 | const MWasmAlignmentCheck* mir = ins->mir(); |
| 20769 | Register ptr = ToRegister(ins->ptr()); |
| 20770 | OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap( |
| 20771 | mir->trapSiteDesc(), wasm::Trap::UnalignedAccess); |
| 20772 | addOutOfLineCode(ool, mir); |
| 20773 | masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1), |
| 20774 | ool->entry()); |
| 20775 | } |
| 20776 | |
| 20777 | void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) { |
| 20778 | const MWasmAlignmentCheck* mir = ins->mir(); |
| 20779 | Register64 ptr = ToRegister64(ins->ptr()); |
| 20780 | #ifdef JS_64BIT1 |
| 20781 | Register r = ptr.reg; |
| 20782 | #else |
| 20783 | Register r = ptr.low; |
| 20784 | #endif |
| 20785 | OutOfLineAbortingWasmTrap* ool = new (alloc()) OutOfLineAbortingWasmTrap( |
| 20786 | mir->trapSiteDesc(), wasm::Trap::UnalignedAccess); |
| 20787 | addOutOfLineCode(ool, mir); |
| 20788 | masm.branchTestPtr(Assembler::NonZero, r, Imm32(mir->byteSize() - 1), |
| 20789 | ool->entry()); |
| 20790 | } |
| 20791 | |
| 20792 | void CodeGenerator::visitWasmLoadInstance(LWasmLoadInstance* ins) { |
| 20793 | switch (ins->mir()->type()) { |
| 20794 | case MIRType::WasmAnyRef: |
| 20795 | case MIRType::Pointer: |
| 20796 | masm.loadPtr(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
| 20797 | ToRegister(ins->output())); |
| 20798 | break; |
| 20799 | case MIRType::Int32: |
| 20800 | masm.load32(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
| 20801 | ToRegister(ins->output())); |
| 20802 | break; |
| 20803 | default: |
| 20804 | MOZ_CRASH("MIRType not supported in WasmLoadInstance")do { do { } while (false); MOZ_ReportCrash("" "MIRType not supported in WasmLoadInstance" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20804); AnnotateMozCrashReason("MOZ_CRASH(" "MIRType not supported in WasmLoadInstance" ")"); do { *((volatile int*)__null) = 20804; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 20805 | } |
| 20806 | } |
| 20807 | |
| 20808 | void CodeGenerator::visitWasmLoadInstance64(LWasmLoadInstance64* ins) { |
| 20809 | MOZ_ASSERT(ins->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->type() == MIRType::Int64)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ins->mir()->type() == MIRType::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ins->mir()->type() == MIRType::Int64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 20809); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 20809; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20810 | masm.load64(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
| 20811 | ToOutRegister64(ins)); |
| 20812 | } |
| 20813 | |
| 20814 | void CodeGenerator::incrementWarmUpCounter(AbsoluteAddress warmUpCount, |
| 20815 | JSScript* script, Register tmp) { |
| 20816 | // The code depends on the JitScript* not being discarded without also |
| 20817 | // invalidating Ion code. Assert this. |
| 20818 | #ifdef DEBUG1 |
| 20819 | Label ok; |
| 20820 | masm.movePtr(ImmGCPtr(script), tmp); |
| 20821 | masm.loadJitScript(tmp, tmp); |
| 20822 | masm.branchPtr(Assembler::Equal, tmp, ImmPtr(script->jitScript()), &ok); |
| 20823 | masm.assumeUnreachable("Didn't find JitScript?"); |
| 20824 | masm.bind(&ok); |
| 20825 | #endif |
| 20826 | |
| 20827 | masm.load32(warmUpCount, tmp); |
| 20828 | masm.add32(Imm32(1), tmp); |
| 20829 | masm.store32(tmp, warmUpCount); |
| 20830 | } |
| 20831 | |
| 20832 | void CodeGenerator::visitIncrementWarmUpCounter(LIncrementWarmUpCounter* ins) { |
| 20833 | Register tmp = ToRegister(ins->temp0()); |
| 20834 | |
| 20835 | AbsoluteAddress warmUpCount = |
| 20836 | AbsoluteAddress(ins->mir()->script()->jitScript()) |
| 20837 | .offset(JitScript::offsetOfWarmUpCount()); |
| 20838 | incrementWarmUpCounter(warmUpCount, ins->mir()->script(), tmp); |
| 20839 | } |
| 20840 | |
| 20841 | void CodeGenerator::visitLexicalCheck(LLexicalCheck* ins) { |
| 20842 | ValueOperand inputValue = ToValue(ins, LLexicalCheck::InputIndex); |
| 20843 | Label bail; |
| 20844 | masm.branchTestMagicValue(Assembler::Equal, inputValue, |
| 20845 | JS_UNINITIALIZED_LEXICAL, &bail); |
| 20846 | bailoutFrom(&bail, ins->snapshot()); |
| 20847 | } |
| 20848 | |
| 20849 | void CodeGenerator::visitThrowRuntimeLexicalError( |
| 20850 | LThrowRuntimeLexicalError* ins) { |
| 20851 | pushArg(Imm32(ins->mir()->errorNumber())); |
| 20852 | |
| 20853 | using Fn = bool (*)(JSContext*, unsigned); |
| 20854 | callVM<Fn, jit::ThrowRuntimeLexicalError>(ins); |
| 20855 | } |
| 20856 | |
| 20857 | void CodeGenerator::visitThrowMsg(LThrowMsg* ins) { |
| 20858 | pushArg(Imm32(static_cast<int32_t>(ins->mir()->throwMsgKind()))); |
| 20859 | |
| 20860 | using Fn = bool (*)(JSContext*, unsigned); |
| 20861 | callVM<Fn, js::ThrowMsgOperation>(ins); |
| 20862 | } |
| 20863 | |
| 20864 | void CodeGenerator::visitGlobalDeclInstantiation( |
| 20865 | LGlobalDeclInstantiation* ins) { |
| 20866 | pushArg(ImmPtr(ins->mir()->resumePoint()->pc())); |
| 20867 | pushArg(ImmGCPtr(ins->mir()->block()->info().script())); |
| 20868 | |
| 20869 | using Fn = bool (*)(JSContext*, HandleScript, const jsbytecode*); |
| 20870 | callVM<Fn, GlobalDeclInstantiationFromIon>(ins); |
| 20871 | } |
| 20872 | |
| 20873 | void CodeGenerator::visitDebugger(LDebugger* ins) { |
| 20874 | Register cx = ToRegister(ins->temp0()); |
| 20875 | |
| 20876 | masm.loadJSContext(cx); |
| 20877 | using Fn = bool (*)(JSContext* cx); |
| 20878 | masm.setupAlignedABICall(); |
| 20879 | masm.passABIArg(cx); |
| 20880 | masm.callWithABI<Fn, GlobalHasLiveOnDebuggerStatement>(); |
| 20881 | |
| 20882 | Label bail; |
| 20883 | masm.branchIfTrueBool(ReturnReg, &bail); |
| 20884 | bailoutFrom(&bail, ins->snapshot()); |
| 20885 | } |
| 20886 | |
| 20887 | void CodeGenerator::visitNewTarget(LNewTarget* ins) { |
| 20888 | ValueOperand output = ToOutValue(ins); |
| 20889 | |
| 20890 | // if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)] |
| 20891 | Label notConstructing, done; |
| 20892 | Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
| 20893 | masm.branchTestPtr(Assembler::Zero, calleeToken, |
| 20894 | Imm32(CalleeToken_FunctionConstructing), ¬Constructing); |
| 20895 | |
| 20896 | Register argvLen = output.scratchReg(); |
| 20897 | masm.loadNumActualArgs(FramePointer, argvLen); |
| 20898 | |
| 20899 | Label useNFormals; |
| 20900 | |
| 20901 | size_t numFormalArgs = ins->mirRaw()->block()->info().nargs(); |
| 20902 | masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs), &useNFormals); |
| 20903 | |
| 20904 | size_t argsOffset = JitFrameLayout::offsetOfActualArgs(); |
| 20905 | { |
| 20906 | BaseValueIndex newTarget(FramePointer, argvLen, argsOffset); |
| 20907 | masm.loadValue(newTarget, output); |
| 20908 | masm.jump(&done); |
| 20909 | } |
| 20910 | |
| 20911 | masm.bind(&useNFormals); |
| 20912 | |
| 20913 | { |
| 20914 | Address newTarget(FramePointer, |
| 20915 | argsOffset + (numFormalArgs * sizeof(Value))); |
| 20916 | masm.loadValue(newTarget, output); |
| 20917 | masm.jump(&done); |
| 20918 | } |
| 20919 | |
| 20920 | // else output = undefined |
| 20921 | masm.bind(¬Constructing); |
| 20922 | masm.moveValue(UndefinedValue(), output); |
| 20923 | masm.bind(&done); |
| 20924 | } |
| 20925 | |
| 20926 | void CodeGenerator::visitCheckReturn(LCheckReturn* ins) { |
| 20927 | ValueOperand returnValue = ToValue(ins, LCheckReturn::ReturnValueIndex); |
| 20928 | ValueOperand thisValue = ToValue(ins, LCheckReturn::ThisValueIndex); |
| 20929 | ValueOperand output = ToOutValue(ins); |
| 20930 | |
| 20931 | using Fn = bool (*)(JSContext*, HandleValue); |
| 20932 | OutOfLineCode* ool = oolCallVM<Fn, ThrowBadDerivedReturnOrUninitializedThis>( |
| 20933 | ins, ArgList(returnValue), StoreNothing()); |
| 20934 | |
| 20935 | Label noChecks; |
| 20936 | masm.branchTestObject(Assembler::Equal, returnValue, &noChecks); |
| 20937 | masm.branchTestUndefined(Assembler::NotEqual, returnValue, ool->entry()); |
| 20938 | masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry()); |
| 20939 | masm.moveValue(thisValue, output); |
| 20940 | masm.jump(ool->rejoin()); |
| 20941 | masm.bind(&noChecks); |
| 20942 | masm.moveValue(returnValue, output); |
| 20943 | masm.bind(ool->rejoin()); |
| 20944 | } |
| 20945 | |
| 20946 | void CodeGenerator::visitCheckIsObj(LCheckIsObj* ins) { |
| 20947 | ValueOperand value = ToValue(ins, LCheckIsObj::ValueIndex); |
| 20948 | Register output = ToRegister(ins->output()); |
| 20949 | |
| 20950 | using Fn = bool (*)(JSContext*, CheckIsObjectKind); |
| 20951 | OutOfLineCode* ool = oolCallVM<Fn, ThrowCheckIsObject>( |
| 20952 | ins, ArgList(Imm32(ins->mir()->checkKind())), StoreNothing()); |
| 20953 | |
| 20954 | masm.fallibleUnboxObject(value, output, ool->entry()); |
| 20955 | masm.bind(ool->rejoin()); |
| 20956 | } |
| 20957 | |
| 20958 | void CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) { |
| 20959 | ValueOperand checkValue = ToValue(ins, LCheckObjCoercible::ValueIndex); |
| 20960 | |
| 20961 | using Fn = bool (*)(JSContext*, HandleValue); |
| 20962 | OutOfLineCode* ool = oolCallVM<Fn, ThrowObjectCoercible>( |
| 20963 | ins, ArgList(checkValue), StoreNothing()); |
| 20964 | masm.branchTestNull(Assembler::Equal, checkValue, ool->entry()); |
| 20965 | masm.branchTestUndefined(Assembler::Equal, checkValue, ool->entry()); |
| 20966 | masm.bind(ool->rejoin()); |
| 20967 | } |
| 20968 | |
| 20969 | void CodeGenerator::visitCheckClassHeritage(LCheckClassHeritage* ins) { |
| 20970 | ValueOperand heritage = ToValue(ins, LCheckClassHeritage::HeritageIndex); |
| 20971 | Register temp0 = ToRegister(ins->temp0()); |
| 20972 | Register temp1 = ToRegister(ins->temp1()); |
| 20973 | |
| 20974 | using Fn = bool (*)(JSContext*, HandleValue); |
| 20975 | OutOfLineCode* ool = oolCallVM<Fn, CheckClassHeritageOperation>( |
| 20976 | ins, ArgList(heritage), StoreNothing()); |
| 20977 | |
| 20978 | masm.branchTestNull(Assembler::Equal, heritage, ool->rejoin()); |
| 20979 | masm.fallibleUnboxObject(heritage, temp0, ool->entry()); |
| 20980 | |
| 20981 | masm.isConstructor(temp0, temp1, ool->entry()); |
| 20982 | masm.branchTest32(Assembler::Zero, temp1, temp1, ool->entry()); |
| 20983 | |
| 20984 | masm.bind(ool->rejoin()); |
| 20985 | } |
| 20986 | |
| 20987 | void CodeGenerator::visitCheckThis(LCheckThis* ins) { |
| 20988 | ValueOperand thisValue = ToValue(ins, LCheckThis::ValueIndex); |
| 20989 | |
| 20990 | using Fn = bool (*)(JSContext*); |
| 20991 | OutOfLineCode* ool = |
| 20992 | oolCallVM<Fn, ThrowUninitializedThis>(ins, ArgList(), StoreNothing()); |
| 20993 | masm.branchTestMagic(Assembler::Equal, thisValue, ool->entry()); |
| 20994 | masm.bind(ool->rejoin()); |
| 20995 | } |
| 20996 | |
| 20997 | void CodeGenerator::visitCheckThisReinit(LCheckThisReinit* ins) { |
| 20998 | ValueOperand thisValue = ToValue(ins, LCheckThisReinit::ThisValueIndex); |
| 20999 | |
| 21000 | using Fn = bool (*)(JSContext*); |
| 21001 | OutOfLineCode* ool = |
| 21002 | oolCallVM<Fn, ThrowInitializedThis>(ins, ArgList(), StoreNothing()); |
| 21003 | masm.branchTestMagic(Assembler::NotEqual, thisValue, ool->entry()); |
| 21004 | masm.bind(ool->rejoin()); |
| 21005 | } |
| 21006 | |
| 21007 | void CodeGenerator::visitGenerator(LGenerator* lir) { |
| 21008 | Register callee = ToRegister(lir->callee()); |
| 21009 | Register environmentChain = ToRegister(lir->environmentChain()); |
| 21010 | Register argsObject = ToRegister(lir->argsObject()); |
| 21011 | |
| 21012 | pushArg(argsObject); |
| 21013 | pushArg(environmentChain); |
| 21014 | pushArg(ImmGCPtr(current->mir()->info().script())); |
| 21015 | pushArg(callee); |
| 21016 | |
| 21017 | using Fn = JSObject* (*)(JSContext* cx, HandleFunction, HandleScript, |
| 21018 | HandleObject, HandleObject); |
| 21019 | callVM<Fn, CreateGenerator>(lir); |
| 21020 | } |
| 21021 | |
| 21022 | void CodeGenerator::visitAsyncResolve(LAsyncResolve* lir) { |
| 21023 | Register generator = ToRegister(lir->generator()); |
| 21024 | ValueOperand value = ToValue(lir, LAsyncResolve::ValueIndex); |
| 21025 | |
| 21026 | pushArg(value); |
| 21027 | pushArg(generator); |
| 21028 | |
| 21029 | using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>, |
| 21030 | HandleValue); |
| 21031 | callVM<Fn, js::AsyncFunctionResolve>(lir); |
| 21032 | } |
| 21033 | |
| 21034 | void CodeGenerator::visitAsyncReject(LAsyncReject* lir) { |
| 21035 | Register generator = ToRegister(lir->generator()); |
| 21036 | ValueOperand reason = ToValue(lir, LAsyncReject::ReasonIndex); |
| 21037 | ValueOperand stack = ToValue(lir, LAsyncReject::StackIndex); |
| 21038 | |
| 21039 | pushArg(stack); |
| 21040 | pushArg(reason); |
| 21041 | pushArg(generator); |
| 21042 | |
| 21043 | using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>, |
| 21044 | HandleValue, HandleValue); |
| 21045 | callVM<Fn, js::AsyncFunctionReject>(lir); |
| 21046 | } |
| 21047 | |
| 21048 | void CodeGenerator::visitAsyncAwait(LAsyncAwait* lir) { |
| 21049 | ValueOperand value = ToValue(lir, LAsyncAwait::ValueIndex); |
| 21050 | Register generator = ToRegister(lir->generator()); |
| 21051 | |
| 21052 | pushArg(value); |
| 21053 | pushArg(generator); |
| 21054 | |
| 21055 | using Fn = |
| 21056 | JSObject* (*)(JSContext* cx, Handle<AsyncFunctionGeneratorObject*> genObj, |
| 21057 | HandleValue value); |
| 21058 | callVM<Fn, js::AsyncFunctionAwait>(lir); |
| 21059 | } |
| 21060 | |
| 21061 | void CodeGenerator::visitCanSkipAwait(LCanSkipAwait* lir) { |
| 21062 | ValueOperand value = ToValue(lir, LCanSkipAwait::ValueIndex); |
| 21063 | |
| 21064 | pushArg(value); |
| 21065 | |
| 21066 | using Fn = bool (*)(JSContext*, HandleValue, bool* canSkip); |
| 21067 | callVM<Fn, js::CanSkipAwait>(lir); |
| 21068 | } |
| 21069 | |
| 21070 | void CodeGenerator::visitMaybeExtractAwaitValue(LMaybeExtractAwaitValue* lir) { |
| 21071 | ValueOperand value = ToValue(lir, LMaybeExtractAwaitValue::ValueIndex); |
| 21072 | ValueOperand output = ToOutValue(lir); |
| 21073 | Register canSkip = ToRegister(lir->canSkip()); |
| 21074 | |
| 21075 | Label cantExtract, finished; |
| 21076 | masm.branchIfFalseBool(canSkip, &cantExtract); |
| 21077 | |
| 21078 | pushArg(value); |
| 21079 | |
| 21080 | using Fn = bool (*)(JSContext*, HandleValue, MutableHandleValue); |
| 21081 | callVM<Fn, js::ExtractAwaitValue>(lir); |
| 21082 | masm.jump(&finished); |
| 21083 | masm.bind(&cantExtract); |
| 21084 | |
| 21085 | masm.moveValue(value, output); |
| 21086 | |
| 21087 | masm.bind(&finished); |
| 21088 | } |
| 21089 | |
| 21090 | void CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) { |
| 21091 | ValueOperand checkValue = ToValue(ins, LDebugCheckSelfHosted::ValueIndex); |
| 21092 | pushArg(checkValue); |
| 21093 | using Fn = bool (*)(JSContext*, HandleValue); |
| 21094 | callVM<Fn, js::Debug_CheckSelfHosted>(ins); |
| 21095 | } |
| 21096 | |
| 21097 | void CodeGenerator::visitRandom(LRandom* ins) { |
| 21098 | using mozilla::non_crypto::XorShift128PlusRNG; |
| 21099 | |
| 21100 | FloatRegister output = ToFloatRegister(ins->output()); |
| 21101 | Register rngReg = ToRegister(ins->temp0()); |
| 21102 | |
| 21103 | Register64 temp1 = ToRegister64(ins->temp1()); |
| 21104 | Register64 temp2 = ToRegister64(ins->temp2()); |
| 21105 | |
| 21106 | const XorShift128PlusRNG* rng = gen->realm->addressOfRandomNumberGenerator(); |
| 21107 | masm.movePtr(ImmPtr(rng), rngReg); |
| 21108 | |
| 21109 | masm.randomDouble(rngReg, output, temp1, temp2); |
| 21110 | if (js::SupportDifferentialTesting()) { |
| 21111 | masm.loadConstantDouble(0.0, output); |
| 21112 | } |
| 21113 | } |
| 21114 | |
| 21115 | void CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins) { |
| 21116 | Register input = ToRegister(ins->input()); |
| 21117 | Register output = ToRegister(ins->output()); |
| 21118 | |
| 21119 | switch (ins->mir()->mode()) { |
| 21120 | case MSignExtendInt32::Byte: |
| 21121 | masm.move8SignExtend(input, output); |
| 21122 | break; |
| 21123 | case MSignExtendInt32::Half: |
| 21124 | masm.move16SignExtend(input, output); |
| 21125 | break; |
| 21126 | } |
| 21127 | } |
| 21128 | |
| 21129 | void CodeGenerator::visitSignExtendIntPtr(LSignExtendIntPtr* ins) { |
| 21130 | Register input = ToRegister(ins->input()); |
| 21131 | Register output = ToRegister(ins->output()); |
| 21132 | |
| 21133 | switch (ins->mir()->mode()) { |
| 21134 | case MSignExtendIntPtr::Byte: |
| 21135 | masm.move8SignExtendToPtr(input, output); |
| 21136 | break; |
| 21137 | case MSignExtendIntPtr::Half: |
| 21138 | masm.move16SignExtendToPtr(input, output); |
| 21139 | break; |
| 21140 | case MSignExtendIntPtr::Word: |
| 21141 | masm.move32SignExtendToPtr(input, output); |
| 21142 | break; |
| 21143 | } |
| 21144 | } |
| 21145 | |
| 21146 | void CodeGenerator::visitRotate(LRotate* ins) { |
| 21147 | MRotate* mir = ins->mir(); |
| 21148 | Register input = ToRegister(ins->input()); |
| 21149 | Register dest = ToRegister(ins->output()); |
| 21150 | |
| 21151 | const LAllocation* count = ins->count(); |
| 21152 | if (count->isConstant()) { |
| 21153 | int32_t c = ToInt32(count) & 0x1F; |
| 21154 | if (mir->isLeftRotate()) { |
| 21155 | masm.rotateLeft(Imm32(c), input, dest); |
| 21156 | } else { |
| 21157 | masm.rotateRight(Imm32(c), input, dest); |
| 21158 | } |
| 21159 | } else { |
| 21160 | Register creg = ToRegister(count); |
| 21161 | if (mir->isLeftRotate()) { |
| 21162 | masm.rotateLeft(creg, input, dest); |
| 21163 | } else { |
| 21164 | masm.rotateRight(creg, input, dest); |
| 21165 | } |
| 21166 | } |
| 21167 | } |
| 21168 | |
| 21169 | void CodeGenerator::visitReinterpretCast(LReinterpretCast* lir) { |
| 21170 | MReinterpretCast* ins = lir->mir(); |
| 21171 | |
| 21172 | MIRType to = ins->type(); |
| 21173 | mozilla::DebugOnly<MIRType> from = ins->input()->type(); |
| 21174 | |
| 21175 | switch (to) { |
| 21176 | case MIRType::Int32: |
| 21177 | MOZ_ASSERT(from == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(from == MIRType::Float32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(from == MIRType::Float32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("from == MIRType::Float32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21177); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from == MIRType::Float32" ")"); do { *((volatile int*)__null) = 21177; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21178 | masm.moveFloat32ToGPR(ToFloatRegister(lir->input()), |
| 21179 | ToRegister(lir->output())); |
| 21180 | break; |
| 21181 | case MIRType::Float32: |
| 21182 | MOZ_ASSERT(from == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(from == MIRType::Int32)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(from == MIRType::Int32))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("from == MIRType::Int32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21182); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from == MIRType::Int32" ")"); do { *((volatile int*)__null) = 21182; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21183 | masm.moveGPRToFloat32(ToRegister(lir->input()), |
| 21184 | ToFloatRegister(lir->output())); |
| 21185 | break; |
| 21186 | case MIRType::Double: |
| 21187 | case MIRType::Int64: |
| 21188 | MOZ_CRASH("not handled by this LIR opcode")do { do { } while (false); MOZ_ReportCrash("" "not handled by this LIR opcode" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21188); AnnotateMozCrashReason("MOZ_CRASH(" "not handled by this LIR opcode" ")"); do { *((volatile int*)__null) = 21188; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 21189 | default: |
| 21190 | MOZ_CRASH("unexpected ReinterpretCast")do { do { } while (false); MOZ_ReportCrash("" "unexpected ReinterpretCast" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21190); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected ReinterpretCast" ")"); do { *((volatile int*)__null) = 21190; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 21191 | } |
| 21192 | } |
| 21193 | |
| 21194 | void CodeGenerator::visitReinterpretCastFromI64(LReinterpretCastFromI64* lir) { |
| 21195 | MOZ_ASSERT(lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Double)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Double))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Double" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21195); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double" ")"); do { *((volatile int*)__null) = 21195; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21196 | MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->input()->type() == MIRType::Int64 )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->input()->type() == MIRType::Int64 ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->input()->type() == MIRType::Int64", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21196); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 21196; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21197 | masm.moveGPR64ToDouble(ToRegister64(lir->input()), |
| 21198 | ToFloatRegister(lir->output())); |
| 21199 | } |
| 21200 | |
| 21201 | void CodeGenerator::visitReinterpretCastToI64(LReinterpretCastToI64* lir) { |
| 21202 | MOZ_ASSERT(lir->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Int64)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21202); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 21202; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21203 | MOZ_ASSERT(lir->mir()->input()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->input()->type() == MIRType::Double )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(lir->mir()->input()->type() == MIRType::Double ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->mir()->input()->type() == MIRType::Double", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21203); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Double" ")"); do { *((volatile int*)__null) = 21203; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21204 | masm.moveDoubleToGPR64(ToFloatRegister(lir->input()), ToOutRegister64(lir)); |
| 21205 | } |
| 21206 | |
| 21207 | class OutOfLineNaNToZero : public OutOfLineCodeBase<CodeGenerator> { |
| 21208 | LNaNToZero* lir_; |
| 21209 | |
| 21210 | public: |
| 21211 | explicit OutOfLineNaNToZero(LNaNToZero* lir) : lir_(lir) {} |
| 21212 | |
| 21213 | void accept(CodeGenerator* codegen) override { |
| 21214 | codegen->visitOutOfLineNaNToZero(this); |
| 21215 | } |
| 21216 | LNaNToZero* lir() const { return lir_; } |
| 21217 | }; |
| 21218 | |
| 21219 | void CodeGenerator::visitOutOfLineNaNToZero(OutOfLineNaNToZero* ool) { |
| 21220 | FloatRegister output = ToFloatRegister(ool->lir()->output()); |
| 21221 | masm.loadConstantDouble(0.0, output); |
| 21222 | masm.jump(ool->rejoin()); |
| 21223 | } |
| 21224 | |
| 21225 | void CodeGenerator::visitNaNToZero(LNaNToZero* lir) { |
| 21226 | FloatRegister input = ToFloatRegister(lir->input()); |
| 21227 | |
| 21228 | OutOfLineNaNToZero* ool = new (alloc()) OutOfLineNaNToZero(lir); |
| 21229 | addOutOfLineCode(ool, lir->mir()); |
| 21230 | |
| 21231 | if (lir->mir()->operandIsNeverNegativeZero()) { |
| 21232 | masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry()); |
| 21233 | } else { |
| 21234 | FloatRegister scratch = ToFloatRegister(lir->temp0()); |
| 21235 | masm.loadConstantDouble(0.0, scratch); |
| 21236 | masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, |
| 21237 | ool->entry()); |
| 21238 | } |
| 21239 | masm.bind(ool->rejoin()); |
| 21240 | } |
| 21241 | |
| 21242 | void CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) { |
| 21243 | Register obj = ToRegister(lir->object()); |
| 21244 | Register output = ToRegister(lir->output()); |
| 21245 | Register temp = ToRegister(lir->temp0()); |
| 21246 | |
| 21247 | masm.setIsPackedArray(obj, output, temp); |
| 21248 | } |
| 21249 | |
| 21250 | void CodeGenerator::visitGuardArrayIsPacked(LGuardArrayIsPacked* lir) { |
| 21251 | Register array = ToRegister(lir->array()); |
| 21252 | Register temp0 = ToRegister(lir->temp0()); |
| 21253 | Register temp1 = ToRegister(lir->temp1()); |
| 21254 | |
| 21255 | Label bail; |
| 21256 | masm.branchArrayIsNotPacked(array, temp0, temp1, &bail); |
| 21257 | bailoutFrom(&bail, lir->snapshot()); |
| 21258 | } |
| 21259 | |
| 21260 | void CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) { |
| 21261 | Register target = ToRegister(lir->target()); |
| 21262 | ValueOperand out = ToOutValue(lir); |
| 21263 | Register scratch = out.scratchReg(); |
| 21264 | |
| 21265 | using Fn = bool (*)(JSContext*, HandleObject, MutableHandleValue); |
| 21266 | OutOfLineCode* ool = oolCallVM<Fn, jit::GetPrototypeOf>(lir, ArgList(target), |
| 21267 | StoreValueTo(out)); |
| 21268 | |
| 21269 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21269); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 21269; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21270 | |
| 21271 | masm.loadObjProto(target, scratch); |
| 21272 | |
| 21273 | Label hasProto; |
| 21274 | masm.branchPtr(Assembler::Above, scratch, ImmWord(1), &hasProto); |
| 21275 | |
| 21276 | // Call into the VM for lazy prototypes. |
| 21277 | masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), ool->entry()); |
| 21278 | |
| 21279 | masm.moveValue(NullValue(), out); |
| 21280 | masm.jump(ool->rejoin()); |
| 21281 | |
| 21282 | masm.bind(&hasProto); |
| 21283 | masm.tagValue(JSVAL_TYPE_OBJECT, scratch, out); |
| 21284 | |
| 21285 | masm.bind(ool->rejoin()); |
| 21286 | } |
| 21287 | |
| 21288 | void CodeGenerator::visitObjectWithProto(LObjectWithProto* lir) { |
| 21289 | pushArg(ToValue(lir, LObjectWithProto::PrototypeIndex)); |
| 21290 | |
| 21291 | using Fn = PlainObject* (*)(JSContext*, HandleValue); |
| 21292 | callVM<Fn, js::ObjectWithProtoOperation>(lir); |
| 21293 | } |
| 21294 | |
| 21295 | void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) { |
| 21296 | Register obj = ToRegister(lir->input()); |
| 21297 | Register output = ToRegister(lir->output()); |
| 21298 | |
| 21299 | masm.loadObjProto(obj, output); |
| 21300 | |
| 21301 | #ifdef DEBUG1 |
| 21302 | // We shouldn't encounter a null or lazy proto. |
| 21303 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21303); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 21303; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21304 | |
| 21305 | Label done; |
| 21306 | masm.branchPtr(Assembler::Above, output, ImmWord(1), &done); |
| 21307 | masm.assumeUnreachable("Unexpected null or lazy proto in MObjectStaticProto"); |
| 21308 | masm.bind(&done); |
| 21309 | #endif |
| 21310 | } |
| 21311 | |
| 21312 | void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) { |
| 21313 | pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind()))); |
| 21314 | |
| 21315 | using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind); |
| 21316 | callVM<Fn, js::BuiltinObjectOperation>(lir); |
| 21317 | } |
| 21318 | |
| 21319 | void CodeGenerator::visitSuperFunction(LSuperFunction* lir) { |
| 21320 | Register callee = ToRegister(lir->callee()); |
| 21321 | ValueOperand out = ToOutValue(lir); |
| 21322 | Register temp = ToRegister(lir->temp0()); |
| 21323 | |
| 21324 | #ifdef DEBUG1 |
| 21325 | Label classCheckDone; |
| 21326 | masm.branchTestObjIsFunction(Assembler::Equal, callee, temp, callee, |
| 21327 | &classCheckDone); |
| 21328 | masm.assumeUnreachable("Unexpected non-JSFunction callee in JSOp::SuperFun"); |
| 21329 | masm.bind(&classCheckDone); |
| 21330 | #endif |
| 21331 | |
| 21332 | // Load prototype of callee |
| 21333 | masm.loadObjProto(callee, temp); |
| 21334 | |
| 21335 | #ifdef DEBUG1 |
| 21336 | // We won't encounter a lazy proto, because |callee| is guaranteed to be a |
| 21337 | // JSFunction and only proxy objects can have a lazy proto. |
| 21338 | MOZ_ASSERT(uintptr_t(TaggedProto::LazyProto) == 1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uintptr_t(TaggedProto::LazyProto) == 1)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(uintptr_t(TaggedProto::LazyProto) == 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uintptr_t(TaggedProto::LazyProto) == 1" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21338); AnnotateMozCrashReason("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")"); do { *((volatile int*)__null) = 21338; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21339 | |
| 21340 | Label proxyCheckDone; |
| 21341 | masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone); |
| 21342 | masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperFun"); |
| 21343 | masm.bind(&proxyCheckDone); |
| 21344 | #endif |
| 21345 | |
| 21346 | Label nullProto, done; |
| 21347 | masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto); |
| 21348 | |
| 21349 | // Box prototype and return |
| 21350 | masm.tagValue(JSVAL_TYPE_OBJECT, temp, out); |
| 21351 | masm.jump(&done); |
| 21352 | |
| 21353 | masm.bind(&nullProto); |
| 21354 | masm.moveValue(NullValue(), out); |
| 21355 | |
| 21356 | masm.bind(&done); |
| 21357 | } |
| 21358 | |
| 21359 | void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) { |
| 21360 | Register func = ToRegister(lir->function()); |
| 21361 | ValueOperand homeObject = ToValue(lir, LInitHomeObject::HomeObjectIndex); |
| 21362 | |
| 21363 | masm.assertFunctionIsExtended(func); |
| 21364 | |
| 21365 | Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot()); |
| 21366 | |
| 21367 | emitPreBarrier(addr); |
| 21368 | masm.storeValue(homeObject, addr); |
| 21369 | } |
| 21370 | |
| 21371 | void CodeGenerator::visitIsTypedArrayConstructor( |
| 21372 | LIsTypedArrayConstructor* lir) { |
| 21373 | Register object = ToRegister(lir->object()); |
| 21374 | Register output = ToRegister(lir->output()); |
| 21375 | |
| 21376 | masm.setIsDefinitelyTypedArrayConstructor(object, output); |
| 21377 | } |
| 21378 | |
| 21379 | void CodeGenerator::visitLoadValueTag(LLoadValueTag* lir) { |
| 21380 | ValueOperand value = ToValue(lir, LLoadValueTag::ValueIndex); |
| 21381 | Register output = ToRegister(lir->output()); |
| 21382 | |
| 21383 | Register tag = masm.extractTag(value, output); |
| 21384 | if (tag != output) { |
| 21385 | masm.mov(tag, output); |
| 21386 | } |
| 21387 | } |
| 21388 | |
| 21389 | void CodeGenerator::visitGuardTagNotEqual(LGuardTagNotEqual* lir) { |
| 21390 | Register lhs = ToRegister(lir->lhs()); |
| 21391 | Register rhs = ToRegister(lir->rhs()); |
| 21392 | |
| 21393 | bailoutCmp32(Assembler::Equal, lhs, rhs, lir->snapshot()); |
| 21394 | |
| 21395 | // If both lhs and rhs are numbers, can't use tag comparison to do inequality |
| 21396 | // comparison |
| 21397 | Label done; |
| 21398 | masm.branchTestNumber(Assembler::NotEqual, lhs, &done); |
| 21399 | masm.branchTestNumber(Assembler::NotEqual, rhs, &done); |
| 21400 | bailout(lir->snapshot()); |
| 21401 | |
| 21402 | masm.bind(&done); |
| 21403 | } |
| 21404 | |
| 21405 | void CodeGenerator::visitLoadWrapperTarget(LLoadWrapperTarget* lir) { |
| 21406 | Register object = ToRegister(lir->object()); |
| 21407 | Register output = ToRegister(lir->output()); |
| 21408 | |
| 21409 | masm.loadPtr(Address(object, ProxyObject::offsetOfReservedSlots()), output); |
| 21410 | |
| 21411 | // Bail for revoked proxies. |
| 21412 | Label bail; |
| 21413 | Address targetAddr(output, |
| 21414 | js::detail::ProxyReservedSlots::offsetOfPrivateSlot()); |
| 21415 | if (lir->mir()->fallible()) { |
| 21416 | masm.fallibleUnboxObject(targetAddr, output, &bail); |
| 21417 | bailoutFrom(&bail, lir->snapshot()); |
| 21418 | } else { |
| 21419 | masm.unboxObject(targetAddr, output); |
| 21420 | } |
| 21421 | } |
| 21422 | |
| 21423 | void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) { |
| 21424 | Register object = ToRegister(lir->object()); |
| 21425 | Register temp0 = ToRegister(lir->temp0()); |
| 21426 | Register temp1 = ToRegister(lir->temp1()); |
| 21427 | Register temp2 = ToRegister(lir->temp2()); |
| 21428 | |
| 21429 | masm.movePropertyKey(lir->mir()->propId(), temp1); |
| 21430 | masm.movePtr(ImmGCPtr(lir->mir()->getterSetter()), temp2); |
| 21431 | |
| 21432 | using Fn = bool (*)(JSContext* cx, JSObject* obj, jsid id, |
| 21433 | GetterSetter* getterSetter); |
| 21434 | masm.setupAlignedABICall(); |
| 21435 | masm.loadJSContext(temp0); |
| 21436 | masm.passABIArg(temp0); |
| 21437 | masm.passABIArg(object); |
| 21438 | masm.passABIArg(temp1); |
| 21439 | masm.passABIArg(temp2); |
| 21440 | masm.callWithABI<Fn, ObjectHasGetterSetterPure>(); |
| 21441 | |
| 21442 | bailoutIfFalseBool(ReturnReg, lir->snapshot()); |
| 21443 | } |
| 21444 | |
| 21445 | void CodeGenerator::visitGuardIsExtensible(LGuardIsExtensible* lir) { |
| 21446 | Register object = ToRegister(lir->object()); |
| 21447 | Register temp = ToRegister(lir->temp0()); |
| 21448 | |
| 21449 | Label bail; |
| 21450 | masm.branchIfObjectNotExtensible(object, temp, &bail); |
| 21451 | bailoutFrom(&bail, lir->snapshot()); |
| 21452 | } |
| 21453 | |
| 21454 | void CodeGenerator::visitGuardInt32IsNonNegative( |
| 21455 | LGuardInt32IsNonNegative* lir) { |
| 21456 | Register index = ToRegister(lir->index()); |
| 21457 | |
| 21458 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 21459 | } |
| 21460 | |
| 21461 | void CodeGenerator::visitGuardInt32Range(LGuardInt32Range* lir) { |
| 21462 | Register input = ToRegister(lir->input()); |
| 21463 | |
| 21464 | bailoutCmp32(Assembler::LessThan, input, Imm32(lir->mir()->minimum()), |
| 21465 | lir->snapshot()); |
| 21466 | bailoutCmp32(Assembler::GreaterThan, input, Imm32(lir->mir()->maximum()), |
| 21467 | lir->snapshot()); |
| 21468 | } |
| 21469 | |
| 21470 | void CodeGenerator::visitGuardIndexIsNotDenseElement( |
| 21471 | LGuardIndexIsNotDenseElement* lir) { |
| 21472 | Register object = ToRegister(lir->object()); |
| 21473 | Register index = ToRegister(lir->index()); |
| 21474 | Register temp = ToRegister(lir->temp0()); |
| 21475 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
| 21476 | |
| 21477 | // Load obj->elements. |
| 21478 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
| 21479 | |
| 21480 | // Ensure index >= initLength or the element is a hole. |
| 21481 | Label notDense; |
| 21482 | Address capacity(temp, ObjectElements::offsetOfInitializedLength()); |
| 21483 | masm.spectreBoundsCheck32(index, capacity, spectreTemp, ¬Dense); |
| 21484 | |
| 21485 | BaseValueIndex element(temp, index); |
| 21486 | masm.branchTestMagic(Assembler::Equal, element, ¬Dense); |
| 21487 | |
| 21488 | bailout(lir->snapshot()); |
| 21489 | |
| 21490 | masm.bind(¬Dense); |
| 21491 | } |
| 21492 | |
| 21493 | void CodeGenerator::visitGuardIndexIsValidUpdateOrAdd( |
| 21494 | LGuardIndexIsValidUpdateOrAdd* lir) { |
| 21495 | Register object = ToRegister(lir->object()); |
| 21496 | Register index = ToRegister(lir->index()); |
| 21497 | Register temp = ToRegister(lir->temp0()); |
| 21498 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
| 21499 | |
| 21500 | // Load obj->elements. |
| 21501 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
| 21502 | |
| 21503 | Label success; |
| 21504 | |
| 21505 | // If length is writable, branch to &success. All indices are writable. |
| 21506 | Address flags(temp, ObjectElements::offsetOfFlags()); |
| 21507 | masm.branchTest32(Assembler::Zero, flags, |
| 21508 | Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH), |
| 21509 | &success); |
| 21510 | |
| 21511 | // Otherwise, ensure index is in bounds. |
| 21512 | Label bail; |
| 21513 | Address length(temp, ObjectElements::offsetOfLength()); |
| 21514 | masm.spectreBoundsCheck32(index, length, spectreTemp, &bail); |
| 21515 | masm.bind(&success); |
| 21516 | |
| 21517 | bailoutFrom(&bail, lir->snapshot()); |
| 21518 | } |
| 21519 | |
| 21520 | void CodeGenerator::visitCallAddOrUpdateSparseElement( |
| 21521 | LCallAddOrUpdateSparseElement* lir) { |
| 21522 | Register object = ToRegister(lir->object()); |
| 21523 | Register index = ToRegister(lir->index()); |
| 21524 | ValueOperand value = ToValue(lir, LCallAddOrUpdateSparseElement::ValueIndex); |
| 21525 | |
| 21526 | pushArg(Imm32(lir->mir()->strict())); |
| 21527 | pushArg(value); |
| 21528 | pushArg(index); |
| 21529 | pushArg(object); |
| 21530 | |
| 21531 | using Fn = |
| 21532 | bool (*)(JSContext*, Handle<NativeObject*>, int32_t, HandleValue, bool); |
| 21533 | callVM<Fn, js::AddOrUpdateSparseElementHelper>(lir); |
| 21534 | } |
| 21535 | |
| 21536 | void CodeGenerator::visitCallGetSparseElement(LCallGetSparseElement* lir) { |
| 21537 | Register object = ToRegister(lir->object()); |
| 21538 | Register index = ToRegister(lir->index()); |
| 21539 | |
| 21540 | pushArg(index); |
| 21541 | pushArg(object); |
| 21542 | |
| 21543 | using Fn = |
| 21544 | bool (*)(JSContext*, Handle<NativeObject*>, int32_t, MutableHandleValue); |
| 21545 | callVM<Fn, js::GetSparseElementHelper>(lir); |
| 21546 | } |
| 21547 | |
| 21548 | void CodeGenerator::visitCallNativeGetElement(LCallNativeGetElement* lir) { |
| 21549 | Register object = ToRegister(lir->object()); |
| 21550 | Register index = ToRegister(lir->index()); |
| 21551 | |
| 21552 | pushArg(index); |
| 21553 | pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(object))); |
| 21554 | pushArg(object); |
| 21555 | |
| 21556 | using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t, |
| 21557 | MutableHandleValue); |
| 21558 | callVM<Fn, js::NativeGetElement>(lir); |
| 21559 | } |
| 21560 | |
| 21561 | void CodeGenerator::visitCallNativeGetElementSuper( |
| 21562 | LCallNativeGetElementSuper* lir) { |
| 21563 | Register object = ToRegister(lir->object()); |
| 21564 | Register index = ToRegister(lir->index()); |
| 21565 | ValueOperand receiver = |
| 21566 | ToValue(lir, LCallNativeGetElementSuper::ReceiverIndex); |
| 21567 | |
| 21568 | pushArg(index); |
| 21569 | pushArg(receiver); |
| 21570 | pushArg(object); |
| 21571 | |
| 21572 | using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t, |
| 21573 | MutableHandleValue); |
| 21574 | callVM<Fn, js::NativeGetElement>(lir); |
| 21575 | } |
| 21576 | |
| 21577 | void CodeGenerator::visitCallObjectHasSparseElement( |
| 21578 | LCallObjectHasSparseElement* lir) { |
| 21579 | Register object = ToRegister(lir->object()); |
| 21580 | Register index = ToRegister(lir->index()); |
| 21581 | Register temp0 = ToRegister(lir->temp0()); |
| 21582 | Register temp1 = ToRegister(lir->temp1()); |
| 21583 | Register output = ToRegister(lir->output()); |
| 21584 | |
| 21585 | masm.reserveStack(sizeof(Value)); |
| 21586 | masm.moveStackPtrTo(temp1); |
| 21587 | |
| 21588 | using Fn = bool (*)(JSContext*, NativeObject*, int32_t, Value*); |
| 21589 | masm.setupAlignedABICall(); |
| 21590 | masm.loadJSContext(temp0); |
| 21591 | masm.passABIArg(temp0); |
| 21592 | masm.passABIArg(object); |
| 21593 | masm.passABIArg(index); |
| 21594 | masm.passABIArg(temp1); |
| 21595 | masm.callWithABI<Fn, HasNativeElementPure>(); |
| 21596 | masm.storeCallPointerResult(temp0); |
| 21597 | |
| 21598 | Label bail, ok; |
| 21599 | uint32_t framePushed = masm.framePushed(); |
| 21600 | masm.branchIfTrueBool(temp0, &ok); |
| 21601 | masm.adjustStack(sizeof(Value)); |
| 21602 | masm.jump(&bail); |
| 21603 | |
| 21604 | masm.bind(&ok); |
| 21605 | masm.setFramePushed(framePushed); |
| 21606 | masm.unboxBoolean(Address(masm.getStackPointer(), 0), output); |
| 21607 | masm.adjustStack(sizeof(Value)); |
| 21608 | |
| 21609 | bailoutFrom(&bail, lir->snapshot()); |
| 21610 | } |
| 21611 | |
| 21612 | void CodeGenerator::visitBigIntAsIntN(LBigIntAsIntN* ins) { |
| 21613 | Register bits = ToRegister(ins->bits()); |
| 21614 | Register input = ToRegister(ins->input()); |
| 21615 | |
| 21616 | pushArg(bits); |
| 21617 | pushArg(input); |
| 21618 | |
| 21619 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t); |
| 21620 | callVM<Fn, jit::BigIntAsIntN>(ins); |
| 21621 | } |
| 21622 | |
| 21623 | void CodeGenerator::visitBigIntAsUintN(LBigIntAsUintN* ins) { |
| 21624 | Register bits = ToRegister(ins->bits()); |
| 21625 | Register input = ToRegister(ins->input()); |
| 21626 | |
| 21627 | pushArg(bits); |
| 21628 | pushArg(input); |
| 21629 | |
| 21630 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t); |
| 21631 | callVM<Fn, jit::BigIntAsUintN>(ins); |
| 21632 | } |
| 21633 | |
| 21634 | void CodeGenerator::visitGuardNonGCThing(LGuardNonGCThing* ins) { |
| 21635 | ValueOperand input = ToValue(ins, LGuardNonGCThing::InputIndex); |
| 21636 | |
| 21637 | Label bail; |
| 21638 | masm.branchTestGCThing(Assembler::Equal, input, &bail); |
| 21639 | bailoutFrom(&bail, ins->snapshot()); |
| 21640 | } |
| 21641 | |
| 21642 | void CodeGenerator::visitToHashableNonGCThing(LToHashableNonGCThing* ins) { |
| 21643 | ValueOperand input = ToValue(ins, LToHashableNonGCThing::InputIndex); |
| 21644 | FloatRegister tempFloat = ToFloatRegister(ins->temp0()); |
| 21645 | ValueOperand output = ToOutValue(ins); |
| 21646 | |
| 21647 | masm.toHashableNonGCThing(input, output, tempFloat); |
| 21648 | } |
| 21649 | |
| 21650 | void CodeGenerator::visitToHashableString(LToHashableString* ins) { |
| 21651 | Register input = ToRegister(ins->input()); |
| 21652 | Register output = ToRegister(ins->output()); |
| 21653 | |
| 21654 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
| 21655 | auto* ool = oolCallVM<Fn, js::AtomizeString>(ins, ArgList(input), |
| 21656 | StoreRegisterTo(output)); |
| 21657 | |
| 21658 | Label isAtom; |
| 21659 | masm.branchTest32(Assembler::NonZero, |
| 21660 | Address(input, JSString::offsetOfFlags()), |
| 21661 | Imm32(JSString::ATOM_BIT), &isAtom); |
| 21662 | |
| 21663 | masm.tryFastAtomize(input, output, output, ool->entry()); |
| 21664 | masm.jump(ool->rejoin()); |
| 21665 | masm.bind(&isAtom); |
| 21666 | masm.movePtr(input, output); |
| 21667 | masm.bind(ool->rejoin()); |
| 21668 | } |
| 21669 | |
| 21670 | void CodeGenerator::visitToHashableValue(LToHashableValue* ins) { |
| 21671 | ValueOperand input = ToValue(ins, LToHashableValue::InputIndex); |
| 21672 | FloatRegister tempFloat = ToFloatRegister(ins->temp0()); |
| 21673 | ValueOperand output = ToOutValue(ins); |
| 21674 | |
| 21675 | Register str = output.scratchReg(); |
| 21676 | |
| 21677 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
| 21678 | auto* ool = |
| 21679 | oolCallVM<Fn, js::AtomizeString>(ins, ArgList(str), StoreRegisterTo(str)); |
| 21680 | |
| 21681 | masm.toHashableValue(input, output, tempFloat, ool->entry(), ool->rejoin()); |
| 21682 | } |
| 21683 | |
| 21684 | void CodeGenerator::visitHashNonGCThing(LHashNonGCThing* ins) { |
| 21685 | ValueOperand input = ToValue(ins, LHashNonGCThing::InputIndex); |
| 21686 | Register temp = ToRegister(ins->temp0()); |
| 21687 | Register output = ToRegister(ins->output()); |
| 21688 | |
| 21689 | masm.prepareHashNonGCThing(input, output, temp); |
| 21690 | } |
| 21691 | |
| 21692 | void CodeGenerator::visitHashString(LHashString* ins) { |
| 21693 | Register input = ToRegister(ins->input()); |
| 21694 | Register temp = ToRegister(ins->temp0()); |
| 21695 | Register output = ToRegister(ins->output()); |
| 21696 | |
| 21697 | masm.prepareHashString(input, output, temp); |
| 21698 | } |
| 21699 | |
| 21700 | void CodeGenerator::visitHashSymbol(LHashSymbol* ins) { |
| 21701 | Register input = ToRegister(ins->input()); |
| 21702 | Register output = ToRegister(ins->output()); |
| 21703 | |
| 21704 | masm.prepareHashSymbol(input, output); |
| 21705 | } |
| 21706 | |
| 21707 | void CodeGenerator::visitHashBigInt(LHashBigInt* ins) { |
| 21708 | Register input = ToRegister(ins->input()); |
| 21709 | Register temp0 = ToRegister(ins->temp0()); |
| 21710 | Register temp1 = ToRegister(ins->temp1()); |
| 21711 | Register temp2 = ToRegister(ins->temp2()); |
| 21712 | Register output = ToRegister(ins->output()); |
| 21713 | |
| 21714 | masm.prepareHashBigInt(input, output, temp0, temp1, temp2); |
| 21715 | } |
| 21716 | |
| 21717 | void CodeGenerator::visitHashObject(LHashObject* ins) { |
| 21718 | Register setObj = ToRegister(ins->setObject()); |
| 21719 | ValueOperand input = ToValue(ins, LHashObject::InputIndex); |
| 21720 | Register temp0 = ToRegister(ins->temp0()); |
| 21721 | Register temp1 = ToRegister(ins->temp1()); |
| 21722 | Register temp2 = ToRegister(ins->temp2()); |
| 21723 | Register temp3 = ToRegister(ins->temp3()); |
| 21724 | Register output = ToRegister(ins->output()); |
| 21725 | |
| 21726 | masm.prepareHashObject(setObj, input, output, temp0, temp1, temp2, temp3); |
| 21727 | } |
| 21728 | |
| 21729 | void CodeGenerator::visitHashValue(LHashValue* ins) { |
| 21730 | Register setObj = ToRegister(ins->setObject()); |
| 21731 | ValueOperand input = ToValue(ins, LHashValue::InputIndex); |
| 21732 | Register temp0 = ToRegister(ins->temp0()); |
| 21733 | Register temp1 = ToRegister(ins->temp1()); |
| 21734 | Register temp2 = ToRegister(ins->temp2()); |
| 21735 | Register temp3 = ToRegister(ins->temp3()); |
| 21736 | Register output = ToRegister(ins->output()); |
| 21737 | |
| 21738 | masm.prepareHashValue(setObj, input, output, temp0, temp1, temp2, temp3); |
| 21739 | } |
| 21740 | |
| 21741 | void CodeGenerator::visitSetObjectHasNonBigInt(LSetObjectHasNonBigInt* ins) { |
| 21742 | Register setObj = ToRegister(ins->setObject()); |
| 21743 | ValueOperand input = ToValue(ins, LSetObjectHasNonBigInt::InputIndex); |
| 21744 | Register hash = ToRegister(ins->hash()); |
| 21745 | Register temp0 = ToRegister(ins->temp0()); |
| 21746 | Register temp1 = ToRegister(ins->temp1()); |
| 21747 | Register output = ToRegister(ins->output()); |
| 21748 | |
| 21749 | masm.setObjectHasNonBigInt(setObj, input, hash, output, temp0, temp1); |
| 21750 | } |
| 21751 | |
| 21752 | void CodeGenerator::visitSetObjectHasBigInt(LSetObjectHasBigInt* ins) { |
| 21753 | Register setObj = ToRegister(ins->setObject()); |
| 21754 | ValueOperand input = ToValue(ins, LSetObjectHasBigInt::InputIndex); |
| 21755 | Register hash = ToRegister(ins->hash()); |
| 21756 | Register temp0 = ToRegister(ins->temp0()); |
| 21757 | Register temp1 = ToRegister(ins->temp1()); |
| 21758 | Register temp2 = ToRegister(ins->temp2()); |
| 21759 | Register temp3 = ToRegister(ins->temp3()); |
| 21760 | Register output = ToRegister(ins->output()); |
| 21761 | |
| 21762 | masm.setObjectHasBigInt(setObj, input, hash, output, temp0, temp1, temp2, |
| 21763 | temp3); |
| 21764 | } |
| 21765 | |
| 21766 | void CodeGenerator::visitSetObjectHasValue(LSetObjectHasValue* ins) { |
| 21767 | Register setObj = ToRegister(ins->setObject()); |
| 21768 | ValueOperand input = ToValue(ins, LSetObjectHasValue::InputIndex); |
| 21769 | Register hash = ToRegister(ins->hash()); |
| 21770 | Register temp0 = ToRegister(ins->temp0()); |
| 21771 | Register temp1 = ToRegister(ins->temp1()); |
| 21772 | Register temp2 = ToRegister(ins->temp2()); |
| 21773 | Register temp3 = ToRegister(ins->temp3()); |
| 21774 | Register output = ToRegister(ins->output()); |
| 21775 | |
| 21776 | masm.setObjectHasValue(setObj, input, hash, output, temp0, temp1, temp2, |
| 21777 | temp3); |
| 21778 | } |
| 21779 | |
| 21780 | void CodeGenerator::visitSetObjectHasValueVMCall( |
| 21781 | LSetObjectHasValueVMCall* ins) { |
| 21782 | pushArg(ToValue(ins, LSetObjectHasValueVMCall::InputIndex)); |
| 21783 | pushArg(ToRegister(ins->setObject())); |
| 21784 | |
| 21785 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*); |
| 21786 | callVM<Fn, jit::SetObjectHas>(ins); |
| 21787 | } |
| 21788 | |
| 21789 | void CodeGenerator::visitSetObjectDelete(LSetObjectDelete* ins) { |
| 21790 | pushArg(ToValue(ins, LSetObjectDelete::KeyIndex)); |
| 21791 | pushArg(ToRegister(ins->setObject())); |
| 21792 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*); |
| 21793 | callVM<Fn, jit::SetObjectDelete>(ins); |
| 21794 | } |
| 21795 | |
| 21796 | void CodeGenerator::visitSetObjectAdd(LSetObjectAdd* ins) { |
| 21797 | pushArg(ToValue(ins, LSetObjectAdd::KeyIndex)); |
| 21798 | pushArg(ToRegister(ins->setObject())); |
| 21799 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue); |
| 21800 | callVM<Fn, jit::SetObjectAdd>(ins); |
| 21801 | } |
| 21802 | |
| 21803 | void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) { |
| 21804 | Register setObj = ToRegister(ins->setObject()); |
| 21805 | Register output = ToRegister(ins->output()); |
| 21806 | |
| 21807 | masm.loadSetObjectSize(setObj, output); |
| 21808 | } |
| 21809 | |
| 21810 | void CodeGenerator::visitMapObjectHasNonBigInt(LMapObjectHasNonBigInt* ins) { |
| 21811 | Register mapObj = ToRegister(ins->mapObject()); |
| 21812 | ValueOperand input = ToValue(ins, LMapObjectHasNonBigInt::InputIndex); |
| 21813 | Register hash = ToRegister(ins->hash()); |
| 21814 | Register temp0 = ToRegister(ins->temp0()); |
| 21815 | Register temp1 = ToRegister(ins->temp1()); |
| 21816 | Register output = ToRegister(ins->output()); |
| 21817 | |
| 21818 | masm.mapObjectHasNonBigInt(mapObj, input, hash, output, temp0, temp1); |
| 21819 | } |
| 21820 | |
| 21821 | void CodeGenerator::visitMapObjectHasBigInt(LMapObjectHasBigInt* ins) { |
| 21822 | Register mapObj = ToRegister(ins->mapObject()); |
| 21823 | ValueOperand input = ToValue(ins, LMapObjectHasBigInt::InputIndex); |
| 21824 | Register hash = ToRegister(ins->hash()); |
| 21825 | Register temp0 = ToRegister(ins->temp0()); |
| 21826 | Register temp1 = ToRegister(ins->temp1()); |
| 21827 | Register temp2 = ToRegister(ins->temp2()); |
| 21828 | Register temp3 = ToRegister(ins->temp3()); |
| 21829 | Register output = ToRegister(ins->output()); |
| 21830 | |
| 21831 | masm.mapObjectHasBigInt(mapObj, input, hash, output, temp0, temp1, temp2, |
| 21832 | temp3); |
| 21833 | } |
| 21834 | |
| 21835 | void CodeGenerator::visitMapObjectHasValue(LMapObjectHasValue* ins) { |
| 21836 | Register mapObj = ToRegister(ins->mapObject()); |
| 21837 | ValueOperand input = ToValue(ins, LMapObjectHasValue::InputIndex); |
| 21838 | Register hash = ToRegister(ins->hash()); |
| 21839 | Register temp0 = ToRegister(ins->temp0()); |
| 21840 | Register temp1 = ToRegister(ins->temp1()); |
| 21841 | Register temp2 = ToRegister(ins->temp2()); |
| 21842 | Register temp3 = ToRegister(ins->temp3()); |
| 21843 | Register output = ToRegister(ins->output()); |
| 21844 | |
| 21845 | masm.mapObjectHasValue(mapObj, input, hash, output, temp0, temp1, temp2, |
| 21846 | temp3); |
| 21847 | } |
| 21848 | |
| 21849 | void CodeGenerator::visitMapObjectHasValueVMCall( |
| 21850 | LMapObjectHasValueVMCall* ins) { |
| 21851 | pushArg(ToValue(ins, LMapObjectHasValueVMCall::InputIndex)); |
| 21852 | pushArg(ToRegister(ins->mapObject())); |
| 21853 | |
| 21854 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*); |
| 21855 | callVM<Fn, jit::MapObjectHas>(ins); |
| 21856 | } |
| 21857 | |
| 21858 | void CodeGenerator::visitMapObjectGetNonBigInt(LMapObjectGetNonBigInt* ins) { |
| 21859 | Register mapObj = ToRegister(ins->mapObject()); |
| 21860 | ValueOperand input = ToValue(ins, LMapObjectGetNonBigInt::InputIndex); |
| 21861 | Register hash = ToRegister(ins->hash()); |
| 21862 | Register temp0 = ToRegister(ins->temp0()); |
| 21863 | Register temp1 = ToRegister(ins->temp1()); |
| 21864 | ValueOperand output = ToOutValue(ins); |
| 21865 | |
| 21866 | masm.mapObjectGetNonBigInt(mapObj, input, hash, output, temp0, temp1, |
| 21867 | output.scratchReg()); |
| 21868 | } |
| 21869 | |
| 21870 | void CodeGenerator::visitMapObjectGetBigInt(LMapObjectGetBigInt* ins) { |
| 21871 | Register mapObj = ToRegister(ins->mapObject()); |
| 21872 | ValueOperand input = ToValue(ins, LMapObjectGetBigInt::InputIndex); |
| 21873 | Register hash = ToRegister(ins->hash()); |
| 21874 | Register temp0 = ToRegister(ins->temp0()); |
| 21875 | Register temp1 = ToRegister(ins->temp1()); |
| 21876 | Register temp2 = ToRegister(ins->temp2()); |
| 21877 | Register temp3 = ToRegister(ins->temp3()); |
| 21878 | ValueOperand output = ToOutValue(ins); |
| 21879 | |
| 21880 | masm.mapObjectGetBigInt(mapObj, input, hash, output, temp0, temp1, temp2, |
| 21881 | temp3, output.scratchReg()); |
| 21882 | } |
| 21883 | |
| 21884 | void CodeGenerator::visitMapObjectGetValue(LMapObjectGetValue* ins) { |
| 21885 | Register mapObj = ToRegister(ins->map()); |
| 21886 | ValueOperand input = ToValue(ins, LMapObjectGetValue::ValueIndex); |
| 21887 | Register hash = ToRegister(ins->hash()); |
| 21888 | Register temp0 = ToRegister(ins->temp0()); |
| 21889 | Register temp1 = ToRegister(ins->temp1()); |
| 21890 | Register temp2 = ToRegister(ins->temp2()); |
| 21891 | Register temp3 = ToRegister(ins->temp3()); |
| 21892 | ValueOperand output = ToOutValue(ins); |
| 21893 | |
| 21894 | masm.mapObjectGetValue(mapObj, input, hash, output, temp0, temp1, temp2, |
| 21895 | temp3, output.scratchReg()); |
| 21896 | } |
| 21897 | |
| 21898 | void CodeGenerator::visitMapObjectGetValueVMCall( |
| 21899 | LMapObjectGetValueVMCall* ins) { |
| 21900 | pushArg(ToValue(ins, LMapObjectGetValueVMCall::InputIndex)); |
| 21901 | pushArg(ToRegister(ins->mapObject())); |
| 21902 | |
| 21903 | using Fn = |
| 21904 | bool (*)(JSContext*, Handle<MapObject*>, HandleValue, MutableHandleValue); |
| 21905 | callVM<Fn, jit::MapObjectGet>(ins); |
| 21906 | } |
| 21907 | |
| 21908 | void CodeGenerator::visitMapObjectDelete(LMapObjectDelete* ins) { |
| 21909 | pushArg(ToValue(ins, LMapObjectDelete::KeyIndex)); |
| 21910 | pushArg(ToRegister(ins->mapObject())); |
| 21911 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*); |
| 21912 | callVM<Fn, jit::MapObjectDelete>(ins); |
| 21913 | } |
| 21914 | |
| 21915 | void CodeGenerator::visitMapObjectSet(LMapObjectSet* ins) { |
| 21916 | pushArg(ToValue(ins, LMapObjectSet::ValueIndex)); |
| 21917 | pushArg(ToValue(ins, LMapObjectSet::KeyIndex)); |
| 21918 | pushArg(ToRegister(ins->mapObject())); |
| 21919 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, HandleValue); |
| 21920 | callVM<Fn, jit::MapObjectSet>(ins); |
| 21921 | } |
| 21922 | |
| 21923 | void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) { |
| 21924 | Register mapObj = ToRegister(ins->mapObject()); |
| 21925 | Register output = ToRegister(ins->output()); |
| 21926 | |
| 21927 | masm.loadMapObjectSize(mapObj, output); |
| 21928 | } |
| 21929 | |
| 21930 | void CodeGenerator::visitDateFillLocalTimeSlots(LDateFillLocalTimeSlots* ins) { |
| 21931 | Register date = ToRegister(ins->date()); |
| 21932 | Register temp = ToRegister(ins->temp0()); |
| 21933 | |
| 21934 | masm.dateFillLocalTimeSlots(date, temp, liveVolatileRegs(ins)); |
| 21935 | } |
| 21936 | |
| 21937 | void CodeGenerator::visitDateHoursFromSecondsIntoYear( |
| 21938 | LDateHoursFromSecondsIntoYear* ins) { |
| 21939 | auto secondsIntoYear = |
| 21940 | ToValue(ins, LDateHoursFromSecondsIntoYear::SecondsIntoYearIndex); |
| 21941 | auto output = ToOutValue(ins); |
| 21942 | Register temp0 = ToRegister(ins->temp0()); |
| 21943 | Register temp1 = ToRegister(ins->temp1()); |
| 21944 | |
| 21945 | masm.dateHoursFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
| 21946 | } |
| 21947 | |
| 21948 | void CodeGenerator::visitDateMinutesFromSecondsIntoYear( |
| 21949 | LDateMinutesFromSecondsIntoYear* ins) { |
| 21950 | auto secondsIntoYear = |
| 21951 | ToValue(ins, LDateMinutesFromSecondsIntoYear::SecondsIntoYearIndex); |
| 21952 | auto output = ToOutValue(ins); |
| 21953 | Register temp0 = ToRegister(ins->temp0()); |
| 21954 | Register temp1 = ToRegister(ins->temp1()); |
| 21955 | |
| 21956 | masm.dateMinutesFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
| 21957 | } |
| 21958 | |
| 21959 | void CodeGenerator::visitDateSecondsFromSecondsIntoYear( |
| 21960 | LDateSecondsFromSecondsIntoYear* ins) { |
| 21961 | auto secondsIntoYear = |
| 21962 | ToValue(ins, LDateSecondsFromSecondsIntoYear::SecondsIntoYearIndex); |
| 21963 | auto output = ToOutValue(ins); |
| 21964 | Register temp0 = ToRegister(ins->temp0()); |
| 21965 | Register temp1 = ToRegister(ins->temp1()); |
| 21966 | |
| 21967 | masm.dateSecondsFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
| 21968 | } |
| 21969 | |
| 21970 | template <size_t NumDefs> |
| 21971 | void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) { |
| 21972 | wasm::JitCallStackArgVector stackArgs; |
| 21973 | masm.propagateOOM(stackArgs.reserve(lir->numOperands())); |
| 21974 | if (masm.oom()) { |
| 21975 | return; |
| 21976 | } |
| 21977 | |
| 21978 | MIonToWasmCall* mir = lir->mir(); |
| 21979 | const wasm::FuncExport& funcExport = mir->funcExport(); |
| 21980 | const wasm::FuncType& sig = |
| 21981 | mir->instance()->code().codeMeta().getFuncType(funcExport.funcIndex()); |
| 21982 | |
| 21983 | WasmABIArgGenerator abi; |
| 21984 | for (size_t i = 0; i < lir->numOperands(); i++) { |
| 21985 | MIRType argMir; |
| 21986 | switch (sig.args()[i].kind()) { |
| 21987 | case wasm::ValType::I32: |
| 21988 | case wasm::ValType::I64: |
| 21989 | case wasm::ValType::F32: |
| 21990 | case wasm::ValType::F64: |
| 21991 | argMir = sig.args()[i].toMIRType(); |
| 21992 | break; |
| 21993 | case wasm::ValType::V128: |
| 21994 | MOZ_CRASH("unexpected argument type when calling from ion to wasm")do { do { } while (false); MOZ_ReportCrash("" "unexpected argument type when calling from ion to wasm" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21994); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected argument type when calling from ion to wasm" ")"); do { *((volatile int*)__null) = 21994; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 21995 | case wasm::ValType::Ref: |
| 21996 | // temporarilyUnsupportedReftypeForEntry() restricts args to externref |
| 21997 | MOZ_RELEASE_ASSERT(sig.args()[i].refType().isExtern())do { static_assert( mozilla::detail::AssertionConditionType< decltype(sig.args()[i].refType().isExtern())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(sig.args()[i].refType().isExtern ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("sig.args()[i].refType().isExtern()", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 21997); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "sig.args()[i].refType().isExtern()" ")"); do { *((volatile int*)__null) = 21997; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21998 | // Argument is boxed on the JS side to an anyref, so passed as a |
| 21999 | // pointer here. |
| 22000 | argMir = sig.args()[i].toMIRType(); |
| 22001 | break; |
| 22002 | } |
| 22003 | |
| 22004 | ABIArg arg = abi.next(argMir); |
| 22005 | switch (arg.kind()) { |
| 22006 | case ABIArg::GPR: |
| 22007 | case ABIArg::FPU: { |
| 22008 | MOZ_ASSERT(ToAnyRegister(lir->getOperand(i)) == arg.reg())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToAnyRegister(lir->getOperand(i)) == arg.reg())> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToAnyRegister(lir->getOperand(i)) == arg.reg()))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToAnyRegister(lir->getOperand(i)) == arg.reg()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22008); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToAnyRegister(lir->getOperand(i)) == arg.reg()" ")"); do { *((volatile int*)__null) = 22008; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22009 | stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg()); |
| 22010 | break; |
| 22011 | } |
| 22012 | case ABIArg::Stack: { |
| 22013 | const LAllocation* larg = lir->getOperand(i); |
| 22014 | if (larg->isConstant()) { |
| 22015 | stackArgs.infallibleEmplaceBack(ToInt32(larg)); |
| 22016 | } else if (larg->isGeneralReg()) { |
| 22017 | stackArgs.infallibleEmplaceBack(ToRegister(larg)); |
| 22018 | } else if (larg->isFloatReg()) { |
| 22019 | stackArgs.infallibleEmplaceBack(ToFloatRegister(larg)); |
| 22020 | } else { |
| 22021 | // Always use the stack pointer here because GenerateDirectCallFromJit |
| 22022 | // depends on this. |
| 22023 | Address addr = ToAddress<BaseRegForAddress::SP>(larg); |
| 22024 | stackArgs.infallibleEmplaceBack(addr); |
| 22025 | } |
| 22026 | break; |
| 22027 | } |
| 22028 | #ifdef JS_CODEGEN_REGISTER_PAIR |
| 22029 | case ABIArg::GPR_PAIR: { |
| 22030 | MOZ_CRASH(do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22031); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls" ")"); do { *((volatile int*)__null) = 22031; __attribute__(( nomerge)) ::abort(); } while (false); } while (false) |
| 22031 | "no way to pass i64, and wasm uses hardfp for function calls")do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22031); AnnotateMozCrashReason("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls" ")"); do { *((volatile int*)__null) = 22031; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 22032 | } |
| 22033 | #endif |
| 22034 | case ABIArg::Uninitialized: { |
| 22035 | MOZ_CRASH("Uninitialized ABIArg kind")do { do { } while (false); MOZ_ReportCrash("" "Uninitialized ABIArg kind" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22035); AnnotateMozCrashReason("MOZ_CRASH(" "Uninitialized ABIArg kind" ")"); do { *((volatile int*)__null) = 22035; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 22036 | } |
| 22037 | } |
| 22038 | } |
| 22039 | |
| 22040 | const wasm::ValTypeVector& results = sig.results(); |
| 22041 | if (results.length() == 0) { |
| 22042 | MOZ_ASSERT(lir->mir()->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Value)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Value))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Value" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22042); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value" ")"); do { *((volatile int*)__null) = 22042; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22043 | } else { |
| 22044 | MOZ_ASSERT(results.length() == 1, "multi-value return unimplemented")do { static_assert( mozilla::detail::AssertionConditionType< decltype(results.length() == 1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(results.length() == 1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("results.length() == 1" " (" "multi-value return unimplemented" ")", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 1" ") (" "multi-value return unimplemented" ")"); do { *((volatile int*)__null) = 22044; __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
| 22045 | switch (results[0].kind()) { |
| 22046 | case wasm::ValType::I32: |
| 22047 | MOZ_ASSERT(lir->mir()->type() == MIRType::Int32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Int32)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Int32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int32" ")"); do { *((volatile int*)__null) = 22047; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22048 | MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToRegister(lir->output()) == ReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(ToRegister(lir->output()) == ReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToRegister(lir->output()) == ReturnReg" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22048); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { *((volatile int*)__null) = 22048; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22049 | break; |
| 22050 | case wasm::ValType::I64: |
| 22051 | MOZ_ASSERT(lir->mir()->type() == MIRType::Int64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Int64)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Int64))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Int64" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22051); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64" ")"); do { *((volatile int*)__null) = 22051; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22052 | MOZ_ASSERT(ToOutRegister64(lir) == ReturnReg64)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutRegister64(lir) == ReturnReg64)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ToOutRegister64(lir) == ReturnReg64 ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToOutRegister64(lir) == ReturnReg64", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22052); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutRegister64(lir) == ReturnReg64" ")"); do { *((volatile int*)__null) = 22052; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22053 | break; |
| 22054 | case wasm::ValType::F32: |
| 22055 | MOZ_ASSERT(lir->mir()->type() == MIRType::Float32)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Float32)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Float32))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Float32" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32" ")"); do { *((volatile int*)__null) = 22055; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22056 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnFloat32Reg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnFloat32Reg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnFloat32Reg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnFloat32Reg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22056); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg" ")"); do { *((volatile int*)__null) = 22056; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22057 | break; |
| 22058 | case wasm::ValType::F64: |
| 22059 | MOZ_ASSERT(lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Double)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Double))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Double" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22059); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double" ")"); do { *((volatile int*)__null) = 22059; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22060 | MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToFloatRegister(lir->output()) == ReturnDoubleReg )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToFloatRegister(lir->output()) == ReturnDoubleReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "ToFloatRegister(lir->output()) == ReturnDoubleReg", "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22060); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { *((volatile int*)__null) = 22060; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22061 | break; |
| 22062 | case wasm::ValType::V128: |
| 22063 | MOZ_CRASH("unexpected return type when calling from ion to wasm")do { do { } while (false); MOZ_ReportCrash("" "unexpected return type when calling from ion to wasm" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22063); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected return type when calling from ion to wasm" ")"); do { *((volatile int*)__null) = 22063; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 22064 | case wasm::ValType::Ref: |
| 22065 | // The wasm stubs layer unboxes anything that needs to be unboxed |
| 22066 | // and leaves it in a Value. A FuncRef/EqRef we could in principle |
| 22067 | // leave it as a raw object pointer but for now it complicates the |
| 22068 | // API to do so. |
| 22069 | MOZ_ASSERT(lir->mir()->type() == MIRType::Value)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Value)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(lir->mir()->type() == MIRType::Value))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Value" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value" ")"); do { *((volatile int*)__null) = 22069; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22070 | break; |
| 22071 | } |
| 22072 | } |
| 22073 | |
| 22074 | WasmInstanceObject* instObj = lir->mir()->instanceObject(); |
| 22075 | |
| 22076 | Register scratch = ToRegister(lir->temp()); |
| 22077 | |
| 22078 | uint32_t callOffset; |
| 22079 | ensureOsiSpace(); |
| 22080 | GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs, |
| 22081 | scratch, &callOffset); |
| 22082 | |
| 22083 | // Add the instance object to the constant pool, so it is transferred to |
| 22084 | // the owning IonScript and so that it gets traced as long as the IonScript |
| 22085 | // lives. |
| 22086 | |
| 22087 | uint32_t unused; |
| 22088 | masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused)); |
| 22089 | |
| 22090 | markSafepointAt(callOffset, lir); |
| 22091 | } |
| 22092 | |
| 22093 | void CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) { |
| 22094 | emitIonToWasmCallBase(lir); |
| 22095 | } |
| 22096 | void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) { |
| 22097 | emitIonToWasmCallBase(lir); |
| 22098 | } |
| 22099 | void CodeGenerator::visitIonToWasmCallI64(LIonToWasmCallI64* lir) { |
| 22100 | emitIonToWasmCallBase(lir); |
| 22101 | } |
| 22102 | |
| 22103 | void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) { |
| 22104 | masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output())); |
| 22105 | } |
| 22106 | |
| 22107 | void CodeGenerator::visitWasmFence(LWasmFence* lir) { |
| 22108 | MOZ_ASSERT(gen->compilingWasm())do { static_assert( mozilla::detail::AssertionConditionType< decltype(gen->compilingWasm())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gen->compilingWasm()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("gen->compilingWasm()" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22108); AnnotateMozCrashReason("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { *((volatile int*)__null) = 22108; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22109 | masm.memoryBarrier(MembarFull); |
| 22110 | } |
| 22111 | |
| 22112 | void CodeGenerator::visitWasmAnyRefFromJSValue(LWasmAnyRefFromJSValue* lir) { |
| 22113 | ValueOperand input = ToValue(lir, LWasmAnyRefFromJSValue::InputIndex); |
| 22114 | Register output = ToRegister(lir->output()); |
| 22115 | FloatRegister tempFloat = ToFloatRegister(lir->temp0()); |
| 22116 | |
| 22117 | using Fn = JSObject* (*)(JSContext* cx, HandleValue value); |
| 22118 | OutOfLineCode* oolBoxValue = oolCallVM<Fn, wasm::AnyRef::boxValue>( |
| 22119 | lir, ArgList(input), StoreRegisterTo(output)); |
| 22120 | masm.convertValueToWasmAnyRef(input, output, tempFloat, oolBoxValue->entry()); |
| 22121 | masm.bind(oolBoxValue->rejoin()); |
| 22122 | } |
| 22123 | |
| 22124 | void CodeGenerator::visitWasmAnyRefFromJSObject(LWasmAnyRefFromJSObject* lir) { |
| 22125 | Register input = ToRegister(lir->input()); |
| 22126 | Register output = ToRegister(lir->output()); |
| 22127 | masm.convertObjectToWasmAnyRef(input, output); |
| 22128 | } |
| 22129 | |
| 22130 | void CodeGenerator::visitWasmAnyRefFromJSString(LWasmAnyRefFromJSString* lir) { |
| 22131 | Register input = ToRegister(lir->input()); |
| 22132 | Register output = ToRegister(lir->output()); |
| 22133 | masm.convertStringToWasmAnyRef(input, output); |
| 22134 | } |
| 22135 | |
| 22136 | void CodeGenerator::visitWasmAnyRefIsJSString(LWasmAnyRefIsJSString* lir) { |
| 22137 | Register input = ToRegister(lir->input()); |
| 22138 | Register output = ToRegister(lir->output()); |
| 22139 | Register temp = ToRegister(lir->temp0()); |
| 22140 | Label fallthrough; |
| 22141 | Label isJSString; |
| 22142 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
| 22143 | masm.move32(Imm32(0), output); |
| 22144 | masm.jump(&fallthrough); |
| 22145 | masm.bind(&isJSString); |
| 22146 | masm.move32(Imm32(1), output); |
| 22147 | masm.bind(&fallthrough); |
| 22148 | } |
| 22149 | |
| 22150 | void CodeGenerator::visitWasmTrapIfAnyRefIsNotJSString( |
| 22151 | LWasmTrapIfAnyRefIsNotJSString* lir) { |
| 22152 | Register input = ToRegister(lir->input()); |
| 22153 | Register temp = ToRegister(lir->temp0()); |
| 22154 | Label isJSString; |
| 22155 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
| 22156 | masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc()); |
| 22157 | masm.bind(&isJSString); |
| 22158 | } |
| 22159 | |
| 22160 | void CodeGenerator::visitWasmAnyRefJSStringLength( |
| 22161 | LWasmAnyRefJSStringLength* lir) { |
| 22162 | Register input = ToRegister(lir->input()); |
| 22163 | Register output = ToRegister(lir->output()); |
| 22164 | Register temp = ToRegister(lir->temp0()); |
| 22165 | Label isJSString; |
| 22166 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
| 22167 | masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc()); |
| 22168 | masm.bind(&isJSString); |
| 22169 | masm.untagWasmAnyRef(input, temp, wasm::AnyRefTag::String); |
| 22170 | masm.loadStringLength(temp, output); |
| 22171 | } |
| 22172 | |
| 22173 | void CodeGenerator::visitWasmNewI31Ref(LWasmNewI31Ref* lir) { |
| 22174 | if (lir->value()->isConstant()) { |
| 22175 | // i31ref are often created with constants. If that's the case we will |
| 22176 | // do the operation statically here. This is similar to what is done |
| 22177 | // in masm.truncate32ToWasmI31Ref. |
| 22178 | Register output = ToRegister(lir->output()); |
| 22179 | uint32_t value = |
| 22180 | static_cast<uint32_t>(lir->value()->toConstant()->toInt32()); |
| 22181 | uintptr_t ptr = wasm::AnyRef::fromUint32Truncate(value).rawValue(); |
| 22182 | masm.movePtr(ImmWord(ptr), output); |
| 22183 | } else { |
| 22184 | Register value = ToRegister(lir->value()); |
| 22185 | Register output = ToRegister(lir->output()); |
| 22186 | masm.truncate32ToWasmI31Ref(value, output); |
| 22187 | } |
| 22188 | } |
| 22189 | |
| 22190 | void CodeGenerator::visitWasmI31RefGet(LWasmI31RefGet* lir) { |
| 22191 | Register value = ToRegister(lir->value()); |
| 22192 | Register output = ToRegister(lir->output()); |
| 22193 | if (lir->mir()->wideningOp() == wasm::FieldWideningOp::Signed) { |
| 22194 | masm.convertWasmI31RefTo32Signed(value, output); |
| 22195 | } else { |
| 22196 | masm.convertWasmI31RefTo32Unsigned(value, output); |
| 22197 | } |
| 22198 | } |
| 22199 | |
| 22200 | #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT1 |
| 22201 | void CodeGenerator::visitAddDisposableResource(LAddDisposableResource* lir) { |
| 22202 | Register environment = ToRegister(lir->environment()); |
| 22203 | ValueOperand resource = ToValue(lir, LAddDisposableResource::ResourceIndex); |
| 22204 | ValueOperand method = ToValue(lir, LAddDisposableResource::MethodIndex); |
| 22205 | Register needsClosure = ToRegister(lir->needsClosure()); |
| 22206 | uint8_t hint = lir->hint(); |
| 22207 | |
| 22208 | pushArg(Imm32(hint)); |
| 22209 | pushArg(needsClosure); |
| 22210 | pushArg(method); |
| 22211 | pushArg(resource); |
| 22212 | pushArg(environment); |
| 22213 | |
| 22214 | using Fn = bool (*)(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, |
| 22215 | JS::Handle<JS::Value>, bool, UsingHint); |
| 22216 | callVM<Fn, js::AddDisposableResourceToCapability>(lir); |
| 22217 | } |
| 22218 | |
| 22219 | void CodeGenerator::visitTakeDisposeCapability(LTakeDisposeCapability* lir) { |
| 22220 | Register environment = ToRegister(lir->environment()); |
| 22221 | ValueOperand output = ToOutValue(lir); |
| 22222 | |
| 22223 | Address capabilityAddr( |
| 22224 | environment, DisposableEnvironmentObject::offsetOfDisposeCapability()); |
| 22225 | emitPreBarrier(capabilityAddr); |
| 22226 | masm.loadValue(capabilityAddr, output); |
| 22227 | masm.storeValue(JS::UndefinedValue(), capabilityAddr); |
| 22228 | } |
| 22229 | |
| 22230 | void CodeGenerator::visitCreateSuppressedError(LCreateSuppressedError* lir) { |
| 22231 | ValueOperand error = ToValue(lir, LCreateSuppressedError::ErrorIndex); |
| 22232 | ValueOperand suppressed = |
| 22233 | ToValue(lir, LCreateSuppressedError::SuppressedIndex); |
| 22234 | |
| 22235 | pushArg(suppressed); |
| 22236 | pushArg(error); |
| 22237 | |
| 22238 | using Fn = ErrorObject* (*)(JSContext*, JS::Handle<JS::Value>, |
| 22239 | JS::Handle<JS::Value>); |
| 22240 | callVM<Fn, js::CreateSuppressedError>(lir); |
| 22241 | } |
| 22242 | #endif |
| 22243 | |
| 22244 | #ifdef FUZZING_JS_FUZZILLI |
| 22245 | void CodeGenerator::emitFuzzilliHashObject(LInstruction* lir, Register obj, |
| 22246 | Register output) { |
| 22247 | using Fn = void (*)(JSContext* cx, JSObject* obj, uint32_t* out); |
| 22248 | OutOfLineCode* ool = oolCallVM<Fn, FuzzilliHashObjectInl>( |
| 22249 | lir, ArgList(obj), StoreRegisterTo(output)); |
| 22250 | |
| 22251 | masm.jump(ool->entry()); |
| 22252 | masm.bind(ool->rejoin()); |
| 22253 | } |
| 22254 | |
| 22255 | void CodeGenerator::emitFuzzilliHashBigInt(LInstruction* lir, Register bigInt, |
| 22256 | Register output) { |
| 22257 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 22258 | volatileRegs.takeUnchecked(output); |
| 22259 | |
| 22260 | masm.PushRegsInMask(volatileRegs); |
| 22261 | |
| 22262 | using Fn = uint32_t (*)(BigInt* bigInt); |
| 22263 | masm.setupUnalignedABICall(output); |
| 22264 | masm.passABIArg(bigInt); |
| 22265 | masm.callWithABI<Fn, js::FuzzilliHashBigInt>(); |
| 22266 | masm.storeCallInt32Result(output); |
| 22267 | |
| 22268 | masm.PopRegsInMask(volatileRegs); |
| 22269 | } |
| 22270 | |
| 22271 | void CodeGenerator::visitFuzzilliHashV(LFuzzilliHashV* ins) { |
| 22272 | ValueOperand value = ToValue(ins, 0); |
| 22273 | |
| 22274 | FloatRegister scratchFloat = ToFloatRegister(ins->getTemp(1)); |
| 22275 | Register scratch = ToRegister(ins->getTemp(0)); |
| 22276 | Register output = ToRegister(ins->output()); |
| 22277 | MOZ_ASSERT(scratch != output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch != output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(scratch != output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch != output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22277); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output" ")"); do { *((volatile int*)__null) = 22277; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22278 | |
| 22279 | Label hashDouble, done; |
| 22280 | |
| 22281 | Label isInt32, isDouble, isNull, isUndefined, isBoolean, isBigInt, isObject; |
| 22282 | { |
| 22283 | ScratchTagScope tag(masm, value); |
| 22284 | masm.splitTagForTest(value, tag); |
| 22285 | |
| 22286 | masm.branchTestInt32(Assembler::Equal, tag, &isInt32); |
| 22287 | masm.branchTestDouble(Assembler::Equal, tag, &isDouble); |
| 22288 | masm.branchTestNull(Assembler::Equal, tag, &isNull); |
| 22289 | masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined); |
| 22290 | masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean); |
| 22291 | masm.branchTestBigInt(Assembler::Equal, tag, &isBigInt); |
| 22292 | masm.branchTestObject(Assembler::Equal, tag, &isObject); |
| 22293 | |
| 22294 | // Symbol or String. |
| 22295 | masm.move32(Imm32(0), output); |
| 22296 | masm.jump(&done); |
| 22297 | } |
| 22298 | |
| 22299 | masm.bind(&isInt32); |
| 22300 | { |
| 22301 | masm.unboxInt32(value, scratch); |
| 22302 | masm.convertInt32ToDouble(scratch, scratchFloat); |
| 22303 | masm.jump(&hashDouble); |
| 22304 | } |
| 22305 | |
| 22306 | masm.bind(&isDouble); |
| 22307 | { |
| 22308 | masm.unboxDouble(value, scratchFloat); |
| 22309 | masm.jump(&hashDouble); |
| 22310 | } |
| 22311 | |
| 22312 | masm.bind(&isNull); |
| 22313 | { |
| 22314 | masm.loadConstantDouble(1.0, scratchFloat); |
| 22315 | masm.jump(&hashDouble); |
| 22316 | } |
| 22317 | |
| 22318 | masm.bind(&isUndefined); |
| 22319 | { |
| 22320 | masm.loadConstantDouble(2.0, scratchFloat); |
| 22321 | masm.jump(&hashDouble); |
| 22322 | } |
| 22323 | |
| 22324 | masm.bind(&isBoolean); |
| 22325 | { |
| 22326 | masm.unboxBoolean(value, scratch); |
| 22327 | masm.add32(Imm32(3), scratch); |
| 22328 | masm.convertInt32ToDouble(scratch, scratchFloat); |
| 22329 | masm.jump(&hashDouble); |
| 22330 | } |
| 22331 | |
| 22332 | masm.bind(&isBigInt); |
| 22333 | { |
| 22334 | masm.unboxBigInt(value, scratch); |
| 22335 | emitFuzzilliHashBigInt(ins, scratch, output); |
| 22336 | masm.jump(&done); |
| 22337 | } |
| 22338 | |
| 22339 | masm.bind(&isObject); |
| 22340 | { |
| 22341 | masm.unboxObject(value, scratch); |
| 22342 | emitFuzzilliHashObject(ins, scratch, output); |
| 22343 | masm.jump(&done); |
| 22344 | } |
| 22345 | |
| 22346 | masm.bind(&hashDouble); |
| 22347 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 22348 | |
| 22349 | masm.bind(&done); |
| 22350 | } |
| 22351 | |
| 22352 | void CodeGenerator::visitFuzzilliHashT(LFuzzilliHashT* ins) { |
| 22353 | const LAllocation* value = ins->value(); |
| 22354 | MIRType mirType = ins->mir()->getOperand(0)->type(); |
| 22355 | |
| 22356 | Register scratch = ToTempRegisterOrInvalid(ins->getTemp(0)); |
| 22357 | FloatRegister scratchFloat = ToTempFloatRegisterOrInvalid(ins->getTemp(1)); |
| 22358 | |
| 22359 | Register output = ToRegister(ins->output()); |
| 22360 | MOZ_ASSERT(scratch != output)do { static_assert( mozilla::detail::AssertionConditionType< decltype(scratch != output)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(scratch != output))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("scratch != output" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22360); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scratch != output" ")"); do { *((volatile int*)__null) = 22360; __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22361 | |
| 22362 | switch (mirType) { |
| 22363 | case MIRType::Undefined: { |
| 22364 | masm.loadConstantDouble(2.0, scratchFloat); |
| 22365 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 22366 | break; |
| 22367 | } |
| 22368 | |
| 22369 | case MIRType::Null: { |
| 22370 | masm.loadConstantDouble(1.0, scratchFloat); |
| 22371 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 22372 | break; |
| 22373 | } |
| 22374 | |
| 22375 | case MIRType::Int32: { |
| 22376 | masm.move32(ToRegister(value), scratch); |
| 22377 | masm.convertInt32ToDouble(scratch, scratchFloat); |
| 22378 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 22379 | break; |
| 22380 | } |
| 22381 | |
| 22382 | case MIRType::Double: { |
| 22383 | masm.moveDouble(ToFloatRegister(value), scratchFloat); |
| 22384 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 22385 | break; |
| 22386 | } |
| 22387 | |
| 22388 | case MIRType::Float32: { |
| 22389 | masm.convertFloat32ToDouble(ToFloatRegister(value), scratchFloat); |
| 22390 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 22391 | break; |
| 22392 | } |
| 22393 | |
| 22394 | case MIRType::Boolean: { |
| 22395 | masm.move32(ToRegister(value), scratch); |
| 22396 | masm.add32(Imm32(3), scratch); |
| 22397 | masm.convertInt32ToDouble(scratch, scratchFloat); |
| 22398 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 22399 | break; |
| 22400 | } |
| 22401 | |
| 22402 | case MIRType::BigInt: { |
| 22403 | emitFuzzilliHashBigInt(ins, ToRegister(value), output); |
| 22404 | break; |
| 22405 | } |
| 22406 | |
| 22407 | case MIRType::Object: { |
| 22408 | emitFuzzilliHashObject(ins, ToRegister(value), output); |
| 22409 | break; |
| 22410 | } |
| 22411 | |
| 22412 | default: |
| 22413 | MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type" , "/var/lib/jenkins/workspace/firefox-scan-build/js/src/jit/CodeGenerator.cpp" , 22413); AnnotateMozCrashReason("MOZ_CRASH(" "unexpected type" ")"); do { *((volatile int*)__null) = 22413; __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 22414 | } |
| 22415 | } |
| 22416 | |
| 22417 | void CodeGenerator::visitFuzzilliHashStore(LFuzzilliHashStore* ins) { |
| 22418 | Register value = ToRegister(ins->value()); |
| 22419 | Register temp0 = ToRegister(ins->getTemp(0)); |
| 22420 | Register temp1 = ToRegister(ins->getTemp(1)); |
| 22421 | |
| 22422 | masm.fuzzilliStoreHash(value, temp0, temp1); |
| 22423 | } |
| 22424 | #endif |
| 22425 | |
| 22426 | static_assert(!std::is_polymorphic_v<CodeGenerator>, |
| 22427 | "CodeGenerator should not have any virtual methods"); |
| 22428 | |
| 22429 | } // namespace jit |
| 22430 | } // namespace js |