| File: | root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/jit/./../../../../js/src/jit/CodeGenerator.cpp |
| Warning: | line 13451, column 8 Value stored to 'extractObject' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 4 | |
| 5 | #include "jit/CodeGenerator.h" |
| 6 | |
| 7 | #include "mozilla/Assertions.h" |
| 8 | #include "mozilla/CheckedArithmetic.h" |
| 9 | #include "mozilla/DebugOnly.h" |
| 10 | #include "mozilla/EnumeratedArray.h" |
| 11 | #include "mozilla/EnumeratedRange.h" |
| 12 | #include "mozilla/EnumSet.h" |
| 13 | #include "mozilla/IntegerTypeTraits.h" |
| 14 | #include "mozilla/Latin1.h" |
| 15 | #include "mozilla/MathAlgorithms.h" |
| 16 | #include "mozilla/ScopeExit.h" |
| 17 | #include "mozilla/SIMD.h" |
| 18 | |
| 19 | #include <algorithm> |
| 20 | #include <bit> |
| 21 | #include <cmath> |
| 22 | #include <limits> |
| 23 | #include <type_traits> |
| 24 | #include <utility> |
| 25 | |
| 26 | #include "builtin/MapObject.h" |
| 27 | #include "builtin/Math.h" |
| 28 | #include "builtin/Number.h" |
| 29 | #include "builtin/RegExp.h" |
| 30 | #include "builtin/String.h" |
| 31 | #include "irregexp/RegExpTypes.h" |
| 32 | #include "jit/ABIArgGenerator.h" |
| 33 | #include "jit/CompileInfo.h" |
| 34 | #include "jit/InlineScriptTree.h" |
| 35 | #include "jit/Invalidation.h" |
| 36 | #include "jit/IonGenericCallStub.h" |
| 37 | #include "jit/IonIC.h" |
| 38 | #include "jit/IonScript.h" |
| 39 | #include "jit/JitcodeMap.h" |
| 40 | #include "jit/JitFrames.h" |
| 41 | #include "jit/JitRuntime.h" |
| 42 | #include "jit/JitSpewer.h" |
| 43 | #include "jit/JitZone.h" |
| 44 | #include "jit/Linker.h" |
| 45 | #include "jit/MIRGenerator.h" |
| 46 | #include "jit/MoveEmitter.h" |
| 47 | #include "jit/RangeAnalysis.h" |
| 48 | #include "jit/RegExpStubConstants.h" |
| 49 | #include "jit/SafepointIndex.h" |
| 50 | #include "jit/SharedICHelpers.h" |
| 51 | #include "jit/SharedICRegisters.h" |
| 52 | #include "jit/VMFunctions.h" |
| 53 | #include "jit/WarpSnapshot.h" |
| 54 | #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin |
| 55 | #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter}CallArgs, JSJitMethodCallArgsTraits, JSJitInfo |
| 56 | #include "js/friend/DOMProxy.h" // JS::ExpandoAndGeneration |
| 57 | #include "js/RegExpFlags.h" // JS::RegExpFlag |
| 58 | #include "js/ScalarType.h" // js::Scalar::Type |
| 59 | #include "proxy/DOMProxy.h" |
| 60 | #include "proxy/ScriptedProxyHandler.h" |
| 61 | #include "util/DifferentialTesting.h" |
| 62 | #include "util/PortableMath.h" |
| 63 | #include "util/Unicode.h" |
| 64 | #include "vm/ArrayBufferViewObject.h" |
| 65 | #include "vm/AsyncFunction.h" |
| 66 | #include "vm/AsyncIteration.h" |
| 67 | #include "vm/BuiltinObjectKind.h" |
| 68 | #include "vm/DateObject.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/RuntimeFuses.h" |
| 76 | #include "vm/StaticStrings.h" |
| 77 | #include "vm/StringObject.h" |
| 78 | #include "vm/StringType.h" |
| 79 | #include "vm/TypedArrayObject.h" |
| 80 | #include "wasm/WasmCodegenConstants.h" |
| 81 | #include "wasm/WasmPI.h" |
| 82 | #include "wasm/WasmStacks.h" |
| 83 | #include "wasm/WasmValType.h" |
| 84 | #ifdef MOZ_VTUNE1 |
| 85 | # include "vtune/VTuneWrapper.h" |
| 86 | #endif |
| 87 | #include "wasm/WasmBinary.h" |
| 88 | #include "wasm/WasmGC.h" |
| 89 | #include "wasm/WasmGcObject.h" |
| 90 | #include "wasm/WasmStubs.h" |
| 91 | |
| 92 | #include "builtin/Boolean-inl.h" |
| 93 | #include "jit/MacroAssembler-inl.h" |
| 94 | #include "jit/shared/CodeGenerator-shared-inl.h" |
| 95 | #include "jit/TemplateObject-inl.h" |
| 96 | #include "jit/VMFunctionList-inl.h" |
| 97 | #include "vm/BytecodeUtil-inl.h" |
| 98 | #include "vm/JSScript-inl.h" |
| 99 | #include "wasm/WasmInstance-inl.h" |
| 100 | |
| 101 | using namespace js; |
| 102 | using namespace js::jit; |
| 103 | |
| 104 | using mozilla::CheckedUint32; |
| 105 | using mozilla::DebugOnly; |
| 106 | using mozilla::FloatingPoint; |
| 107 | using mozilla::NegativeInfinity; |
| 108 | using mozilla::PositiveInfinity; |
| 109 | |
| 110 | using JS::ExpandoAndGeneration; |
| 111 | |
| 112 | namespace js { |
| 113 | namespace jit { |
| 114 | |
| 115 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
| 116 | template <class Op> |
| 117 | static void HandleRegisterDump(Op op, MacroAssembler& masm, |
| 118 | LiveRegisterSet liveRegs, Register activation, |
| 119 | Register scratch) { |
| 120 | const size_t baseOffset = JitActivation::offsetOfRegs(); |
| 121 | |
| 122 | // Handle live GPRs. |
| 123 | for (GeneralRegisterIterator iter(liveRegs.gprs()); iter.more(); ++iter) { |
| 124 | Register reg = *iter; |
| 125 | Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
| 126 | |
| 127 | if (reg == activation) { |
| 128 | // To use the original value of the activation register (that's |
| 129 | // now on top of the stack), we need the scratch register. |
| 130 | masm.push(scratch); |
| 131 | masm.loadPtr(Address(masm.getStackPointer(), sizeof(uintptr_t)), scratch); |
| 132 | op(scratch, dump); |
| 133 | masm.pop(scratch); |
| 134 | } else { |
| 135 | op(reg, dump); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | // Handle live FPRs. |
| 140 | for (FloatRegisterIterator iter(liveRegs.fpus()); iter.more(); ++iter) { |
| 141 | FloatRegister reg = *iter; |
| 142 | Address dump(activation, baseOffset + RegisterDump::offsetOfRegister(reg)); |
| 143 | op(reg, dump); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | class StoreOp { |
| 148 | MacroAssembler& masm; |
| 149 | |
| 150 | public: |
| 151 | explicit StoreOp(MacroAssembler& masm) : masm(masm) {} |
| 152 | |
| 153 | void operator()(Register reg, Address dump) { masm.storePtr(reg, dump); } |
| 154 | void operator()(FloatRegister reg, Address dump) { |
| 155 | if (reg.isDouble()) { |
| 156 | masm.storeDouble(reg, dump); |
| 157 | } else if (reg.isSingle()) { |
| 158 | masm.storeFloat32(reg, dump); |
| 159 | } else if (reg.isSimd128()) { |
| 160 | MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD" , "./../../../../js/src/jit/CodeGenerator.cpp", 160); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { MOZ_CrashSequence (__null, 160); __attribute__((nomerge)) ::abort(); } while (false ); } while (false); |
| 161 | } else { |
| 162 | MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type." , "./../../../../js/src/jit/CodeGenerator.cpp", 162); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected register type." ")"); do { MOZ_CrashSequence (__null, 162); __attribute__((nomerge)) ::abort(); } while (false ); } while (false); |
| 163 | } |
| 164 | } |
| 165 | }; |
| 166 | |
| 167 | class VerifyOp { |
| 168 | MacroAssembler& masm; |
| 169 | Label* failure_; |
| 170 | |
| 171 | public: |
| 172 | VerifyOp(MacroAssembler& masm, Label* failure) |
| 173 | : masm(masm), failure_(failure) {} |
| 174 | |
| 175 | void operator()(Register reg, Address dump) { |
| 176 | masm.branchPtr(Assembler::NotEqual, dump, reg, failure_); |
| 177 | } |
| 178 | void operator()(FloatRegister reg, Address dump) { |
| 179 | if (reg.isDouble()) { |
| 180 | ScratchDoubleScope scratch(masm); |
| 181 | masm.loadDouble(dump, scratch); |
| 182 | masm.branchDouble(Assembler::DoubleNotEqual, scratch, reg, failure_); |
| 183 | } else if (reg.isSingle()) { |
| 184 | ScratchFloat32Scope scratch(masm); |
| 185 | masm.loadFloat32(dump, scratch); |
| 186 | masm.branchFloat(Assembler::DoubleNotEqual, scratch, reg, failure_); |
| 187 | } else if (reg.isSimd128()) { |
| 188 | MOZ_CRASH("Unexpected case for SIMD")do { do { } while (false); MOZ_ReportCrash("" "Unexpected case for SIMD" , "./../../../../js/src/jit/CodeGenerator.cpp", 188); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected case for SIMD" ")"); do { MOZ_CrashSequence (__null, 188); __attribute__((nomerge)) ::abort(); } while (false ); } while (false); |
| 189 | } else { |
| 190 | MOZ_CRASH("Unexpected register type.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected register type." , "./../../../../js/src/jit/CodeGenerator.cpp", 190); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected register type." ")"); do { MOZ_CrashSequence (__null, 190); __attribute__((nomerge)) ::abort(); } while (false ); } while (false); |
| 191 | } |
| 192 | } |
| 193 | }; |
| 194 | |
| 195 | void CodeGenerator::verifyOsiPointRegs(LSafepoint* safepoint) { |
| 196 | // Ensure the live registers stored by callVM did not change between |
| 197 | // the call and this OsiPoint. Try-catch relies on this invariant. |
| 198 | |
| 199 | // Load pointer to the JitActivation in a scratch register. |
| 200 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 201 | Register scratch = allRegs.takeAny(); |
| 202 | masm.push(scratch); |
| 203 | masm.loadJitActivation(scratch); |
| 204 | |
| 205 | // If we should not check registers (because the instruction did not call |
| 206 | // into the VM, or a GC happened), we're done. |
| 207 | Label failure, done; |
| 208 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
| 209 | masm.branch32(Assembler::Equal, checkRegs, Imm32(0), &done); |
| 210 | |
| 211 | // Having more than one VM function call made in one visit function at |
| 212 | // runtime is a sec-ciritcal error, because if we conservatively assume that |
| 213 | // one of the function call can re-enter Ion, then the invalidation process |
| 214 | // will potentially add a call at a random location, by patching the code |
| 215 | // before the return address. |
| 216 | masm.branch32(Assembler::NotEqual, checkRegs, Imm32(1), &failure); |
| 217 | |
| 218 | // Set checkRegs to 0, so that we don't try to verify registers after we |
| 219 | // return from this script to the caller. |
| 220 | masm.store32(Imm32(0), checkRegs); |
| 221 | |
| 222 | // Ignore clobbered registers. Some instructions (like LValueToInt32) modify |
| 223 | // temps after calling into the VM. This is fine because no other |
| 224 | // instructions (including this OsiPoint) will depend on them. Also |
| 225 | // backtracking can also use the same register for an input and an output. |
| 226 | // These are marked as clobbered and shouldn't get checked. |
| 227 | LiveRegisterSet liveRegs; |
| 228 | liveRegs.set() = RegisterSet::Intersect( |
| 229 | safepoint->liveRegs().set(), |
| 230 | RegisterSet::Not(safepoint->clobberedRegs().set())); |
| 231 | |
| 232 | VerifyOp op(masm, &failure); |
| 233 | HandleRegisterDump<VerifyOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
| 234 | |
| 235 | masm.jump(&done); |
| 236 | |
| 237 | // Do not profile the callWithABI that occurs below. This is to avoid a |
| 238 | // rare corner case that occurs when profiling interacts with itself: |
| 239 | // |
| 240 | // When slow profiling assertions are turned on, FunctionBoundary ops |
| 241 | // (which update the profiler pseudo-stack) may emit a callVM, which |
| 242 | // forces them to have an osi point associated with them. The |
| 243 | // FunctionBoundary for inline function entry is added to the caller's |
| 244 | // graph with a PC from the caller's code, but during codegen it modifies |
| 245 | // Gecko Profiler instrumentation to add the callee as the current top-most |
| 246 | // script. When codegen gets to the OSIPoint, and the callWithABI below is |
| 247 | // emitted, the codegen thinks that the current frame is the callee, but |
| 248 | // the PC it's using from the OSIPoint refers to the caller. This causes |
| 249 | // the profiler instrumentation of the callWithABI below to ASSERT, since |
| 250 | // the script and pc are mismatched. To avoid this, we simply omit |
| 251 | // instrumentation for these callWithABIs. |
| 252 | |
| 253 | // Any live register captured by a safepoint (other than temp registers) |
| 254 | // must remain unchanged between the call and the OsiPoint instruction. |
| 255 | masm.bind(&failure); |
| 256 | masm.assumeUnreachable("Modified registers between VM call and OsiPoint"); |
| 257 | |
| 258 | masm.bind(&done); |
| 259 | masm.pop(scratch); |
| 260 | } |
| 261 | |
| 262 | bool CodeGenerator::shouldVerifyOsiPointRegs(LSafepoint* safepoint) { |
| 263 | if (!checkOsiPointRegisters) { |
| 264 | return false; |
| 265 | } |
| 266 | |
| 267 | if (safepoint->liveRegs().emptyGeneral() && |
| 268 | safepoint->liveRegs().emptyFloat()) { |
| 269 | return false; // No registers to check. |
| 270 | } |
| 271 | |
| 272 | return true; |
| 273 | } |
| 274 | |
| 275 | void CodeGenerator::resetOsiPointRegs(LSafepoint* safepoint) { |
| 276 | if (!shouldVerifyOsiPointRegs(safepoint)) { |
| 277 | return; |
| 278 | } |
| 279 | |
| 280 | // Set checkRegs to 0. If we perform a VM call, the instruction |
| 281 | // will set it to 1. |
| 282 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 283 | Register scratch = allRegs.takeAny(); |
| 284 | masm.push(scratch); |
| 285 | masm.loadJitActivation(scratch); |
| 286 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
| 287 | masm.store32(Imm32(0), checkRegs); |
| 288 | masm.pop(scratch); |
| 289 | } |
| 290 | |
| 291 | static void StoreAllLiveRegs(MacroAssembler& masm, LiveRegisterSet liveRegs) { |
| 292 | // Store a copy of all live registers before performing the call. |
| 293 | // When we reach the OsiPoint, we can use this to check nothing |
| 294 | // modified them in the meantime. |
| 295 | |
| 296 | // Load pointer to the JitActivation in a scratch register. |
| 297 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 298 | Register scratch = allRegs.takeAny(); |
| 299 | masm.push(scratch); |
| 300 | masm.loadJitActivation(scratch); |
| 301 | |
| 302 | Address checkRegs(scratch, JitActivation::offsetOfCheckRegs()); |
| 303 | masm.add32(Imm32(1), checkRegs); |
| 304 | |
| 305 | StoreOp op(masm); |
| 306 | HandleRegisterDump<StoreOp>(op, masm, liveRegs, scratch, allRegs.getAny()); |
| 307 | |
| 308 | masm.pop(scratch); |
| 309 | } |
| 310 | #endif // CHECK_OSIPOINT_REGISTERS |
| 311 | |
| 312 | // Before doing any call to Cpp, you should ensure that volatile |
| 313 | // registers are evicted by the register allocator. |
| 314 | void CodeGenerator::callVMInternal(VMFunctionId id, LInstruction* ins) { |
| 315 | TrampolinePtr code = gen->jitRuntime()->getVMWrapper(id); |
| 316 | const VMFunctionData& fun = GetVMFunction(id); |
| 317 | |
| 318 | // Stack is: |
| 319 | // ... frame ... |
| 320 | // [args] |
| 321 | #ifdef DEBUG1 |
| 322 | MOZ_ASSERT(pushedArgs_ == fun.explicitArgs)do { static_assert( mozilla::detail::AssertionConditionType< decltype(pushedArgs_ == fun.explicitArgs)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(pushedArgs_ == fun.explicitArgs ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "pushedArgs_ == fun.explicitArgs", "./../../../../js/src/jit/CodeGenerator.cpp" , 322); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pushedArgs_ == fun.explicitArgs" ")"); do { MOZ_CrashSequence(__null, 322); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 323 | pushedArgs_ = 0; |
| 324 | #endif |
| 325 | |
| 326 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
| 327 | if (shouldVerifyOsiPointRegs(ins->safepoint())) { |
| 328 | StoreAllLiveRegs(masm, ins->safepoint()->liveRegs()); |
| 329 | } |
| 330 | #endif |
| 331 | |
| 332 | #ifdef DEBUG1 |
| 333 | if (ins->mirRaw()) { |
| 334 | MOZ_ASSERT(ins->mirRaw()->isInstruction())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mirRaw()->isInstruction())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mirRaw()->isInstruction ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ins->mirRaw()->isInstruction()", "./../../../../js/src/jit/CodeGenerator.cpp" , 334); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mirRaw()->isInstruction()" ")"); do { MOZ_CrashSequence(__null, 334); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 335 | MInstruction* mir = ins->mirRaw()->toInstruction(); |
| 336 | MOZ_ASSERT_IF(mir->needsResumePoint(), mir->resumePoint())do { if (mir->needsResumePoint()) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(mir->resumePoint ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(mir->resumePoint()))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("mir->resumePoint()", "./../../../../js/src/jit/CodeGenerator.cpp" , 336); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->resumePoint()" ")"); do { MOZ_CrashSequence(__null, 336); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 337 | |
| 338 | // If this MIR instruction has an overridden AliasSet, set the JitRuntime's |
| 339 | // disallowArbitraryCode_ flag so we can assert this VMFunction doesn't call |
| 340 | // RunScript. Whitelist MInterruptCheck and MCheckOverRecursed because |
| 341 | // interrupt callbacks can call JS (chrome JS or shell testing functions). |
| 342 | // MCheckOverRecursed for a generator resume doesn't check for interrupts. |
| 343 | bool isWhitelisted = mir->isInterruptCheck() || |
| 344 | (mir->isCheckOverRecursed() && |
| 345 | !mir->toCheckOverRecursed()->isResumingGenerator()); |
| 346 | if (!mir->hasDefaultAliasSet() && !isWhitelisted) { |
| 347 | const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode(); |
| 348 | masm.move32(Imm32(1), ReturnReg); |
| 349 | masm.store32(ReturnReg, AbsoluteAddress(addr)); |
| 350 | } |
| 351 | } |
| 352 | #endif |
| 353 | |
| 354 | // Push an exit frame descriptor. |
| 355 | masm.Push(FrameDescriptor(FrameType::IonJS)); |
| 356 | |
| 357 | // Call the wrapper function. The wrapper is in charge to unwind the stack |
| 358 | // when returning from the call. Failures are handled with exceptions based |
| 359 | // on the return value of the C functions. To guard the outcome of the |
| 360 | // returned value, use another LIR instruction. |
| 361 | ensureOsiSpace(); |
| 362 | uint32_t callOffset = masm.callJit(code); |
| 363 | markSafepointAt(callOffset, ins); |
| 364 | |
| 365 | #ifdef DEBUG1 |
| 366 | // Reset the disallowArbitraryCode flag after the call. |
| 367 | { |
| 368 | const void* addr = gen->jitRuntime()->addressOfDisallowArbitraryCode(); |
| 369 | masm.push(ReturnReg); |
| 370 | masm.move32(Imm32(0), ReturnReg); |
| 371 | masm.store32(ReturnReg, AbsoluteAddress(addr)); |
| 372 | masm.pop(ReturnReg); |
| 373 | } |
| 374 | #endif |
| 375 | |
| 376 | // Pop rest of the exit frame and the arguments left on the stack. |
| 377 | int framePop = |
| 378 | sizeof(ExitFrameLayout) - ExitFrameLayout::bytesPoppedAfterCall(); |
| 379 | masm.implicitPop(fun.explicitStackSlots() * sizeof(void*) + framePop); |
| 380 | |
| 381 | // Stack is: |
| 382 | // ... frame ... |
| 383 | } |
| 384 | |
| 385 | template <typename Fn, Fn fn> |
| 386 | void CodeGenerator::callVM(LInstruction* ins) { |
| 387 | VMFunctionId id = VMFunctionToId<Fn, fn>::id; |
| 388 | callVMInternal(id, ins); |
| 389 | } |
| 390 | |
| 391 | // ArgSeq store arguments for OutOfLineCallVM. |
| 392 | // |
| 393 | // OutOfLineCallVM are created with "oolCallVM" function. The third argument of |
| 394 | // this function is an instance of a class which provides a "generate" in charge |
| 395 | // of pushing the argument, with "pushArg", for a VMFunction. |
| 396 | // |
| 397 | // Such list of arguments can be created by using the "ArgList" function which |
| 398 | // creates one instance of "ArgSeq", where the type of the arguments are |
| 399 | // inferred from the type of the arguments. |
| 400 | // |
| 401 | // The list of arguments must be written in the same order as if you were |
| 402 | // calling the function in C++. |
| 403 | // |
| 404 | // Example: |
| 405 | // ArgList(ToRegister(lir->lhs()), ToRegister(lir->rhs())) |
| 406 | |
| 407 | template <typename... ArgTypes> |
| 408 | class ArgSeq { |
| 409 | std::tuple<std::remove_reference_t<ArgTypes>...> args_; |
| 410 | |
| 411 | template <std::size_t... ISeq> |
| 412 | inline void generate(CodeGenerator* codegen, |
| 413 | std::index_sequence<ISeq...>) const { |
| 414 | // Arguments are pushed in reverse order, from last argument to first |
| 415 | // argument. |
| 416 | (codegen->pushArg(std::get<sizeof...(ISeq) - 1 - ISeq>(args_)), ...); |
| 417 | } |
| 418 | |
| 419 | public: |
| 420 | explicit ArgSeq(ArgTypes&&... args) |
| 421 | : args_(std::forward<ArgTypes>(args)...) {} |
| 422 | |
| 423 | inline void generate(CodeGenerator* codegen) const { |
| 424 | generate(codegen, std::index_sequence_for<ArgTypes...>{}); |
| 425 | } |
| 426 | |
| 427 | #ifdef DEBUG1 |
| 428 | static constexpr size_t numArgs = sizeof...(ArgTypes); |
| 429 | #endif |
| 430 | }; |
| 431 | |
| 432 | template <typename... ArgTypes> |
| 433 | inline ArgSeq<ArgTypes...> ArgList(ArgTypes&&... args) { |
| 434 | return ArgSeq<ArgTypes...>(std::forward<ArgTypes>(args)...); |
| 435 | } |
| 436 | |
| 437 | // Store wrappers, to generate the right move of data after the VM call. |
| 438 | |
| 439 | struct StoreNothing { |
| 440 | inline void generate(CodeGenerator* codegen) const {} |
| 441 | inline LiveRegisterSet clobbered() const { |
| 442 | return LiveRegisterSet(); // No register gets clobbered |
| 443 | } |
| 444 | }; |
| 445 | |
| 446 | class StoreRegisterTo { |
| 447 | private: |
| 448 | Register out_; |
| 449 | |
| 450 | public: |
| 451 | explicit StoreRegisterTo(Register out) : out_(out) {} |
| 452 | |
| 453 | inline void generate(CodeGenerator* codegen) const { |
| 454 | // It's okay to use storePointerResultTo here - the VMFunction wrapper |
| 455 | // ensures the upper bytes are zero for bool/int32 return values. |
| 456 | codegen->storePointerResultTo(out_); |
| 457 | } |
| 458 | inline LiveRegisterSet clobbered() const { |
| 459 | LiveRegisterSet set; |
| 460 | set.add(out_); |
| 461 | return set; |
| 462 | } |
| 463 | }; |
| 464 | |
| 465 | class StoreFloatRegisterTo { |
| 466 | private: |
| 467 | FloatRegister out_; |
| 468 | |
| 469 | public: |
| 470 | explicit StoreFloatRegisterTo(FloatRegister out) : out_(out) {} |
| 471 | |
| 472 | inline void generate(CodeGenerator* codegen) const { |
| 473 | codegen->storeFloatResultTo(out_); |
| 474 | } |
| 475 | inline LiveRegisterSet clobbered() const { |
| 476 | LiveRegisterSet set; |
| 477 | set.add(out_); |
| 478 | return set; |
| 479 | } |
| 480 | }; |
| 481 | |
| 482 | template <typename Output> |
| 483 | class StoreValueTo_ { |
| 484 | private: |
| 485 | Output out_; |
| 486 | |
| 487 | public: |
| 488 | explicit StoreValueTo_(const Output& out) : out_(out) {} |
| 489 | |
| 490 | inline void generate(CodeGenerator* codegen) const { |
| 491 | codegen->storeResultValueTo(out_); |
| 492 | } |
| 493 | inline LiveRegisterSet clobbered() const { |
| 494 | LiveRegisterSet set; |
| 495 | set.add(out_); |
| 496 | return set; |
| 497 | } |
| 498 | }; |
| 499 | |
| 500 | template <typename Output> |
| 501 | StoreValueTo_<Output> StoreValueTo(const Output& out) { |
| 502 | return StoreValueTo_<Output>(out); |
| 503 | } |
| 504 | |
| 505 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
| 506 | class OutOfLineCallVM : public OutOfLineCodeBase<CodeGenerator> { |
| 507 | private: |
| 508 | LInstruction* lir_; |
| 509 | ArgSeq args_; |
| 510 | StoreOutputTo out_; |
| 511 | |
| 512 | public: |
| 513 | OutOfLineCallVM(LInstruction* lir, const ArgSeq& args, |
| 514 | const StoreOutputTo& out) |
| 515 | : lir_(lir), args_(args), out_(out) {} |
| 516 | |
| 517 | void accept(CodeGenerator* codegen) override { |
| 518 | codegen->visitOutOfLineCallVM(this); |
| 519 | } |
| 520 | |
| 521 | LInstruction* lir() const { return lir_; } |
| 522 | const ArgSeq& args() const { return args_; } |
| 523 | const StoreOutputTo& out() const { return out_; } |
| 524 | }; |
| 525 | |
| 526 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
| 527 | OutOfLineCode* CodeGenerator::oolCallVM(LInstruction* lir, const ArgSeq& args, |
| 528 | const StoreOutputTo& out) { |
| 529 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 529); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mirRaw()" ")"); do { MOZ_CrashSequence (__null, 529); __attribute__((nomerge)) ::abort(); } while (false ); } } while (false); |
| 530 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 530); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mirRaw()->isInstruction()" ")"); do { MOZ_CrashSequence(__null, 530); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 531 | |
| 532 | #ifdef DEBUG1 |
| 533 | VMFunctionId id = VMFunctionToId<Fn, fn>::id; |
| 534 | const VMFunctionData& fun = GetVMFunction(id); |
| 535 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun.explicitArgs == args.numArgs" ")"); do { MOZ_CrashSequence(__null, 535); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 536 | 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>)" , "./../../../../js/src/jit/CodeGenerator.cpp", 537); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" ")"); do { MOZ_CrashSequence(__null, 537); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 537 | (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>)" , "./../../../../js/src/jit/CodeGenerator.cpp", 537); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun.returnsData() != (std::is_same_v<StoreOutputTo, StoreNothing>)" ")"); do { MOZ_CrashSequence(__null, 537); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 538 | #endif |
| 539 | |
| 540 | OutOfLineCode* ool = new (alloc()) |
| 541 | OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>(lir, args, out); |
| 542 | addOutOfLineCode(ool, lir->mirRaw()->toInstruction()); |
| 543 | return ool; |
| 544 | } |
| 545 | |
| 546 | template <typename Fn, Fn fn, class ArgSeq, class StoreOutputTo> |
| 547 | void CodeGenerator::visitOutOfLineCallVM( |
| 548 | OutOfLineCallVM<Fn, fn, ArgSeq, StoreOutputTo>* ool) { |
| 549 | LInstruction* lir = ool->lir(); |
| 550 | |
| 551 | #ifdef JS_JITSPEW1 |
| 552 | { |
| 553 | AutoJitSpewMessage msg(JitSpew_Codegen, |
| 554 | " # LIR=%s", |
| 555 | lir->opName()); |
| 556 | if (const char* extra = lir->getExtraName()) { |
| 557 | msg.append(":%s", extra); |
| 558 | } |
| 559 | } |
| 560 | #endif |
| 561 | perfSpewer().recordInstruction(masm, lir); |
| 562 | if (!lir->isCall()) { |
| 563 | saveLive(lir); |
| 564 | } |
| 565 | ool->args().generate(this); |
| 566 | callVM<Fn, fn>(lir); |
| 567 | ool->out().generate(this); |
| 568 | if (!lir->isCall()) { |
| 569 | restoreLiveIgnore(lir, ool->out().clobbered()); |
| 570 | } |
| 571 | masm.jump(ool->rejoin()); |
| 572 | } |
| 573 | |
| 574 | class OutOfLineICFallback : public OutOfLineCodeBase<CodeGenerator> { |
| 575 | private: |
| 576 | LInstruction* lir_; |
| 577 | size_t cacheIndex_; |
| 578 | size_t cacheInfoIndex_; |
| 579 | |
| 580 | public: |
| 581 | OutOfLineICFallback(LInstruction* lir, size_t cacheIndex, |
| 582 | size_t cacheInfoIndex) |
| 583 | : lir_(lir), cacheIndex_(cacheIndex), cacheInfoIndex_(cacheInfoIndex) {} |
| 584 | |
| 585 | void bind(MacroAssembler* masm) override { |
| 586 | // The binding of the initial jump is done in |
| 587 | // CodeGenerator::visitOutOfLineICFallback. |
| 588 | } |
| 589 | |
| 590 | size_t cacheIndex() const { return cacheIndex_; } |
| 591 | size_t cacheInfoIndex() const { return cacheInfoIndex_; } |
| 592 | LInstruction* lir() const { return lir_; } |
| 593 | |
| 594 | void accept(CodeGenerator* codegen) override { |
| 595 | codegen->visitOutOfLineICFallback(this); |
| 596 | } |
| 597 | }; |
| 598 | |
| 599 | void CodeGeneratorShared::addIC(LInstruction* lir, size_t cacheIndex) { |
| 600 | if (cacheIndex == SIZE_MAX(18446744073709551615UL)) { |
| 601 | masm.setOOM(); |
| 602 | return; |
| 603 | } |
| 604 | |
| 605 | DataPtr<IonIC> cache(this, cacheIndex); |
| 606 | MInstruction* mir = lir->mirRaw()->toInstruction(); |
| 607 | cache->setScriptedLocation(mir->block()->info().script(), |
| 608 | mir->resumePoint()->pc()); |
| 609 | |
| 610 | Register temp = cache->scratchRegisterForEntryJump(); |
| 611 | icInfo_.back().icOffsetForJump = masm.movWithPatch(ImmWord(-1), temp); |
| 612 | masm.jump(Address(temp, 0)); |
| 613 | |
| 614 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 614); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!icInfo_.empty()" ")"); do { MOZ_CrashSequence (__null, 614); __attribute__((nomerge)) ::abort(); } while (false ); } } while (false); |
| 615 | |
| 616 | OutOfLineICFallback* ool = |
| 617 | new (alloc()) OutOfLineICFallback(lir, cacheIndex, icInfo_.length() - 1); |
| 618 | addOutOfLineCode(ool, mir); |
| 619 | |
| 620 | masm.bind(ool->rejoin()); |
| 621 | cache->setRejoinOffset(CodeOffset(ool->rejoin()->offset())); |
| 622 | } |
| 623 | |
| 624 | void CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool) { |
| 625 | LInstruction* lir = ool->lir(); |
| 626 | size_t cacheIndex = ool->cacheIndex(); |
| 627 | size_t cacheInfoIndex = ool->cacheInfoIndex(); |
| 628 | |
| 629 | DataPtr<IonIC> ic(this, cacheIndex); |
| 630 | |
| 631 | // Register the location of the OOL path in the IC. |
| 632 | ic->setFallbackOffset(CodeOffset(masm.currentOffset())); |
| 633 | |
| 634 | switch (ic->kind()) { |
| 635 | case CacheKind::GetProp: |
| 636 | case CacheKind::GetElem: { |
| 637 | IonGetPropertyIC* getPropIC = ic->asGetPropertyIC(); |
| 638 | |
| 639 | saveLive(lir); |
| 640 | |
| 641 | pushArg(getPropIC->id()); |
| 642 | pushArg(getPropIC->value()); |
| 643 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 644 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 645 | |
| 646 | using Fn = bool (*)(JSContext*, HandleScript, IonGetPropertyIC*, |
| 647 | HandleValue, HandleValue, MutableHandleValue); |
| 648 | callVM<Fn, IonGetPropertyIC::update>(lir); |
| 649 | |
| 650 | StoreValueTo(getPropIC->output()).generate(this); |
| 651 | restoreLiveIgnore(lir, StoreValueTo(getPropIC->output()).clobbered()); |
| 652 | |
| 653 | masm.jump(ool->rejoin()); |
| 654 | return; |
| 655 | } |
| 656 | case CacheKind::GetPropSuper: |
| 657 | case CacheKind::GetElemSuper: { |
| 658 | IonGetPropSuperIC* getPropSuperIC = ic->asGetPropSuperIC(); |
| 659 | |
| 660 | saveLive(lir); |
| 661 | |
| 662 | pushArg(getPropSuperIC->id()); |
| 663 | pushArg(getPropSuperIC->receiver()); |
| 664 | pushArg(getPropSuperIC->object()); |
| 665 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 666 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 667 | |
| 668 | using Fn = |
| 669 | bool (*)(JSContext*, HandleScript, IonGetPropSuperIC*, HandleObject, |
| 670 | HandleValue, HandleValue, MutableHandleValue); |
| 671 | callVM<Fn, IonGetPropSuperIC::update>(lir); |
| 672 | |
| 673 | StoreValueTo(getPropSuperIC->output()).generate(this); |
| 674 | restoreLiveIgnore(lir, |
| 675 | StoreValueTo(getPropSuperIC->output()).clobbered()); |
| 676 | |
| 677 | masm.jump(ool->rejoin()); |
| 678 | return; |
| 679 | } |
| 680 | case CacheKind::SetProp: |
| 681 | case CacheKind::SetElem: { |
| 682 | IonSetPropertyIC* setPropIC = ic->asSetPropertyIC(); |
| 683 | |
| 684 | saveLive(lir); |
| 685 | |
| 686 | pushArg(setPropIC->rhs()); |
| 687 | pushArg(setPropIC->id()); |
| 688 | pushArg(setPropIC->object()); |
| 689 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 690 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 691 | |
| 692 | using Fn = bool (*)(JSContext*, HandleScript, IonSetPropertyIC*, |
| 693 | HandleObject, HandleValue, HandleValue); |
| 694 | callVM<Fn, IonSetPropertyIC::update>(lir); |
| 695 | |
| 696 | restoreLive(lir); |
| 697 | |
| 698 | masm.jump(ool->rejoin()); |
| 699 | return; |
| 700 | } |
| 701 | case CacheKind::GetName: { |
| 702 | IonGetNameIC* getNameIC = ic->asGetNameIC(); |
| 703 | |
| 704 | saveLive(lir); |
| 705 | |
| 706 | pushArg(getNameIC->environment()); |
| 707 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 708 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 709 | |
| 710 | using Fn = bool (*)(JSContext*, HandleScript, IonGetNameIC*, HandleObject, |
| 711 | MutableHandleValue); |
| 712 | callVM<Fn, IonGetNameIC::update>(lir); |
| 713 | |
| 714 | StoreValueTo(getNameIC->output()).generate(this); |
| 715 | restoreLiveIgnore(lir, StoreValueTo(getNameIC->output()).clobbered()); |
| 716 | |
| 717 | masm.jump(ool->rejoin()); |
| 718 | return; |
| 719 | } |
| 720 | case CacheKind::BindName: { |
| 721 | IonBindNameIC* bindNameIC = ic->asBindNameIC(); |
| 722 | |
| 723 | saveLive(lir); |
| 724 | |
| 725 | pushArg(bindNameIC->environment()); |
| 726 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 727 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 728 | |
| 729 | using Fn = |
| 730 | JSObject* (*)(JSContext*, HandleScript, IonBindNameIC*, HandleObject); |
| 731 | callVM<Fn, IonBindNameIC::update>(lir); |
| 732 | |
| 733 | StoreRegisterTo(bindNameIC->output()).generate(this); |
| 734 | restoreLiveIgnore(lir, StoreRegisterTo(bindNameIC->output()).clobbered()); |
| 735 | |
| 736 | masm.jump(ool->rejoin()); |
| 737 | return; |
| 738 | } |
| 739 | case CacheKind::GetIterator: { |
| 740 | IonGetIteratorIC* getIteratorIC = ic->asGetIteratorIC(); |
| 741 | |
| 742 | saveLive(lir); |
| 743 | |
| 744 | pushArg(getIteratorIC->value()); |
| 745 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 746 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 747 | |
| 748 | using Fn = JSObject* (*)(JSContext*, HandleScript, IonGetIteratorIC*, |
| 749 | HandleValue); |
| 750 | callVM<Fn, IonGetIteratorIC::update>(lir); |
| 751 | |
| 752 | StoreRegisterTo(getIteratorIC->output()).generate(this); |
| 753 | restoreLiveIgnore(lir, |
| 754 | StoreRegisterTo(getIteratorIC->output()).clobbered()); |
| 755 | |
| 756 | masm.jump(ool->rejoin()); |
| 757 | return; |
| 758 | } |
| 759 | case CacheKind::OptimizeSpreadCall: { |
| 760 | auto* optimizeSpreadCallIC = ic->asOptimizeSpreadCallIC(); |
| 761 | |
| 762 | saveLive(lir); |
| 763 | |
| 764 | pushArg(optimizeSpreadCallIC->value()); |
| 765 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 766 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 767 | |
| 768 | using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeSpreadCallIC*, |
| 769 | HandleValue, MutableHandleValue); |
| 770 | callVM<Fn, IonOptimizeSpreadCallIC::update>(lir); |
| 771 | |
| 772 | StoreValueTo(optimizeSpreadCallIC->output()).generate(this); |
| 773 | restoreLiveIgnore( |
| 774 | lir, StoreValueTo(optimizeSpreadCallIC->output()).clobbered()); |
| 775 | |
| 776 | masm.jump(ool->rejoin()); |
| 777 | return; |
| 778 | } |
| 779 | case CacheKind::In: { |
| 780 | IonInIC* inIC = ic->asInIC(); |
| 781 | |
| 782 | saveLive(lir); |
| 783 | |
| 784 | pushArg(inIC->object()); |
| 785 | pushArg(inIC->key()); |
| 786 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 787 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 788 | |
| 789 | using Fn = bool (*)(JSContext*, HandleScript, IonInIC*, HandleValue, |
| 790 | HandleObject, bool*); |
| 791 | callVM<Fn, IonInIC::update>(lir); |
| 792 | |
| 793 | StoreRegisterTo(inIC->output()).generate(this); |
| 794 | restoreLiveIgnore(lir, StoreRegisterTo(inIC->output()).clobbered()); |
| 795 | |
| 796 | masm.jump(ool->rejoin()); |
| 797 | return; |
| 798 | } |
| 799 | case CacheKind::HasOwn: { |
| 800 | IonHasOwnIC* hasOwnIC = ic->asHasOwnIC(); |
| 801 | |
| 802 | saveLive(lir); |
| 803 | |
| 804 | pushArg(hasOwnIC->id()); |
| 805 | pushArg(hasOwnIC->value()); |
| 806 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 807 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 808 | |
| 809 | using Fn = bool (*)(JSContext*, HandleScript, IonHasOwnIC*, HandleValue, |
| 810 | HandleValue, int32_t*); |
| 811 | callVM<Fn, IonHasOwnIC::update>(lir); |
| 812 | |
| 813 | StoreRegisterTo(hasOwnIC->output()).generate(this); |
| 814 | restoreLiveIgnore(lir, StoreRegisterTo(hasOwnIC->output()).clobbered()); |
| 815 | |
| 816 | masm.jump(ool->rejoin()); |
| 817 | return; |
| 818 | } |
| 819 | case CacheKind::CheckPrivateField: { |
| 820 | IonCheckPrivateFieldIC* checkPrivateFieldIC = ic->asCheckPrivateFieldIC(); |
| 821 | |
| 822 | saveLive(lir); |
| 823 | |
| 824 | pushArg(checkPrivateFieldIC->id()); |
| 825 | pushArg(checkPrivateFieldIC->value()); |
| 826 | |
| 827 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 828 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 829 | |
| 830 | using Fn = bool (*)(JSContext*, HandleScript, IonCheckPrivateFieldIC*, |
| 831 | HandleValue, HandleValue, bool*); |
| 832 | callVM<Fn, IonCheckPrivateFieldIC::update>(lir); |
| 833 | |
| 834 | StoreRegisterTo(checkPrivateFieldIC->output()).generate(this); |
| 835 | restoreLiveIgnore( |
| 836 | lir, StoreRegisterTo(checkPrivateFieldIC->output()).clobbered()); |
| 837 | |
| 838 | masm.jump(ool->rejoin()); |
| 839 | return; |
| 840 | } |
| 841 | case CacheKind::InstanceOf: { |
| 842 | IonInstanceOfIC* hasInstanceOfIC = ic->asInstanceOfIC(); |
| 843 | |
| 844 | saveLive(lir); |
| 845 | |
| 846 | pushArg(hasInstanceOfIC->rhs()); |
| 847 | pushArg(hasInstanceOfIC->lhs()); |
| 848 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 849 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 850 | |
| 851 | using Fn = bool (*)(JSContext*, HandleScript, IonInstanceOfIC*, |
| 852 | HandleValue lhs, HandleObject rhs, bool* res); |
| 853 | callVM<Fn, IonInstanceOfIC::update>(lir); |
| 854 | |
| 855 | StoreRegisterTo(hasInstanceOfIC->output()).generate(this); |
| 856 | restoreLiveIgnore(lir, |
| 857 | StoreRegisterTo(hasInstanceOfIC->output()).clobbered()); |
| 858 | |
| 859 | masm.jump(ool->rejoin()); |
| 860 | return; |
| 861 | } |
| 862 | case CacheKind::UnaryArith: { |
| 863 | IonUnaryArithIC* unaryArithIC = ic->asUnaryArithIC(); |
| 864 | |
| 865 | saveLive(lir); |
| 866 | |
| 867 | pushArg(unaryArithIC->input()); |
| 868 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 869 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 870 | |
| 871 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
| 872 | IonUnaryArithIC* stub, HandleValue val, |
| 873 | MutableHandleValue res); |
| 874 | callVM<Fn, IonUnaryArithIC::update>(lir); |
| 875 | |
| 876 | StoreValueTo(unaryArithIC->output()).generate(this); |
| 877 | restoreLiveIgnore(lir, StoreValueTo(unaryArithIC->output()).clobbered()); |
| 878 | |
| 879 | masm.jump(ool->rejoin()); |
| 880 | return; |
| 881 | } |
| 882 | case CacheKind::ToPropertyKey: { |
| 883 | IonToPropertyKeyIC* toPropertyKeyIC = ic->asToPropertyKeyIC(); |
| 884 | |
| 885 | saveLive(lir); |
| 886 | |
| 887 | pushArg(toPropertyKeyIC->input()); |
| 888 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 889 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 890 | |
| 891 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
| 892 | IonToPropertyKeyIC* ic, HandleValue val, |
| 893 | MutableHandleValue res); |
| 894 | callVM<Fn, IonToPropertyKeyIC::update>(lir); |
| 895 | |
| 896 | StoreValueTo(toPropertyKeyIC->output()).generate(this); |
| 897 | restoreLiveIgnore(lir, |
| 898 | StoreValueTo(toPropertyKeyIC->output()).clobbered()); |
| 899 | |
| 900 | masm.jump(ool->rejoin()); |
| 901 | return; |
| 902 | } |
| 903 | case CacheKind::BinaryArith: { |
| 904 | IonBinaryArithIC* binaryArithIC = ic->asBinaryArithIC(); |
| 905 | |
| 906 | saveLive(lir); |
| 907 | |
| 908 | pushArg(binaryArithIC->rhs()); |
| 909 | pushArg(binaryArithIC->lhs()); |
| 910 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 911 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 912 | |
| 913 | using Fn = bool (*)(JSContext* cx, HandleScript outerScript, |
| 914 | IonBinaryArithIC* stub, HandleValue lhs, |
| 915 | HandleValue rhs, MutableHandleValue res); |
| 916 | callVM<Fn, IonBinaryArithIC::update>(lir); |
| 917 | |
| 918 | StoreValueTo(binaryArithIC->output()).generate(this); |
| 919 | restoreLiveIgnore(lir, StoreValueTo(binaryArithIC->output()).clobbered()); |
| 920 | |
| 921 | masm.jump(ool->rejoin()); |
| 922 | return; |
| 923 | } |
| 924 | case CacheKind::Compare: { |
| 925 | IonCompareIC* compareIC = ic->asCompareIC(); |
| 926 | |
| 927 | saveLive(lir); |
| 928 | |
| 929 | pushArg(compareIC->rhs()); |
| 930 | pushArg(compareIC->lhs()); |
| 931 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 932 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 933 | |
| 934 | using Fn = |
| 935 | bool (*)(JSContext* cx, HandleScript outerScript, IonCompareIC* stub, |
| 936 | HandleValue lhs, HandleValue rhs, bool* res); |
| 937 | callVM<Fn, IonCompareIC::update>(lir); |
| 938 | |
| 939 | StoreRegisterTo(compareIC->output()).generate(this); |
| 940 | restoreLiveIgnore(lir, StoreRegisterTo(compareIC->output()).clobbered()); |
| 941 | |
| 942 | masm.jump(ool->rejoin()); |
| 943 | return; |
| 944 | } |
| 945 | case CacheKind::CloseIter: { |
| 946 | IonCloseIterIC* closeIterIC = ic->asCloseIterIC(); |
| 947 | |
| 948 | saveLive(lir); |
| 949 | |
| 950 | pushArg(closeIterIC->iter()); |
| 951 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 952 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 953 | |
| 954 | using Fn = |
| 955 | bool (*)(JSContext*, HandleScript, IonCloseIterIC*, HandleObject); |
| 956 | callVM<Fn, IonCloseIterIC::update>(lir); |
| 957 | |
| 958 | restoreLive(lir); |
| 959 | |
| 960 | masm.jump(ool->rejoin()); |
| 961 | return; |
| 962 | } |
| 963 | case CacheKind::OptimizeGetIterator: { |
| 964 | auto* optimizeGetIteratorIC = ic->asOptimizeGetIteratorIC(); |
| 965 | |
| 966 | saveLive(lir); |
| 967 | |
| 968 | pushArg(optimizeGetIteratorIC->value()); |
| 969 | icInfo_[cacheInfoIndex].icOffsetForPush = pushArgWithPatch(ImmWord(-1)); |
| 970 | pushArg(ImmGCPtr(gen->outerInfo().script())); |
| 971 | |
| 972 | using Fn = bool (*)(JSContext*, HandleScript, IonOptimizeGetIteratorIC*, |
| 973 | HandleValue, bool* res); |
| 974 | callVM<Fn, IonOptimizeGetIteratorIC::update>(lir); |
| 975 | |
| 976 | StoreRegisterTo(optimizeGetIteratorIC->output()).generate(this); |
| 977 | restoreLiveIgnore( |
| 978 | lir, StoreRegisterTo(optimizeGetIteratorIC->output()).clobbered()); |
| 979 | |
| 980 | masm.jump(ool->rejoin()); |
| 981 | return; |
| 982 | } |
| 983 | case CacheKind::Call: |
| 984 | case CacheKind::TypeOf: |
| 985 | case CacheKind::TypeOfEq: |
| 986 | case CacheKind::ToBool: |
| 987 | case CacheKind::LazyConstant: |
| 988 | case CacheKind::NewArray: |
| 989 | case CacheKind::NewObject: |
| 990 | case CacheKind::Lambda: |
| 991 | case CacheKind::GetImport: |
| 992 | MOZ_CRASH("Unsupported IC")do { do { } while (false); MOZ_ReportCrash("" "Unsupported IC" , "./../../../../js/src/jit/CodeGenerator.cpp", 992); AnnotateMozCrashReason ("MOZ_CRASH(" "Unsupported IC" ")"); do { MOZ_CrashSequence(__null , 992); __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
| 993 | } |
| 994 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp" , 994); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence (__null, 994); __attribute__((nomerge)) ::abort(); } while (false ); } while (false); |
| 995 | } |
| 996 | |
| 997 | StringObject* MNewStringObject::templateObj() const { |
| 998 | return &templateObj_->as<StringObject>(); |
| 999 | } |
| 1000 | |
| 1001 | CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph, |
| 1002 | MacroAssembler* masm, |
| 1003 | const wasm::CodeMetadata* wasmCodeMeta) |
| 1004 | : CodeGeneratorSpecific(gen, graph, masm, wasmCodeMeta), |
| 1005 | ionScriptLabels_(gen->alloc()), |
| 1006 | nurseryObjectLabels_(gen->alloc()), |
| 1007 | nurseryValueLabels_(gen->alloc()), |
| 1008 | scriptCounts_(nullptr) {} |
| 1009 | |
| 1010 | CodeGenerator::~CodeGenerator() { js_delete(scriptCounts_); } |
| 1011 | |
| 1012 | void CodeGenerator::visitValueToNumberInt32(LValueToNumberInt32* lir) { |
| 1013 | ValueOperand operand = ToValue(lir->input()); |
| 1014 | Register output = ToRegister(lir->output()); |
| 1015 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 1016 | |
| 1017 | Label fails; |
| 1018 | masm.convertValueToInt32(operand, temp, output, &fails, |
| 1019 | lir->mir()->needsNegativeZeroCheck(), |
| 1020 | lir->mir()->conversion()); |
| 1021 | |
| 1022 | bailoutFrom(&fails, lir->snapshot()); |
| 1023 | } |
| 1024 | |
| 1025 | void CodeGenerator::visitValueTruncateToInt32(LValueTruncateToInt32* lir) { |
| 1026 | ValueOperand operand = ToValue(lir->input()); |
| 1027 | Register output = ToRegister(lir->output()); |
| 1028 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 1029 | Register stringReg = ToRegister(lir->temp1()); |
| 1030 | |
| 1031 | auto* oolDouble = oolTruncateDouble(temp, output, lir->mir()); |
| 1032 | |
| 1033 | using Fn = bool (*)(JSContext*, JSString*, double*); |
| 1034 | auto* oolString = oolCallVM<Fn, StringToNumber>(lir, ArgList(stringReg), |
| 1035 | StoreFloatRegisterTo(temp)); |
| 1036 | Label* stringEntry = oolString->entry(); |
| 1037 | Label* stringRejoin = oolString->rejoin(); |
| 1038 | |
| 1039 | Label fails; |
| 1040 | masm.truncateValueToInt32(operand, stringEntry, stringRejoin, |
| 1041 | oolDouble->entry(), stringReg, temp, output, |
| 1042 | &fails); |
| 1043 | masm.bind(oolDouble->rejoin()); |
| 1044 | |
| 1045 | bailoutFrom(&fails, lir->snapshot()); |
| 1046 | } |
| 1047 | |
| 1048 | void CodeGenerator::visitValueToDouble(LValueToDouble* lir) { |
| 1049 | ValueOperand operand = ToValue(lir->input()); |
| 1050 | FloatRegister output = ToFloatRegister(lir->output()); |
| 1051 | |
| 1052 | Label fail; |
| 1053 | masm.convertValueToDouble(operand, output, &fail); |
| 1054 | bailoutFrom(&fail, lir->snapshot()); |
| 1055 | } |
| 1056 | |
| 1057 | void CodeGenerator::visitValueToFloat32(LValueToFloat32* lir) { |
| 1058 | ValueOperand operand = ToValue(lir->input()); |
| 1059 | FloatRegister output = ToFloatRegister(lir->output()); |
| 1060 | |
| 1061 | Label fail; |
| 1062 | masm.convertValueToFloat32(operand, output, &fail); |
| 1063 | bailoutFrom(&fail, lir->snapshot()); |
| 1064 | } |
| 1065 | |
| 1066 | void CodeGenerator::visitValueToFloat16(LValueToFloat16* lir) { |
| 1067 | ValueOperand operand = ToValue(lir->input()); |
| 1068 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 1069 | FloatRegister output = ToFloatRegister(lir->output()); |
| 1070 | |
| 1071 | LiveRegisterSet volatileRegs; |
| 1072 | if (!MacroAssembler::SupportsFloat64To16()) { |
| 1073 | volatileRegs = liveVolatileRegs(lir); |
| 1074 | } |
| 1075 | |
| 1076 | Label fail; |
| 1077 | masm.convertValueToFloat16(operand, output, temp, volatileRegs, &fail); |
| 1078 | bailoutFrom(&fail, lir->snapshot()); |
| 1079 | } |
| 1080 | |
| 1081 | void CodeGenerator::visitValueToBigInt(LValueToBigInt* lir) { |
| 1082 | ValueOperand operand = ToValue(lir->input()); |
| 1083 | Register output = ToRegister(lir->output()); |
| 1084 | |
| 1085 | using Fn = BigInt* (*)(JSContext*, HandleValue); |
| 1086 | auto* ool = |
| 1087 | oolCallVM<Fn, ToBigInt>(lir, ArgList(operand), StoreRegisterTo(output)); |
| 1088 | |
| 1089 | Register tag = masm.extractTag(operand, output); |
| 1090 | |
| 1091 | Label notBigInt, done; |
| 1092 | masm.branchTestBigInt(Assembler::NotEqual, tag, ¬BigInt); |
| 1093 | masm.unboxBigInt(operand, output); |
| 1094 | masm.jump(&done); |
| 1095 | masm.bind(¬BigInt); |
| 1096 | |
| 1097 | masm.branchTestBoolean(Assembler::Equal, tag, ool->entry()); |
| 1098 | masm.branchTestString(Assembler::Equal, tag, ool->entry()); |
| 1099 | |
| 1100 | // ToBigInt(object) can have side-effects; all other types throw a TypeError. |
| 1101 | bailout(lir->snapshot()); |
| 1102 | |
| 1103 | masm.bind(ool->rejoin()); |
| 1104 | masm.bind(&done); |
| 1105 | } |
| 1106 | |
| 1107 | void CodeGenerator::visitInt32ToDouble(LInt32ToDouble* lir) { |
| 1108 | masm.convertInt32ToDouble(ToRegister(lir->input()), |
| 1109 | ToFloatRegister(lir->output())); |
| 1110 | } |
| 1111 | |
| 1112 | void CodeGenerator::visitFloat32ToDouble(LFloat32ToDouble* lir) { |
| 1113 | masm.convertFloat32ToDouble(ToFloatRegister(lir->input()), |
| 1114 | ToFloatRegister(lir->output())); |
| 1115 | } |
| 1116 | |
| 1117 | void CodeGenerator::visitDoubleToFloat32(LDoubleToFloat32* lir) { |
| 1118 | masm.convertDoubleToFloat32(ToFloatRegister(lir->input()), |
| 1119 | ToFloatRegister(lir->output())); |
| 1120 | } |
| 1121 | |
| 1122 | void CodeGenerator::visitInt32ToFloat32(LInt32ToFloat32* lir) { |
| 1123 | masm.convertInt32ToFloat32(ToRegister(lir->input()), |
| 1124 | ToFloatRegister(lir->output())); |
| 1125 | } |
| 1126 | |
| 1127 | void CodeGenerator::visitDoubleToFloat16(LDoubleToFloat16* lir) { |
| 1128 | LiveRegisterSet volatileRegs; |
| 1129 | if (!MacroAssembler::SupportsFloat64To16()) { |
| 1130 | volatileRegs = liveVolatileRegs(lir); |
| 1131 | } |
| 1132 | masm.convertDoubleToFloat16( |
| 1133 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
| 1134 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
| 1135 | } |
| 1136 | |
| 1137 | void CodeGenerator::visitDoubleToFloat32ToFloat16( |
| 1138 | LDoubleToFloat32ToFloat16* lir) { |
| 1139 | masm.convertDoubleToFloat16( |
| 1140 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
| 1141 | ToRegister(lir->temp0()), ToRegister(lir->temp1())); |
| 1142 | } |
| 1143 | |
| 1144 | void CodeGenerator::visitFloat32ToFloat16(LFloat32ToFloat16* lir) { |
| 1145 | LiveRegisterSet volatileRegs; |
| 1146 | if (!MacroAssembler::SupportsFloat32To16()) { |
| 1147 | volatileRegs = liveVolatileRegs(lir); |
| 1148 | } |
| 1149 | masm.convertFloat32ToFloat16( |
| 1150 | ToFloatRegister(lir->input()), ToFloatRegister(lir->output()), |
| 1151 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
| 1152 | } |
| 1153 | |
| 1154 | void CodeGenerator::visitInt32ToFloat16(LInt32ToFloat16* lir) { |
| 1155 | LiveRegisterSet volatileRegs; |
| 1156 | if (!MacroAssembler::SupportsFloat32To16()) { |
| 1157 | volatileRegs = liveVolatileRegs(lir); |
| 1158 | } |
| 1159 | masm.convertInt32ToFloat16( |
| 1160 | ToRegister(lir->input()), ToFloatRegister(lir->output()), |
| 1161 | ToTempRegisterOrInvalid(lir->temp0()), volatileRegs); |
| 1162 | } |
| 1163 | |
| 1164 | void CodeGenerator::visitDoubleToInt32(LDoubleToInt32* lir) { |
| 1165 | Label fail; |
| 1166 | FloatRegister input = ToFloatRegister(lir->input()); |
| 1167 | Register output = ToRegister(lir->output()); |
| 1168 | masm.convertDoubleToInt32(input, output, &fail, |
| 1169 | lir->mir()->needsNegativeZeroCheck()); |
| 1170 | bailoutFrom(&fail, lir->snapshot()); |
| 1171 | } |
| 1172 | |
| 1173 | void CodeGenerator::visitFloat32ToInt32(LFloat32ToInt32* lir) { |
| 1174 | Label fail; |
| 1175 | FloatRegister input = ToFloatRegister(lir->input()); |
| 1176 | Register output = ToRegister(lir->output()); |
| 1177 | masm.convertFloat32ToInt32(input, output, &fail, |
| 1178 | lir->mir()->needsNegativeZeroCheck()); |
| 1179 | bailoutFrom(&fail, lir->snapshot()); |
| 1180 | } |
| 1181 | |
| 1182 | void CodeGenerator::visitInt32ToIntPtr(LInt32ToIntPtr* lir) { |
| 1183 | #ifdef JS_64BIT1 |
| 1184 | Register output = ToRegister(lir->output()); |
| 1185 | const LAllocation* input = lir->input(); |
| 1186 | if (input->isGeneralReg()) { |
| 1187 | masm.move32SignExtendToPtr(ToRegister(input), output); |
| 1188 | } else { |
| 1189 | masm.load32SignExtendToPtr(ToAddress(input), output); |
| 1190 | } |
| 1191 | #else |
| 1192 | MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms" , "./../../../../js/src/jit/CodeGenerator.cpp", 1192); AnnotateMozCrashReason ("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { MOZ_CrashSequence (__null, 1192); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 1193 | #endif |
| 1194 | } |
| 1195 | |
| 1196 | void CodeGenerator::visitNonNegativeIntPtrToInt32( |
| 1197 | LNonNegativeIntPtrToInt32* lir) { |
| 1198 | #ifdef JS_64BIT1 |
| 1199 | Register output = ToRegister(lir->output()); |
| 1200 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1200); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")" ); do { MOZ_CrashSequence(__null, 1200); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1201 | |
| 1202 | Label bail; |
| 1203 | masm.guardNonNegativeIntPtrToInt32(output, &bail); |
| 1204 | bailoutFrom(&bail, lir->snapshot()); |
| 1205 | #else |
| 1206 | MOZ_CRASH("Not used on 32-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 32-bit platforms" , "./../../../../js/src/jit/CodeGenerator.cpp", 1206); AnnotateMozCrashReason ("MOZ_CRASH(" "Not used on 32-bit platforms" ")"); do { MOZ_CrashSequence (__null, 1206); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 1207 | #endif |
| 1208 | } |
| 1209 | |
| 1210 | void CodeGenerator::visitIntPtrToDouble(LIntPtrToDouble* lir) { |
| 1211 | Register input = ToRegister(lir->input()); |
| 1212 | FloatRegister output = ToFloatRegister(lir->output()); |
| 1213 | masm.convertIntPtrToDouble(input, output); |
| 1214 | } |
| 1215 | |
| 1216 | void CodeGenerator::visitAdjustDataViewLength(LAdjustDataViewLength* lir) { |
| 1217 | Register output = ToRegister(lir->output()); |
| 1218 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1218); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->input()) == output" ")" ); do { MOZ_CrashSequence(__null, 1218); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1219 | |
| 1220 | uint32_t byteSize = lir->mir()->byteSize(); |
| 1221 | |
| 1222 | #ifdef DEBUG1 |
| 1223 | Label ok; |
| 1224 | masm.branchTestPtr(Assembler::NotSigned, output, output, &ok); |
| 1225 | masm.assumeUnreachable("Unexpected negative value in LAdjustDataViewLength"); |
| 1226 | masm.bind(&ok); |
| 1227 | #endif |
| 1228 | |
| 1229 | Label bail; |
| 1230 | masm.branchSubPtr(Assembler::Signed, Imm32(byteSize - 1), output, &bail); |
| 1231 | bailoutFrom(&bail, lir->snapshot()); |
| 1232 | } |
| 1233 | |
| 1234 | void CodeGenerator::emitOOLTestObject(Register objreg, |
| 1235 | Label* ifEmulatesUndefined, |
| 1236 | Label* ifDoesntEmulateUndefined, |
| 1237 | Register scratch) { |
| 1238 | saveVolatile(scratch); |
| 1239 | #if defined(DEBUG1) || defined(FUZZING) |
| 1240 | masm.loadRuntimeFuse( |
| 1241 | RuntimeFuses::FuseIndex::HasSeenObjectEmulateUndefinedFuse, scratch); |
| 1242 | using Fn = bool (*)(JSObject* obj, size_t fuseValue); |
| 1243 | masm.setupAlignedABICall(); |
| 1244 | masm.passABIArg(objreg); |
| 1245 | masm.passABIArg(scratch); |
| 1246 | masm.callWithABI<Fn, js::EmulatesUndefinedCheckFuse>(); |
| 1247 | #else |
| 1248 | using Fn = bool (*)(JSObject* obj); |
| 1249 | masm.setupAlignedABICall(); |
| 1250 | masm.passABIArg(objreg); |
| 1251 | masm.callWithABI<Fn, js::EmulatesUndefined>(); |
| 1252 | #endif |
| 1253 | masm.storeCallPointerResult(scratch); |
| 1254 | restoreVolatile(scratch); |
| 1255 | |
| 1256 | masm.branchIfTrueBool(scratch, ifEmulatesUndefined); |
| 1257 | masm.jump(ifDoesntEmulateUndefined); |
| 1258 | } |
| 1259 | |
| 1260 | // Base out-of-line code generator for all tests of the truthiness of an |
| 1261 | // object, where the object might not be truthy. (Recall that per spec all |
| 1262 | // objects are truthy, but we implement the JSCLASS_EMULATES_UNDEFINED class |
| 1263 | // flag to permit objects to look like |undefined| in certain contexts, |
| 1264 | // including in object truthiness testing.) We check truthiness inline except |
| 1265 | // when we're testing it on a proxy, in which case out-of-line code will call |
| 1266 | // EmulatesUndefined for a conclusive answer. |
| 1267 | class OutOfLineTestObject : public OutOfLineCodeBase<CodeGenerator> { |
| 1268 | Register objreg_; |
| 1269 | Register scratch_; |
| 1270 | |
| 1271 | Label* ifEmulatesUndefined_; |
| 1272 | Label* ifDoesntEmulateUndefined_; |
| 1273 | |
| 1274 | #ifdef DEBUG1 |
| 1275 | bool initialized() { return ifEmulatesUndefined_ != nullptr; } |
| 1276 | #endif |
| 1277 | |
| 1278 | public: |
| 1279 | OutOfLineTestObject() |
| 1280 | : ifEmulatesUndefined_(nullptr), ifDoesntEmulateUndefined_(nullptr) {} |
| 1281 | |
| 1282 | void accept(CodeGenerator* codegen) final { |
| 1283 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp", 1283); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "initialized()" ")"); do { MOZ_CrashSequence (__null, 1283); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 1284 | codegen->emitOOLTestObject(objreg_, ifEmulatesUndefined_, |
| 1285 | ifDoesntEmulateUndefined_, scratch_); |
| 1286 | } |
| 1287 | |
| 1288 | // Specify the register where the object to be tested is found, labels to |
| 1289 | // jump to if the object is truthy or falsy, and a scratch register for |
| 1290 | // use in the out-of-line path. |
| 1291 | void setInputAndTargets(Register objreg, Label* ifEmulatesUndefined, |
| 1292 | Label* ifDoesntEmulateUndefined, Register scratch) { |
| 1293 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 1293); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!initialized()" ")"); do { MOZ_CrashSequence (__null, 1293); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 1294 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1294); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ifEmulatesUndefined" ")"); do { MOZ_CrashSequence (__null, 1294); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 1295 | objreg_ = objreg; |
| 1296 | scratch_ = scratch; |
| 1297 | ifEmulatesUndefined_ = ifEmulatesUndefined; |
| 1298 | ifDoesntEmulateUndefined_ = ifDoesntEmulateUndefined; |
| 1299 | } |
| 1300 | }; |
| 1301 | |
| 1302 | // A subclass of OutOfLineTestObject containing two extra labels, for use when |
| 1303 | // the ifTruthy/ifFalsy labels are needed in inline code as well as out-of-line |
| 1304 | // code. The user should bind these labels in inline code, and specify them as |
| 1305 | // targets via setInputAndTargets, as appropriate. |
| 1306 | class OutOfLineTestObjectWithLabels : public OutOfLineTestObject { |
| 1307 | Label label1_; |
| 1308 | Label label2_; |
| 1309 | |
| 1310 | public: |
| 1311 | OutOfLineTestObjectWithLabels() = default; |
| 1312 | |
| 1313 | Label* label1() { return &label1_; } |
| 1314 | Label* label2() { return &label2_; } |
| 1315 | }; |
| 1316 | |
| 1317 | void CodeGenerator::testObjectEmulatesUndefinedKernel( |
| 1318 | Register objreg, Label* ifEmulatesUndefined, |
| 1319 | Label* ifDoesntEmulateUndefined, Register scratch, |
| 1320 | OutOfLineTestObject* ool) { |
| 1321 | ool->setInputAndTargets(objreg, ifEmulatesUndefined, ifDoesntEmulateUndefined, |
| 1322 | scratch); |
| 1323 | |
| 1324 | // Perform a fast-path check of the object's class flags if the object's |
| 1325 | // not a proxy. Let out-of-line code handle the slow cases that require |
| 1326 | // saving registers, making a function call, and restoring registers. |
| 1327 | masm.branchIfObjectEmulatesUndefined(objreg, scratch, ool->entry(), |
| 1328 | ifEmulatesUndefined); |
| 1329 | } |
| 1330 | |
| 1331 | void CodeGenerator::branchTestObjectEmulatesUndefined( |
| 1332 | Register objreg, Label* ifEmulatesUndefined, |
| 1333 | Label* ifDoesntEmulateUndefined, Register scratch, |
| 1334 | OutOfLineTestObject* ool) { |
| 1335 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 1336); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")"); do { MOZ_CrashSequence(__null, 1336); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1336 | "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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 1336); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!ifDoesntEmulateUndefined->bound()" ") (" "ifDoesntEmulateUndefined will be bound to the fallthrough path" ")"); do { MOZ_CrashSequence(__null, 1336); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1337 | |
| 1338 | testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, |
| 1339 | ifDoesntEmulateUndefined, scratch, ool); |
| 1340 | masm.bind(ifDoesntEmulateUndefined); |
| 1341 | } |
| 1342 | |
| 1343 | void CodeGenerator::testObjectEmulatesUndefined(Register objreg, |
| 1344 | Label* ifEmulatesUndefined, |
| 1345 | Label* ifDoesntEmulateUndefined, |
| 1346 | Register scratch, |
| 1347 | OutOfLineTestObject* ool) { |
| 1348 | testObjectEmulatesUndefinedKernel(objreg, ifEmulatesUndefined, |
| 1349 | ifDoesntEmulateUndefined, scratch, ool); |
| 1350 | masm.jump(ifDoesntEmulateUndefined); |
| 1351 | } |
| 1352 | |
| 1353 | void CodeGenerator::testValueTruthyForType( |
| 1354 | JSValueType type, ScratchTagScope& tag, const ValueOperand& value, |
| 1355 | Register tempToUnbox, Register temp, FloatRegister floatTemp, |
| 1356 | Label* ifTruthy, Label* ifFalsy, OutOfLineTestObject* ool, |
| 1357 | bool skipTypeTest) { |
| 1358 | #ifdef DEBUG1 |
| 1359 | if (skipTypeTest) { |
| 1360 | Label expected; |
| 1361 | masm.branchTestType(Assembler::Equal, tag, type, &expected); |
| 1362 | masm.assumeUnreachable("Unexpected Value type in testValueTruthyForType"); |
| 1363 | masm.bind(&expected); |
| 1364 | } |
| 1365 | #endif |
| 1366 | |
| 1367 | // Handle irregular types first. |
| 1368 | switch (type) { |
| 1369 | case JSVAL_TYPE_UNDEFINED: |
| 1370 | case JSVAL_TYPE_NULL: |
| 1371 | // Undefined and null are falsy. |
| 1372 | if (!skipTypeTest) { |
| 1373 | masm.branchTestType(Assembler::Equal, tag, type, ifFalsy); |
| 1374 | } else { |
| 1375 | masm.jump(ifFalsy); |
| 1376 | } |
| 1377 | return; |
| 1378 | case JSVAL_TYPE_SYMBOL: |
| 1379 | // Symbols are truthy. |
| 1380 | if (!skipTypeTest) { |
| 1381 | masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy); |
| 1382 | } else { |
| 1383 | masm.jump(ifTruthy); |
| 1384 | } |
| 1385 | return; |
| 1386 | case JSVAL_TYPE_OBJECT: { |
| 1387 | if (!ool) { |
| 1388 | // If we have no ool path, then the hasSeenObjectEmulateUndefined fuse |
| 1389 | // is intact, and all objects are truthy. |
| 1390 | if (!skipTypeTest) { |
| 1391 | masm.branchTestObject(Assembler::Equal, tag, ifTruthy); |
| 1392 | } else { |
| 1393 | masm.jump(ifTruthy); |
| 1394 | } |
| 1395 | return; |
| 1396 | } |
| 1397 | Label notObject; |
| 1398 | if (!skipTypeTest) { |
| 1399 | masm.branchTestObject(Assembler::NotEqual, tag, ¬Object); |
| 1400 | } |
| 1401 | ScratchTagScopeRelease _(&tag); |
| 1402 | Register objreg = masm.extractObject(value, tempToUnbox); |
| 1403 | testObjectEmulatesUndefined(objreg, ifFalsy, ifTruthy, temp, ool); |
| 1404 | masm.bind(¬Object); |
| 1405 | return; |
| 1406 | } |
| 1407 | default: |
| 1408 | break; |
| 1409 | } |
| 1410 | |
| 1411 | // Check the type of the value (unless this is the last possible type). |
| 1412 | Label differentType; |
| 1413 | if (!skipTypeTest) { |
| 1414 | masm.branchTestType(Assembler::NotEqual, tag, type, &differentType); |
| 1415 | } |
| 1416 | |
| 1417 | // Branch if the value is falsy. |
| 1418 | ScratchTagScopeRelease _(&tag); |
| 1419 | switch (type) { |
| 1420 | case JSVAL_TYPE_BOOLEAN: { |
| 1421 | masm.branchTestBooleanTruthy(false, value, ifFalsy); |
| 1422 | break; |
| 1423 | } |
| 1424 | case JSVAL_TYPE_INT32: { |
| 1425 | masm.branchTestInt32Truthy(false, value, ifFalsy); |
| 1426 | break; |
| 1427 | } |
| 1428 | case JSVAL_TYPE_STRING: { |
| 1429 | masm.branchTestStringTruthy(false, value, ifFalsy); |
| 1430 | break; |
| 1431 | } |
| 1432 | case JSVAL_TYPE_BIGINT: { |
| 1433 | masm.branchTestBigIntTruthy(false, value, ifFalsy); |
| 1434 | break; |
| 1435 | } |
| 1436 | case JSVAL_TYPE_DOUBLE: { |
| 1437 | masm.unboxDouble(value, floatTemp); |
| 1438 | masm.branchTestDoubleTruthy(false, floatTemp, ifFalsy); |
| 1439 | break; |
| 1440 | } |
| 1441 | default: |
| 1442 | MOZ_CRASH("Unexpected value type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected value type" , "./../../../../js/src/jit/CodeGenerator.cpp", 1442); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected value type" ")"); do { MOZ_CrashSequence (__null, 1442); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 1443 | } |
| 1444 | |
| 1445 | // If we reach this point, the value is truthy. We fall through for |
| 1446 | // truthy on the last test; otherwise, branch. |
| 1447 | if (!skipTypeTest) { |
| 1448 | masm.jump(ifTruthy); |
| 1449 | } |
| 1450 | |
| 1451 | masm.bind(&differentType); |
| 1452 | } |
| 1453 | |
| 1454 | void CodeGenerator::testValueTruthy(const ValueOperand& value, |
| 1455 | Register tempToUnbox, Register temp, |
| 1456 | FloatRegister floatTemp, |
| 1457 | const TypeDataList& observedTypes, |
| 1458 | Label* ifTruthy, Label* ifFalsy, |
| 1459 | OutOfLineTestObject* ool) { |
| 1460 | ScratchTagScope tag(masm, value); |
| 1461 | masm.splitTagForTest(value, tag); |
| 1462 | |
| 1463 | const std::initializer_list<JSValueType> defaultOrder = { |
| 1464 | JSVAL_TYPE_UNDEFINED, JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, |
| 1465 | JSVAL_TYPE_INT32, JSVAL_TYPE_OBJECT, JSVAL_TYPE_STRING, |
| 1466 | JSVAL_TYPE_DOUBLE, JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT}; |
| 1467 | |
| 1468 | mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder); |
| 1469 | |
| 1470 | // Generate tests for previously observed types first. |
| 1471 | // The TypeDataList is sorted by descending frequency. |
| 1472 | for (auto& observed : observedTypes) { |
| 1473 | JSValueType type = observed.type(); |
| 1474 | remaining -= type; |
| 1475 | |
| 1476 | testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp, |
| 1477 | ifTruthy, ifFalsy, ool, /*skipTypeTest*/ false); |
| 1478 | } |
| 1479 | |
| 1480 | // Generate tests for remaining types. |
| 1481 | for (auto type : defaultOrder) { |
| 1482 | if (!remaining.contains(type)) { |
| 1483 | continue; |
| 1484 | } |
| 1485 | remaining -= type; |
| 1486 | |
| 1487 | // We don't need a type test for the last possible type. |
| 1488 | bool skipTypeTest = remaining.isEmpty(); |
| 1489 | testValueTruthyForType(type, tag, value, tempToUnbox, temp, floatTemp, |
| 1490 | ifTruthy, ifFalsy, ool, skipTypeTest); |
| 1491 | } |
| 1492 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 1492); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { MOZ_CrashSequence (__null, 1492); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 1493 | |
| 1494 | // We fall through if the final test is truthy. |
| 1495 | } |
| 1496 | |
| 1497 | void CodeGenerator::visitTestIAndBranch(LTestIAndBranch* test) { |
| 1498 | Register input = ToRegister(test->input()); |
| 1499 | MBasicBlock* ifTrue = test->ifTrue(); |
| 1500 | MBasicBlock* ifFalse = test->ifFalse(); |
| 1501 | |
| 1502 | if (isNextBlock(ifFalse->lir())) { |
| 1503 | masm.branchTest32(Assembler::NonZero, input, input, |
| 1504 | getJumpLabelForBranch(ifTrue)); |
| 1505 | } else { |
| 1506 | masm.branchTest32(Assembler::Zero, input, input, |
| 1507 | getJumpLabelForBranch(ifFalse)); |
| 1508 | jumpToBlock(ifTrue); |
| 1509 | } |
| 1510 | } |
| 1511 | |
| 1512 | void CodeGenerator::visitTestIPtrAndBranch(LTestIPtrAndBranch* test) { |
| 1513 | Register input = ToRegister(test->input()); |
| 1514 | MBasicBlock* ifTrue = test->ifTrue(); |
| 1515 | MBasicBlock* ifFalse = test->ifFalse(); |
| 1516 | |
| 1517 | if (isNextBlock(ifFalse->lir())) { |
| 1518 | masm.branchTestPtr(Assembler::NonZero, input, input, |
| 1519 | getJumpLabelForBranch(ifTrue)); |
| 1520 | } else { |
| 1521 | masm.branchTestPtr(Assembler::Zero, input, input, |
| 1522 | getJumpLabelForBranch(ifFalse)); |
| 1523 | jumpToBlock(ifTrue); |
| 1524 | } |
| 1525 | } |
| 1526 | |
| 1527 | void CodeGenerator::visitTestI64AndBranch(LTestI64AndBranch* test) { |
| 1528 | Register64 input = ToRegister64(test->input()); |
| 1529 | MBasicBlock* ifTrue = test->ifTrue(); |
| 1530 | MBasicBlock* ifFalse = test->ifFalse(); |
| 1531 | |
| 1532 | if (isNextBlock(ifFalse->lir())) { |
| 1533 | masm.branchTest64(Assembler::NonZero, input, input, |
| 1534 | getJumpLabelForBranch(ifTrue)); |
| 1535 | } else if (isNextBlock(ifTrue->lir())) { |
| 1536 | masm.branchTest64(Assembler::Zero, input, input, |
| 1537 | getJumpLabelForBranch(ifFalse)); |
| 1538 | } else { |
| 1539 | masm.branchTest64(Assembler::NonZero, input, input, |
| 1540 | getJumpLabelForBranch(ifTrue), |
| 1541 | getJumpLabelForBranch(ifFalse)); |
| 1542 | } |
| 1543 | } |
| 1544 | |
| 1545 | void CodeGenerator::visitTestBIAndBranch(LTestBIAndBranch* lir) { |
| 1546 | Register input = ToRegister(lir->input()); |
| 1547 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 1548 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 1549 | |
| 1550 | if (isNextBlock(ifFalse->lir())) { |
| 1551 | masm.branchIfBigIntIsNonZero(input, getJumpLabelForBranch(ifTrue)); |
| 1552 | } else { |
| 1553 | masm.branchIfBigIntIsZero(input, getJumpLabelForBranch(ifFalse)); |
| 1554 | jumpToBlock(ifTrue); |
| 1555 | } |
| 1556 | } |
| 1557 | |
| 1558 | static Assembler::Condition ReverseCondition(Assembler::Condition condition) { |
| 1559 | switch (condition) { |
| 1560 | case Assembler::Equal: |
| 1561 | case Assembler::NotEqual: |
| 1562 | return condition; |
| 1563 | case Assembler::Above: |
| 1564 | return Assembler::Below; |
| 1565 | case Assembler::AboveOrEqual: |
| 1566 | return Assembler::BelowOrEqual; |
| 1567 | case Assembler::Below: |
| 1568 | return Assembler::Above; |
| 1569 | case Assembler::BelowOrEqual: |
| 1570 | return Assembler::AboveOrEqual; |
| 1571 | case Assembler::GreaterThan: |
| 1572 | return Assembler::LessThan; |
| 1573 | case Assembler::GreaterThanOrEqual: |
| 1574 | return Assembler::LessThanOrEqual; |
| 1575 | case Assembler::LessThan: |
| 1576 | return Assembler::GreaterThan; |
| 1577 | case Assembler::LessThanOrEqual: |
| 1578 | return Assembler::GreaterThanOrEqual; |
| 1579 | default: |
| 1580 | break; |
| 1581 | } |
| 1582 | MOZ_CRASH("unhandled condition")do { do { } while (false); MOZ_ReportCrash("" "unhandled condition" , "./../../../../js/src/jit/CodeGenerator.cpp", 1582); AnnotateMozCrashReason ("MOZ_CRASH(" "unhandled condition" ")"); do { MOZ_CrashSequence (__null, 1582); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 1583 | } |
| 1584 | |
| 1585 | void CodeGenerator::visitCompare(LCompare* comp) { |
| 1586 | MCompare::CompareType compareType = comp->mir()->compareType(); |
| 1587 | Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop()); |
| 1588 | Register left = ToRegister(comp->left()); |
| 1589 | const LAllocation* right = comp->right(); |
| 1590 | Register output = ToRegister(comp->output()); |
| 1591 | |
| 1592 | if (compareType == MCompare::Compare_Object || |
| 1593 | compareType == MCompare::Compare_Symbol || |
| 1594 | compareType == MCompare::Compare_IntPtr || |
| 1595 | compareType == MCompare::Compare_UIntPtr || |
| 1596 | compareType == MCompare::Compare_WasmAnyRef) { |
| 1597 | if (right->isConstant()) { |
| 1598 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1599); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { MOZ_CrashSequence(__null, 1599); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1599 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1599); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { MOZ_CrashSequence(__null, 1599); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1600 | masm.cmpPtrSet(cond, left, ImmWord(ToInt32(right)), output); |
| 1601 | } else if (right->isGeneralReg()) { |
| 1602 | masm.cmpPtrSet(cond, left, ToRegister(right), output); |
| 1603 | } else { |
| 1604 | masm.cmpPtrSet(ReverseCondition(cond), ToAddress(right), left, output); |
| 1605 | } |
| 1606 | return; |
| 1607 | } |
| 1608 | |
| 1609 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1610); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { MOZ_CrashSequence(__null, 1610); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1610 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1610); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { MOZ_CrashSequence(__null, 1610); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1611 | |
| 1612 | if (right->isConstant()) { |
| 1613 | masm.cmp32Set(cond, left, Imm32(ToInt32(right)), output); |
| 1614 | } else if (right->isGeneralReg()) { |
| 1615 | masm.cmp32Set(cond, left, ToRegister(right), output); |
| 1616 | } else { |
| 1617 | masm.cmp32Set(ReverseCondition(cond), ToAddress(right), left, output); |
| 1618 | } |
| 1619 | } |
| 1620 | |
| 1621 | void CodeGenerator::visitStrictConstantCompareInt32( |
| 1622 | LStrictConstantCompareInt32* comp) { |
| 1623 | ValueOperand value = ToValue(comp->value()); |
| 1624 | Register temp = ToRegister(comp->temp0()); |
| 1625 | Register output = ToRegister(comp->output()); |
| 1626 | |
| 1627 | int32_t constantVal = comp->mir()->constant(); |
| 1628 | JSOp op = comp->mir()->jsop(); |
| 1629 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 1629); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 1629); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 1630 | |
| 1631 | masm.testValueSet(JSOpToCondition(op, false), value, Int32Value(constantVal), |
| 1632 | output); |
| 1633 | masm.testValueSet(JSOpToCondition(op, false), value, DoubleValue(constantVal), |
| 1634 | temp); |
| 1635 | |
| 1636 | if (op == JSOp::StrictEq) { |
| 1637 | masm.or32(temp, output); |
| 1638 | } else { |
| 1639 | masm.and32(temp, output); |
| 1640 | } |
| 1641 | |
| 1642 | if (constantVal == 0) { |
| 1643 | masm.testValueSet(JSOpToCondition(op, false), value, DoubleValue(-0.0), |
| 1644 | temp); |
| 1645 | |
| 1646 | if (op == JSOp::StrictEq) { |
| 1647 | masm.or32(temp, output); |
| 1648 | } else { |
| 1649 | masm.and32(temp, output); |
| 1650 | } |
| 1651 | } |
| 1652 | } |
| 1653 | |
| 1654 | void CodeGenerator::visitStrictConstantCompareInt32AndBranch( |
| 1655 | LStrictConstantCompareInt32AndBranch* comp) { |
| 1656 | ValueOperand value = ToValue(comp->value()); |
| 1657 | |
| 1658 | int32_t constantVal = comp->cmpMir()->constant(); |
| 1659 | JSOp op = comp->cmpMir()->jsop(); |
| 1660 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 1660); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 1660); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 1661 | Assembler::Condition cond = JSOpToCondition(op, false); |
| 1662 | |
| 1663 | MBasicBlock* ifTrue = comp->ifTrue(); |
| 1664 | MBasicBlock* ifFalse = comp->ifFalse(); |
| 1665 | |
| 1666 | Label* trueLabel = getJumpLabelForBranch(ifTrue); |
| 1667 | Label* falseLabel = getJumpLabelForBranch(ifFalse); |
| 1668 | |
| 1669 | Label* onEqual = op == JSOp::StrictEq ? trueLabel : falseLabel; |
| 1670 | |
| 1671 | // If the next block is the true case, invert the condition to fall through. |
| 1672 | if (isNextBlock(ifTrue->lir())) { |
| 1673 | cond = Assembler::InvertCondition(cond); |
| 1674 | trueLabel = falseLabel; |
| 1675 | falseLabel = nullptr; |
| 1676 | } else if (isNextBlock(ifFalse->lir())) { |
| 1677 | falseLabel = nullptr; |
| 1678 | } |
| 1679 | |
| 1680 | masm.branch64(Assembler::Equal, value.toRegister64(), |
| 1681 | Imm64(Int32Value(constantVal).asRawBits()), onEqual); |
| 1682 | if (constantVal == 0) { |
| 1683 | masm.branch64(Assembler::Equal, value.toRegister64(), |
| 1684 | Imm64(DoubleValue(0.0).asRawBits()), onEqual); |
| 1685 | masm.branch64(cond, value.toRegister64(), |
| 1686 | Imm64(DoubleValue(-0.0).asRawBits()), trueLabel, falseLabel); |
| 1687 | } else { |
| 1688 | masm.branch64(cond, value.toRegister64(), |
| 1689 | Imm64(DoubleValue(constantVal).asRawBits()), trueLabel, |
| 1690 | falseLabel); |
| 1691 | } |
| 1692 | } |
| 1693 | |
| 1694 | void CodeGenerator::visitStrictConstantCompareBoolean( |
| 1695 | LStrictConstantCompareBoolean* comp) { |
| 1696 | ValueOperand value = ToValue(comp->value()); |
| 1697 | Register output = ToRegister(comp->output()); |
| 1698 | |
| 1699 | bool constantVal = comp->mir()->constant(); |
| 1700 | JSOp op = comp->mir()->jsop(); |
| 1701 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 1701); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 1701); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 1702 | |
| 1703 | masm.testValueSet(JSOpToCondition(op, false), value, |
| 1704 | BooleanValue(constantVal), output); |
| 1705 | } |
| 1706 | |
| 1707 | void CodeGenerator::visitStrictConstantCompareBooleanAndBranch( |
| 1708 | LStrictConstantCompareBooleanAndBranch* comp) { |
| 1709 | ValueOperand value = ToValue(comp->value()); |
| 1710 | |
| 1711 | bool constantVal = comp->cmpMir()->constant(); |
| 1712 | JSOp op = comp->cmpMir()->jsop(); |
| 1713 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 1713); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 1713); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 1714 | Assembler::Condition cond = JSOpToCondition(op, false); |
| 1715 | |
| 1716 | MBasicBlock* ifTrue = comp->ifTrue(); |
| 1717 | MBasicBlock* ifFalse = comp->ifFalse(); |
| 1718 | |
| 1719 | Label* trueLabel = getJumpLabelForBranch(ifTrue); |
| 1720 | Label* falseLabel = getJumpLabelForBranch(ifFalse); |
| 1721 | |
| 1722 | // If the next block is the true case, invert the condition to fall through. |
| 1723 | if (isNextBlock(ifTrue->lir())) { |
| 1724 | cond = Assembler::InvertCondition(cond); |
| 1725 | trueLabel = falseLabel; |
| 1726 | falseLabel = nullptr; |
| 1727 | } else if (isNextBlock(ifFalse->lir())) { |
| 1728 | falseLabel = nullptr; |
| 1729 | } |
| 1730 | |
| 1731 | masm.branch64(cond, value.toRegister64(), |
| 1732 | Imm64(BooleanValue(constantVal).asRawBits()), trueLabel, |
| 1733 | falseLabel); |
| 1734 | } |
| 1735 | |
| 1736 | void CodeGenerator::visitCompareAndBranch(LCompareAndBranch* comp) { |
| 1737 | MCompare::CompareType compareType = comp->cmpMir()->compareType(); |
| 1738 | Assembler::Condition cond = JSOpToCondition(compareType, comp->jsop()); |
| 1739 | Register left = ToRegister(comp->left()); |
| 1740 | const LAllocation* right = comp->right(); |
| 1741 | |
| 1742 | MBasicBlock* ifTrue = comp->ifTrue(); |
| 1743 | MBasicBlock* ifFalse = comp->ifFalse(); |
| 1744 | |
| 1745 | // If the next block is the true case, invert the condition to fall through. |
| 1746 | Label* label; |
| 1747 | if (isNextBlock(ifTrue->lir())) { |
| 1748 | cond = Assembler::InvertCondition(cond); |
| 1749 | label = getJumpLabelForBranch(ifFalse); |
| 1750 | } else { |
| 1751 | label = getJumpLabelForBranch(ifTrue); |
| 1752 | } |
| 1753 | |
| 1754 | if (compareType == MCompare::Compare_Object || |
| 1755 | compareType == MCompare::Compare_Symbol || |
| 1756 | compareType == MCompare::Compare_IntPtr || |
| 1757 | compareType == MCompare::Compare_UIntPtr || |
| 1758 | compareType == MCompare::Compare_WasmAnyRef) { |
| 1759 | if (right->isConstant()) { |
| 1760 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1761); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { MOZ_CrashSequence(__null, 1761); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1761 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1761); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_IntPtr || compareType == MCompare::Compare_UIntPtr" ")"); do { MOZ_CrashSequence(__null, 1761); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1762 | masm.branchPtr(cond, left, ImmWord(ToInt32(right)), label); |
| 1763 | } else if (right->isGeneralReg()) { |
| 1764 | masm.branchPtr(cond, left, ToRegister(right), label); |
| 1765 | } else { |
| 1766 | masm.branchPtr(ReverseCondition(cond), ToAddress(right), left, label); |
| 1767 | } |
| 1768 | } else { |
| 1769 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1770); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { MOZ_CrashSequence(__null, 1770); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1770 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1770); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int32 || compareType == MCompare::Compare_UInt32" ")"); do { MOZ_CrashSequence(__null, 1770); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1771 | |
| 1772 | if (right->isConstant()) { |
| 1773 | masm.branch32(cond, left, Imm32(ToInt32(right)), label); |
| 1774 | } else if (right->isGeneralReg()) { |
| 1775 | masm.branch32(cond, left, ToRegister(right), label); |
| 1776 | } else { |
| 1777 | masm.branch32(ReverseCondition(cond), ToAddress(right), left, label); |
| 1778 | } |
| 1779 | } |
| 1780 | |
| 1781 | if (!isNextBlock(ifTrue->lir())) { |
| 1782 | jumpToBlock(ifFalse); |
| 1783 | } |
| 1784 | } |
| 1785 | |
| 1786 | void CodeGenerator::visitCompareI64(LCompareI64* lir) { |
| 1787 | MCompare::CompareType compareType = lir->mir()->compareType(); |
| 1788 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1789); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { MOZ_CrashSequence(__null, 1789); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1789 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1789); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { MOZ_CrashSequence(__null, 1789); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1790 | bool isSigned = compareType == MCompare::Compare_Int64; |
| 1791 | Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned); |
| 1792 | Register64 left = ToRegister64(lir->left()); |
| 1793 | LInt64Allocation right = lir->right(); |
| 1794 | Register output = ToRegister(lir->output()); |
| 1795 | |
| 1796 | if (IsConstant(right)) { |
| 1797 | masm.cmp64Set(cond, left, Imm64(ToInt64(right)), output); |
| 1798 | } else if (IsRegister64(right)) { |
| 1799 | masm.cmp64Set(cond, left, ToRegister64(right), output); |
| 1800 | } else { |
| 1801 | masm.cmp64Set(ReverseCondition(cond), ToAddress(right), left, output); |
| 1802 | } |
| 1803 | } |
| 1804 | |
| 1805 | void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) { |
| 1806 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
| 1807 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1808); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { MOZ_CrashSequence(__null, 1808); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 1808 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 1808); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Int64 || compareType == MCompare::Compare_UInt64" ")"); do { MOZ_CrashSequence(__null, 1808); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1809 | bool isSigned = compareType == MCompare::Compare_Int64; |
| 1810 | Assembler::Condition cond = JSOpToCondition(lir->jsop(), isSigned); |
| 1811 | Register64 left = ToRegister64(lir->left()); |
| 1812 | LInt64Allocation right = lir->right(); |
| 1813 | |
| 1814 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 1815 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 1816 | |
| 1817 | Label* trueLabel = getJumpLabelForBranch(ifTrue); |
| 1818 | Label* falseLabel = getJumpLabelForBranch(ifFalse); |
| 1819 | |
| 1820 | // If the next block is the true case, invert the condition to fall through. |
| 1821 | if (isNextBlock(ifTrue->lir())) { |
| 1822 | cond = Assembler::InvertCondition(cond); |
| 1823 | trueLabel = falseLabel; |
| 1824 | falseLabel = nullptr; |
| 1825 | } else if (isNextBlock(ifFalse->lir())) { |
| 1826 | falseLabel = nullptr; |
| 1827 | } |
| 1828 | |
| 1829 | if (IsConstant(right)) { |
| 1830 | masm.branch64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel); |
| 1831 | } else if (IsRegister64(right)) { |
| 1832 | masm.branch64(cond, left, ToRegister64(right), trueLabel, falseLabel); |
| 1833 | } else { |
| 1834 | masm.branch64(ReverseCondition(cond), ToAddress(right), left, trueLabel, |
| 1835 | falseLabel); |
| 1836 | } |
| 1837 | } |
| 1838 | |
| 1839 | void CodeGenerator::visitBitAndAndBranch(LBitAndAndBranch* baab) { |
| 1840 | Assembler::Condition cond = baab->cond(); |
| 1841 | MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero)do { static_assert( mozilla::detail::AssertionConditionType< decltype(cond == Assembler::Zero || cond == Assembler::NonZero )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(cond == Assembler::Zero || cond == Assembler::NonZero ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "cond == Assembler::Zero || cond == Assembler::NonZero", "./../../../../js/src/jit/CodeGenerator.cpp" , 1841); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cond == Assembler::Zero || cond == Assembler::NonZero" ")"); do { MOZ_CrashSequence(__null, 1841); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1842 | |
| 1843 | Register left = ToRegister(baab->left()); |
| 1844 | const LAllocation* right = baab->right(); |
| 1845 | |
| 1846 | MBasicBlock* ifTrue = baab->ifTrue(); |
| 1847 | MBasicBlock* ifFalse = baab->ifFalse(); |
| 1848 | |
| 1849 | // If the next block is the true case, invert the condition to fall through. |
| 1850 | Label* label; |
| 1851 | if (isNextBlock(ifTrue->lir())) { |
| 1852 | cond = Assembler::InvertCondition(cond); |
| 1853 | label = getJumpLabelForBranch(ifFalse); |
| 1854 | } else { |
| 1855 | label = getJumpLabelForBranch(ifTrue); |
| 1856 | } |
| 1857 | |
| 1858 | if (right->isConstant()) { |
| 1859 | masm.branchTest32(cond, left, Imm32(ToInt32(right)), label); |
| 1860 | } else { |
| 1861 | masm.branchTest32(cond, left, ToRegister(right), label); |
| 1862 | } |
| 1863 | |
| 1864 | if (!isNextBlock(ifTrue->lir())) { |
| 1865 | jumpToBlock(ifFalse); |
| 1866 | } |
| 1867 | } |
| 1868 | |
| 1869 | void CodeGenerator::visitBitAnd64AndBranch(LBitAnd64AndBranch* baab) { |
| 1870 | Assembler::Condition cond = baab->cond(); |
| 1871 | MOZ_ASSERT(cond == Assembler::Zero || cond == Assembler::NonZero)do { static_assert( mozilla::detail::AssertionConditionType< decltype(cond == Assembler::Zero || cond == Assembler::NonZero )>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(cond == Assembler::Zero || cond == Assembler::NonZero ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "cond == Assembler::Zero || cond == Assembler::NonZero", "./../../../../js/src/jit/CodeGenerator.cpp" , 1871); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cond == Assembler::Zero || cond == Assembler::NonZero" ")"); do { MOZ_CrashSequence(__null, 1871); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 1872 | |
| 1873 | Register64 left = ToRegister64(baab->left()); |
| 1874 | LInt64Allocation right = baab->right(); |
| 1875 | |
| 1876 | MBasicBlock* ifTrue = baab->ifTrue(); |
| 1877 | MBasicBlock* ifFalse = baab->ifFalse(); |
| 1878 | |
| 1879 | Label* trueLabel = getJumpLabelForBranch(ifTrue); |
| 1880 | Label* falseLabel = getJumpLabelForBranch(ifFalse); |
| 1881 | |
| 1882 | // If the next block is the true case, invert the condition to fall through. |
| 1883 | if (isNextBlock(ifTrue->lir())) { |
| 1884 | cond = Assembler::InvertCondition(cond); |
| 1885 | trueLabel = falseLabel; |
| 1886 | falseLabel = nullptr; |
| 1887 | } else if (isNextBlock(ifFalse->lir())) { |
| 1888 | falseLabel = nullptr; |
| 1889 | } |
| 1890 | |
| 1891 | if (IsConstant(right)) { |
| 1892 | masm.branchTest64(cond, left, Imm64(ToInt64(right)), trueLabel, falseLabel); |
| 1893 | } else { |
| 1894 | masm.branchTest64(cond, left, ToRegister64(right), trueLabel, falseLabel); |
| 1895 | } |
| 1896 | } |
| 1897 | |
| 1898 | void CodeGenerator::assertObjectDoesNotEmulateUndefined( |
| 1899 | Register input, Register temp, const MInstruction* mir) { |
| 1900 | #if defined(DEBUG1) || defined(FUZZING) |
| 1901 | // Validate that the object indeed doesn't have the emulates undefined flag. |
| 1902 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 1903 | addOutOfLineCode(ool, mir); |
| 1904 | |
| 1905 | Label* doesNotEmulateUndefined = ool->label1(); |
| 1906 | Label* emulatesUndefined = ool->label2(); |
| 1907 | |
| 1908 | testObjectEmulatesUndefined(input, emulatesUndefined, doesNotEmulateUndefined, |
| 1909 | temp, ool); |
| 1910 | masm.bind(emulatesUndefined); |
| 1911 | masm.assumeUnreachable( |
| 1912 | "Found an object emulating undefined while the fuse is intact"); |
| 1913 | masm.bind(doesNotEmulateUndefined); |
| 1914 | #endif |
| 1915 | } |
| 1916 | |
| 1917 | void CodeGenerator::visitTestOAndBranch(LTestOAndBranch* lir) { |
| 1918 | Label* truthy = getJumpLabelForBranch(lir->ifTruthy()); |
| 1919 | Label* falsy = getJumpLabelForBranch(lir->ifFalsy()); |
| 1920 | Register input = ToRegister(lir->input()); |
| 1921 | Register temp = ToRegister(lir->temp0()); |
| 1922 | |
| 1923 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 1924 | if (intact) { |
| 1925 | assertObjectDoesNotEmulateUndefined(input, temp, lir->mir()); |
| 1926 | // Bug 1874905: It would be fantastic if this could be optimized out |
| 1927 | masm.jump(truthy); |
| 1928 | } else { |
| 1929 | auto* ool = new (alloc()) OutOfLineTestObject(); |
| 1930 | addOutOfLineCode(ool, lir->mir()); |
| 1931 | |
| 1932 | testObjectEmulatesUndefined(input, falsy, truthy, temp, ool); |
| 1933 | } |
| 1934 | } |
| 1935 | |
| 1936 | void CodeGenerator::visitTestVAndBranch(LTestVAndBranch* lir) { |
| 1937 | OutOfLineTestObject* ool = nullptr; |
| 1938 | if (!hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted()) { |
| 1939 | ool = new (alloc()) OutOfLineTestObject(); |
| 1940 | addOutOfLineCode(ool, lir->mir()); |
| 1941 | } |
| 1942 | |
| 1943 | Label* truthy = getJumpLabelForBranch(lir->ifTruthy()); |
| 1944 | Label* falsy = getJumpLabelForBranch(lir->ifFalsy()); |
| 1945 | |
| 1946 | ValueOperand input = ToValue(lir->input()); |
| 1947 | Register tempToUnbox = ToTempUnboxRegister(lir->temp1()); |
| 1948 | Register temp = ToRegister(lir->temp2()); |
| 1949 | FloatRegister floatTemp = ToFloatRegister(lir->temp0()); |
| 1950 | const TypeDataList& observedTypes = lir->mir()->observedTypes(); |
| 1951 | |
| 1952 | testValueTruthy(input, tempToUnbox, temp, floatTemp, observedTypes, truthy, |
| 1953 | falsy, ool); |
| 1954 | masm.jump(truthy); |
| 1955 | } |
| 1956 | |
| 1957 | void CodeGenerator::visitBooleanToString(LBooleanToString* lir) { |
| 1958 | Register input = ToRegister(lir->input()); |
| 1959 | Register output = ToRegister(lir->output()); |
| 1960 | const JSAtomState& names = gen->runtime->names(); |
| 1961 | Label true_, done; |
| 1962 | |
| 1963 | masm.branchTest32(Assembler::NonZero, input, input, &true_); |
| 1964 | masm.movePtr(ImmGCPtr(names.false_), output); |
| 1965 | masm.jump(&done); |
| 1966 | |
| 1967 | masm.bind(&true_); |
| 1968 | masm.movePtr(ImmGCPtr(names.true_), output); |
| 1969 | |
| 1970 | masm.bind(&done); |
| 1971 | } |
| 1972 | |
| 1973 | void CodeGenerator::visitIntToString(LIntToString* lir) { |
| 1974 | Register input = ToRegister(lir->input()); |
| 1975 | Register output = ToRegister(lir->output()); |
| 1976 | |
| 1977 | using Fn = JSLinearString* (*)(JSContext*, int); |
| 1978 | OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>( |
| 1979 | lir, ArgList(input), StoreRegisterTo(output)); |
| 1980 | |
| 1981 | masm.lookupStaticIntString(input, output, gen->runtime->staticStrings(), |
| 1982 | ool->entry()); |
| 1983 | |
| 1984 | masm.bind(ool->rejoin()); |
| 1985 | } |
| 1986 | |
| 1987 | void CodeGenerator::visitDoubleToString(LDoubleToString* lir) { |
| 1988 | FloatRegister input = ToFloatRegister(lir->input()); |
| 1989 | Register temp = ToRegister(lir->temp0()); |
| 1990 | Register output = ToRegister(lir->output()); |
| 1991 | |
| 1992 | using Fn = JSString* (*)(JSContext*, double); |
| 1993 | OutOfLineCode* ool = oolCallVM<Fn, NumberToString<CanGC>>( |
| 1994 | lir, ArgList(input), StoreRegisterTo(output)); |
| 1995 | |
| 1996 | // Try double to integer conversion and run integer to string code. |
| 1997 | masm.convertDoubleToInt32(input, temp, ool->entry(), false); |
| 1998 | masm.lookupStaticIntString(temp, output, gen->runtime->staticStrings(), |
| 1999 | ool->entry()); |
| 2000 | |
| 2001 | masm.bind(ool->rejoin()); |
| 2002 | } |
| 2003 | |
| 2004 | void CodeGenerator::visitValueToString(LValueToString* lir) { |
| 2005 | ValueOperand input = ToValue(lir->input()); |
| 2006 | Register output = ToRegister(lir->output()); |
| 2007 | |
| 2008 | using Fn = JSString* (*)(JSContext*, HandleValue); |
| 2009 | OutOfLineCode* ool = oolCallVM<Fn, ToStringSlow<CanGC>>( |
| 2010 | lir, ArgList(input), StoreRegisterTo(output)); |
| 2011 | |
| 2012 | Label done; |
| 2013 | Register tag = masm.extractTag(input, output); |
| 2014 | const JSAtomState& names = gen->runtime->names(); |
| 2015 | |
| 2016 | // String |
| 2017 | { |
| 2018 | Label notString; |
| 2019 | masm.branchTestString(Assembler::NotEqual, tag, ¬String); |
| 2020 | masm.unboxString(input, output); |
| 2021 | masm.jump(&done); |
| 2022 | masm.bind(¬String); |
| 2023 | } |
| 2024 | |
| 2025 | // Integer |
| 2026 | { |
| 2027 | Label notInteger; |
| 2028 | masm.branchTestInt32(Assembler::NotEqual, tag, ¬Integer); |
| 2029 | Register unboxed = ToTempUnboxRegister(lir->temp0()); |
| 2030 | unboxed = masm.extractInt32(input, unboxed); |
| 2031 | masm.lookupStaticIntString(unboxed, output, gen->runtime->staticStrings(), |
| 2032 | ool->entry()); |
| 2033 | masm.jump(&done); |
| 2034 | masm.bind(¬Integer); |
| 2035 | } |
| 2036 | |
| 2037 | // Double |
| 2038 | { |
| 2039 | // Note: no fastpath. Need two extra registers and can only convert doubles |
| 2040 | // that fit integers and are smaller than StaticStrings::INT_STATIC_LIMIT. |
| 2041 | masm.branchTestDouble(Assembler::Equal, tag, ool->entry()); |
| 2042 | } |
| 2043 | |
| 2044 | // Undefined |
| 2045 | { |
| 2046 | Label notUndefined; |
| 2047 | masm.branchTestUndefined(Assembler::NotEqual, tag, ¬Undefined); |
| 2048 | masm.movePtr(ImmGCPtr(names.undefined), output); |
| 2049 | masm.jump(&done); |
| 2050 | masm.bind(¬Undefined); |
| 2051 | } |
| 2052 | |
| 2053 | // Null |
| 2054 | { |
| 2055 | Label notNull; |
| 2056 | masm.branchTestNull(Assembler::NotEqual, tag, ¬Null); |
| 2057 | masm.movePtr(ImmGCPtr(names.null), output); |
| 2058 | masm.jump(&done); |
| 2059 | masm.bind(¬Null); |
| 2060 | } |
| 2061 | |
| 2062 | // Boolean |
| 2063 | { |
| 2064 | Label notBoolean, true_; |
| 2065 | masm.branchTestBoolean(Assembler::NotEqual, tag, ¬Boolean); |
| 2066 | masm.branchTestBooleanTruthy(true, input, &true_); |
| 2067 | masm.movePtr(ImmGCPtr(names.false_), output); |
| 2068 | masm.jump(&done); |
| 2069 | masm.bind(&true_); |
| 2070 | masm.movePtr(ImmGCPtr(names.true_), output); |
| 2071 | masm.jump(&done); |
| 2072 | masm.bind(¬Boolean); |
| 2073 | } |
| 2074 | |
| 2075 | // Objects/symbols are only possible when |mir->mightHaveSideEffects()|. |
| 2076 | if (lir->mir()->mightHaveSideEffects()) { |
| 2077 | // Object |
| 2078 | if (lir->mir()->supportSideEffects()) { |
| 2079 | masm.branchTestObject(Assembler::Equal, tag, ool->entry()); |
| 2080 | } else { |
| 2081 | // Bail. |
| 2082 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 2082); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()" ")"); do { MOZ_CrashSequence(__null, 2082); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 2083 | Label bail; |
| 2084 | masm.branchTestObject(Assembler::Equal, tag, &bail); |
| 2085 | bailoutFrom(&bail, lir->snapshot()); |
| 2086 | } |
| 2087 | |
| 2088 | // Symbol |
| 2089 | if (lir->mir()->supportSideEffects()) { |
| 2090 | masm.branchTestSymbol(Assembler::Equal, tag, ool->entry()); |
| 2091 | } else { |
| 2092 | // Bail. |
| 2093 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 2093); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->needsSnapshot()" ")"); do { MOZ_CrashSequence(__null, 2093); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 2094 | Label bail; |
| 2095 | masm.branchTestSymbol(Assembler::Equal, tag, &bail); |
| 2096 | bailoutFrom(&bail, lir->snapshot()); |
| 2097 | } |
| 2098 | } |
| 2099 | |
| 2100 | // BigInt |
| 2101 | { |
| 2102 | // No fastpath currently implemented. |
| 2103 | masm.branchTestBigInt(Assembler::Equal, tag, ool->entry()); |
| 2104 | } |
| 2105 | |
| 2106 | masm.assumeUnreachable("Unexpected type for LValueToString."); |
| 2107 | |
| 2108 | masm.bind(&done); |
| 2109 | masm.bind(ool->rejoin()); |
| 2110 | } |
| 2111 | |
| 2112 | using StoreBufferMutationFn = void (*)(js::gc::StoreBuffer*, js::gc::Cell**); |
| 2113 | |
| 2114 | static void EmitStoreBufferMutation(MacroAssembler& masm, Register holder, |
| 2115 | size_t offset, Register buffer, |
| 2116 | LiveGeneralRegisterSet& liveVolatiles, |
| 2117 | StoreBufferMutationFn fun) { |
| 2118 | Label callVM; |
| 2119 | Label exit; |
| 2120 | |
| 2121 | // Call into the VM to barrier the write. The only registers that need to |
| 2122 | // be preserved are those in liveVolatiles, so once they are saved on the |
| 2123 | // stack all volatile registers are available for use. |
| 2124 | masm.bind(&callVM); |
| 2125 | masm.PushRegsInMask(liveVolatiles); |
| 2126 | |
| 2127 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 2128 | regs.takeUnchecked(buffer); |
| 2129 | regs.takeUnchecked(holder); |
| 2130 | Register addrReg = regs.takeAny(); |
| 2131 | |
| 2132 | masm.computeEffectiveAddress(Address(holder, offset), addrReg); |
| 2133 | |
| 2134 | bool needExtraReg = !regs.hasAny<GeneralRegisterSet::DefaultType>(); |
| 2135 | if (needExtraReg) { |
| 2136 | masm.push(holder); |
| 2137 | masm.setupUnalignedABICall(holder); |
| 2138 | } else { |
| 2139 | masm.setupUnalignedABICall(regs.takeAny()); |
| 2140 | } |
| 2141 | masm.passABIArg(buffer); |
| 2142 | masm.passABIArg(addrReg); |
| 2143 | masm.callWithABI(DynamicFunction<StoreBufferMutationFn>(fun), |
| 2144 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 2145 | |
| 2146 | if (needExtraReg) { |
| 2147 | masm.pop(holder); |
| 2148 | } |
| 2149 | masm.PopRegsInMask(liveVolatiles); |
| 2150 | masm.bind(&exit); |
| 2151 | } |
| 2152 | |
| 2153 | // Warning: this function modifies prev and next. |
| 2154 | static void EmitPostWriteBarrierS(MacroAssembler& masm, Register holder, |
| 2155 | size_t offset, Register prev, Register next, |
| 2156 | LiveGeneralRegisterSet& liveVolatiles) { |
| 2157 | Label exit; |
| 2158 | Label checkRemove, putCell; |
| 2159 | |
| 2160 | // if (next && (buffer = next->storeBuffer())) |
| 2161 | // but we never pass in nullptr for next. |
| 2162 | Register storebuffer = next; |
| 2163 | masm.loadStoreBuffer(next, storebuffer); |
| 2164 | masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &checkRemove); |
| 2165 | |
| 2166 | // if (prev && prev->storeBuffer()) |
| 2167 | masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &putCell); |
| 2168 | masm.loadStoreBuffer(prev, prev); |
| 2169 | masm.branchPtr(Assembler::NotEqual, prev, ImmWord(0), &exit); |
| 2170 | |
| 2171 | // buffer->putCell(cellp) |
| 2172 | masm.bind(&putCell); |
| 2173 | EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles, |
| 2174 | JSString::addCellAddressToStoreBuffer); |
| 2175 | masm.jump(&exit); |
| 2176 | |
| 2177 | // if (prev && (buffer = prev->storeBuffer())) |
| 2178 | masm.bind(&checkRemove); |
| 2179 | masm.branchPtr(Assembler::Equal, prev, ImmWord(0), &exit); |
| 2180 | masm.loadStoreBuffer(prev, storebuffer); |
| 2181 | masm.branchPtr(Assembler::Equal, storebuffer, ImmWord(0), &exit); |
| 2182 | EmitStoreBufferMutation(masm, holder, offset, storebuffer, liveVolatiles, |
| 2183 | JSString::removeCellAddressFromStoreBuffer); |
| 2184 | |
| 2185 | masm.bind(&exit); |
| 2186 | } |
| 2187 | |
| 2188 | void CodeGenerator::visitRegExp(LRegExp* lir) { |
| 2189 | Register output = ToRegister(lir->output()); |
| 2190 | Register temp = ToRegister(lir->temp0()); |
| 2191 | JSObject* source = lir->mir()->source(); |
| 2192 | |
| 2193 | using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>); |
| 2194 | OutOfLineCode* ool = oolCallVM<Fn, CloneRegExpObject>( |
| 2195 | lir, ArgList(ImmGCPtr(source)), StoreRegisterTo(output)); |
| 2196 | if (lir->mir()->hasShared()) { |
| 2197 | TemplateObject templateObject(source); |
| 2198 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 2199 | ool->entry()); |
| 2200 | } else { |
| 2201 | masm.jump(ool->entry()); |
| 2202 | } |
| 2203 | masm.bind(ool->rejoin()); |
| 2204 | } |
| 2205 | |
| 2206 | /* |
| 2207 | * [SMDOC] RegExp stubs |
| 2208 | * |
| 2209 | * The RegExp stubs are a set of lazily generated per-zone stubs |
| 2210 | * providing fast paths for regexp execution in baseline and Ion. |
| 2211 | * In general, they are invoked from self-hosted code. |
| 2212 | * |
| 2213 | * There are four stubs: |
| 2214 | * - RegExpMatcher: Given a regular expression, an input string, |
| 2215 | * and the current lastIndex, return the match result object. |
| 2216 | * - RegExpExecMatch: The same as RegExpMatcher, but lastIndex is |
| 2217 | * not an argument. Instead, for sticky/global regexps, it is |
| 2218 | * loaded from the regexp, and the new value is stored back to |
| 2219 | * the regexp after execution. Otherwise, it is hardcoded to 0. |
| 2220 | * - RegExpSearcher: Given a regular expression, an input string, |
| 2221 | * and the current lastIndex, return the index of the next match. |
| 2222 | * - RegExpExecTest: Given a regular expression and an input string, |
| 2223 | * return a boolean indicating whether a match was found. This |
| 2224 | * stub has the same behaviour as RegExpExecMatch with respect to |
| 2225 | * lastIndex. |
| 2226 | */ |
| 2227 | |
| 2228 | // Offset of the InputOutputData relative to the frame pointer in regexp stubs. |
| 2229 | // The InputOutputData is allocated by the caller, so it is placed above the |
| 2230 | // frame pointer and return address on the stack. |
| 2231 | static constexpr size_t RegExpInputOutputDataOffset = 2 * sizeof(void*); |
| 2232 | |
| 2233 | static constexpr size_t RegExpPairsVectorStartOffset = |
| 2234 | RegExpInputOutputDataOffset + InputOutputDataSize + sizeof(MatchPairs); |
| 2235 | |
| 2236 | static Address RegExpPairCountAddress() { |
| 2237 | return Address(FramePointer, RegExpInputOutputDataOffset + |
| 2238 | int32_t(InputOutputDataSize) + |
| 2239 | MatchPairs::offsetOfPairCount()); |
| 2240 | } |
| 2241 | |
| 2242 | static void UpdateRegExpStatics(MacroAssembler& masm, Register regexp, |
| 2243 | Register input, Register lastIndex, |
| 2244 | Register staticsReg, Register temp1, |
| 2245 | Register temp2, gc::Heap initialStringHeap, |
| 2246 | LiveGeneralRegisterSet& volatileRegs) { |
| 2247 | Address pendingInputAddress(staticsReg, |
| 2248 | RegExpStatics::offsetOfPendingInput()); |
| 2249 | Address matchesInputAddress(staticsReg, |
| 2250 | RegExpStatics::offsetOfMatchesInput()); |
| 2251 | Address lazySourceAddress(staticsReg, RegExpStatics::offsetOfLazySource()); |
| 2252 | Address lazyIndexAddress(staticsReg, RegExpStatics::offsetOfLazyIndex()); |
| 2253 | Label legacyFeaturesEnabled, done; |
| 2254 | if (JS::Prefs::experimental_legacy_regexp()) { |
| 2255 | Address invalidatedAddress(staticsReg, |
| 2256 | RegExpStatics::offsetOfInvalidated()); |
| 2257 | |
| 2258 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffset( |
| 2259 | RegExpObject::flagsSlot())), |
| 2260 | temp1, JSVAL_TYPE_INT32); |
| 2261 | masm.branchTest32(Assembler::NonZero, temp1, |
| 2262 | Imm32(RegExpObject::LegacyFeaturesEnabledBit), |
| 2263 | &legacyFeaturesEnabled); |
| 2264 | masm.store8(Imm32(1), invalidatedAddress); |
| 2265 | masm.jump(&done); |
| 2266 | masm.bind(&legacyFeaturesEnabled); |
| 2267 | masm.store8(Imm32(0), invalidatedAddress); |
| 2268 | } |
| 2269 | |
| 2270 | masm.guardedCallPreBarrier(pendingInputAddress, MIRType::String); |
| 2271 | masm.guardedCallPreBarrier(matchesInputAddress, MIRType::String); |
| 2272 | masm.guardedCallPreBarrier(lazySourceAddress, MIRType::String); |
| 2273 | |
| 2274 | if (initialStringHeap == gc::Heap::Default) { |
| 2275 | // Writing into RegExpStatics tenured memory; must post-barrier. |
| 2276 | if (staticsReg.volatile_()) { |
| 2277 | volatileRegs.add(staticsReg); |
| 2278 | } |
| 2279 | |
| 2280 | masm.loadPtr(pendingInputAddress, temp1); |
| 2281 | masm.storePtr(input, pendingInputAddress); |
| 2282 | masm.movePtr(input, temp2); |
| 2283 | EmitPostWriteBarrierS(masm, staticsReg, |
| 2284 | RegExpStatics::offsetOfPendingInput(), |
| 2285 | temp1 /* prev */, temp2 /* next */, volatileRegs); |
| 2286 | |
| 2287 | masm.loadPtr(matchesInputAddress, temp1); |
| 2288 | masm.storePtr(input, matchesInputAddress); |
| 2289 | masm.movePtr(input, temp2); |
| 2290 | EmitPostWriteBarrierS(masm, staticsReg, |
| 2291 | RegExpStatics::offsetOfMatchesInput(), |
| 2292 | temp1 /* prev */, temp2 /* next */, volatileRegs); |
| 2293 | } else { |
| 2294 | masm.debugAssertGCThingIsTenured(input, temp1); |
| 2295 | masm.storePtr(input, pendingInputAddress); |
| 2296 | masm.storePtr(input, matchesInputAddress); |
| 2297 | } |
| 2298 | |
| 2299 | masm.storePtr(lastIndex, |
| 2300 | Address(staticsReg, RegExpStatics::offsetOfLazyIndex())); |
| 2301 | masm.store32( |
| 2302 | Imm32(1), |
| 2303 | Address(staticsReg, RegExpStatics::offsetOfPendingLazyEvaluation())); |
| 2304 | |
| 2305 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffsetTyped( |
| 2306 | RegExpObject::SHARED_SLOT)), |
| 2307 | temp1, JSVAL_TYPE_PRIVATE_GCTHING); |
| 2308 | masm.loadPtr(Address(temp1, RegExpShared::offsetOfSource()), temp2); |
| 2309 | masm.storePtr(temp2, lazySourceAddress); |
| 2310 | static_assert(sizeof(JS::RegExpFlags) == 1, "load size must match flag size"); |
| 2311 | masm.load8ZeroExtend(Address(temp1, RegExpShared::offsetOfFlags()), temp2); |
| 2312 | masm.store8(temp2, Address(staticsReg, RegExpStatics::offsetOfLazyFlags())); |
| 2313 | masm.bind(&done); |
| 2314 | } |
| 2315 | |
| 2316 | // Prepare an InputOutputData and optional MatchPairs which space has been |
| 2317 | // allocated for on the stack, and try to execute a RegExp on a string input. |
| 2318 | // If the RegExp was successfully executed and matched the input, fallthrough. |
| 2319 | // Otherwise, jump to notFound or failure. |
| 2320 | static bool PrepareAndExecuteRegExp(MacroAssembler& masm, Register regexp, |
| 2321 | Register input, Register lastIndex, |
| 2322 | Register temp1, Register temp2, |
| 2323 | Register temp3, gc::Heap initialStringHeap, |
| 2324 | Label* notFound, Label* failure, |
| 2325 | JitZone::StubKind kind) { |
| 2326 | JitSpew(JitSpew_Codegen, "# Emitting PrepareAndExecuteRegExp"); |
| 2327 | |
| 2328 | using irregexp::InputOutputData; |
| 2329 | |
| 2330 | /* |
| 2331 | * [SMDOC] Stack layout for PrepareAndExecuteRegExp |
| 2332 | * |
| 2333 | * Before this function is called, the caller is responsible for |
| 2334 | * allocating enough stack space for the result data. This code |
| 2335 | * will fill in that data. This means that the match pairs will |
| 2336 | * not be freed when we return from a match stub, which allows us |
| 2337 | * to reuse them if we have to call into the VM to allocate results, |
| 2338 | * instead of executing the regexp from scratch. For consistency, |
| 2339 | * we use the same approach for stubs that don't use match pairs. |
| 2340 | * |
| 2341 | * +---------------+ |
| 2342 | * | Saved frameptr| |
| 2343 | * | Return address| |
| 2344 | * Current frame +---------------+ |
| 2345 | *------------------------------------------------------------ |
| 2346 | * Caller's frame +---------------+ |
| 2347 | * |InputOutputData| |
| 2348 | * inputStartAddress +----------> inputStart| |
| 2349 | * inputEndAddress +----------> inputEnd| |
| 2350 | * startIndexAddress +----------> startIndex| |
| 2351 | * matchesAddress +----------> matches|-----+ |
| 2352 | * +---------------+ | |
| 2353 | * matchPairs(Address|Offset) +-----> +---------------+ <--+ |
| 2354 | * | MatchPairs | |
| 2355 | * pairCountAddress +----------> count | |
| 2356 | * pairsPointerAddress +----------> pairs |-----+ |
| 2357 | * +---------------+ | |
| 2358 | * pairsArray(Address|Offset) +-----> +---------------+ <--+ |
| 2359 | * | MatchPair | |
| 2360 | * firstMatchStartAddress +----------> start | <--+ |
| 2361 | * | limit | | |
| 2362 | * +---------------+ | |
| 2363 | * . | |
| 2364 | * . Reserved space for |
| 2365 | * . RegExpObject::MaxPairCount |
| 2366 | * . MatchPair objects |
| 2367 | * . | |
| 2368 | * +---------------+ | |
| 2369 | * | MatchPair | | |
| 2370 | * | start | | |
| 2371 | * | limit | <--+ |
| 2372 | * +---------------+ |
| 2373 | */ |
| 2374 | |
| 2375 | int32_t ioOffset = RegExpInputOutputDataOffset; |
| 2376 | int32_t matchPairsOffset = ioOffset + int32_t(sizeof(InputOutputData)); |
| 2377 | int32_t pairsArrayOffset = matchPairsOffset + int32_t(sizeof(MatchPairs)); |
| 2378 | |
| 2379 | Address inputStartAddress(FramePointer, |
| 2380 | ioOffset + InputOutputData::offsetOfInputStart()); |
| 2381 | Address inputEndAddress(FramePointer, |
| 2382 | ioOffset + InputOutputData::offsetOfInputEnd()); |
| 2383 | Address startIndexAddress(FramePointer, |
| 2384 | ioOffset + InputOutputData::offsetOfStartIndex()); |
| 2385 | Address matchesAddress(FramePointer, |
| 2386 | ioOffset + InputOutputData::offsetOfMatches()); |
| 2387 | |
| 2388 | Address matchPairsAddress(FramePointer, matchPairsOffset); |
| 2389 | Address pairCountAddress(FramePointer, |
| 2390 | matchPairsOffset + MatchPairs::offsetOfPairCount()); |
| 2391 | Address pairsPointerAddress(FramePointer, |
| 2392 | matchPairsOffset + MatchPairs::offsetOfPairs()); |
| 2393 | |
| 2394 | Address pairsArrayAddress(FramePointer, pairsArrayOffset); |
| 2395 | Address firstMatchStartAddress(FramePointer, |
| 2396 | pairsArrayOffset + MatchPair::offsetOfStart()); |
| 2397 | |
| 2398 | // First, fill in a skeletal MatchPairs instance on the stack. This will be |
| 2399 | // passed to the OOL stub in the caller if we aren't able to execute the |
| 2400 | // RegExp inline, and that stub needs to be able to determine whether the |
| 2401 | // execution finished successfully. |
| 2402 | |
| 2403 | // Initialize MatchPairs::pairCount to 1. The correct value can only |
| 2404 | // be determined after loading the RegExpShared. If the RegExpShared |
| 2405 | // has Kind::Atom, this is the correct pairCount. |
| 2406 | masm.store32(Imm32(1), pairCountAddress); |
| 2407 | |
| 2408 | // Initialize MatchPairs::pairs pointer |
| 2409 | masm.computeEffectiveAddress(pairsArrayAddress, temp1); |
| 2410 | masm.storePtr(temp1, pairsPointerAddress); |
| 2411 | |
| 2412 | // Initialize MatchPairs::pairs[0]::start to MatchPair::NoMatch |
| 2413 | masm.store32(Imm32(MatchPair::NoMatch), firstMatchStartAddress); |
| 2414 | |
| 2415 | // Determine the set of volatile inputs to save when calling into C++ or |
| 2416 | // regexp code. |
| 2417 | LiveGeneralRegisterSet volatileRegs; |
| 2418 | if (lastIndex.volatile_()) { |
| 2419 | volatileRegs.add(lastIndex); |
| 2420 | } |
| 2421 | if (input.volatile_()) { |
| 2422 | volatileRegs.add(input); |
| 2423 | } |
| 2424 | if (regexp.volatile_()) { |
| 2425 | volatileRegs.add(regexp); |
| 2426 | } |
| 2427 | |
| 2428 | // Ensure the input string is not a rope. |
| 2429 | Label isLinear; |
| 2430 | masm.branchIfNotRope(input, &isLinear); |
| 2431 | { |
| 2432 | masm.PushRegsInMask(volatileRegs); |
| 2433 | |
| 2434 | using Fn = JSLinearString* (*)(JSString*); |
| 2435 | masm.setupUnalignedABICall(temp1); |
| 2436 | masm.passABIArg(input); |
| 2437 | masm.callWithABI<Fn, js::jit::LinearizeForCharAccessPure>(); |
| 2438 | |
| 2439 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 2439); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { MOZ_CrashSequence (__null, 2439); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 2440 | masm.storeCallPointerResult(temp1); |
| 2441 | masm.PopRegsInMask(volatileRegs); |
| 2442 | |
| 2443 | masm.branchTestPtr(Assembler::Zero, temp1, temp1, failure); |
| 2444 | } |
| 2445 | masm.bind(&isLinear); |
| 2446 | |
| 2447 | // Load the RegExpShared. |
| 2448 | Register regexpReg = temp1; |
| 2449 | Address sharedSlot = Address( |
| 2450 | regexp, NativeObject::getFixedSlotOffsetTyped(RegExpObject::SHARED_SLOT)); |
| 2451 | masm.branchTestUndefined(Assembler::Equal, sharedSlot, failure); |
| 2452 | masm.unboxNonDouble(sharedSlot, regexpReg, JSVAL_TYPE_PRIVATE_GCTHING); |
| 2453 | |
| 2454 | // Handle Atom matches |
| 2455 | Label notAtom, checkSuccess; |
| 2456 | masm.branchPtr(Assembler::Equal, |
| 2457 | Address(regexpReg, RegExpShared::offsetOfPatternAtom()), |
| 2458 | ImmWord(0), ¬Atom); |
| 2459 | { |
| 2460 | masm.computeEffectiveAddress(matchPairsAddress, temp3); |
| 2461 | |
| 2462 | masm.PushRegsInMask(volatileRegs); |
| 2463 | using Fn = |
| 2464 | RegExpRunStatus (*)(RegExpShared* re, const JSLinearString* input, |
| 2465 | size_t start, MatchPairs* matchPairs); |
| 2466 | masm.setupUnalignedABICall(temp2); |
| 2467 | masm.passABIArg(regexpReg); |
| 2468 | masm.passABIArg(input); |
| 2469 | masm.passABIArg(lastIndex); |
| 2470 | masm.passABIArg(temp3); |
| 2471 | masm.callWithABI<Fn, js::ExecuteRegExpAtomRaw>(); |
| 2472 | |
| 2473 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 2473); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!volatileRegs.has(temp1)" ")"); do { MOZ_CrashSequence (__null, 2473); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 2474 | masm.storeCallInt32Result(temp1); |
| 2475 | masm.PopRegsInMask(volatileRegs); |
| 2476 | |
| 2477 | masm.jump(&checkSuccess); |
| 2478 | } |
| 2479 | masm.bind(¬Atom); |
| 2480 | |
| 2481 | // If we don't need to look at the capture groups, we can leave pairCount at 1 |
| 2482 | // (set above). The regexp code is special-cased to skip copying capture |
| 2483 | // groups if the pair count is 1, which also lets us avoid having to allocate |
| 2484 | // memory to store them. |
| 2485 | bool skipMatchPairs = kind == JitZone::StubKind::RegExpSearcher || |
| 2486 | kind == JitZone::StubKind::RegExpExecTest; |
| 2487 | if (!skipMatchPairs) { |
| 2488 | // Don't handle regexps with too many capture pairs. |
| 2489 | masm.load32(Address(regexpReg, RegExpShared::offsetOfPairCount()), temp2); |
| 2490 | masm.branch32(Assembler::Above, temp2, Imm32(RegExpObject::MaxPairCount), |
| 2491 | failure); |
| 2492 | |
| 2493 | // Fill in the pair count in the MatchPairs on the stack. |
| 2494 | masm.store32(temp2, pairCountAddress); |
| 2495 | } |
| 2496 | |
| 2497 | // Load code pointer and length of input (in bytes). |
| 2498 | // Store the input start in the InputOutputData. |
| 2499 | Register codePointer = temp1; // Note: temp1 was previously regexpReg. |
| 2500 | Register byteLength = temp3; |
| 2501 | { |
| 2502 | Label isLatin1, done; |
| 2503 | masm.loadStringLength(input, byteLength); |
| 2504 | |
| 2505 | masm.branchLatin1String(input, &isLatin1); |
| 2506 | |
| 2507 | // Two-byte input |
| 2508 | masm.loadStringChars(input, temp2, CharEncoding::TwoByte); |
| 2509 | masm.storePtr(temp2, inputStartAddress); |
| 2510 | masm.loadPtr( |
| 2511 | Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/false)), |
| 2512 | codePointer); |
| 2513 | masm.lshiftPtr(Imm32(1), byteLength); |
| 2514 | masm.jump(&done); |
| 2515 | |
| 2516 | // Latin1 input |
| 2517 | masm.bind(&isLatin1); |
| 2518 | masm.loadStringChars(input, temp2, CharEncoding::Latin1); |
| 2519 | masm.storePtr(temp2, inputStartAddress); |
| 2520 | masm.loadPtr( |
| 2521 | Address(regexpReg, RegExpShared::offsetOfJitCode(/*latin1 =*/true)), |
| 2522 | codePointer); |
| 2523 | |
| 2524 | masm.bind(&done); |
| 2525 | |
| 2526 | // Store end pointer |
| 2527 | masm.addPtr(byteLength, temp2); |
| 2528 | masm.storePtr(temp2, inputEndAddress); |
| 2529 | } |
| 2530 | |
| 2531 | // Guard that the RegExpShared has been compiled for this type of input. |
| 2532 | // If it has not been compiled, we fall back to the OOL case, which will |
| 2533 | // do a VM call into the interpreter. |
| 2534 | // TODO: add an interpreter trampoline? |
| 2535 | masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure); |
| 2536 | masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer); |
| 2537 | |
| 2538 | // Finish filling in the InputOutputData instance on the stack |
| 2539 | masm.computeEffectiveAddress(matchPairsAddress, temp2); |
| 2540 | masm.storePtr(temp2, matchesAddress); |
| 2541 | masm.storePtr(lastIndex, startIndexAddress); |
| 2542 | |
| 2543 | // Execute the RegExp. |
| 2544 | masm.computeEffectiveAddress(Address(FramePointer, ioOffset), temp2); |
| 2545 | masm.PushRegsInMask(volatileRegs); |
| 2546 | masm.setupUnalignedABICall(temp3); |
| 2547 | masm.passABIArg(temp2); |
| 2548 | masm.callWithABI(codePointer); |
| 2549 | masm.storeCallInt32Result(temp1); |
| 2550 | masm.PopRegsInMask(volatileRegs); |
| 2551 | |
| 2552 | masm.bind(&checkSuccess); |
| 2553 | masm.branch32(Assembler::Equal, temp1, |
| 2554 | Imm32(int32_t(RegExpRunStatus::Success_NotFound)), notFound); |
| 2555 | masm.branch32(Assembler::Equal, temp1, Imm32(int32_t(RegExpRunStatus::Error)), |
| 2556 | failure); |
| 2557 | |
| 2558 | // Lazily update the RegExpStatics. |
| 2559 | size_t offset = GlobalObjectData::offsetOfRegExpRealm() + |
| 2560 | RegExpRealm::offsetOfRegExpStatics(); |
| 2561 | masm.loadGlobalObjectData(temp1); |
| 2562 | masm.loadPtr(Address(temp1, offset), temp1); |
| 2563 | UpdateRegExpStatics(masm, regexp, input, lastIndex, temp1, temp2, temp3, |
| 2564 | initialStringHeap, volatileRegs); |
| 2565 | |
| 2566 | return true; |
| 2567 | } |
| 2568 | |
| 2569 | // Shift a bit within a 32-bit word from one bit position to another. |
| 2570 | // Both FromBitMask and ToBitMask must have a single bit set. |
| 2571 | template <uint32_t FromBitMask, uint32_t ToBitMask> |
| 2572 | static void ShiftFlag32(MacroAssembler& masm, Register reg) { |
| 2573 | static_assert(std::has_single_bit(FromBitMask)); |
| 2574 | static_assert(std::has_single_bit(ToBitMask)); |
| 2575 | static_assert(FromBitMask != ToBitMask); |
| 2576 | constexpr uint32_t fromShift = std::countr_zero(FromBitMask); |
| 2577 | constexpr uint32_t toShift = std::countr_zero(ToBitMask); |
| 2578 | if (fromShift < toShift) { |
| 2579 | masm.lshift32(Imm32(toShift - fromShift), reg); |
| 2580 | } else { |
| 2581 | masm.rshift32(Imm32(fromShift - toShift), reg); |
| 2582 | } |
| 2583 | } |
| 2584 | |
| 2585 | static void EmitInitDependentStringBase(MacroAssembler& masm, |
| 2586 | Register dependent, Register base, |
| 2587 | Register temp1, Register temp2, |
| 2588 | bool needsPostBarrier) { |
| 2589 | // Determine the base string to use and store it in temp2. |
| 2590 | Label notDependent, markedDependedOn; |
| 2591 | masm.load32(Address(base, JSString::offsetOfFlags()), temp1); |
| 2592 | masm.branchTest32(Assembler::Zero, temp1, Imm32(StringFlags::DEPENDENT_BIT), |
| 2593 | ¬Dependent); |
| 2594 | { |
| 2595 | // The base is also a dependent string. Load its base to prevent chains of |
| 2596 | // dependent strings in most cases. This must either be an atom or already |
| 2597 | // have the DEPENDED_ON_BIT set. |
| 2598 | masm.loadDependentStringBase(base, temp2); |
| 2599 | masm.jump(&markedDependedOn); |
| 2600 | } |
| 2601 | masm.bind(¬Dependent); |
| 2602 | { |
| 2603 | // The base is not a dependent string. Set the DEPENDED_ON_BIT if it's not |
| 2604 | // an atom (ATOM_BIT is not set). Roughly: |
| 2605 | // |
| 2606 | // flags |= ((~flags) & ATOM_BIT) << (DEPENDED_ON_BIT - ATOM_BIT)) |
| 2607 | // |
| 2608 | // but further modified to combine the initial move with an OR: |
| 2609 | // |
| 2610 | // flags |= ~(flags | ~ATOM_BIT) << (DEPENDED_ON_BIT - ATOM_BIT) |
| 2611 | // |
| 2612 | masm.nor32(Imm32(~StringFlags::ATOM_BIT), temp1, temp2); |
| 2613 | ShiftFlag32<StringFlags::ATOM_BIT, StringFlags::DEPENDED_ON_BIT>(masm, |
| 2614 | temp2); |
| 2615 | masm.or32(temp2, temp1); |
| 2616 | masm.movePtr(base, temp2); |
| 2617 | masm.store32(temp1, Address(temp2, JSString::offsetOfFlags())); |
| 2618 | } |
| 2619 | masm.bind(&markedDependedOn); |
| 2620 | |
| 2621 | #ifdef DEBUG1 |
| 2622 | // Assert the base has the DEPENDED_ON_BIT set or is an atom. |
| 2623 | Label isAppropriatelyMarked; |
| 2624 | masm.branchTest32(Assembler::NonZero, |
| 2625 | Address(temp2, JSString::offsetOfFlags()), |
| 2626 | Imm32(StringFlags::ATOM_BIT | StringFlags::DEPENDED_ON_BIT), |
| 2627 | &isAppropriatelyMarked); |
| 2628 | masm.assumeUnreachable("Base string is missing DEPENDED_ON_BIT"); |
| 2629 | masm.bind(&isAppropriatelyMarked); |
| 2630 | #endif |
| 2631 | masm.storeDependentStringBase(temp2, dependent); |
| 2632 | |
| 2633 | // Post-barrier the base store. The base is still in temp2. |
| 2634 | if (needsPostBarrier) { |
| 2635 | Label done; |
| 2636 | masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done); |
| 2637 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done); |
| 2638 | |
| 2639 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
| 2640 | regsToSave.takeUnchecked(temp1); |
| 2641 | regsToSave.takeUnchecked(temp2); |
| 2642 | |
| 2643 | masm.PushRegsInMask(regsToSave); |
| 2644 | |
| 2645 | masm.mov(ImmPtr(masm.runtime()), temp1); |
| 2646 | |
| 2647 | using Fn = void (*)(JSRuntime* rt, js::gc::Cell* cell); |
| 2648 | masm.setupUnalignedABICall(temp2); |
| 2649 | masm.passABIArg(temp1); |
| 2650 | masm.passABIArg(dependent); |
| 2651 | masm.callWithABI<Fn, PostWriteBarrier>(); |
| 2652 | |
| 2653 | masm.PopRegsInMask(regsToSave); |
| 2654 | |
| 2655 | masm.bind(&done); |
| 2656 | } else { |
| 2657 | #ifdef DEBUG1 |
| 2658 | Label done; |
| 2659 | masm.branchPtrInNurseryChunk(Assembler::Equal, dependent, temp1, &done); |
| 2660 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, temp2, temp1, &done); |
| 2661 | masm.assumeUnreachable("Missing post barrier for dependent string base"); |
| 2662 | masm.bind(&done); |
| 2663 | #endif |
| 2664 | } |
| 2665 | } |
| 2666 | |
| 2667 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
| 2668 | Register len, Register byteOpScratch, |
| 2669 | CharEncoding encoding, |
| 2670 | size_t maximumLength = SIZE_MAX(18446744073709551615UL)); |
| 2671 | |
| 2672 | class CreateDependentString { |
| 2673 | CharEncoding encoding_; |
| 2674 | Register string_; |
| 2675 | Register temp1_; |
| 2676 | Register temp2_; |
| 2677 | Label* failure_; |
| 2678 | |
| 2679 | enum class FallbackKind : uint8_t { |
| 2680 | InlineString, |
| 2681 | FatInlineString, |
| 2682 | NotInlineString, |
| 2683 | Count |
| 2684 | }; |
| 2685 | mozilla::EnumeratedArray<FallbackKind, Label, size_t(FallbackKind::Count)> |
| 2686 | fallbacks_, joins_; |
| 2687 | |
| 2688 | public: |
| 2689 | CreateDependentString(CharEncoding encoding, Register string, Register temp1, |
| 2690 | Register temp2, Label* failure) |
| 2691 | : encoding_(encoding), |
| 2692 | string_(string), |
| 2693 | temp1_(temp1), |
| 2694 | temp2_(temp2), |
| 2695 | failure_(failure) {} |
| 2696 | |
| 2697 | Register string() const { return string_; } |
| 2698 | CharEncoding encoding() const { return encoding_; } |
| 2699 | |
| 2700 | // Generate code that creates DependentString. |
| 2701 | // Caller should call generateFallback after masm.ret(), to generate |
| 2702 | // fallback path. |
| 2703 | void generate(MacroAssembler& masm, const JSAtomState& names, |
| 2704 | CompileRuntime* runtime, Register base, |
| 2705 | BaseIndex startIndexAddress, BaseIndex limitIndexAddress, |
| 2706 | gc::Heap initialStringHeap); |
| 2707 | |
| 2708 | // Generate fallback path for creating DependentString. |
| 2709 | void generateFallback(MacroAssembler& masm); |
| 2710 | }; |
| 2711 | |
| 2712 | void CreateDependentString::generate(MacroAssembler& masm, |
| 2713 | const JSAtomState& names, |
| 2714 | CompileRuntime* runtime, Register base, |
| 2715 | BaseIndex startIndexAddress, |
| 2716 | BaseIndex limitIndexAddress, |
| 2717 | gc::Heap initialStringHeap) { |
| 2718 | JitSpew(JitSpew_Codegen, "# Emitting CreateDependentString (encoding=%s)", |
| 2719 | (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
| 2720 | |
| 2721 | auto newGCString = [&](FallbackKind kind) { |
| 2722 | uint32_t flags; |
| 2723 | switch (kind) { |
| 2724 | case FallbackKind::InlineString: |
| 2725 | flags = StringFlags::thinInlineStringFlags(encoding_); |
| 2726 | break; |
| 2727 | case FallbackKind::FatInlineString: |
| 2728 | flags = StringFlags::fatInlineStringFlags(encoding_); |
| 2729 | break; |
| 2730 | case FallbackKind::NotInlineString: |
| 2731 | flags = StringFlags::dependentStringFlags(encoding_); |
| 2732 | break; |
| 2733 | default: |
| 2734 | MOZ_CRASH("Unexpected FallbackKind")do { do { } while (false); MOZ_ReportCrash("" "Unexpected FallbackKind" , "./../../../../js/src/jit/CodeGenerator.cpp", 2734); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected FallbackKind" ")"); do { MOZ_CrashSequence (__null, 2734); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 2735 | } |
| 2736 | |
| 2737 | if (kind != FallbackKind::FatInlineString) { |
| 2738 | masm.newGCString(string_, temp2_, initialStringHeap, &fallbacks_[kind]); |
| 2739 | } else { |
| 2740 | masm.newGCFatInlineString(string_, temp2_, initialStringHeap, |
| 2741 | &fallbacks_[kind]); |
| 2742 | } |
| 2743 | masm.bind(&joins_[kind]); |
| 2744 | masm.store32(Imm32(flags), Address(string_, JSString::offsetOfFlags())); |
| 2745 | }; |
| 2746 | |
| 2747 | // Compute the string length. |
| 2748 | masm.load32(startIndexAddress, temp2_); |
| 2749 | masm.load32(limitIndexAddress, temp1_); |
| 2750 | masm.sub32(temp2_, temp1_); |
| 2751 | |
| 2752 | Label done, nonEmpty; |
| 2753 | |
| 2754 | // Zero length matches use the empty string. |
| 2755 | masm.branchTest32(Assembler::NonZero, temp1_, temp1_, &nonEmpty); |
| 2756 | masm.movePtr(ImmGCPtr(names.empty_), string_); |
| 2757 | masm.jump(&done); |
| 2758 | |
| 2759 | masm.bind(&nonEmpty); |
| 2760 | |
| 2761 | // Complete matches use the base string. |
| 2762 | Label nonBaseStringMatch; |
| 2763 | masm.branchTest32(Assembler::NonZero, temp2_, temp2_, &nonBaseStringMatch); |
| 2764 | masm.branch32(Assembler::NotEqual, Address(base, JSString::offsetOfLength()), |
| 2765 | temp1_, &nonBaseStringMatch); |
| 2766 | masm.movePtr(base, string_); |
| 2767 | masm.jump(&done); |
| 2768 | |
| 2769 | masm.bind(&nonBaseStringMatch); |
| 2770 | |
| 2771 | Label notInline; |
| 2772 | |
| 2773 | int32_t maxInlineLength = encoding_ == CharEncoding::Latin1 |
| 2774 | ? JSFatInlineString::MAX_LENGTH_LATIN1 |
| 2775 | : JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
| 2776 | masm.branch32(Assembler::Above, temp1_, Imm32(maxInlineLength), ¬Inline); |
| 2777 | { |
| 2778 | // Make a thin or fat inline string. |
| 2779 | Label stringAllocated, fatInline; |
| 2780 | |
| 2781 | int32_t maxThinInlineLength = encoding_ == CharEncoding::Latin1 |
| 2782 | ? JSThinInlineString::MAX_LENGTH_LATIN1 |
| 2783 | : JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
| 2784 | masm.branch32(Assembler::Above, temp1_, Imm32(maxThinInlineLength), |
| 2785 | &fatInline); |
| 2786 | if (encoding_ == CharEncoding::Latin1) { |
| 2787 | // One character Latin-1 strings can be loaded directly from the |
| 2788 | // static strings table. |
| 2789 | Label thinInline; |
| 2790 | masm.branch32(Assembler::Above, temp1_, Imm32(1), &thinInline); |
| 2791 | { |
| 2792 | static_assert( |
| 2793 | StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR, |
| 2794 | "Latin-1 strings can be loaded from static strings"); |
| 2795 | |
| 2796 | masm.loadStringChars(base, temp1_, encoding_); |
| 2797 | masm.loadChar(temp1_, temp2_, temp1_, encoding_); |
| 2798 | |
| 2799 | masm.lookupStaticString(temp1_, string_, runtime->staticStrings()); |
| 2800 | |
| 2801 | masm.jump(&done); |
| 2802 | } |
| 2803 | masm.bind(&thinInline); |
| 2804 | } |
| 2805 | { |
| 2806 | newGCString(FallbackKind::InlineString); |
| 2807 | masm.jump(&stringAllocated); |
| 2808 | } |
| 2809 | masm.bind(&fatInline); |
| 2810 | { |
| 2811 | newGCString(FallbackKind::FatInlineString); |
| 2812 | } |
| 2813 | masm.bind(&stringAllocated); |
| 2814 | |
| 2815 | masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); |
| 2816 | |
| 2817 | masm.push(string_); |
| 2818 | masm.push(base); |
| 2819 | |
| 2820 | 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" ")" , "./../../../../js/src/jit/CodeGenerator.cpp", 2821); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") (" "startIndexAddress is still valid after stack pushes" ")"); do { MOZ_CrashSequence(__null, 2821); __attribute__((nomerge)) :: abort(); } while (false); } } while (false) |
| 2821 | "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" ")" , "./../../../../js/src/jit/CodeGenerator.cpp", 2821); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "startIndexAddress.base == FramePointer" ") (" "startIndexAddress is still valid after stack pushes" ")"); do { MOZ_CrashSequence(__null, 2821); __attribute__((nomerge)) :: abort(); } while (false); } } while (false); |
| 2822 | |
| 2823 | // Load chars pointer for the new string. |
| 2824 | masm.loadInlineStringCharsForStore(string_, string_); |
| 2825 | |
| 2826 | // Load the source characters pointer. |
| 2827 | masm.loadStringChars(base, temp2_, encoding_); |
| 2828 | masm.load32(startIndexAddress, base); |
| 2829 | masm.addToCharPtr(temp2_, base, encoding_); |
| 2830 | |
| 2831 | CopyStringChars(masm, string_, temp2_, temp1_, base, encoding_); |
| 2832 | |
| 2833 | masm.pop(base); |
| 2834 | masm.pop(string_); |
| 2835 | |
| 2836 | masm.jump(&done); |
| 2837 | } |
| 2838 | |
| 2839 | masm.bind(¬Inline); |
| 2840 | |
| 2841 | { |
| 2842 | // Make a dependent string. |
| 2843 | // Warning: string may be tenured (if the fallback case is hit), so |
| 2844 | // stores into it must be post barriered. |
| 2845 | newGCString(FallbackKind::NotInlineString); |
| 2846 | |
| 2847 | masm.store32(temp1_, Address(string_, JSString::offsetOfLength())); |
| 2848 | |
| 2849 | masm.loadNonInlineStringChars(base, temp1_, encoding_); |
| 2850 | masm.load32(startIndexAddress, temp2_); |
| 2851 | masm.addToCharPtr(temp1_, temp2_, encoding_); |
| 2852 | masm.storeNonInlineStringChars(temp1_, string_); |
| 2853 | |
| 2854 | EmitInitDependentStringBase(masm, string_, base, temp1_, temp2_, |
| 2855 | /* needsPostBarrier = */ true); |
| 2856 | } |
| 2857 | |
| 2858 | masm.bind(&done); |
| 2859 | } |
| 2860 | |
| 2861 | void CreateDependentString::generateFallback(MacroAssembler& masm) { |
| 2862 | JitSpew(JitSpew_Codegen, |
| 2863 | "# Emitting CreateDependentString fallback (encoding=%s)", |
| 2864 | (encoding_ == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
| 2865 | |
| 2866 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
| 2867 | regsToSave.takeUnchecked(string_); |
| 2868 | regsToSave.takeUnchecked(temp2_); |
| 2869 | |
| 2870 | for (FallbackKind kind : mozilla::MakeEnumeratedRange(FallbackKind::Count)) { |
| 2871 | masm.bind(&fallbacks_[kind]); |
| 2872 | |
| 2873 | masm.PushRegsInMask(regsToSave); |
| 2874 | |
| 2875 | using Fn = void* (*)(JSContext * cx); |
| 2876 | masm.setupUnalignedABICall(string_); |
| 2877 | masm.loadJSContext(string_); |
| 2878 | masm.passABIArg(string_); |
| 2879 | if (kind == FallbackKind::FatInlineString) { |
| 2880 | masm.callWithABI<Fn, AllocateFatInlineString>(); |
| 2881 | } else { |
| 2882 | masm.callWithABI<Fn, AllocateDependentString>(); |
| 2883 | } |
| 2884 | masm.storeCallPointerResult(string_); |
| 2885 | |
| 2886 | masm.PopRegsInMask(regsToSave); |
| 2887 | |
| 2888 | masm.branchPtr(Assembler::Equal, string_, ImmWord(0), failure_); |
| 2889 | |
| 2890 | masm.jump(&joins_[kind]); |
| 2891 | } |
| 2892 | } |
| 2893 | |
| 2894 | // Generate the RegExpMatcher and RegExpExecMatch stubs. These are very similar, |
| 2895 | // but RegExpExecMatch also has to load and update .lastIndex for global/sticky |
| 2896 | // regular expressions. |
| 2897 | static JitCode* GenerateRegExpMatchStubShared(JSContext* cx, |
| 2898 | gc::Heap initialStringHeap, |
| 2899 | JitZone::StubKind kind) { |
| 2900 | bool isExecMatch = kind == JitZone::StubKind::RegExpExecMatch; |
| 2901 | MOZ_ASSERT_IF(!isExecMatch, kind == JitZone::StubKind::RegExpMatcher)do { if (!isExecMatch) { do { static_assert( mozilla::detail:: AssertionConditionType<decltype(kind == JitZone::StubKind:: RegExpMatcher)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(kind == JitZone::StubKind::RegExpMatcher ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "kind == JitZone::StubKind::RegExpMatcher", "./../../../../js/src/jit/CodeGenerator.cpp" , 2901); AnnotateMozCrashReason("MOZ_ASSERT" "(" "kind == JitZone::StubKind::RegExpMatcher" ")"); do { MOZ_CrashSequence(__null, 2901); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 2902 | |
| 2903 | if (isExecMatch) { |
| 2904 | JitSpew(JitSpew_Codegen, "# Emitting RegExpExecMatch stub"); |
| 2905 | } else { |
| 2906 | JitSpew(JitSpew_Codegen, "# Emitting RegExpMatcher stub"); |
| 2907 | } |
| 2908 | |
| 2909 | // |initialStringHeap| could be stale after a GC. |
| 2910 | JS::AutoCheckCannotGC nogc(cx); |
| 2911 | |
| 2912 | Register regexp = RegExpMatcherRegExpReg; |
| 2913 | Register input = RegExpMatcherStringReg; |
| 2914 | Register lastIndex = RegExpMatcherLastIndexReg; |
| 2915 | ValueOperand result = JSReturnOperand; |
| 2916 | |
| 2917 | // We are free to clobber all registers, as LRegExpMatcher is a call |
| 2918 | // instruction. |
| 2919 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 2920 | regs.take(input); |
| 2921 | regs.take(regexp); |
| 2922 | regs.take(lastIndex); |
| 2923 | |
| 2924 | Register temp1 = regs.takeAny(); |
| 2925 | Register temp2 = regs.takeAny(); |
| 2926 | Register temp3 = regs.takeAny(); |
| 2927 | Register maybeTemp4 = InvalidReg; |
| 2928 | if (!regs.empty()) { |
| 2929 | // There are not enough registers on x86. |
| 2930 | maybeTemp4 = regs.takeAny(); |
| 2931 | } |
| 2932 | Register maybeTemp5 = InvalidReg; |
| 2933 | if (!regs.empty()) { |
| 2934 | // There are not enough registers on x86. |
| 2935 | maybeTemp5 = regs.takeAny(); |
| 2936 | } |
| 2937 | |
| 2938 | Address flagsSlot(regexp, RegExpObject::offsetOfFlags()); |
| 2939 | Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex()); |
| 2940 | |
| 2941 | TempAllocator temp(&cx->tempLifoAlloc()); |
| 2942 | JitContext jcx(cx); |
| 2943 | StackMacroAssembler masm(cx, temp); |
| 2944 | AutoCreatedBy acb(masm, "GenerateRegExpMatchStubShared"); |
| 2945 | |
| 2946 | #ifdef JS_USE_LINK_REGISTER |
| 2947 | masm.pushReturnAddress(); |
| 2948 | #endif |
| 2949 | masm.push(FramePointer); |
| 2950 | masm.moveStackPtrTo(FramePointer); |
| 2951 | |
| 2952 | Label notFoundZeroLastIndex; |
| 2953 | if (isExecMatch) { |
| 2954 | masm.loadRegExpLastIndex(regexp, input, lastIndex, ¬FoundZeroLastIndex); |
| 2955 | } |
| 2956 | |
| 2957 | Label notFound, oolEntry; |
| 2958 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
| 2959 | temp3, initialStringHeap, ¬Found, &oolEntry, |
| 2960 | kind)) { |
| 2961 | return nullptr; |
| 2962 | } |
| 2963 | |
| 2964 | // If a regexp has named captures, fall back to the OOL stub, which |
| 2965 | // will end up calling CreateRegExpMatchResults. |
| 2966 | Register shared = temp2; |
| 2967 | masm.unboxNonDouble(Address(regexp, NativeObject::getFixedSlotOffsetTyped( |
| 2968 | RegExpObject::SHARED_SLOT)), |
| 2969 | shared, JSVAL_TYPE_PRIVATE_GCTHING); |
| 2970 | masm.branchPtr(Assembler::NotEqual, |
| 2971 | Address(shared, RegExpShared::offsetOfGroupsTemplate()), |
| 2972 | ImmWord(0), &oolEntry); |
| 2973 | |
| 2974 | // Similarly, if the |hasIndices| flag is set, fall back to the OOL stub. |
| 2975 | masm.branchTest32(Assembler::NonZero, |
| 2976 | Address(shared, RegExpShared::offsetOfFlags()), |
| 2977 | Imm32(int32_t(JS::RegExpFlag::HasIndices)), &oolEntry); |
| 2978 | |
| 2979 | Address pairCountAddress = RegExpPairCountAddress(); |
| 2980 | |
| 2981 | // Construct the result. |
| 2982 | Register object = temp1; |
| 2983 | { |
| 2984 | // In most cases, the array will have just 1-2 elements, so we optimize for |
| 2985 | // that by emitting separate code paths for capacity 2/6/14 (= 4/8/16 slots |
| 2986 | // because two slots are used for the elements header). |
| 2987 | |
| 2988 | // Load the array length in temp2 and the shape in temp3. |
| 2989 | Label allocated; |
| 2990 | masm.load32(pairCountAddress, temp2); |
| 2991 | size_t offset = GlobalObjectData::offsetOfRegExpRealm() + |
| 2992 | RegExpRealm::offsetOfNormalMatchResultShape(); |
| 2993 | masm.loadGlobalObjectData(temp3); |
| 2994 | masm.loadPtr(Address(temp3, offset), temp3); |
| 2995 | |
| 2996 | auto emitAllocObject = [&](size_t elementCapacity) { |
| 2997 | gc::AllocKind kind = GuessArrayGCKind(elementCapacity); |
| 2998 | MOZ_ASSERT(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(& ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" , "./../../../../js/src/jit/CodeGenerator.cpp", 2999); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" ")"); do { MOZ_CrashSequence(__null, 2999); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 2999 | gc::FinalizeKind::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(& ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" , "./../../../../js/src/jit/CodeGenerator.cpp", 2999); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" ")"); do { MOZ_CrashSequence(__null, 2999); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3000 | MOZ_ASSERT(!IsFinalizedKind(kind))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsFinalizedKind(kind))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsFinalizedKind(kind)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!IsFinalizedKind(kind)" , "./../../../../js/src/jit/CodeGenerator.cpp", 3000); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!IsFinalizedKind(kind)" ")"); do { MOZ_CrashSequence (__null, 3000); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 3001 | |
| 3002 | #ifdef DEBUG1 |
| 3003 | // Assert all of the available slots are used for |elementCapacity| |
| 3004 | // elements. |
| 3005 | size_t usedSlots = ObjectElements::VALUES_PER_HEADER + elementCapacity; |
| 3006 | 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)", "./../../../../js/src/jit/CodeGenerator.cpp" , 3006); AnnotateMozCrashReason("MOZ_ASSERT" "(" "usedSlots == GetGCKindSlots(kind)" ")"); do { MOZ_CrashSequence(__null, 3006); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3007 | #endif |
| 3008 | |
| 3009 | constexpr size_t numUsedDynamicSlots = |
| 3010 | RegExpRealm::MatchResultObjectSlotSpan; |
| 3011 | constexpr size_t numDynamicSlots = |
| 3012 | RegExpRealm::MatchResultObjectNumDynamicSlots; |
| 3013 | constexpr size_t arrayLength = 1; |
| 3014 | masm.createArrayWithFixedElements(object, temp3, temp2, temp3, |
| 3015 | arrayLength, elementCapacity, |
| 3016 | numUsedDynamicSlots, numDynamicSlots, |
| 3017 | kind, gc::Heap::Default, &oolEntry); |
| 3018 | }; |
| 3019 | |
| 3020 | Label moreThan2; |
| 3021 | masm.branch32(Assembler::Above, temp2, Imm32(2), &moreThan2); |
| 3022 | emitAllocObject(2); |
| 3023 | masm.jump(&allocated); |
| 3024 | |
| 3025 | Label moreThan6; |
| 3026 | masm.bind(&moreThan2); |
| 3027 | masm.branch32(Assembler::Above, temp2, Imm32(6), &moreThan6); |
| 3028 | emitAllocObject(6); |
| 3029 | masm.jump(&allocated); |
| 3030 | |
| 3031 | masm.bind(&moreThan6); |
| 3032 | static_assert(RegExpObject::MaxPairCount == 14); |
| 3033 | emitAllocObject(RegExpObject::MaxPairCount); |
| 3034 | |
| 3035 | masm.bind(&allocated); |
| 3036 | } |
| 3037 | |
| 3038 | static_assert(sizeof(MatchPair) == 2 * sizeof(int32_t), |
| 3039 | "MatchPair consists of two int32 values representing the start" |
| 3040 | "and the end offset of the match"); |
| 3041 | |
| 3042 | int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset; |
| 3043 | |
| 3044 | // Incremented by one below for each match pair. |
| 3045 | Register matchIndex = temp2; |
| 3046 | masm.move32(Imm32(0), matchIndex); |
| 3047 | |
| 3048 | // The element in which to store the result of the current match. |
| 3049 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
| 3050 | BaseObjectElementIndex objectMatchElement(object, matchIndex, elementsOffset); |
| 3051 | |
| 3052 | // The current match pair's "start" and "limit" member. |
| 3053 | BaseIndex matchPairStart(FramePointer, matchIndex, TimesEight, |
| 3054 | pairsVectorStartOffset + MatchPair::offsetOfStart()); |
| 3055 | BaseIndex matchPairLimit(FramePointer, matchIndex, TimesEight, |
| 3056 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
| 3057 | |
| 3058 | Label* depStrFailure = &oolEntry; |
| 3059 | Label restoreRegExpAndLastIndex; |
| 3060 | |
| 3061 | Register temp4; |
| 3062 | if (maybeTemp4 == InvalidReg) { |
| 3063 | depStrFailure = &restoreRegExpAndLastIndex; |
| 3064 | |
| 3065 | // We don't have enough registers for a fourth temporary. Reuse |regexp| |
| 3066 | // as a temporary. We restore its value at |restoreRegExpAndLastIndex|. |
| 3067 | masm.push(regexp); |
| 3068 | temp4 = regexp; |
| 3069 | } else { |
| 3070 | temp4 = maybeTemp4; |
| 3071 | } |
| 3072 | |
| 3073 | Register temp5; |
| 3074 | if (maybeTemp5 == InvalidReg) { |
| 3075 | depStrFailure = &restoreRegExpAndLastIndex; |
| 3076 | |
| 3077 | // We don't have enough registers for a fifth temporary. Reuse |lastIndex| |
| 3078 | // as a temporary. We restore its value at |restoreRegExpAndLastIndex|. |
| 3079 | masm.push(lastIndex); |
| 3080 | temp5 = lastIndex; |
| 3081 | } else { |
| 3082 | temp5 = maybeTemp5; |
| 3083 | } |
| 3084 | |
| 3085 | auto maybeRestoreRegExpAndLastIndex = [&]() { |
| 3086 | if (maybeTemp5 == InvalidReg) { |
| 3087 | masm.pop(lastIndex); |
| 3088 | } |
| 3089 | if (maybeTemp4 == InvalidReg) { |
| 3090 | masm.pop(regexp); |
| 3091 | } |
| 3092 | }; |
| 3093 | |
| 3094 | // Loop to construct the match strings. There are two different loops, |
| 3095 | // depending on whether the input is a Two-Byte or a Latin-1 string. |
| 3096 | CreateDependentString depStrs[]{ |
| 3097 | {CharEncoding::TwoByte, temp3, temp4, temp5, depStrFailure}, |
| 3098 | {CharEncoding::Latin1, temp3, temp4, temp5, depStrFailure}, |
| 3099 | }; |
| 3100 | |
| 3101 | { |
| 3102 | Label isLatin1, done; |
| 3103 | masm.branchLatin1String(input, &isLatin1); |
| 3104 | |
| 3105 | for (auto& depStr : depStrs) { |
| 3106 | if (depStr.encoding() == CharEncoding::Latin1) { |
| 3107 | masm.bind(&isLatin1); |
| 3108 | } |
| 3109 | |
| 3110 | Label matchLoop; |
| 3111 | masm.bind(&matchLoop); |
| 3112 | |
| 3113 | static_assert(MatchPair::NoMatch == -1, |
| 3114 | "MatchPair::start is negative if no match was found"); |
| 3115 | |
| 3116 | Label isUndefined, storeDone; |
| 3117 | masm.branch32(Assembler::LessThan, matchPairStart, Imm32(0), |
| 3118 | &isUndefined); |
| 3119 | { |
| 3120 | depStr.generate(masm, cx->names(), CompileRuntime::get(cx->runtime()), |
| 3121 | input, matchPairStart, matchPairLimit, |
| 3122 | initialStringHeap); |
| 3123 | |
| 3124 | // Storing into nursery-allocated results object's elements; no post |
| 3125 | // barrier. |
| 3126 | masm.storeValue(JSVAL_TYPE_STRING, depStr.string(), objectMatchElement); |
| 3127 | masm.jump(&storeDone); |
| 3128 | } |
| 3129 | masm.bind(&isUndefined); |
| 3130 | { |
| 3131 | masm.storeValue(UndefinedValue(), objectMatchElement); |
| 3132 | } |
| 3133 | masm.bind(&storeDone); |
| 3134 | |
| 3135 | masm.add32(Imm32(1), matchIndex); |
| 3136 | masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, |
| 3137 | &done); |
| 3138 | masm.jump(&matchLoop); |
| 3139 | } |
| 3140 | |
| 3141 | #ifdef DEBUG1 |
| 3142 | masm.assumeUnreachable("The match string loop doesn't fall through."); |
| 3143 | #endif |
| 3144 | |
| 3145 | masm.bind(&done); |
| 3146 | } |
| 3147 | |
| 3148 | maybeRestoreRegExpAndLastIndex(); |
| 3149 | |
| 3150 | // Fill in the rest of the output object. |
| 3151 | masm.store32( |
| 3152 | matchIndex, |
| 3153 | Address(object, |
| 3154 | elementsOffset + ObjectElements::offsetOfInitializedLength())); |
| 3155 | masm.store32( |
| 3156 | matchIndex, |
| 3157 | Address(object, elementsOffset + ObjectElements::offsetOfLength())); |
| 3158 | |
| 3159 | Address firstMatchPairStartAddress( |
| 3160 | FramePointer, pairsVectorStartOffset + MatchPair::offsetOfStart()); |
| 3161 | Address firstMatchPairLimitAddress( |
| 3162 | FramePointer, pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
| 3163 | |
| 3164 | static_assert(RegExpRealm::MatchResultObjectIndexSlot == 0, |
| 3165 | "First slot holds the 'index' property"); |
| 3166 | static_assert(RegExpRealm::MatchResultObjectInputSlot == 1, |
| 3167 | "Second slot holds the 'input' property"); |
| 3168 | |
| 3169 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2); |
| 3170 | |
| 3171 | masm.load32(firstMatchPairStartAddress, temp3); |
| 3172 | masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0)); |
| 3173 | |
| 3174 | // No post barrier needed (address is within nursery object.) |
| 3175 | masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value))); |
| 3176 | |
| 3177 | // For the ExecMatch stub, if the regular expression is global or sticky, we |
| 3178 | // have to update its .lastIndex slot. |
| 3179 | if (isExecMatch) { |
| 3180 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 3180); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "object != lastIndex" ")"); do { MOZ_CrashSequence (__null, 3180); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 3181 | Label notGlobalOrSticky; |
| 3182 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 3183 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
| 3184 | ¬GlobalOrSticky); |
| 3185 | masm.load32(firstMatchPairLimitAddress, lastIndex); |
| 3186 | masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot); |
| 3187 | masm.bind(¬GlobalOrSticky); |
| 3188 | } |
| 3189 | |
| 3190 | // All done! |
| 3191 | masm.tagValue(JSVAL_TYPE_OBJECT, object, result); |
| 3192 | masm.pop(FramePointer); |
| 3193 | masm.ret(); |
| 3194 | |
| 3195 | masm.bind(¬Found); |
| 3196 | if (isExecMatch) { |
| 3197 | Label notGlobalOrSticky; |
| 3198 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 3199 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
| 3200 | ¬GlobalOrSticky); |
| 3201 | masm.bind(¬FoundZeroLastIndex); |
| 3202 | masm.storeValue(Int32Value(0), lastIndexSlot); |
| 3203 | masm.bind(¬GlobalOrSticky); |
| 3204 | } |
| 3205 | masm.moveValue(NullValue(), result); |
| 3206 | masm.pop(FramePointer); |
| 3207 | masm.ret(); |
| 3208 | |
| 3209 | // Fallback paths for CreateDependentString. |
| 3210 | for (auto& depStr : depStrs) { |
| 3211 | depStr.generateFallback(masm); |
| 3212 | } |
| 3213 | |
| 3214 | // Fall-through to the ool entry after restoring the registers. |
| 3215 | masm.bind(&restoreRegExpAndLastIndex); |
| 3216 | maybeRestoreRegExpAndLastIndex(); |
| 3217 | |
| 3218 | // Use an undefined value to signal to the caller that the OOL stub needs to |
| 3219 | // be called. |
| 3220 | masm.bind(&oolEntry); |
| 3221 | masm.moveValue(UndefinedValue(), result); |
| 3222 | masm.pop(FramePointer); |
| 3223 | masm.ret(); |
| 3224 | |
| 3225 | Linker linker(masm); |
| 3226 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
| 3227 | if (!code) { |
| 3228 | return nullptr; |
| 3229 | } |
| 3230 | |
| 3231 | const char* name = isExecMatch ? "RegExpExecMatchStub" : "RegExpMatcherStub"; |
| 3232 | CollectPerfSpewerJitCodeProfile(code, name); |
| 3233 | #ifdef MOZ_VTUNE1 |
| 3234 | vtune::MarkStub(code, name); |
| 3235 | #endif |
| 3236 | |
| 3237 | return code; |
| 3238 | } |
| 3239 | |
| 3240 | JitCode* JitZone::generateRegExpMatcherStub(JSContext* cx) { |
| 3241 | return GenerateRegExpMatchStubShared(cx, initialStringHeap, |
| 3242 | JitZone::StubKind::RegExpMatcher); |
| 3243 | } |
| 3244 | |
| 3245 | JitCode* JitZone::generateRegExpExecMatchStub(JSContext* cx) { |
| 3246 | return GenerateRegExpMatchStubShared(cx, initialStringHeap, |
| 3247 | JitZone::StubKind::RegExpExecMatch); |
| 3248 | } |
| 3249 | |
| 3250 | void CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir) { |
| 3251 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3251); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg" ")"); do { MOZ_CrashSequence(__null, 3251); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3252 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3252); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg" ")"); do { MOZ_CrashSequence(__null, 3252); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3253 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 3253); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg" ")"); do { MOZ_CrashSequence(__null, 3253); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3254 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3254); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand" ")"); do { MOZ_CrashSequence(__null, 3254); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3255 | |
| 3256 | #if defined(JS_NUNBOX32) |
| 3257 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type); |
| 3258 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data); |
| 3259 | static_assert(RegExpMatcherStringReg != JSReturnReg_Type); |
| 3260 | static_assert(RegExpMatcherStringReg != JSReturnReg_Data); |
| 3261 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Type); |
| 3262 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg_Data); |
| 3263 | #elif defined(JS_PUNBOX641) |
| 3264 | static_assert(RegExpMatcherRegExpReg != JSReturnReg); |
| 3265 | static_assert(RegExpMatcherStringReg != JSReturnReg); |
| 3266 | static_assert(RegExpMatcherLastIndexReg != JSReturnReg); |
| 3267 | #endif |
| 3268 | |
| 3269 | masm.reserveStack(RegExpReservedStack); |
| 3270 | |
| 3271 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 3272 | Register lastIndex = ToRegister(lir->lastIndex()); |
| 3273 | Register input = ToRegister(lir->string()); |
| 3274 | Register regexp = ToRegister(lir->regexp()); |
| 3275 | |
| 3276 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3277 | regs.take(lastIndex); |
| 3278 | regs.take(input); |
| 3279 | regs.take(regexp); |
| 3280 | Register temp = regs.takeAny(); |
| 3281 | |
| 3282 | masm.computeEffectiveAddress( |
| 3283 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
| 3284 | |
| 3285 | pushArg(temp); |
| 3286 | pushArg(lastIndex); |
| 3287 | pushArg(input); |
| 3288 | pushArg(regexp); |
| 3289 | |
| 3290 | // We are not using oolCallVM because we are in a Call, and that live |
| 3291 | // registers are already saved by the the register allocator. |
| 3292 | using Fn = bool (*)(JSContext*, HandleObject regexp, HandleString input, |
| 3293 | int32_t lastIndex, MatchPairs* pairs, |
| 3294 | MutableHandleValue output); |
| 3295 | callVM<Fn, RegExpMatcherRaw>(lir); |
| 3296 | |
| 3297 | masm.jump(ool.rejoin()); |
| 3298 | }); |
| 3299 | addOutOfLineCode(ool, lir->mir()); |
| 3300 | |
| 3301 | JitCode* regExpMatcherStub = |
| 3302 | snapshot_->getZoneStub(JitZone::StubKind::RegExpMatcher); |
| 3303 | masm.call(regExpMatcherStub); |
| 3304 | masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry()); |
| 3305 | masm.bind(ool->rejoin()); |
| 3306 | |
| 3307 | masm.freeStack(RegExpReservedStack); |
| 3308 | } |
| 3309 | |
| 3310 | void CodeGenerator::visitRegExpExecMatch(LRegExpExecMatch* lir) { |
| 3311 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3311); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpMatcherRegExpReg" ")"); do { MOZ_CrashSequence(__null, 3311); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3312 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3312); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpMatcherStringReg" ")"); do { MOZ_CrashSequence(__null, 3312); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3313 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutValue(lir) == JSReturnOperand" ")"); do { MOZ_CrashSequence(__null, 3313); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3314 | |
| 3315 | #if defined(JS_NUNBOX32) |
| 3316 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Type); |
| 3317 | static_assert(RegExpMatcherRegExpReg != JSReturnReg_Data); |
| 3318 | static_assert(RegExpMatcherStringReg != JSReturnReg_Type); |
| 3319 | static_assert(RegExpMatcherStringReg != JSReturnReg_Data); |
| 3320 | #elif defined(JS_PUNBOX641) |
| 3321 | static_assert(RegExpMatcherRegExpReg != JSReturnReg); |
| 3322 | static_assert(RegExpMatcherStringReg != JSReturnReg); |
| 3323 | #endif |
| 3324 | |
| 3325 | masm.reserveStack(RegExpReservedStack); |
| 3326 | |
| 3327 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 3328 | Register input = ToRegister(lir->string()); |
| 3329 | Register regexp = ToRegister(lir->regexp()); |
| 3330 | |
| 3331 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3332 | regs.take(input); |
| 3333 | regs.take(regexp); |
| 3334 | Register temp = regs.takeAny(); |
| 3335 | |
| 3336 | masm.computeEffectiveAddress( |
| 3337 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
| 3338 | |
| 3339 | pushArg(temp); |
| 3340 | pushArg(input); |
| 3341 | pushArg(regexp); |
| 3342 | |
| 3343 | // We are not using oolCallVM because we are in a Call and live registers |
| 3344 | // have already been saved by the register allocator. |
| 3345 | using Fn = |
| 3346 | bool (*)(JSContext*, Handle<RegExpObject*> regexp, HandleString input, |
| 3347 | MatchPairs* pairs, MutableHandleValue output); |
| 3348 | callVM<Fn, RegExpBuiltinExecMatchFromJit>(lir); |
| 3349 | masm.jump(ool.rejoin()); |
| 3350 | }); |
| 3351 | addOutOfLineCode(ool, lir->mir()); |
| 3352 | |
| 3353 | JitCode* regExpExecMatchStub = |
| 3354 | snapshot_->getZoneStub(JitZone::StubKind::RegExpExecMatch); |
| 3355 | masm.call(regExpExecMatchStub); |
| 3356 | masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry()); |
| 3357 | |
| 3358 | masm.bind(ool->rejoin()); |
| 3359 | masm.freeStack(RegExpReservedStack); |
| 3360 | } |
| 3361 | |
| 3362 | JitCode* JitZone::generateRegExpSearcherStub(JSContext* cx) { |
| 3363 | JitSpew(JitSpew_Codegen, "# Emitting RegExpSearcher stub"); |
| 3364 | |
| 3365 | Register regexp = RegExpSearcherRegExpReg; |
| 3366 | Register input = RegExpSearcherStringReg; |
| 3367 | Register lastIndex = RegExpSearcherLastIndexReg; |
| 3368 | Register result = ReturnReg; |
| 3369 | |
| 3370 | // We are free to clobber all registers, as LRegExpSearcher is a call |
| 3371 | // instruction. |
| 3372 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3373 | regs.take(input); |
| 3374 | regs.take(regexp); |
| 3375 | regs.take(lastIndex); |
| 3376 | |
| 3377 | Register temp1 = regs.takeAny(); |
| 3378 | Register temp2 = regs.takeAny(); |
| 3379 | Register temp3 = regs.takeAny(); |
| 3380 | |
| 3381 | TempAllocator temp(&cx->tempLifoAlloc()); |
| 3382 | JitContext jcx(cx); |
| 3383 | StackMacroAssembler masm(cx, temp); |
| 3384 | AutoCreatedBy acb(masm, "JitZone::generateRegExpSearcherStub"); |
| 3385 | |
| 3386 | #ifdef JS_USE_LINK_REGISTER |
| 3387 | masm.pushReturnAddress(); |
| 3388 | #endif |
| 3389 | masm.push(FramePointer); |
| 3390 | masm.moveStackPtrTo(FramePointer); |
| 3391 | |
| 3392 | #ifdef DEBUG1 |
| 3393 | // Store sentinel value to cx->regExpSearcherLastLimit. |
| 3394 | // See comment in RegExpSearcherImpl. |
| 3395 | masm.loadJSContext(temp1); |
| 3396 | masm.store32(Imm32(RegExpSearcherLastLimitSentinel), |
| 3397 | Address(temp1, JSContext::offsetOfRegExpSearcherLastLimit())); |
| 3398 | #endif |
| 3399 | |
| 3400 | Label notFound, oolEntry; |
| 3401 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
| 3402 | temp3, initialStringHeap, ¬Found, &oolEntry, |
| 3403 | JitZone::StubKind::RegExpSearcher)) { |
| 3404 | return nullptr; |
| 3405 | } |
| 3406 | |
| 3407 | int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset; |
| 3408 | Address matchPairStart(FramePointer, |
| 3409 | pairsVectorStartOffset + MatchPair::offsetOfStart()); |
| 3410 | Address matchPairLimit(FramePointer, |
| 3411 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
| 3412 | |
| 3413 | // Store match limit to cx->regExpSearcherLastLimit and return the index. |
| 3414 | masm.load32(matchPairLimit, result); |
| 3415 | masm.loadJSContext(input); |
| 3416 | masm.store32(result, |
| 3417 | Address(input, JSContext::offsetOfRegExpSearcherLastLimit())); |
| 3418 | masm.load32(matchPairStart, result); |
| 3419 | masm.pop(FramePointer); |
| 3420 | masm.ret(); |
| 3421 | |
| 3422 | masm.bind(¬Found); |
| 3423 | masm.move32(Imm32(RegExpSearcherResultNotFound), result); |
| 3424 | masm.pop(FramePointer); |
| 3425 | masm.ret(); |
| 3426 | |
| 3427 | masm.bind(&oolEntry); |
| 3428 | masm.move32(Imm32(RegExpSearcherResultFailed), result); |
| 3429 | masm.pop(FramePointer); |
| 3430 | masm.ret(); |
| 3431 | |
| 3432 | Linker linker(masm); |
| 3433 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
| 3434 | if (!code) { |
| 3435 | return nullptr; |
| 3436 | } |
| 3437 | |
| 3438 | CollectPerfSpewerJitCodeProfile(code, "RegExpSearcherStub"); |
| 3439 | #ifdef MOZ_VTUNE1 |
| 3440 | vtune::MarkStub(code, "RegExpSearcherStub"); |
| 3441 | #endif |
| 3442 | |
| 3443 | return code; |
| 3444 | } |
| 3445 | |
| 3446 | void CodeGenerator::visitRegExpSearcher(LRegExpSearcher* lir) { |
| 3447 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3447); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpSearcherRegExpReg" ")"); do { MOZ_CrashSequence(__null, 3447); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3448 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpSearcherStringReg" ")"); do { MOZ_CrashSequence(__null, 3448); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3449 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 3449); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->lastIndex()) == RegExpSearcherLastIndexReg" ")"); do { MOZ_CrashSequence(__null, 3449); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3450 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 3450); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { MOZ_CrashSequence(__null, 3450); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3451 | |
| 3452 | static_assert(RegExpSearcherRegExpReg != ReturnReg); |
| 3453 | static_assert(RegExpSearcherStringReg != ReturnReg); |
| 3454 | static_assert(RegExpSearcherLastIndexReg != ReturnReg); |
| 3455 | |
| 3456 | masm.reserveStack(RegExpReservedStack); |
| 3457 | |
| 3458 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 3459 | Register lastIndex = ToRegister(lir->lastIndex()); |
| 3460 | Register input = ToRegister(lir->string()); |
| 3461 | Register regexp = ToRegister(lir->regexp()); |
| 3462 | |
| 3463 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3464 | regs.take(lastIndex); |
| 3465 | regs.take(input); |
| 3466 | regs.take(regexp); |
| 3467 | Register temp = regs.takeAny(); |
| 3468 | |
| 3469 | masm.computeEffectiveAddress( |
| 3470 | Address(masm.getStackPointer(), InputOutputDataSize), temp); |
| 3471 | |
| 3472 | pushArg(temp); |
| 3473 | pushArg(lastIndex); |
| 3474 | pushArg(input); |
| 3475 | pushArg(regexp); |
| 3476 | |
| 3477 | // We are not using oolCallVM because we are in a Call, and that live |
| 3478 | // registers are already saved by the the register allocator. |
| 3479 | using Fn = bool (*)(JSContext* cx, HandleObject regexp, HandleString input, |
| 3480 | int32_t lastIndex, MatchPairs* pairs, int32_t* result); |
| 3481 | callVM<Fn, RegExpSearcherRaw>(lir); |
| 3482 | |
| 3483 | masm.jump(ool.rejoin()); |
| 3484 | }); |
| 3485 | addOutOfLineCode(ool, lir->mir()); |
| 3486 | |
| 3487 | JitCode* regExpSearcherStub = |
| 3488 | snapshot_->getZoneStub(JitZone::StubKind::RegExpSearcher); |
| 3489 | masm.call(regExpSearcherStub); |
| 3490 | masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpSearcherResultFailed), |
| 3491 | ool->entry()); |
| 3492 | masm.bind(ool->rejoin()); |
| 3493 | |
| 3494 | masm.freeStack(RegExpReservedStack); |
| 3495 | } |
| 3496 | |
| 3497 | void CodeGenerator::visitRegExpSearcherLastLimit( |
| 3498 | LRegExpSearcherLastLimit* lir) { |
| 3499 | Register result = ToRegister(lir->output()); |
| 3500 | Register scratch = ToRegister(lir->temp0()); |
| 3501 | |
| 3502 | masm.loadAndClearRegExpSearcherLastLimit(result, scratch); |
| 3503 | } |
| 3504 | |
| 3505 | JitCode* JitZone::generateRegExpExecTestStub(JSContext* cx) { |
| 3506 | JitSpew(JitSpew_Codegen, "# Emitting RegExpExecTest stub"); |
| 3507 | |
| 3508 | Register regexp = RegExpExecTestRegExpReg; |
| 3509 | Register input = RegExpExecTestStringReg; |
| 3510 | Register result = ReturnReg; |
| 3511 | |
| 3512 | TempAllocator temp(&cx->tempLifoAlloc()); |
| 3513 | JitContext jcx(cx); |
| 3514 | StackMacroAssembler masm(cx, temp); |
| 3515 | AutoCreatedBy acb(masm, "JitZone::generateRegExpExecTestStub"); |
| 3516 | |
| 3517 | #ifdef JS_USE_LINK_REGISTER |
| 3518 | masm.pushReturnAddress(); |
| 3519 | #endif |
| 3520 | masm.push(FramePointer); |
| 3521 | masm.moveStackPtrTo(FramePointer); |
| 3522 | |
| 3523 | // We are free to clobber all registers, as LRegExpExecTest is a call |
| 3524 | // instruction. |
| 3525 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 3526 | regs.take(input); |
| 3527 | regs.take(regexp); |
| 3528 | |
| 3529 | // Ensure lastIndex != result. |
| 3530 | regs.take(result); |
| 3531 | Register lastIndex = regs.takeAny(); |
| 3532 | regs.add(result); |
| 3533 | Register temp1 = regs.takeAny(); |
| 3534 | Register temp2 = regs.takeAny(); |
| 3535 | Register temp3 = regs.takeAny(); |
| 3536 | |
| 3537 | Address flagsSlot(regexp, RegExpObject::offsetOfFlags()); |
| 3538 | Address lastIndexSlot(regexp, RegExpObject::offsetOfLastIndex()); |
| 3539 | |
| 3540 | // Load lastIndex and skip RegExp execution if needed. |
| 3541 | Label notFoundZeroLastIndex; |
| 3542 | masm.loadRegExpLastIndex(regexp, input, lastIndex, ¬FoundZeroLastIndex); |
| 3543 | |
| 3544 | Label notFound, oolEntry; |
| 3545 | if (!PrepareAndExecuteRegExp(masm, regexp, input, lastIndex, temp1, temp2, |
| 3546 | temp3, initialStringHeap, ¬Found, &oolEntry, |
| 3547 | JitZone::StubKind::RegExpExecTest)) { |
| 3548 | return nullptr; |
| 3549 | } |
| 3550 | |
| 3551 | // Set `result` to true/false to indicate found/not-found, or to |
| 3552 | // RegExpExecTestResultFailed if we have to retry in C++. If the regular |
| 3553 | // expression is global or sticky, we also have to update its .lastIndex slot. |
| 3554 | |
| 3555 | Label done; |
| 3556 | int32_t pairsVectorStartOffset = RegExpPairsVectorStartOffset; |
| 3557 | Address matchPairLimit(FramePointer, |
| 3558 | pairsVectorStartOffset + MatchPair::offsetOfLimit()); |
| 3559 | |
| 3560 | masm.move32(Imm32(1), result); |
| 3561 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 3562 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
| 3563 | &done); |
| 3564 | masm.load32(matchPairLimit, lastIndex); |
| 3565 | masm.storeValue(JSVAL_TYPE_INT32, lastIndex, lastIndexSlot); |
| 3566 | masm.jump(&done); |
| 3567 | |
| 3568 | masm.bind(¬Found); |
| 3569 | masm.move32(Imm32(0), result); |
| 3570 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 3571 | Imm32(JS::RegExpFlag::Global | JS::RegExpFlag::Sticky), |
| 3572 | &done); |
| 3573 | masm.storeValue(Int32Value(0), lastIndexSlot); |
| 3574 | masm.jump(&done); |
| 3575 | |
| 3576 | masm.bind(¬FoundZeroLastIndex); |
| 3577 | masm.move32(Imm32(0), result); |
| 3578 | masm.storeValue(Int32Value(0), lastIndexSlot); |
| 3579 | masm.jump(&done); |
| 3580 | |
| 3581 | masm.bind(&oolEntry); |
| 3582 | masm.move32(Imm32(RegExpExecTestResultFailed), result); |
| 3583 | |
| 3584 | masm.bind(&done); |
| 3585 | masm.pop(FramePointer); |
| 3586 | masm.ret(); |
| 3587 | |
| 3588 | Linker linker(masm); |
| 3589 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
| 3590 | if (!code) { |
| 3591 | return nullptr; |
| 3592 | } |
| 3593 | |
| 3594 | CollectPerfSpewerJitCodeProfile(code, "RegExpExecTestStub"); |
| 3595 | #ifdef MOZ_VTUNE1 |
| 3596 | vtune::MarkStub(code, "RegExpExecTestStub"); |
| 3597 | #endif |
| 3598 | |
| 3599 | return code; |
| 3600 | } |
| 3601 | |
| 3602 | void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) { |
| 3603 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3603); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->regexp()) == RegExpExecTestRegExpReg" ")"); do { MOZ_CrashSequence(__null, 3603); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3604 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3604); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(lir->string()) == RegExpExecTestStringReg" ")"); do { MOZ_CrashSequence(__null, 3604); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3605 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 3605); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { MOZ_CrashSequence(__null, 3605); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3606 | |
| 3607 | static_assert(RegExpExecTestRegExpReg != ReturnReg); |
| 3608 | static_assert(RegExpExecTestStringReg != ReturnReg); |
| 3609 | |
| 3610 | masm.reserveStack(RegExpReservedStack); |
| 3611 | |
| 3612 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 3613 | Register input = ToRegister(lir->string()); |
| 3614 | Register regexp = ToRegister(lir->regexp()); |
| 3615 | |
| 3616 | pushArg(input); |
| 3617 | pushArg(regexp); |
| 3618 | |
| 3619 | // We are not using oolCallVM because we are in a Call and live registers |
| 3620 | // have already been saved by the register allocator. |
| 3621 | using Fn = bool (*)(JSContext* cx, Handle<RegExpObject*> regexp, |
| 3622 | HandleString input, bool* result); |
| 3623 | callVM<Fn, RegExpBuiltinExecTestFromJit>(lir); |
| 3624 | |
| 3625 | masm.jump(ool.rejoin()); |
| 3626 | }); |
| 3627 | addOutOfLineCode(ool, lir->mir()); |
| 3628 | |
| 3629 | JitCode* regExpExecTestStub = |
| 3630 | snapshot_->getZoneStub(JitZone::StubKind::RegExpExecTest); |
| 3631 | masm.call(regExpExecTestStub); |
| 3632 | |
| 3633 | masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpExecTestResultFailed), |
| 3634 | ool->entry()); |
| 3635 | |
| 3636 | masm.bind(ool->rejoin()); |
| 3637 | |
| 3638 | masm.freeStack(RegExpReservedStack); |
| 3639 | } |
| 3640 | |
| 3641 | void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) { |
| 3642 | Register regexp = ToRegister(ins->regexp()); |
| 3643 | Register input = ToRegister(ins->input()); |
| 3644 | Register output = ToRegister(ins->output()); |
| 3645 | |
| 3646 | using Fn = |
| 3647 | bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*); |
| 3648 | auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>( |
| 3649 | ins, ArgList(regexp, input), StoreRegisterTo(output)); |
| 3650 | |
| 3651 | // Load RegExpShared in |output|. |
| 3652 | Label vmCall; |
| 3653 | masm.loadParsedRegExpShared(regexp, output, ool->entry()); |
| 3654 | |
| 3655 | // Return true iff pairCount > 1. |
| 3656 | Label returnTrue; |
| 3657 | masm.branch32(Assembler::Above, |
| 3658 | Address(output, RegExpShared::offsetOfPairCount()), Imm32(1), |
| 3659 | &returnTrue); |
| 3660 | masm.move32(Imm32(0), output); |
| 3661 | masm.jump(ool->rejoin()); |
| 3662 | |
| 3663 | masm.bind(&returnTrue); |
| 3664 | masm.move32(Imm32(1), output); |
| 3665 | |
| 3666 | masm.bind(ool->rejoin()); |
| 3667 | } |
| 3668 | |
| 3669 | static void FindFirstDollarIndex(MacroAssembler& masm, Register str, |
| 3670 | Register len, Register temp0, Register temp1, |
| 3671 | Register output, CharEncoding encoding) { |
| 3672 | #ifdef DEBUG1 |
| 3673 | Label ok; |
| 3674 | masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); |
| 3675 | masm.assumeUnreachable("Length should be greater than 0."); |
| 3676 | masm.bind(&ok); |
| 3677 | #endif |
| 3678 | |
| 3679 | Register chars = temp0; |
| 3680 | masm.loadStringChars(str, chars, encoding); |
| 3681 | |
| 3682 | masm.move32(Imm32(0), output); |
| 3683 | |
| 3684 | Label start, done; |
| 3685 | masm.bind(&start); |
| 3686 | |
| 3687 | Register currentChar = temp1; |
| 3688 | masm.loadChar(chars, output, currentChar, encoding); |
| 3689 | masm.branch32(Assembler::Equal, currentChar, Imm32('$'), &done); |
| 3690 | |
| 3691 | masm.add32(Imm32(1), output); |
| 3692 | masm.branch32(Assembler::NotEqual, output, len, &start); |
| 3693 | |
| 3694 | masm.move32(Imm32(-1), output); |
| 3695 | |
| 3696 | masm.bind(&done); |
| 3697 | } |
| 3698 | |
| 3699 | void CodeGenerator::visitGetFirstDollarIndex(LGetFirstDollarIndex* ins) { |
| 3700 | Register str = ToRegister(ins->str()); |
| 3701 | Register output = ToRegister(ins->output()); |
| 3702 | Register temp0 = ToRegister(ins->temp0()); |
| 3703 | Register temp1 = ToRegister(ins->temp1()); |
| 3704 | Register len = ToRegister(ins->temp2()); |
| 3705 | |
| 3706 | using Fn = bool (*)(JSContext*, JSString*, int32_t*); |
| 3707 | OutOfLineCode* ool = oolCallVM<Fn, GetFirstDollarIndexRaw>( |
| 3708 | ins, ArgList(str), StoreRegisterTo(output)); |
| 3709 | |
| 3710 | masm.branchIfRope(str, ool->entry()); |
| 3711 | masm.loadStringLength(str, len); |
| 3712 | |
| 3713 | Label isLatin1, done; |
| 3714 | masm.branchLatin1String(str, &isLatin1); |
| 3715 | { |
| 3716 | FindFirstDollarIndex(masm, str, len, temp0, temp1, output, |
| 3717 | CharEncoding::TwoByte); |
| 3718 | masm.jump(&done); |
| 3719 | } |
| 3720 | masm.bind(&isLatin1); |
| 3721 | { |
| 3722 | FindFirstDollarIndex(masm, str, len, temp0, temp1, output, |
| 3723 | CharEncoding::Latin1); |
| 3724 | } |
| 3725 | masm.bind(&done); |
| 3726 | masm.bind(ool->rejoin()); |
| 3727 | } |
| 3728 | |
| 3729 | void CodeGenerator::visitStringReplace(LStringReplace* lir) { |
| 3730 | if (lir->replacement()->isConstant()) { |
| 3731 | pushArg(ImmGCPtr(lir->replacement()->toConstant()->toString())); |
| 3732 | } else { |
| 3733 | pushArg(ToRegister(lir->replacement())); |
| 3734 | } |
| 3735 | |
| 3736 | if (lir->pattern()->isConstant()) { |
| 3737 | pushArg(ImmGCPtr(lir->pattern()->toConstant()->toString())); |
| 3738 | } else { |
| 3739 | pushArg(ToRegister(lir->pattern())); |
| 3740 | } |
| 3741 | |
| 3742 | if (lir->string()->isConstant()) { |
| 3743 | pushArg(ImmGCPtr(lir->string()->toConstant()->toString())); |
| 3744 | } else { |
| 3745 | pushArg(ToRegister(lir->string())); |
| 3746 | } |
| 3747 | |
| 3748 | using Fn = |
| 3749 | JSString* (*)(JSContext*, HandleString, HandleString, HandleString); |
| 3750 | if (lir->mir()->isFlatReplacement()) { |
| 3751 | callVM<Fn, StringFlatReplaceString>(lir); |
| 3752 | } else { |
| 3753 | callVM<Fn, StringReplace>(lir); |
| 3754 | } |
| 3755 | } |
| 3756 | |
| 3757 | void CodeGenerator::visitBinaryValueCache(LBinaryValueCache* lir) { |
| 3758 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 3759 | TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir->lhs())); |
| 3760 | TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir->rhs())); |
| 3761 | ValueOperand output = ToOutValue(lir); |
| 3762 | |
| 3763 | JSOp jsop = lir->mir()->jsop(); |
| 3764 | |
| 3765 | switch (jsop) { |
| 3766 | case JSOp::Add: |
| 3767 | case JSOp::Sub: |
| 3768 | case JSOp::Mul: |
| 3769 | case JSOp::Div: |
| 3770 | case JSOp::Mod: |
| 3771 | case JSOp::Pow: |
| 3772 | case JSOp::BitAnd: |
| 3773 | case JSOp::BitOr: |
| 3774 | case JSOp::BitXor: |
| 3775 | case JSOp::Lsh: |
| 3776 | case JSOp::Rsh: |
| 3777 | case JSOp::Ursh: { |
| 3778 | IonBinaryArithIC ic(liveRegs, lhs, rhs, output); |
| 3779 | addIC(lir, allocateIC(ic)); |
| 3780 | return; |
| 3781 | } |
| 3782 | default: |
| 3783 | MOZ_CRASH("Unsupported jsop in MBinaryValueCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryValueCache" , "./../../../../js/src/jit/CodeGenerator.cpp", 3783); AnnotateMozCrashReason ("MOZ_CRASH(" "Unsupported jsop in MBinaryValueCache" ")"); do { MOZ_CrashSequence(__null, 3783); __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
| 3784 | } |
| 3785 | } |
| 3786 | |
| 3787 | void CodeGenerator::visitBinaryBoolCache(LBinaryBoolCache* lir) { |
| 3788 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 3789 | TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(lir->lhs())); |
| 3790 | TypedOrValueRegister rhs = TypedOrValueRegister(ToValue(lir->rhs())); |
| 3791 | Register output = ToRegister(lir->output()); |
| 3792 | |
| 3793 | JSOp jsop = lir->mir()->jsop(); |
| 3794 | |
| 3795 | switch (jsop) { |
| 3796 | case JSOp::Lt: |
| 3797 | case JSOp::Le: |
| 3798 | case JSOp::Gt: |
| 3799 | case JSOp::Ge: |
| 3800 | case JSOp::Eq: |
| 3801 | case JSOp::Ne: |
| 3802 | case JSOp::StrictEq: |
| 3803 | case JSOp::StrictNe: { |
| 3804 | IonCompareIC ic(liveRegs, lhs, rhs, output); |
| 3805 | addIC(lir, allocateIC(ic)); |
| 3806 | return; |
| 3807 | } |
| 3808 | default: |
| 3809 | MOZ_CRASH("Unsupported jsop in MBinaryBoolCache")do { do { } while (false); MOZ_ReportCrash("" "Unsupported jsop in MBinaryBoolCache" , "./../../../../js/src/jit/CodeGenerator.cpp", 3809); AnnotateMozCrashReason ("MOZ_CRASH(" "Unsupported jsop in MBinaryBoolCache" ")"); do { MOZ_CrashSequence(__null, 3809); __attribute__((nomerge)) :: abort(); } while (false); } while (false); |
| 3810 | } |
| 3811 | } |
| 3812 | |
| 3813 | void CodeGenerator::visitUnaryCache(LUnaryCache* lir) { |
| 3814 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 3815 | TypedOrValueRegister input = TypedOrValueRegister(ToValue(lir->input())); |
| 3816 | ValueOperand output = ToOutValue(lir); |
| 3817 | |
| 3818 | IonUnaryArithIC ic(liveRegs, input, output); |
| 3819 | addIC(lir, allocateIC(ic)); |
| 3820 | } |
| 3821 | |
| 3822 | void CodeGenerator::visitModuleMetadata(LModuleMetadata* lir) { |
| 3823 | pushArg(ImmGCPtr(lir->mir()->module())); |
| 3824 | |
| 3825 | using Fn = JSObject* (*)(JSContext*, HandleObject); |
| 3826 | callVM<Fn, js::GetOrCreateModuleMetaObject>(lir); |
| 3827 | } |
| 3828 | |
| 3829 | void CodeGenerator::visitDynamicImport(LDynamicImport* lir) { |
| 3830 | pushArg(Imm32(uint8_t(lir->mir()->phase()))); |
| 3831 | pushArg(ToValue(lir->options())); |
| 3832 | pushArg(ToValue(lir->specifier())); |
| 3833 | pushArg(ImmGCPtr(current->mir()->info().script())); |
| 3834 | |
| 3835 | using Fn = JSObject* (*)(JSContext*, HandleScript, HandleValue, HandleValue, |
| 3836 | ImportPhase); |
| 3837 | callVM<Fn, js::StartDynamicModuleImport>(lir); |
| 3838 | } |
| 3839 | |
| 3840 | void CodeGenerator::visitLambda(LLambda* lir) { |
| 3841 | Register envChain = ToRegister(lir->environmentChain()); |
| 3842 | Register output = ToRegister(lir->output()); |
| 3843 | Register tempReg = ToRegister(lir->temp0()); |
| 3844 | gc::Heap heap = lir->mir()->initialHeap(); |
| 3845 | |
| 3846 | JSFunction* fun = lir->mir()->templateFunction(); |
| 3847 | MOZ_ASSERT(fun->isTenured())do { static_assert( mozilla::detail::AssertionConditionType< decltype(fun->isTenured())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(fun->isTenured()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("fun->isTenured()" , "./../../../../js/src/jit/CodeGenerator.cpp", 3847); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun->isTenured()" ")"); do { MOZ_CrashSequence (__null, 3847); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 3848 | |
| 3849 | using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject, gc::Heap); |
| 3850 | OutOfLineCode* ool = oolCallVM<Fn, js::LambdaOptimizedFallback>( |
| 3851 | lir, ArgList(ImmGCPtr(fun), envChain, Imm32(uint32_t(heap))), |
| 3852 | StoreRegisterTo(output)); |
| 3853 | |
| 3854 | TemplateObject templateObject(fun); |
| 3855 | masm.createGCObject(output, tempReg, templateObject, heap, ool->entry(), |
| 3856 | /* initContents = */ true, |
| 3857 | AllocSiteInput(gc::CatchAllAllocSite::Optimized)); |
| 3858 | |
| 3859 | masm.storeValue(JSVAL_TYPE_OBJECT, envChain, |
| 3860 | Address(output, JSFunction::offsetOfEnvironment())); |
| 3861 | |
| 3862 | // If we specified the tenured heap then we need a post barrier. Otherwise no |
| 3863 | // post barrier needed as the output is guaranteed to be allocated in the |
| 3864 | // nursery. |
| 3865 | if (heap == gc::Heap::Tenured) { |
| 3866 | Label skipBarrier; |
| 3867 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, envChain, tempReg, |
| 3868 | &skipBarrier); |
| 3869 | saveVolatile(tempReg); |
| 3870 | emitPostWriteBarrier(output); |
| 3871 | restoreVolatile(tempReg); |
| 3872 | masm.bind(&skipBarrier); |
| 3873 | } |
| 3874 | |
| 3875 | masm.bind(ool->rejoin()); |
| 3876 | } |
| 3877 | |
| 3878 | void CodeGenerator::visitFunctionWithProto(LFunctionWithProto* lir) { |
| 3879 | Register envChain = ToRegister(lir->envChain()); |
| 3880 | Register prototype = ToRegister(lir->prototype()); |
| 3881 | |
| 3882 | pushArg(prototype); |
| 3883 | pushArg(envChain); |
| 3884 | pushArg(ImmGCPtr(lir->mir()->function())); |
| 3885 | |
| 3886 | using Fn = |
| 3887 | JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject); |
| 3888 | callVM<Fn, js::FunWithProtoOperation>(lir); |
| 3889 | } |
| 3890 | |
| 3891 | void CodeGenerator::visitSetFunName(LSetFunName* lir) { |
| 3892 | pushArg(Imm32(lir->mir()->prefixKind())); |
| 3893 | pushArg(ToValue(lir->name())); |
| 3894 | pushArg(ToRegister(lir->fun())); |
| 3895 | |
| 3896 | using Fn = |
| 3897 | bool (*)(JSContext*, HandleFunction, HandleValue, FunctionPrefixKind); |
| 3898 | callVM<Fn, js::SetFunctionName>(lir); |
| 3899 | } |
| 3900 | |
| 3901 | void CodeGenerator::visitOsiPoint(LOsiPoint* lir) { |
| 3902 | // Note: markOsiPoint ensures enough space exists between the last |
| 3903 | // LOsiPoint and this one to patch adjacent call instructions. |
| 3904 | |
| 3905 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 3905); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { MOZ_CrashSequence(__null, 3905); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3906 | |
| 3907 | uint32_t osiCallPointOffset = markOsiPoint(lir); |
| 3908 | |
| 3909 | LSafepoint* safepoint = lir->associatedSafepoint(); |
| 3910 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 3910); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!safepoint->osiCallPointOffset()" ")"); do { MOZ_CrashSequence(__null, 3910); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3911 | safepoint->setOsiCallPointOffset(osiCallPointOffset); |
| 3912 | |
| 3913 | #ifdef DEBUG1 |
| 3914 | // There should be no movegroups or other instructions between |
| 3915 | // an instruction and its OsiPoint. This is necessary because |
| 3916 | // we use the OsiPoint's snapshot from within VM calls. |
| 3917 | for (LInstructionReverseIterator iter(current->rbegin(lir)); |
| 3918 | iter != current->rend(); iter++) { |
| 3919 | if (*iter == lir) { |
| 3920 | continue; |
| 3921 | } |
| 3922 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 3922); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!iter->isMoveGroup()" ")"); do { MOZ_CrashSequence (__null, 3922); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 3923 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 3923); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter->safepoint() == safepoint" ")"); do { MOZ_CrashSequence(__null, 3923); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3924 | break; |
| 3925 | } |
| 3926 | #endif |
| 3927 | |
| 3928 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
| 3929 | if (shouldVerifyOsiPointRegs(safepoint)) { |
| 3930 | verifyOsiPointRegs(safepoint); |
| 3931 | } |
| 3932 | #endif |
| 3933 | } |
| 3934 | |
| 3935 | void CodeGenerator::visitPhi(LPhi* lir) { |
| 3936 | MOZ_CRASH("Unexpected LPhi in CodeGenerator")do { do { } while (false); MOZ_ReportCrash("" "Unexpected LPhi in CodeGenerator" , "./../../../../js/src/jit/CodeGenerator.cpp", 3936); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected LPhi in CodeGenerator" ")"); do { MOZ_CrashSequence (__null, 3936); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 3937 | } |
| 3938 | |
| 3939 | void CodeGenerator::visitGoto(LGoto* lir) { |
| 3940 | // It would be valid to do simply `jumpToBlock(lir->target()); return;`. |
| 3941 | // That shorts out chains of completely empty (apart from the final Goto) |
| 3942 | // blocks. However, we try to do a bit better by shorting out chains of |
| 3943 | // blocks which are either completely empty or contain only MoveGroups, by |
| 3944 | // emitting the MoveGroups at this point. Hence this is a very limited form |
| 3945 | // of tail duplication, in which the duplicated tail(s) consist entirely of |
| 3946 | // MoveGroups. |
| 3947 | // |
| 3948 | // Ideally this logic should be in CodeGeneratorShared::jumpToBlock as it |
| 3949 | // would cover more use cases. That unfortunately creates a circular |
| 3950 | // dependency between the classes CodeGeneratorShared, CodeGenerator{Arch} |
| 3951 | // and CodeGenerator, which is not easy to resolve; specifically, |
| 3952 | // CodeGeneratorShared would need to call CodeGenerator::visitMoveGroup, but |
| 3953 | // CodeGenerator is (indirectly) a child class of CodeGeneratorShared. |
| 3954 | // |
| 3955 | // See CodeGeneratorShared::jumpToBlock(MBasicBlock*) as reference. |
| 3956 | |
| 3957 | // If we can fall through to the target, don't bother cloning MoveGroups |
| 3958 | // because this would turn the fallthrough into an explicit jump. |
| 3959 | MBasicBlock* target = lir->target(); |
| 3960 | if (isNextBlock(target->lir())) { |
| 3961 | return; |
| 3962 | } |
| 3963 | |
| 3964 | uint32_t numMoveGroupsCloned = 0; |
| 3965 | while (true) { |
| 3966 | LBlock* targetLBlock = target->lir(); |
| 3967 | LBlock* nextLBlock = targetLBlock->isMoveGroupsThenGoto(); |
| 3968 | if (!nextLBlock) { |
| 3969 | break; |
| 3970 | } |
| 3971 | // This block is merely zero-or-more MoveGroups followed by a Goto. Emit |
| 3972 | // the MoveGroups and keep following the chain. |
| 3973 | auto iter = targetLBlock->begin(); |
| 3974 | while (true) { |
| 3975 | LInstruction* ins = *iter; |
| 3976 | if (!ins->isMoveGroup()) { |
| 3977 | break; |
| 3978 | } |
| 3979 | visitMoveGroup(ins->toMoveGroup()); |
| 3980 | iter++; |
| 3981 | numMoveGroupsCloned++; |
| 3982 | } |
| 3983 | // Ensured by LBlock::isMoveGroupsThenGoto |
| 3984 | MOZ_ASSERT((*iter)->isGoto())do { static_assert( mozilla::detail::AssertionConditionType< decltype((*iter)->isGoto())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!((*iter)->isGoto()))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("(*iter)->isGoto()" , "./../../../../js/src/jit/CodeGenerator.cpp", 3984); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "(*iter)->isGoto()" ")"); do { MOZ_CrashSequence (__null, 3984); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 3985 | MOZ_ASSERT((*iter)->toGoto()->getSuccessor(0)->lir() == nextLBlock)do { static_assert( mozilla::detail::AssertionConditionType< decltype((*iter)->toGoto()->getSuccessor(0)->lir() == nextLBlock)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!((*iter)->toGoto()->getSuccessor (0)->lir() == nextLBlock))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("(*iter)->toGoto()->getSuccessor(0)->lir() == nextLBlock" , "./../../../../js/src/jit/CodeGenerator.cpp", 3985); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "(*iter)->toGoto()->getSuccessor(0)->lir() == nextLBlock" ")"); do { MOZ_CrashSequence(__null, 3985); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3986 | iter++; |
| 3987 | MOZ_RELEASE_ASSERT(iter == targetLBlock->end())do { static_assert( mozilla::detail::AssertionConditionType< decltype(iter == targetLBlock->end())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(iter == targetLBlock->end ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("iter == targetLBlock->end()", "./../../../../js/src/jit/CodeGenerator.cpp" , 3987); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "iter == targetLBlock->end()" ")"); do { MOZ_CrashSequence(__null, 3987); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 3988 | target = nextLBlock->mir(); |
| 3989 | if (numMoveGroupsCloned >= 1) { |
| 3990 | // Be very conservative about cloning. Higher numbers give more |
| 3991 | // aggressive chasing but seem to sometimes cause a slight cycle count |
| 3992 | // regression. In practice, cloning one happens occasionally, cloning of |
| 3993 | // two groups happens very rarely, and cloning of more than 2 groups has |
| 3994 | // only been seen in artificially constructed test cases. |
| 3995 | break; |
| 3996 | } |
| 3997 | } |
| 3998 | |
| 3999 | // If the above loop exited due to hitting the MoveGroup clone limit, we |
| 4000 | // still need to skip past any "trivial" blocks, to avoid asserting in |
| 4001 | // `target->lir()->label()` below. |
| 4002 | target = skipTrivialBlocks(target); |
| 4003 | |
| 4004 | // No jump necessary if we can fall through to the next block. |
| 4005 | if (isNextBlock(target->lir())) { |
| 4006 | return; |
| 4007 | } |
| 4008 | |
| 4009 | masm.jump(target->lir()->label()); |
| 4010 | } |
| 4011 | |
| 4012 | void CodeGenerator::visitTableSwitch(LTableSwitch* ins) { |
| 4013 | MTableSwitch* mir = ins->mir(); |
| 4014 | Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); |
| 4015 | |
| 4016 | Register intIndex; |
| 4017 | if (mir->getOperand(0)->type() != MIRType::Int32) { |
| 4018 | intIndex = ToRegister(ins->temp0()); |
| 4019 | |
| 4020 | // The input is a double, so try and convert it to an integer. |
| 4021 | // If it does not fit in an integer, take the default case. |
| 4022 | masm.convertDoubleToInt32(ToFloatRegister(ins->index()), intIndex, |
| 4023 | defaultcase, false); |
| 4024 | } else { |
| 4025 | intIndex = ToRegister(ins->index()); |
| 4026 | } |
| 4027 | |
| 4028 | emitTableSwitchDispatch(mir, intIndex, ToTempRegisterOrInvalid(ins->temp1())); |
| 4029 | } |
| 4030 | |
| 4031 | void CodeGenerator::visitTableSwitchV(LTableSwitchV* ins) { |
| 4032 | MTableSwitch* mir = ins->mir(); |
| 4033 | Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); |
| 4034 | |
| 4035 | Register index = ToRegister(ins->temp0()); |
| 4036 | ValueOperand value = ToValue(ins->input()); |
| 4037 | Register tag = masm.extractTag(value, index); |
| 4038 | masm.branchTestNumber(Assembler::NotEqual, tag, defaultcase); |
| 4039 | |
| 4040 | Label unboxInt, isInt; |
| 4041 | masm.branchTestInt32(Assembler::Equal, tag, &unboxInt); |
| 4042 | { |
| 4043 | FloatRegister floatIndex = ToFloatRegister(ins->temp1()); |
| 4044 | masm.unboxDouble(value, floatIndex); |
| 4045 | masm.convertDoubleToInt32(floatIndex, index, defaultcase, false); |
| 4046 | masm.jump(&isInt); |
| 4047 | } |
| 4048 | |
| 4049 | masm.bind(&unboxInt); |
| 4050 | masm.unboxInt32(value, index); |
| 4051 | |
| 4052 | masm.bind(&isInt); |
| 4053 | |
| 4054 | emitTableSwitchDispatch(mir, index, ToTempRegisterOrInvalid(ins->temp2())); |
| 4055 | } |
| 4056 | |
| 4057 | void CodeGenerator::visitParameter(LParameter* lir) {} |
| 4058 | |
| 4059 | void CodeGenerator::visitCallee(LCallee* lir) { |
| 4060 | Register callee = ToRegister(lir->output()); |
| 4061 | Address ptr(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
| 4062 | |
| 4063 | masm.loadFunctionFromCalleeToken(ptr, callee); |
| 4064 | } |
| 4065 | |
| 4066 | void CodeGenerator::visitIsConstructing(LIsConstructing* lir) { |
| 4067 | Register output = ToRegister(lir->output()); |
| 4068 | Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
| 4069 | masm.loadPtr(calleeToken, output); |
| 4070 | |
| 4071 | // We must be inside a function. |
| 4072 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 4072); AnnotateMozCrashReason("MOZ_ASSERT" "(" "current->mir()->info().script()->function()" ")"); do { MOZ_CrashSequence(__null, 4072); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4073 | |
| 4074 | // The low bit indicates whether this call is constructing, just clear the |
| 4075 | // other bits. |
| 4076 | static_assert(CalleeToken_Function == 0x0, |
| 4077 | "CalleeTokenTag value should match"); |
| 4078 | static_assert(CalleeToken_FunctionConstructing == 0x1, |
| 4079 | "CalleeTokenTag value should match"); |
| 4080 | masm.andPtr(Imm32(0x1), output); |
| 4081 | } |
| 4082 | |
| 4083 | void CodeGenerator::visitReturn(LReturn* lir) { |
| 4084 | #if defined(JS_NUNBOX32) |
| 4085 | DebugOnly<LAllocation*> type = lir->getOperand(TYPE_INDEX); |
| 4086 | DebugOnly<LAllocation*> payload = lir->getOperand(PAYLOAD_INDEX); |
| 4087 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 4087); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(type) == JSReturnReg_Type" ")"); do { MOZ_CrashSequence(__null, 4087); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4088 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 4088); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(payload) == JSReturnReg_Data" ")" ); do { MOZ_CrashSequence(__null, 4088); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4089 | #elif defined(JS_PUNBOX641) |
| 4090 | DebugOnly<LAllocation*> result = lir->getOperand(0); |
| 4091 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 4091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToRegister(result) == JSReturnReg" ")"); do { MOZ_CrashSequence(__null, 4091); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4092 | #endif |
| 4093 | // Don't emit a jump to the return label if this is the last block, as |
| 4094 | // it'll fall through to the epilogue. |
| 4095 | if (current->mir() != *gen->graph().poBegin()) { |
| 4096 | masm.jump(&returnLabel_); |
| 4097 | } |
| 4098 | } |
| 4099 | |
| 4100 | void CodeGenerator::visitOsrEntry(LOsrEntry* lir) { |
| 4101 | Register temp = ToRegister(lir->temp()); |
| 4102 | |
| 4103 | // Remember the OSR entry offset into the code buffer. |
| 4104 | masm.flushBuffer(); |
| 4105 | setOsrEntryOffset(masm.size()); |
| 4106 | |
| 4107 | // Allocate the full frame for this function |
| 4108 | // Note we have a new entry here. So we reset MacroAssembler::framePushed() |
| 4109 | // to 0, before reserving the stack. |
| 4110 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 4110); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { MOZ_CrashSequence(__null, 4110); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4111 | masm.setFramePushed(0); |
| 4112 | |
| 4113 | // The Baseline code ensured both the frame pointer and stack pointer point to |
| 4114 | // the JitFrameLayout on the stack. |
| 4115 | |
| 4116 | // If profiling, save the current frame pointer to a per-thread global field. |
| 4117 | if (isProfilerInstrumentationEnabled()) { |
| 4118 | masm.profilerEnterFrame(FramePointer, temp); |
| 4119 | } |
| 4120 | |
| 4121 | masm.reserveStack(frameSize()); |
| 4122 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 4122); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { MOZ_CrashSequence(__null, 4122); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4123 | |
| 4124 | // Ensure that the Ion frames is properly aligned. |
| 4125 | masm.assertStackAlignment(JitStackAlignment, 0); |
| 4126 | } |
| 4127 | |
| 4128 | void CodeGenerator::visitOsrEnvironmentChain(LOsrEnvironmentChain* lir) { |
| 4129 | const LAllocation* frame = lir->entry(); |
| 4130 | const LDefinition* object = lir->output(); |
| 4131 | |
| 4132 | const ptrdiff_t frameOffset = |
| 4133 | BaselineFrame::reverseOffsetOfEnvironmentChain(); |
| 4134 | |
| 4135 | masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
| 4136 | } |
| 4137 | |
| 4138 | void CodeGenerator::visitOsrArgumentsObject(LOsrArgumentsObject* lir) { |
| 4139 | const LAllocation* frame = lir->entry(); |
| 4140 | const LDefinition* object = lir->output(); |
| 4141 | |
| 4142 | const ptrdiff_t frameOffset = BaselineFrame::reverseOffsetOfArgsObj(); |
| 4143 | |
| 4144 | masm.loadPtr(Address(ToRegister(frame), frameOffset), ToRegister(object)); |
| 4145 | } |
| 4146 | |
| 4147 | void CodeGenerator::visitOsrValue(LOsrValue* value) { |
| 4148 | const LAllocation* frame = value->entry(); |
| 4149 | const ValueOperand out = ToOutValue(value); |
| 4150 | |
| 4151 | const ptrdiff_t frameOffset = value->mir()->frameOffset(); |
| 4152 | |
| 4153 | masm.loadValue(Address(ToRegister(frame), frameOffset), out); |
| 4154 | } |
| 4155 | |
| 4156 | void CodeGenerator::visitOsrReturnValue(LOsrReturnValue* lir) { |
| 4157 | const LAllocation* frame = lir->entry(); |
| 4158 | const ValueOperand out = ToOutValue(lir); |
| 4159 | |
| 4160 | Address flags = |
| 4161 | Address(ToRegister(frame), BaselineFrame::reverseOffsetOfFlags()); |
| 4162 | Address retval = |
| 4163 | Address(ToRegister(frame), BaselineFrame::reverseOffsetOfReturnValue()); |
| 4164 | |
| 4165 | masm.moveValue(UndefinedValue(), out); |
| 4166 | |
| 4167 | Label done; |
| 4168 | masm.branchTest32(Assembler::Zero, flags, Imm32(BaselineFrame::HAS_RVAL), |
| 4169 | &done); |
| 4170 | masm.loadValue(retval, out); |
| 4171 | masm.bind(&done); |
| 4172 | } |
| 4173 | |
| 4174 | void CodeGenerator::visitStackArgT(LStackArgT* lir) { |
| 4175 | const LAllocation* arg = lir->arg(); |
| 4176 | MIRType argType = lir->type(); |
| 4177 | uint32_t argslot = lir->argslot(); |
| 4178 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 4178); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()" ")"); do { MOZ_CrashSequence(__null, 4178); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4179 | |
| 4180 | Address dest = AddressOfPassedArg(argslot); |
| 4181 | |
| 4182 | if (arg->isFloatReg()) { |
| 4183 | masm.boxDouble(ToFloatRegister(arg), dest); |
| 4184 | } else if (arg->isGeneralReg()) { |
| 4185 | masm.storeValue(ValueTypeFromMIRType(argType), ToRegister(arg), dest); |
| 4186 | } else { |
| 4187 | masm.storeValue(arg->toConstant()->toJSValue(), dest); |
| 4188 | } |
| 4189 | } |
| 4190 | |
| 4191 | void CodeGenerator::visitStackArgV(LStackArgV* lir) { |
| 4192 | ValueOperand val = ToValue(lir->value()); |
| 4193 | uint32_t argslot = lir->argslot(); |
| 4194 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 4194); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "argslot - 1u < graph.argumentSlotCount()" ")"); do { MOZ_CrashSequence(__null, 4194); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4195 | |
| 4196 | masm.storeValue(val, AddressOfPassedArg(argslot)); |
| 4197 | } |
| 4198 | |
| 4199 | void CodeGenerator::visitMoveGroup(LMoveGroup* group) { |
| 4200 | if (!group->numMoves()) { |
| 4201 | return; |
| 4202 | } |
| 4203 | |
| 4204 | MoveResolver& resolver = masm.moveResolver(); |
| 4205 | |
| 4206 | for (size_t i = 0; i < group->numMoves(); i++) { |
| 4207 | const LMove& move = group->getMove(i); |
| 4208 | |
| 4209 | LAllocation from = move.from(); |
| 4210 | LAllocation to = move.to(); |
| 4211 | LDefinition::Type type = move.type(); |
| 4212 | |
| 4213 | // No bogus moves. |
| 4214 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 4214); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to" ")"); do { MOZ_CrashSequence(__null, 4214); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4215 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 4215); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!from.isConstant()" ")"); do { MOZ_CrashSequence (__null, 4215); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4216 | MoveOp::Type moveType; |
| 4217 | switch (type) { |
| 4218 | case LDefinition::OBJECT: |
| 4219 | case LDefinition::SLOTS: |
| 4220 | case LDefinition::WASM_ANYREF: |
| 4221 | case LDefinition::WASM_STRUCT_DATA: |
| 4222 | case LDefinition::WASM_ARRAY_DATA: |
| 4223 | #ifdef JS_NUNBOX32 |
| 4224 | case LDefinition::TYPE: |
| 4225 | case LDefinition::PAYLOAD: |
| 4226 | #else |
| 4227 | case LDefinition::BOX: |
| 4228 | #endif |
| 4229 | case LDefinition::GENERAL: |
| 4230 | case LDefinition::STACKRESULTS: |
| 4231 | moveType = MoveOp::GENERAL; |
| 4232 | break; |
| 4233 | case LDefinition::INT32: |
| 4234 | moveType = MoveOp::INT32; |
| 4235 | break; |
| 4236 | case LDefinition::FLOAT32: |
| 4237 | moveType = MoveOp::FLOAT32; |
| 4238 | break; |
| 4239 | case LDefinition::DOUBLE: |
| 4240 | moveType = MoveOp::DOUBLE; |
| 4241 | break; |
| 4242 | case LDefinition::SIMD128: |
| 4243 | moveType = MoveOp::SIMD128; |
| 4244 | break; |
| 4245 | default: |
| 4246 | MOZ_CRASH("Unexpected move type")do { do { } while (false); MOZ_ReportCrash("" "Unexpected move type" , "./../../../../js/src/jit/CodeGenerator.cpp", 4246); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected move type" ")"); do { MOZ_CrashSequence (__null, 4246); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 4247 | } |
| 4248 | |
| 4249 | masm.propagateOOM( |
| 4250 | resolver.addMove(toMoveOperand(from), toMoveOperand(to), moveType)); |
| 4251 | } |
| 4252 | |
| 4253 | masm.propagateOOM(resolver.resolve()); |
| 4254 | if (masm.oom()) { |
| 4255 | return; |
| 4256 | } |
| 4257 | |
| 4258 | MoveEmitter emitter(masm); |
| 4259 | |
| 4260 | #ifdef JS_CODEGEN_X86 |
| 4261 | if (group->maybeScratchRegister().isGeneralReg()) { |
| 4262 | emitter.setScratchRegister( |
| 4263 | group->maybeScratchRegister().toGeneralReg()->reg()); |
| 4264 | } else { |
| 4265 | resolver.sortMemoryToMemoryMoves(); |
| 4266 | } |
| 4267 | #endif |
| 4268 | |
| 4269 | emitter.emit(resolver); |
| 4270 | emitter.finish(); |
| 4271 | } |
| 4272 | |
| 4273 | void CodeGenerator::visitInteger(LInteger* lir) { |
| 4274 | masm.move32(Imm32(lir->i32()), ToRegister(lir->output())); |
| 4275 | } |
| 4276 | |
| 4277 | void CodeGenerator::visitInteger64(LInteger64* lir) { |
| 4278 | masm.move64(Imm64(lir->i64()), ToOutRegister64(lir)); |
| 4279 | } |
| 4280 | |
| 4281 | void CodeGenerator::visitPointer(LPointer* lir) { |
| 4282 | masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output())); |
| 4283 | } |
| 4284 | |
| 4285 | void CodeGenerator::visitDouble(LDouble* ins) { |
| 4286 | masm.loadConstantDouble(ins->value(), ToFloatRegister(ins->output())); |
| 4287 | } |
| 4288 | |
| 4289 | void CodeGenerator::visitFloat32(LFloat32* ins) { |
| 4290 | masm.loadConstantFloat32(ins->value(), ToFloatRegister(ins->output())); |
| 4291 | } |
| 4292 | |
| 4293 | void CodeGenerator::visitValue(LValue* value) { |
| 4294 | ValueOperand result = ToOutValue(value); |
| 4295 | masm.moveValue(value->value(), result); |
| 4296 | } |
| 4297 | |
| 4298 | void CodeGenerator::visitNurseryObject(LNurseryObject* lir) { |
| 4299 | Register output = ToRegister(lir->output()); |
| 4300 | uint32_t nurseryIndex = lir->mir()->nurseryObjectIndex(); |
| 4301 | |
| 4302 | // Load a pointer to the entry in IonScript's nursery objects list. |
| 4303 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), output); |
| 4304 | masm.propagateOOM(nurseryObjectLabels_.emplaceBack(label, nurseryIndex)); |
| 4305 | |
| 4306 | // Load the JSObject*. |
| 4307 | masm.loadPtr(Address(output, 0), output); |
| 4308 | } |
| 4309 | |
| 4310 | void CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir) { |
| 4311 | // No-op. |
| 4312 | } |
| 4313 | |
| 4314 | void CodeGenerator::visitDebugEnterGCUnsafeRegion( |
| 4315 | LDebugEnterGCUnsafeRegion* lir) { |
| 4316 | Register temp = ToRegister(lir->temp0()); |
| 4317 | |
| 4318 | masm.loadJSContext(temp); |
| 4319 | |
| 4320 | Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion()); |
| 4321 | masm.add32(Imm32(1), inUnsafeRegion); |
| 4322 | |
| 4323 | Label ok; |
| 4324 | masm.branch32(Assembler::GreaterThan, inUnsafeRegion, Imm32(0), &ok); |
| 4325 | masm.assumeUnreachable("unbalanced enter/leave GC unsafe region"); |
| 4326 | masm.bind(&ok); |
| 4327 | } |
| 4328 | |
| 4329 | void CodeGenerator::visitDebugLeaveGCUnsafeRegion( |
| 4330 | LDebugLeaveGCUnsafeRegion* lir) { |
| 4331 | Register temp = ToRegister(lir->temp0()); |
| 4332 | |
| 4333 | masm.loadJSContext(temp); |
| 4334 | |
| 4335 | Address inUnsafeRegion(temp, JSContext::offsetOfInUnsafeRegion()); |
| 4336 | masm.add32(Imm32(-1), inUnsafeRegion); |
| 4337 | |
| 4338 | Label ok; |
| 4339 | masm.branch32(Assembler::GreaterThanOrEqual, inUnsafeRegion, Imm32(0), &ok); |
| 4340 | masm.assumeUnreachable("unbalanced enter/leave GC unsafe region"); |
| 4341 | masm.bind(&ok); |
| 4342 | } |
| 4343 | |
| 4344 | void CodeGenerator::visitSlots(LSlots* lir) { |
| 4345 | Address slots(ToRegister(lir->object()), NativeObject::offsetOfSlots()); |
| 4346 | masm.loadPtr(slots, ToRegister(lir->output())); |
| 4347 | } |
| 4348 | |
| 4349 | void CodeGenerator::visitLoadDynamicSlotV(LLoadDynamicSlotV* lir) { |
| 4350 | ValueOperand dest = ToOutValue(lir); |
| 4351 | Register base = ToRegister(lir->input()); |
| 4352 | int32_t offset = lir->mir()->slot() * sizeof(js::Value); |
| 4353 | |
| 4354 | masm.loadValue(Address(base, offset), dest); |
| 4355 | } |
| 4356 | |
| 4357 | void CodeGenerator::visitLoadDynamicSlotFromOffset( |
| 4358 | LLoadDynamicSlotFromOffset* lir) { |
| 4359 | ValueOperand dest = ToOutValue(lir); |
| 4360 | Register slots = ToRegister(lir->slots()); |
| 4361 | Register offset = ToRegister(lir->offset()); |
| 4362 | |
| 4363 | // slots[offset] |
| 4364 | masm.loadValue(BaseIndex(slots, offset, TimesOne), dest); |
| 4365 | } |
| 4366 | |
| 4367 | static ConstantOrRegister ToConstantOrRegister(const LAllocation* value, |
| 4368 | MIRType valueType) { |
| 4369 | if (value->isConstant()) { |
| 4370 | return ConstantOrRegister(value->toConstant()->toJSValue()); |
| 4371 | } |
| 4372 | return TypedOrValueRegister(valueType, ToAnyRegister(value)); |
| 4373 | } |
| 4374 | |
| 4375 | void CodeGenerator::visitStoreDynamicSlotT(LStoreDynamicSlotT* lir) { |
| 4376 | Register base = ToRegister(lir->slots()); |
| 4377 | int32_t offset = lir->mir()->slot() * sizeof(js::Value); |
| 4378 | Address dest(base, offset); |
| 4379 | |
| 4380 | if (lir->mir()->needsBarrier()) { |
| 4381 | emitPreBarrier(dest); |
| 4382 | } |
| 4383 | |
| 4384 | MIRType valueType = lir->mir()->value()->type(); |
| 4385 | ConstantOrRegister value = ToConstantOrRegister(lir->value(), valueType); |
| 4386 | masm.storeUnboxedValue(value, valueType, dest); |
| 4387 | } |
| 4388 | |
| 4389 | void CodeGenerator::visitStoreDynamicSlotV(LStoreDynamicSlotV* lir) { |
| 4390 | Register base = ToRegister(lir->slots()); |
| 4391 | int32_t offset = lir->mir()->slot() * sizeof(Value); |
| 4392 | |
| 4393 | ValueOperand value = ToValue(lir->value()); |
| 4394 | |
| 4395 | if (lir->mir()->needsBarrier()) { |
| 4396 | emitPreBarrier(Address(base, offset)); |
| 4397 | } |
| 4398 | |
| 4399 | masm.storeValue(value, Address(base, offset)); |
| 4400 | } |
| 4401 | |
| 4402 | void CodeGenerator::visitStoreDynamicSlotFromOffsetV( |
| 4403 | LStoreDynamicSlotFromOffsetV* lir) { |
| 4404 | Register slots = ToRegister(lir->slots()); |
| 4405 | Register offset = ToRegister(lir->offset()); |
| 4406 | ValueOperand value = ToValue(lir->value()); |
| 4407 | Register temp = ToRegister(lir->temp0()); |
| 4408 | |
| 4409 | BaseIndex baseIndex(slots, offset, TimesOne); |
| 4410 | masm.computeEffectiveAddress(baseIndex, temp); |
| 4411 | |
| 4412 | Address address(temp, 0); |
| 4413 | |
| 4414 | emitPreBarrier(address); |
| 4415 | |
| 4416 | // obj->slots[offset] |
| 4417 | masm.storeValue(value, address); |
| 4418 | } |
| 4419 | |
| 4420 | void CodeGenerator::visitStoreDynamicSlotFromOffsetT( |
| 4421 | LStoreDynamicSlotFromOffsetT* lir) { |
| 4422 | Register slots = ToRegister(lir->slots()); |
| 4423 | Register offset = ToRegister(lir->offset()); |
| 4424 | const LAllocation* value = lir->value(); |
| 4425 | MIRType valueType = lir->mir()->value()->type(); |
| 4426 | Register temp = ToRegister(lir->temp0()); |
| 4427 | |
| 4428 | BaseIndex baseIndex(slots, offset, TimesOne); |
| 4429 | masm.computeEffectiveAddress(baseIndex, temp); |
| 4430 | |
| 4431 | Address address(temp, 0); |
| 4432 | |
| 4433 | emitPreBarrier(address); |
| 4434 | |
| 4435 | // obj->slots[offset] |
| 4436 | ConstantOrRegister nvalue = |
| 4437 | value->isConstant() |
| 4438 | ? ConstantOrRegister(value->toConstant()->toJSValue()) |
| 4439 | : TypedOrValueRegister(valueType, ToAnyRegister(value)); |
| 4440 | masm.storeConstantOrRegister(nvalue, address); |
| 4441 | } |
| 4442 | |
| 4443 | void CodeGenerator::visitElements(LElements* lir) { |
| 4444 | Address elements(ToRegister(lir->object()), NativeObject::offsetOfElements()); |
| 4445 | masm.loadPtr(elements, ToRegister(lir->output())); |
| 4446 | } |
| 4447 | |
| 4448 | void CodeGenerator::visitFunctionEnvironment(LFunctionEnvironment* lir) { |
| 4449 | Address environment(ToRegister(lir->function()), |
| 4450 | JSFunction::offsetOfEnvironment()); |
| 4451 | masm.unboxObject(environment, ToRegister(lir->output())); |
| 4452 | } |
| 4453 | |
| 4454 | void CodeGenerator::visitHomeObject(LHomeObject* lir) { |
| 4455 | Register func = ToRegister(lir->function()); |
| 4456 | Address homeObject(func, FunctionExtended::offsetOfMethodHomeObjectSlot()); |
| 4457 | |
| 4458 | masm.assertFunctionIsExtended(func); |
| 4459 | #ifdef DEBUG1 |
| 4460 | Label isObject; |
| 4461 | masm.branchTestObject(Assembler::Equal, homeObject, &isObject); |
| 4462 | masm.assumeUnreachable("[[HomeObject]] must be Object"); |
| 4463 | masm.bind(&isObject); |
| 4464 | #endif |
| 4465 | |
| 4466 | masm.unboxObject(homeObject, ToRegister(lir->output())); |
| 4467 | } |
| 4468 | |
| 4469 | void CodeGenerator::visitHomeObjectSuperBase(LHomeObjectSuperBase* lir) { |
| 4470 | Register homeObject = ToRegister(lir->homeObject()); |
| 4471 | ValueOperand output = ToOutValue(lir); |
| 4472 | Register temp = output.scratchReg(); |
| 4473 | |
| 4474 | masm.loadObjProto(homeObject, temp); |
| 4475 | |
| 4476 | #ifdef DEBUG1 |
| 4477 | // We won't encounter a lazy proto, because the prototype is guaranteed to |
| 4478 | // either be a JSFunction or a PlainObject, and only proxy objects can have a |
| 4479 | // lazy proto. |
| 4480 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 4480); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")" ); do { MOZ_CrashSequence(__null, 4480); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4481 | |
| 4482 | Label proxyCheckDone; |
| 4483 | masm.branchPtr(Assembler::NotEqual, temp, ImmWord(1), &proxyCheckDone); |
| 4484 | masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperBase"); |
| 4485 | masm.bind(&proxyCheckDone); |
| 4486 | #endif |
| 4487 | |
| 4488 | Label nullProto, done; |
| 4489 | masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto); |
| 4490 | |
| 4491 | // Box prototype and return |
| 4492 | masm.tagValue(JSVAL_TYPE_OBJECT, temp, output); |
| 4493 | masm.jump(&done); |
| 4494 | |
| 4495 | masm.bind(&nullProto); |
| 4496 | masm.moveValue(NullValue(), output); |
| 4497 | |
| 4498 | masm.bind(&done); |
| 4499 | } |
| 4500 | |
| 4501 | template <class T> |
| 4502 | static T* ToConstantObject(MDefinition* def) { |
| 4503 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 4503); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "def->isConstant()" ")"); do { MOZ_CrashSequence (__null, 4503); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4504 | return &def->toConstant()->toObject().as<T>(); |
| 4505 | } |
| 4506 | |
| 4507 | void CodeGenerator::visitNewLexicalEnvironmentObject( |
| 4508 | LNewLexicalEnvironmentObject* lir) { |
| 4509 | Register output = ToRegister(lir->output()); |
| 4510 | Register temp = ToRegister(lir->temp0()); |
| 4511 | |
| 4512 | auto* templateObj = ToConstantObject<BlockLexicalEnvironmentObject>( |
| 4513 | lir->mir()->templateObj()); |
| 4514 | auto* scope = &templateObj->scope(); |
| 4515 | gc::Heap initialHeap = gc::Heap::Default; |
| 4516 | |
| 4517 | using Fn = |
| 4518 | BlockLexicalEnvironmentObject* (*)(JSContext*, Handle<LexicalScope*>); |
| 4519 | auto* ool = |
| 4520 | oolCallVM<Fn, BlockLexicalEnvironmentObject::createWithoutEnclosing>( |
| 4521 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
| 4522 | |
| 4523 | TemplateObject templateObject(templateObj); |
| 4524 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
| 4525 | |
| 4526 | masm.bind(ool->rejoin()); |
| 4527 | } |
| 4528 | |
| 4529 | void CodeGenerator::visitNewClassBodyEnvironmentObject( |
| 4530 | LNewClassBodyEnvironmentObject* lir) { |
| 4531 | Register output = ToRegister(lir->output()); |
| 4532 | Register temp = ToRegister(lir->temp0()); |
| 4533 | |
| 4534 | auto* templateObj = ToConstantObject<ClassBodyLexicalEnvironmentObject>( |
| 4535 | lir->mir()->templateObj()); |
| 4536 | auto* scope = &templateObj->scope(); |
| 4537 | gc::Heap initialHeap = gc::Heap::Default; |
| 4538 | |
| 4539 | using Fn = ClassBodyLexicalEnvironmentObject* (*)(JSContext*, |
| 4540 | Handle<ClassBodyScope*>); |
| 4541 | auto* ool = |
| 4542 | oolCallVM<Fn, ClassBodyLexicalEnvironmentObject::createWithoutEnclosing>( |
| 4543 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
| 4544 | |
| 4545 | TemplateObject templateObject(templateObj); |
| 4546 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
| 4547 | |
| 4548 | masm.bind(ool->rejoin()); |
| 4549 | } |
| 4550 | |
| 4551 | void CodeGenerator::visitNewVarEnvironmentObject( |
| 4552 | LNewVarEnvironmentObject* lir) { |
| 4553 | Register output = ToRegister(lir->output()); |
| 4554 | Register temp = ToRegister(lir->temp0()); |
| 4555 | |
| 4556 | auto* templateObj = |
| 4557 | ToConstantObject<VarEnvironmentObject>(lir->mir()->templateObj()); |
| 4558 | auto* scope = &templateObj->scope().as<VarScope>(); |
| 4559 | gc::Heap initialHeap = gc::Heap::Default; |
| 4560 | |
| 4561 | using Fn = VarEnvironmentObject* (*)(JSContext*, Handle<VarScope*>); |
| 4562 | auto* ool = oolCallVM<Fn, VarEnvironmentObject::createWithoutEnclosing>( |
| 4563 | lir, ArgList(ImmGCPtr(scope)), StoreRegisterTo(output)); |
| 4564 | |
| 4565 | TemplateObject templateObject(templateObj); |
| 4566 | masm.createGCObject(output, temp, templateObject, initialHeap, ool->entry()); |
| 4567 | |
| 4568 | masm.bind(ool->rejoin()); |
| 4569 | } |
| 4570 | |
| 4571 | void CodeGenerator::visitGuardShape(LGuardShape* guard) { |
| 4572 | Register obj = ToRegister(guard->object()); |
| 4573 | Register temp = ToTempRegisterOrInvalid(guard->temp0()); |
| 4574 | Label bail; |
| 4575 | masm.branchTestObjShape(Assembler::NotEqual, obj, guard->mir()->shape(), temp, |
| 4576 | obj, &bail); |
| 4577 | bailoutFrom(&bail, guard->snapshot()); |
| 4578 | } |
| 4579 | |
| 4580 | void CodeGenerator::visitGuardFuse(LGuardFuse* guard) { |
| 4581 | auto fuseIndex = guard->mir()->fuseIndex(); |
| 4582 | |
| 4583 | Label bail; |
| 4584 | |
| 4585 | // Bake specific fuse address for Ion code, because we won't share this code |
| 4586 | // across realms. |
| 4587 | GuardFuse* fuse = mirGen().realm->realmFuses().getFuseByIndex(fuseIndex); |
| 4588 | masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(fuse->fuseRef()), |
| 4589 | ImmWord(0), &bail); |
| 4590 | |
| 4591 | bailoutFrom(&bail, guard->snapshot()); |
| 4592 | } |
| 4593 | |
| 4594 | void CodeGenerator::visitGuardMultipleShapes(LGuardMultipleShapes* guard) { |
| 4595 | Register obj = ToRegister(guard->object()); |
| 4596 | Register shapeList = ToRegister(guard->shapeList()); |
| 4597 | Register temp = ToRegister(guard->temp0()); |
| 4598 | Register temp2 = ToRegister(guard->temp1()); |
| 4599 | Register temp3 = ToRegister(guard->temp2()); |
| 4600 | Register spectre = ToTempRegisterOrInvalid(guard->temp3()); |
| 4601 | |
| 4602 | Label bail; |
| 4603 | masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp); |
| 4604 | masm.branchTestObjShapeList(obj, temp, temp2, temp3, spectre, &bail); |
| 4605 | bailoutFrom(&bail, guard->snapshot()); |
| 4606 | } |
| 4607 | |
| 4608 | void CodeGenerator::visitGuardShapeList(LGuardShapeList* guard) { |
| 4609 | Register obj = ToRegister(guard->object()); |
| 4610 | Register temp = ToRegister(guard->temp0()); |
| 4611 | Register spectre = ToTempRegisterOrInvalid(guard->temp1()); |
| 4612 | |
| 4613 | Label done, bail; |
| 4614 | masm.loadObjShapeUnsafe(obj, temp); |
| 4615 | |
| 4616 | // Count the number of branches to emit. |
| 4617 | const auto& shapes = guard->mir()->shapeList()->shapes(); |
| 4618 | size_t branchesLeft = std::count_if(shapes.begin(), shapes.end(), |
| 4619 | [](Shape* s) { return s != nullptr; }); |
| 4620 | MOZ_RELEASE_ASSERT(branchesLeft > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(branchesLeft > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(branchesLeft > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("branchesLeft > 0" , "./../../../../js/src/jit/CodeGenerator.cpp", 4620); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "branchesLeft > 0" ")"); do { MOZ_CrashSequence (__null, 4620); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4621 | |
| 4622 | for (Shape* shape : shapes) { |
| 4623 | if (!shape) { |
| 4624 | continue; |
| 4625 | } |
| 4626 | if (branchesLeft > 1) { |
| 4627 | masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(shape), &done); |
| 4628 | if (spectre != InvalidReg) { |
| 4629 | masm.spectreMovePtr(Assembler::Equal, spectre, obj); |
| 4630 | } |
| 4631 | } else { |
| 4632 | // This is the last branch so invert the condition and jump to |bail|. |
| 4633 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &bail); |
| 4634 | if (spectre != InvalidReg) { |
| 4635 | masm.spectreMovePtr(Assembler::NotEqual, spectre, obj); |
| 4636 | } |
| 4637 | } |
| 4638 | branchesLeft--; |
| 4639 | } |
| 4640 | MOZ_ASSERT(branchesLeft == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(branchesLeft == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(branchesLeft == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("branchesLeft == 0" , "./../../../../js/src/jit/CodeGenerator.cpp", 4640); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "branchesLeft == 0" ")"); do { MOZ_CrashSequence (__null, 4640); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4641 | |
| 4642 | masm.bind(&done); |
| 4643 | bailoutFrom(&bail, guard->snapshot()); |
| 4644 | } |
| 4645 | |
| 4646 | void CodeGenerator::visitGuardShapeListToOffset( |
| 4647 | LGuardShapeListToOffset* guard) { |
| 4648 | Register obj = ToRegister(guard->object()); |
| 4649 | Register temp = ToRegister(guard->temp0()); |
| 4650 | Register spectre = ToTempRegisterOrInvalid(guard->temp1()); |
| 4651 | Register offset = ToRegister(guard->output()); |
| 4652 | |
| 4653 | Label done, bail; |
| 4654 | masm.loadObjShapeUnsafe(obj, temp); |
| 4655 | |
| 4656 | // Count the number of branches to emit. |
| 4657 | const auto& shapes = guard->mir()->shapeList()->shapes(); |
| 4658 | const auto& offsets = guard->mir()->shapeList()->offsets(); |
| 4659 | size_t branchesLeft = std::count_if(shapes.begin(), shapes.end(), |
| 4660 | [](Shape* s) { return s != nullptr; }); |
| 4661 | MOZ_RELEASE_ASSERT(branchesLeft > 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(branchesLeft > 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(branchesLeft > 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("branchesLeft > 0" , "./../../../../js/src/jit/CodeGenerator.cpp", 4661); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "branchesLeft > 0" ")"); do { MOZ_CrashSequence (__null, 4661); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4662 | |
| 4663 | size_t index = 0; |
| 4664 | for (Shape* shape : shapes) { |
| 4665 | if (!shape) { |
| 4666 | index++; |
| 4667 | continue; |
| 4668 | } |
| 4669 | |
| 4670 | if (branchesLeft > 1) { |
| 4671 | Label next; |
| 4672 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &next); |
| 4673 | if (spectre != InvalidReg) { |
| 4674 | masm.spectreMovePtr(Assembler::NotEqual, spectre, obj); |
| 4675 | } |
| 4676 | masm.move32(Imm32(offsets[index]), offset); |
| 4677 | masm.jump(&done); |
| 4678 | masm.bind(&next); |
| 4679 | } else { |
| 4680 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), &bail); |
| 4681 | if (spectre != InvalidReg) { |
| 4682 | masm.spectreMovePtr(Assembler::NotEqual, spectre, obj); |
| 4683 | } |
| 4684 | masm.move32(Imm32(offsets[index]), offset); |
| 4685 | } |
| 4686 | |
| 4687 | branchesLeft--; |
| 4688 | index++; |
| 4689 | } |
| 4690 | MOZ_ASSERT(branchesLeft == 0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(branchesLeft == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(branchesLeft == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("branchesLeft == 0" , "./../../../../js/src/jit/CodeGenerator.cpp", 4690); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "branchesLeft == 0" ")"); do { MOZ_CrashSequence (__null, 4690); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4691 | |
| 4692 | masm.bind(&done); |
| 4693 | bailoutFrom(&bail, guard->snapshot()); |
| 4694 | } |
| 4695 | |
| 4696 | void CodeGenerator::visitGuardMultipleShapesToOffset( |
| 4697 | LGuardMultipleShapesToOffset* guard) { |
| 4698 | Register obj = ToRegister(guard->object()); |
| 4699 | Register shapeList = ToRegister(guard->shapeList()); |
| 4700 | Register temp = ToRegister(guard->temp0()); |
| 4701 | Register temp1 = ToRegister(guard->temp1()); |
| 4702 | Register temp2 = ToRegister(guard->temp2()); |
| 4703 | Register offset = ToRegister(guard->output()); |
| 4704 | Register spectre = JitOptions.spectreObjectMitigations ? offset : InvalidReg; |
| 4705 | |
| 4706 | Label bail; |
| 4707 | masm.loadPtr(Address(shapeList, NativeObject::offsetOfElements()), temp); |
| 4708 | masm.branchTestObjShapeListSetOffset(obj, temp, offset, temp1, temp2, spectre, |
| 4709 | &bail); |
| 4710 | bailoutFrom(&bail, guard->snapshot()); |
| 4711 | } |
| 4712 | |
| 4713 | void CodeGenerator::visitGuardProto(LGuardProto* guard) { |
| 4714 | Register obj = ToRegister(guard->object()); |
| 4715 | Register expected = ToRegister(guard->expected()); |
| 4716 | Register temp = ToRegister(guard->temp0()); |
| 4717 | |
| 4718 | masm.loadObjProto(obj, temp); |
| 4719 | |
| 4720 | Label bail; |
| 4721 | masm.branchPtr(Assembler::NotEqual, temp, expected, &bail); |
| 4722 | bailoutFrom(&bail, guard->snapshot()); |
| 4723 | } |
| 4724 | |
| 4725 | void CodeGenerator::visitGuardNullProto(LGuardNullProto* guard) { |
| 4726 | Register obj = ToRegister(guard->object()); |
| 4727 | Register temp = ToRegister(guard->temp0()); |
| 4728 | |
| 4729 | masm.loadObjProto(obj, temp); |
| 4730 | |
| 4731 | Label bail; |
| 4732 | masm.branchTestPtr(Assembler::NonZero, temp, temp, &bail); |
| 4733 | bailoutFrom(&bail, guard->snapshot()); |
| 4734 | } |
| 4735 | |
| 4736 | void CodeGenerator::visitGuardIsNativeObject(LGuardIsNativeObject* guard) { |
| 4737 | Register obj = ToRegister(guard->object()); |
| 4738 | Register temp = ToRegister(guard->temp0()); |
| 4739 | |
| 4740 | Label bail; |
| 4741 | masm.branchIfNonNativeObj(obj, temp, &bail); |
| 4742 | bailoutFrom(&bail, guard->snapshot()); |
| 4743 | } |
| 4744 | |
| 4745 | void CodeGenerator::visitGuardGlobalGeneration(LGuardGlobalGeneration* guard) { |
| 4746 | Register temp = ToRegister(guard->temp0()); |
| 4747 | Label bail; |
| 4748 | |
| 4749 | masm.load32(AbsoluteAddress(guard->mir()->generationAddr()), temp); |
| 4750 | masm.branch32(Assembler::NotEqual, temp, Imm32(guard->mir()->expected()), |
| 4751 | &bail); |
| 4752 | bailoutFrom(&bail, guard->snapshot()); |
| 4753 | } |
| 4754 | |
| 4755 | void CodeGenerator::visitGuardIsProxy(LGuardIsProxy* guard) { |
| 4756 | Register obj = ToRegister(guard->object()); |
| 4757 | Register temp = ToRegister(guard->temp0()); |
| 4758 | |
| 4759 | Label bail; |
| 4760 | masm.branchTestObjectIsProxy(false, obj, temp, &bail); |
| 4761 | bailoutFrom(&bail, guard->snapshot()); |
| 4762 | } |
| 4763 | |
| 4764 | void CodeGenerator::visitGuardIsNotProxy(LGuardIsNotProxy* guard) { |
| 4765 | Register obj = ToRegister(guard->object()); |
| 4766 | Register temp = ToRegister(guard->temp0()); |
| 4767 | |
| 4768 | Label bail; |
| 4769 | masm.branchTestObjectIsProxy(true, obj, temp, &bail); |
| 4770 | bailoutFrom(&bail, guard->snapshot()); |
| 4771 | } |
| 4772 | |
| 4773 | void CodeGenerator::visitGuardIsNotDOMProxy(LGuardIsNotDOMProxy* guard) { |
| 4774 | Register proxy = ToRegister(guard->proxy()); |
| 4775 | Register temp = ToRegister(guard->temp0()); |
| 4776 | |
| 4777 | Label bail; |
| 4778 | masm.branchTestProxyHandlerFamily(Assembler::Equal, proxy, temp, |
| 4779 | GetDOMProxyHandlerFamily(), &bail); |
| 4780 | bailoutFrom(&bail, guard->snapshot()); |
| 4781 | } |
| 4782 | |
| 4783 | void CodeGenerator::visitProxyGet(LProxyGet* lir) { |
| 4784 | Register proxy = ToRegister(lir->proxy()); |
| 4785 | Register temp = ToRegister(lir->temp0()); |
| 4786 | |
| 4787 | pushArg(lir->mir()->id(), temp); |
| 4788 | pushArg(proxy); |
| 4789 | |
| 4790 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, MutableHandleValue); |
| 4791 | callVM<Fn, ProxyGetProperty>(lir); |
| 4792 | } |
| 4793 | |
| 4794 | void CodeGenerator::visitProxyGetByValue(LProxyGetByValue* lir) { |
| 4795 | Register proxy = ToRegister(lir->proxy()); |
| 4796 | ValueOperand idVal = ToValue(lir->idVal()); |
| 4797 | |
| 4798 | pushArg(idVal); |
| 4799 | pushArg(proxy); |
| 4800 | |
| 4801 | using Fn = |
| 4802 | bool (*)(JSContext*, HandleObject, HandleValue, MutableHandleValue); |
| 4803 | callVM<Fn, ProxyGetPropertyByValue>(lir); |
| 4804 | } |
| 4805 | |
| 4806 | void CodeGenerator::visitProxyHasProp(LProxyHasProp* lir) { |
| 4807 | Register proxy = ToRegister(lir->proxy()); |
| 4808 | ValueOperand idVal = ToValue(lir->id()); |
| 4809 | |
| 4810 | pushArg(idVal); |
| 4811 | pushArg(proxy); |
| 4812 | |
| 4813 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool*); |
| 4814 | if (lir->mir()->hasOwn()) { |
| 4815 | callVM<Fn, ProxyHasOwn>(lir); |
| 4816 | } else { |
| 4817 | callVM<Fn, ProxyHas>(lir); |
| 4818 | } |
| 4819 | } |
| 4820 | |
| 4821 | void CodeGenerator::visitProxySet(LProxySet* lir) { |
| 4822 | Register proxy = ToRegister(lir->proxy()); |
| 4823 | ValueOperand rhs = ToValue(lir->rhs()); |
| 4824 | Register temp = ToRegister(lir->temp0()); |
| 4825 | |
| 4826 | pushArg(Imm32(lir->mir()->strict())); |
| 4827 | pushArg(rhs); |
| 4828 | pushArg(lir->mir()->id(), temp); |
| 4829 | pushArg(proxy); |
| 4830 | |
| 4831 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool); |
| 4832 | callVM<Fn, ProxySetProperty>(lir); |
| 4833 | } |
| 4834 | |
| 4835 | void CodeGenerator::visitProxySetByValue(LProxySetByValue* lir) { |
| 4836 | Register proxy = ToRegister(lir->proxy()); |
| 4837 | ValueOperand idVal = ToValue(lir->idVal()); |
| 4838 | ValueOperand rhs = ToValue(lir->rhs()); |
| 4839 | |
| 4840 | pushArg(Imm32(lir->mir()->strict())); |
| 4841 | pushArg(rhs); |
| 4842 | pushArg(idVal); |
| 4843 | pushArg(proxy); |
| 4844 | |
| 4845 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool); |
| 4846 | callVM<Fn, ProxySetPropertyByValue>(lir); |
| 4847 | } |
| 4848 | |
| 4849 | void CodeGenerator::visitCallSetArrayLength(LCallSetArrayLength* lir) { |
| 4850 | Register obj = ToRegister(lir->obj()); |
| 4851 | ValueOperand rhs = ToValue(lir->rhs()); |
| 4852 | |
| 4853 | pushArg(Imm32(lir->mir()->strict())); |
| 4854 | pushArg(rhs); |
| 4855 | pushArg(obj); |
| 4856 | |
| 4857 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, bool); |
| 4858 | callVM<Fn, jit::SetArrayLength>(lir); |
| 4859 | } |
| 4860 | |
| 4861 | void CodeGenerator::visitMegamorphicLoadSlot(LMegamorphicLoadSlot* lir) { |
| 4862 | Register obj = ToRegister(lir->object()); |
| 4863 | Register temp0 = ToRegister(lir->temp0()); |
| 4864 | Register temp1 = ToRegister(lir->temp1()); |
| 4865 | Register temp2 = ToRegister(lir->temp2()); |
| 4866 | Register temp3 = ToRegister(lir->temp3()); |
| 4867 | ValueOperand output = ToOutValue(lir); |
| 4868 | |
| 4869 | Label done; |
| 4870 | PropertyKey id = lir->mir()->name(); |
| 4871 | masm.movePropertyKey(id, temp0); |
| 4872 | masm.move32(Imm32(HashPropertyKeyThreadSafe(id)), temp1); |
| 4873 | |
| 4874 | MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3" , "./../../../../js/src/jit/CodeGenerator.cpp", 4874); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence (__null, 4874); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4875 | MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0" , "./../../../../js/src/jit/CodeGenerator.cpp", 4875); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence (__null, 4875); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4876 | MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1" , "./../../../../js/src/jit/CodeGenerator.cpp", 4876); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence (__null, 4876); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4877 | MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2" , "./../../../../js/src/jit/CodeGenerator.cpp", 4877); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence (__null, 4877); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4878 | #if defined(JS_NUNBOX32) |
| 4879 | MOZ_ASSERT(output.typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.typeReg() == JSReturnReg_Type)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output.typeReg() == JSReturnReg_Type ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "output.typeReg() == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp" , 4879); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type" ")"); do { MOZ_CrashSequence(__null, 4879); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4880 | MOZ_ASSERT(output.payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.payloadReg() == JSReturnReg_Data)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output.payloadReg() == JSReturnReg_Data))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output.payloadReg() == JSReturnReg_Data" , "./../../../../js/src/jit/CodeGenerator.cpp", 4880); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")" ); do { MOZ_CrashSequence(__null, 4880); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4881 | #else |
| 4882 | MOZ_ASSERT(output.payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.payloadOrValueReg() == JSReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output.payloadOrValueReg() == JSReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output.payloadOrValueReg() == JSReturnReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 4882); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg" ")"); do { MOZ_CrashSequence(__null, 4882); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4883 | #endif |
| 4884 | TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub(); |
| 4885 | masm.call(megamorphicLoadStub); |
| 4886 | masm.branchPtr(Assembler::Equal, temp2, |
| 4887 | Imm32(JitRuntime::MegamorphicLoadStubCacheHit), &done); |
| 4888 | |
| 4889 | Label bail; |
| 4890 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
| 4891 | |
| 4892 | masm.Push(UndefinedValue()); |
| 4893 | masm.moveStackPtrTo(temp3); |
| 4894 | |
| 4895 | using Fn = bool (*)(JSContext* cx, JSObject* obj, PropertyKey id, |
| 4896 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
| 4897 | masm.setupAlignedABICall(); |
| 4898 | masm.loadJSContext(temp0); |
| 4899 | masm.passABIArg(temp0); |
| 4900 | masm.passABIArg(obj); |
| 4901 | masm.movePropertyKey(lir->mir()->name(), temp1); |
| 4902 | masm.passABIArg(temp1); |
| 4903 | masm.passABIArg(temp2); |
| 4904 | masm.passABIArg(temp3); |
| 4905 | |
| 4906 | masm.callWithABI<Fn, GetNativeDataPropertyPure>(); |
| 4907 | |
| 4908 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 4908); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!output.aliases(ReturnReg)" ")"); do { MOZ_CrashSequence (__null, 4908); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4909 | masm.Pop(output); |
| 4910 | |
| 4911 | masm.branchIfFalseBool(ReturnReg, &bail); |
| 4912 | masm.bind(&done); |
| 4913 | |
| 4914 | bailoutFrom(&bail, lir->snapshot()); |
| 4915 | } |
| 4916 | |
| 4917 | void CodeGenerator::visitMegamorphicLoadSlotPermissive( |
| 4918 | LMegamorphicLoadSlotPermissive* lir) { |
| 4919 | Register obj = ToRegister(lir->object()); |
| 4920 | Register temp0 = ToRegister(lir->temp0()); |
| 4921 | Register temp1 = ToRegister(lir->temp1()); |
| 4922 | Register temp2 = ToRegister(lir->temp2()); |
| 4923 | Register temp3 = ToRegister(lir->temp3()); |
| 4924 | ValueOperand output = ToOutValue(lir); |
| 4925 | |
| 4926 | masm.movePtr(obj, temp3); |
| 4927 | |
| 4928 | Label done, getter, nullGetter; |
| 4929 | PropertyKey id = lir->mir()->name(); |
| 4930 | masm.movePropertyKey(id, temp0); |
| 4931 | masm.move32(Imm32(HashPropertyKeyThreadSafe(id)), temp1); |
| 4932 | |
| 4933 | MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3" , "./../../../../js/src/jit/CodeGenerator.cpp", 4933); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence (__null, 4933); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4934 | MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0" , "./../../../../js/src/jit/CodeGenerator.cpp", 4934); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence (__null, 4934); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4935 | MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1" , "./../../../../js/src/jit/CodeGenerator.cpp", 4935); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence (__null, 4935); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4936 | MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2" , "./../../../../js/src/jit/CodeGenerator.cpp", 4936); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence (__null, 4936); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4937 | #if defined(JS_NUNBOX32) |
| 4938 | MOZ_ASSERT(output.typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.typeReg() == JSReturnReg_Type)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output.typeReg() == JSReturnReg_Type ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "output.typeReg() == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp" , 4938); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type" ")"); do { MOZ_CrashSequence(__null, 4938); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4939 | MOZ_ASSERT(output.payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.payloadReg() == JSReturnReg_Data)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output.payloadReg() == JSReturnReg_Data))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output.payloadReg() == JSReturnReg_Data" , "./../../../../js/src/jit/CodeGenerator.cpp", 4939); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")" ); do { MOZ_CrashSequence(__null, 4939); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4940 | #else |
| 4941 | MOZ_ASSERT(output.payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.payloadOrValueReg() == JSReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output.payloadOrValueReg() == JSReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output.payloadOrValueReg() == JSReturnReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 4941); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg" ")"); do { MOZ_CrashSequence(__null, 4941); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4942 | #endif |
| 4943 | MOZ_ASSERT(!output.aliases(temp3))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!output.aliases(temp3))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!output.aliases(temp3)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!output.aliases(temp3)" , "./../../../../js/src/jit/CodeGenerator.cpp", 4943); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!output.aliases(temp3)" ")"); do { MOZ_CrashSequence (__null, 4943); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4944 | TrampolinePtr megamorphicLoadStub = |
| 4945 | gen->jitRuntime()->megamorphicLoadStubPermissive(); |
| 4946 | masm.call(megamorphicLoadStub); |
| 4947 | masm.branchPtr(Assembler::Equal, temp2, |
| 4948 | Imm32(JitRuntime::MegamorphicLoadStubCacheHit), &done); |
| 4949 | masm.branchPtr(Assembler::Equal, temp2, |
| 4950 | Imm32(JitRuntime::MegamorphicLoadStubCacheHitGetter), &getter); |
| 4951 | |
| 4952 | masm.movePropertyKey(lir->mir()->name(), temp1); |
| 4953 | pushArg(temp2); |
| 4954 | pushArg(temp1); |
| 4955 | pushArg(obj); |
| 4956 | |
| 4957 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, |
| 4958 | MegamorphicCacheEntry*, MutableHandleValue); |
| 4959 | callVM<Fn, GetPropMaybeCached>(lir); |
| 4960 | |
| 4961 | masm.jump(&done); |
| 4962 | |
| 4963 | masm.bind(&getter); |
| 4964 | |
| 4965 | emitCallMegamorphicGetter(lir, output, temp3, temp1, temp2, &nullGetter); |
| 4966 | masm.jump(&done); |
| 4967 | |
| 4968 | masm.bind(&nullGetter); |
| 4969 | masm.moveValue(UndefinedValue(), output); |
| 4970 | masm.bind(&done); |
| 4971 | } |
| 4972 | |
| 4973 | void CodeGenerator::visitMegamorphicLoadSlotByValue( |
| 4974 | LMegamorphicLoadSlotByValue* lir) { |
| 4975 | Register obj = ToRegister(lir->object()); |
| 4976 | ValueOperand idVal = ToValue(lir->idVal()); |
| 4977 | Register temp0 = ToRegister(lir->temp0()); |
| 4978 | Register temp1 = ToRegister(lir->temp1()); |
| 4979 | Register temp2 = ToRegister(lir->temp2()); |
| 4980 | ValueOperand output = ToOutValue(lir); |
| 4981 | |
| 4982 | Label done, bail, atomizeMiss; |
| 4983 | masm.xorPtr(temp2, temp2); |
| 4984 | masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss); |
| 4985 | |
| 4986 | MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3" , "./../../../../js/src/jit/CodeGenerator.cpp", 4986); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence (__null, 4986); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4987 | MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0" , "./../../../../js/src/jit/CodeGenerator.cpp", 4987); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence (__null, 4987); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4988 | MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1" , "./../../../../js/src/jit/CodeGenerator.cpp", 4988); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence (__null, 4988); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4989 | MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2" , "./../../../../js/src/jit/CodeGenerator.cpp", 4989); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence (__null, 4989); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 4990 | #if defined(JS_NUNBOX32) |
| 4991 | MOZ_ASSERT(output.typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.typeReg() == JSReturnReg_Type)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output.typeReg() == JSReturnReg_Type ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "output.typeReg() == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp" , 4991); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type" ")"); do { MOZ_CrashSequence(__null, 4991); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4992 | MOZ_ASSERT(output.payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.payloadReg() == JSReturnReg_Data)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output.payloadReg() == JSReturnReg_Data))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output.payloadReg() == JSReturnReg_Data" , "./../../../../js/src/jit/CodeGenerator.cpp", 4992); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")" ); do { MOZ_CrashSequence(__null, 4992); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4993 | #else |
| 4994 | MOZ_ASSERT(output.payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.payloadOrValueReg() == JSReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output.payloadOrValueReg() == JSReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output.payloadOrValueReg() == JSReturnReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 4994); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg" ")"); do { MOZ_CrashSequence(__null, 4994); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 4995 | #endif |
| 4996 | TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub(); |
| 4997 | masm.call(megamorphicLoadStub); |
| 4998 | masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done); |
| 4999 | |
| 5000 | masm.bind(&atomizeMiss); |
| 5001 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
| 5002 | |
| 5003 | // idVal will be in vp[0], result will be stored in vp[1]. |
| 5004 | masm.reserveStack(sizeof(Value)); |
| 5005 | masm.Push(idVal); |
| 5006 | masm.moveStackPtrTo(temp0); |
| 5007 | |
| 5008 | using Fn = bool (*)(JSContext* cx, JSObject* obj, |
| 5009 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
| 5010 | masm.setupAlignedABICall(); |
| 5011 | masm.loadJSContext(temp1); |
| 5012 | masm.passABIArg(temp1); |
| 5013 | masm.passABIArg(obj); |
| 5014 | masm.passABIArg(temp2); |
| 5015 | masm.passABIArg(temp0); |
| 5016 | masm.callWithABI<Fn, GetNativeDataPropertyByValuePure>(); |
| 5017 | |
| 5018 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 5018); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { MOZ_CrashSequence (__null, 5018); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5019 | masm.storeCallPointerResult(temp0); |
| 5020 | masm.Pop(idVal); |
| 5021 | |
| 5022 | uint32_t framePushed = masm.framePushed(); |
| 5023 | Label ok; |
| 5024 | masm.branchIfTrueBool(temp0, &ok); |
| 5025 | masm.freeStack(sizeof(Value)); // Discard result Value. |
| 5026 | masm.jump(&bail); |
| 5027 | |
| 5028 | masm.bind(&ok); |
| 5029 | masm.setFramePushed(framePushed); |
| 5030 | masm.Pop(output); |
| 5031 | |
| 5032 | masm.bind(&done); |
| 5033 | |
| 5034 | bailoutFrom(&bail, lir->snapshot()); |
| 5035 | } |
| 5036 | |
| 5037 | void CodeGenerator::visitMegamorphicLoadSlotByValuePermissive( |
| 5038 | LMegamorphicLoadSlotByValuePermissive* lir) { |
| 5039 | Register obj = ToRegister(lir->object()); |
| 5040 | ValueOperand idVal = ToValue(lir->idVal()); |
| 5041 | Register temp0 = ToRegister(lir->temp0()); |
| 5042 | Register temp1 = ToRegister(lir->temp1()); |
| 5043 | Register temp2 = ToRegister(lir->temp2()); |
| 5044 | |
| 5045 | Label done, atomizeMiss; |
| 5046 | |
| 5047 | // If we have enough registers available, we can call getters directly from |
| 5048 | // jitcode. On x86, we have to call into the VM. |
| 5049 | #ifndef JS_CODEGEN_X86 |
| 5050 | ValueOperand output = ToOutValue(lir); |
| 5051 | Label getter, nullGetter; |
| 5052 | Register temp3 = ToRegister(lir->temp3()); |
| 5053 | masm.movePtr(obj, temp3); |
| 5054 | masm.xorPtr(temp2, temp2); |
| 5055 | masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss); |
| 5056 | |
| 5057 | MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3" , "./../../../../js/src/jit/CodeGenerator.cpp", 5057); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence (__null, 5057); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5058 | MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0" , "./../../../../js/src/jit/CodeGenerator.cpp", 5058); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence (__null, 5058); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5059 | MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1" , "./../../../../js/src/jit/CodeGenerator.cpp", 5059); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence (__null, 5059); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5060 | MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2" , "./../../../../js/src/jit/CodeGenerator.cpp", 5060); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence (__null, 5060); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5061 | # if defined(JS_NUNBOX32) |
| 5062 | MOZ_ASSERT(output.typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.typeReg() == JSReturnReg_Type)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output.typeReg() == JSReturnReg_Type ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "output.typeReg() == JSReturnReg_Type", "./../../../../js/src/jit/CodeGenerator.cpp" , 5062); AnnotateMozCrashReason("MOZ_ASSERT" "(" "output.typeReg() == JSReturnReg_Type" ")"); do { MOZ_CrashSequence(__null, 5062); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5063 | MOZ_ASSERT(output.payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.payloadReg() == JSReturnReg_Data)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output.payloadReg() == JSReturnReg_Data))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output.payloadReg() == JSReturnReg_Data" , "./../../../../js/src/jit/CodeGenerator.cpp", 5063); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output.payloadReg() == JSReturnReg_Data" ")" ); do { MOZ_CrashSequence(__null, 5063); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5064 | # else |
| 5065 | MOZ_ASSERT(output.payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output.payloadOrValueReg() == JSReturnReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output.payloadOrValueReg() == JSReturnReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output.payloadOrValueReg() == JSReturnReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 5065); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output.payloadOrValueReg() == JSReturnReg" ")"); do { MOZ_CrashSequence(__null, 5065); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5066 | # endif |
| 5067 | MOZ_ASSERT(!output.aliases(temp3))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!output.aliases(temp3))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!output.aliases(temp3)))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("!output.aliases(temp3)" , "./../../../../js/src/jit/CodeGenerator.cpp", 5067); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!output.aliases(temp3)" ")"); do { MOZ_CrashSequence (__null, 5067); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5068 | TrampolinePtr megamorphicLoadStub = |
| 5069 | gen->jitRuntime()->megamorphicLoadStubPermissive(); |
| 5070 | masm.call(megamorphicLoadStub); |
| 5071 | masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done); |
| 5072 | masm.branchTest32(Assembler::NonZero, temp2, Imm32(2), &getter); |
| 5073 | #else |
| 5074 | masm.xorPtr(temp2, temp2); |
| 5075 | masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss); |
| 5076 | |
| 5077 | MOZ_ASSERT(obj == CallTempReg3)do { static_assert( mozilla::detail::AssertionConditionType< decltype(obj == CallTempReg3)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(obj == CallTempReg3))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("obj == CallTempReg3" , "./../../../../js/src/jit/CodeGenerator.cpp", 5077); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "obj == CallTempReg3" ")"); do { MOZ_CrashSequence (__null, 5077); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5078 | MOZ_ASSERT(temp0 == CallTempReg0)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp0 == CallTempReg0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp0 == CallTempReg0))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp0 == CallTempReg0" , "./../../../../js/src/jit/CodeGenerator.cpp", 5078); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp0 == CallTempReg0" ")"); do { MOZ_CrashSequence (__null, 5078); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5079 | MOZ_ASSERT(temp1 == CallTempReg1)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp1 == CallTempReg1)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp1 == CallTempReg1))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp1 == CallTempReg1" , "./../../../../js/src/jit/CodeGenerator.cpp", 5079); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp1 == CallTempReg1" ")"); do { MOZ_CrashSequence (__null, 5079); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5080 | MOZ_ASSERT(temp2 == CallTempReg2)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp2 == CallTempReg2)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp2 == CallTempReg2))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("temp2 == CallTempReg2" , "./../../../../js/src/jit/CodeGenerator.cpp", 5080); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp2 == CallTempReg2" ")"); do { MOZ_CrashSequence (__null, 5080); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5081 | # if defined(JS_NUNBOX32) |
| 5082 | MOZ_ASSERT(ToOutValue(lir).typeReg() == JSReturnReg_Type)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir).typeReg() == JSReturnReg_Type)>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToOutValue(lir).typeReg() == JSReturnReg_Type))), 0) )) { do { } while (false); MOZ_ReportAssertionFailure("ToOutValue(lir).typeReg() == JSReturnReg_Type" , "./../../../../js/src/jit/CodeGenerator.cpp", 5082); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToOutValue(lir).typeReg() == JSReturnReg_Type" ")"); do { MOZ_CrashSequence(__null, 5082); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5083 | MOZ_ASSERT(ToOutValue(lir).payloadReg() == JSReturnReg_Data)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir).payloadReg() == JSReturnReg_Data)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToOutValue(lir).payloadReg() == JSReturnReg_Data))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToOutValue(lir).payloadReg() == JSReturnReg_Data" , "./../../../../js/src/jit/CodeGenerator.cpp", 5083); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToOutValue(lir).payloadReg() == JSReturnReg_Data" ")"); do { MOZ_CrashSequence(__null, 5083); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5084 | # else |
| 5085 | MOZ_ASSERT(ToOutValue(lir).payloadOrValueReg() == JSReturnReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(ToOutValue(lir).payloadOrValueReg() == JSReturnReg)> ::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(ToOutValue(lir).payloadOrValueReg() == JSReturnReg)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("ToOutValue(lir).payloadOrValueReg() == JSReturnReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 5085); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToOutValue(lir).payloadOrValueReg() == JSReturnReg" ")"); do { MOZ_CrashSequence(__null, 5085); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5086 | # endif |
| 5087 | TrampolinePtr megamorphicLoadStub = gen->jitRuntime()->megamorphicLoadStub(); |
| 5088 | masm.call(megamorphicLoadStub); |
| 5089 | masm.branchTest32(Assembler::NonZero, temp2, Imm32(1), &done); |
| 5090 | #endif |
| 5091 | |
| 5092 | masm.bind(&atomizeMiss); |
| 5093 | |
| 5094 | pushArg(temp2); |
| 5095 | pushArg(idVal); |
| 5096 | pushArg(obj); |
| 5097 | |
| 5098 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, |
| 5099 | MegamorphicCacheEntry*, MutableHandleValue); |
| 5100 | callVM<Fn, GetElemMaybeCached>(lir); |
| 5101 | |
| 5102 | #ifndef JS_CODEGEN_X86 |
| 5103 | masm.jump(&done); |
| 5104 | masm.bind(&getter); |
| 5105 | |
| 5106 | emitCallMegamorphicGetter(lir, output, temp3, temp1, temp2, &nullGetter); |
| 5107 | masm.jump(&done); |
| 5108 | |
| 5109 | masm.bind(&nullGetter); |
| 5110 | masm.moveValue(UndefinedValue(), output); |
| 5111 | #endif |
| 5112 | |
| 5113 | masm.bind(&done); |
| 5114 | } |
| 5115 | |
| 5116 | void CodeGenerator::visitMegamorphicStoreSlot(LMegamorphicStoreSlot* lir) { |
| 5117 | Register obj = ToRegister(lir->object()); |
| 5118 | ValueOperand value = ToValue(lir->rhs()); |
| 5119 | |
| 5120 | Register temp0 = ToRegister(lir->temp0()); |
| 5121 | #ifndef JS_CODEGEN_X86 |
| 5122 | Register temp1 = ToRegister(lir->temp1()); |
| 5123 | Register temp2 = ToRegister(lir->temp2()); |
| 5124 | #endif |
| 5125 | |
| 5126 | // The instruction is marked as call-instruction so only these registers are |
| 5127 | // live. |
| 5128 | LiveRegisterSet liveRegs; |
| 5129 | liveRegs.addUnchecked(obj); |
| 5130 | liveRegs.addUnchecked(value); |
| 5131 | liveRegs.addUnchecked(temp0); |
| 5132 | #ifndef JS_CODEGEN_X86 |
| 5133 | liveRegs.addUnchecked(temp1); |
| 5134 | liveRegs.addUnchecked(temp2); |
| 5135 | #endif |
| 5136 | |
| 5137 | Label cacheHit, done; |
| 5138 | #ifdef JS_CODEGEN_X86 |
| 5139 | masm.emitMegamorphicCachedSetSlot( |
| 5140 | lir->mir()->name(), obj, temp0, value, liveRegs, &cacheHit, |
| 5141 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
| 5142 | EmitPreBarrier(masm, addr, mirType); |
| 5143 | }); |
| 5144 | #else |
| 5145 | masm.emitMegamorphicCachedSetSlot( |
| 5146 | lir->mir()->name(), obj, temp0, temp1, temp2, value, liveRegs, &cacheHit, |
| 5147 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
| 5148 | EmitPreBarrier(masm, addr, mirType); |
| 5149 | }); |
| 5150 | #endif |
| 5151 | |
| 5152 | pushArg(Imm32(lir->mir()->strict())); |
| 5153 | pushArg(value); |
| 5154 | pushArg(lir->mir()->name(), temp0); |
| 5155 | pushArg(obj); |
| 5156 | |
| 5157 | using Fn = bool (*)(JSContext*, HandleObject, HandleId, HandleValue, bool); |
| 5158 | callVM<Fn, SetPropertyMegamorphic<true>>(lir); |
| 5159 | |
| 5160 | masm.jump(&done); |
| 5161 | masm.bind(&cacheHit); |
| 5162 | |
| 5163 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done); |
| 5164 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done); |
| 5165 | |
| 5166 | // Note: because this is a call-instruction, no registers need to be saved. |
| 5167 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 5167); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { MOZ_CrashSequence (__null, 5167); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5168 | emitPostWriteBarrier(obj); |
| 5169 | |
| 5170 | masm.bind(&done); |
| 5171 | } |
| 5172 | |
| 5173 | void CodeGenerator::visitMegamorphicHasProp(LMegamorphicHasProp* lir) { |
| 5174 | Register obj = ToRegister(lir->object()); |
| 5175 | ValueOperand idVal = ToValue(lir->idVal()); |
| 5176 | Register temp0 = ToRegister(lir->temp0()); |
| 5177 | Register temp1 = ToRegister(lir->temp1()); |
| 5178 | Register temp2 = ToRegister(lir->temp2()); |
| 5179 | Register output = ToRegister(lir->output()); |
| 5180 | |
| 5181 | Label bail, cacheHit, atomizeMiss; |
| 5182 | masm.xorPtr(temp2, temp2); |
| 5183 | masm.loadAtomOrSymbolAndHash(idVal, temp0, temp1, &atomizeMiss); |
| 5184 | masm.emitMegamorphicCacheLookupExists(obj, temp0, temp1, temp2, output, |
| 5185 | &cacheHit, lir->mir()->hasOwn()); |
| 5186 | |
| 5187 | masm.bind(&atomizeMiss); |
| 5188 | masm.branchIfNonNativeObj(obj, temp0, &bail); |
| 5189 | |
| 5190 | // idVal will be in vp[0], result will be stored in vp[1]. |
| 5191 | masm.reserveStack(sizeof(Value)); |
| 5192 | masm.Push(idVal); |
| 5193 | masm.moveStackPtrTo(temp0); |
| 5194 | |
| 5195 | using Fn = bool (*)(JSContext* cx, JSObject* obj, |
| 5196 | MegamorphicCache::Entry* cacheEntry, Value* vp); |
| 5197 | masm.setupAlignedABICall(); |
| 5198 | masm.loadJSContext(temp1); |
| 5199 | masm.passABIArg(temp1); |
| 5200 | masm.passABIArg(obj); |
| 5201 | masm.passABIArg(temp2); |
| 5202 | masm.passABIArg(temp0); |
| 5203 | if (lir->mir()->hasOwn()) { |
| 5204 | masm.callWithABI<Fn, HasNativeDataPropertyPure<true>>(); |
| 5205 | } else { |
| 5206 | masm.callWithABI<Fn, HasNativeDataPropertyPure<false>>(); |
| 5207 | } |
| 5208 | |
| 5209 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 5209); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!idVal.aliases(temp0)" ")"); do { MOZ_CrashSequence (__null, 5209); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5210 | masm.storeCallPointerResult(temp0); |
| 5211 | masm.Pop(idVal); |
| 5212 | |
| 5213 | uint32_t framePushed = masm.framePushed(); |
| 5214 | Label ok; |
| 5215 | masm.branchIfTrueBool(temp0, &ok); |
| 5216 | masm.freeStack(sizeof(Value)); // Discard result Value. |
| 5217 | masm.jump(&bail); |
| 5218 | |
| 5219 | masm.bind(&ok); |
| 5220 | masm.setFramePushed(framePushed); |
| 5221 | masm.unboxBoolean(Address(masm.getStackPointer(), 0), output); |
| 5222 | masm.freeStack(sizeof(Value)); |
| 5223 | masm.bind(&cacheHit); |
| 5224 | |
| 5225 | bailoutFrom(&bail, lir->snapshot()); |
| 5226 | } |
| 5227 | |
| 5228 | void CodeGenerator::visitSmallObjectVariableKeyHasProp( |
| 5229 | LSmallObjectVariableKeyHasProp* lir) { |
| 5230 | Register id = ToRegister(lir->idStr()); |
| 5231 | Register output = ToRegister(lir->output()); |
| 5232 | |
| 5233 | #ifdef DEBUG1 |
| 5234 | Label isAtom; |
| 5235 | masm.branchTest32(Assembler::NonZero, Address(id, JSString::offsetOfFlags()), |
| 5236 | Imm32(StringFlags::ATOM_BIT), &isAtom); |
| 5237 | masm.assumeUnreachable("Expected atom input"); |
| 5238 | masm.bind(&isAtom); |
| 5239 | #endif |
| 5240 | |
| 5241 | SharedShape* shape = &lir->mir()->shape()->asShared(); |
| 5242 | |
| 5243 | Label done, success; |
| 5244 | for (SharedShapePropertyIter<NoGC> iter(shape); !iter.done(); iter++) { |
| 5245 | masm.branchPtr(Assembler::Equal, id, ImmGCPtr(iter->key().toAtom()), |
| 5246 | &success); |
| 5247 | } |
| 5248 | masm.move32(Imm32(0), output); |
| 5249 | masm.jump(&done); |
| 5250 | masm.bind(&success); |
| 5251 | masm.move32(Imm32(1), output); |
| 5252 | masm.bind(&done); |
| 5253 | } |
| 5254 | |
| 5255 | void CodeGenerator::visitGuardToArrayBuffer(LGuardToArrayBuffer* guard) { |
| 5256 | Register obj = ToRegister(guard->object()); |
| 5257 | Register temp = ToRegister(guard->temp0()); |
| 5258 | |
| 5259 | // branchIfIsNotArrayBuffer may zero the object register on speculative paths |
| 5260 | // (we should have a defineReuseInput allocation in this case). |
| 5261 | |
| 5262 | Label bail; |
| 5263 | masm.branchIfIsNotArrayBuffer(obj, temp, &bail); |
| 5264 | bailoutFrom(&bail, guard->snapshot()); |
| 5265 | } |
| 5266 | |
| 5267 | void CodeGenerator::visitGuardToSharedArrayBuffer( |
| 5268 | LGuardToSharedArrayBuffer* guard) { |
| 5269 | Register obj = ToRegister(guard->object()); |
| 5270 | Register temp = ToRegister(guard->temp0()); |
| 5271 | |
| 5272 | // branchIfIsNotSharedArrayBuffer may zero the object register on speculative |
| 5273 | // paths (we should have a defineReuseInput allocation in this case). |
| 5274 | |
| 5275 | Label bail; |
| 5276 | masm.branchIfIsNotSharedArrayBuffer(obj, temp, &bail); |
| 5277 | bailoutFrom(&bail, guard->snapshot()); |
| 5278 | } |
| 5279 | |
| 5280 | void CodeGenerator::visitGuardIsNotArrayBufferMaybeShared( |
| 5281 | LGuardIsNotArrayBufferMaybeShared* guard) { |
| 5282 | Register obj = ToRegister(guard->object()); |
| 5283 | Register temp = ToRegister(guard->temp0()); |
| 5284 | |
| 5285 | Label bail; |
| 5286 | masm.branchIfIsArrayBufferMaybeShared(obj, temp, &bail); |
| 5287 | bailoutFrom(&bail, guard->snapshot()); |
| 5288 | } |
| 5289 | |
| 5290 | void CodeGenerator::visitGuardIsNonResizableTypedArray( |
| 5291 | LGuardIsNonResizableTypedArray* guard) { |
| 5292 | Register obj = ToRegister(guard->object()); |
| 5293 | Register temp = ToRegister(guard->temp0()); |
| 5294 | |
| 5295 | Label bail; |
| 5296 | masm.loadObjClassUnsafe(obj, temp); |
| 5297 | masm.branchIfClassIsNotNonResizableTypedArray(temp, &bail); |
| 5298 | bailoutFrom(&bail, guard->snapshot()); |
| 5299 | } |
| 5300 | |
| 5301 | void CodeGenerator::visitGuardIsResizableTypedArray( |
| 5302 | LGuardIsResizableTypedArray* guard) { |
| 5303 | Register obj = ToRegister(guard->object()); |
| 5304 | Register temp = ToRegister(guard->temp0()); |
| 5305 | |
| 5306 | Label bail; |
| 5307 | masm.loadObjClassUnsafe(obj, temp); |
| 5308 | masm.branchIfClassIsNotResizableTypedArray(temp, &bail); |
| 5309 | bailoutFrom(&bail, guard->snapshot()); |
| 5310 | } |
| 5311 | |
| 5312 | void CodeGenerator::visitGuardHasProxyHandler(LGuardHasProxyHandler* guard) { |
| 5313 | Register obj = ToRegister(guard->object()); |
| 5314 | |
| 5315 | Label bail; |
| 5316 | |
| 5317 | Address handlerAddr(obj, ProxyObject::offsetOfHandler()); |
| 5318 | masm.branchPtr(Assembler::NotEqual, handlerAddr, |
| 5319 | ImmPtr(guard->mir()->handler()), &bail); |
| 5320 | |
| 5321 | bailoutFrom(&bail, guard->snapshot()); |
| 5322 | } |
| 5323 | |
| 5324 | void CodeGenerator::visitGuardObjectIdentity(LGuardObjectIdentity* guard) { |
| 5325 | Register input = ToRegister(guard->input()); |
| 5326 | Register expected = ToRegister(guard->expected()); |
| 5327 | |
| 5328 | Assembler::Condition cond = |
| 5329 | guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
| 5330 | bailoutCmpPtr(cond, input, expected, guard->snapshot()); |
| 5331 | } |
| 5332 | |
| 5333 | void CodeGenerator::visitGuardSpecificFunction(LGuardSpecificFunction* guard) { |
| 5334 | Register input = ToRegister(guard->input()); |
| 5335 | Register expected = ToRegister(guard->expected()); |
| 5336 | |
| 5337 | bailoutCmpPtr(Assembler::NotEqual, input, expected, guard->snapshot()); |
| 5338 | } |
| 5339 | |
| 5340 | void CodeGenerator::visitGuardSpecificAtom(LGuardSpecificAtom* guard) { |
| 5341 | Register str = ToRegister(guard->str()); |
| 5342 | Register scratch = ToRegister(guard->temp0()); |
| 5343 | |
| 5344 | LiveRegisterSet volatileRegs = liveVolatileRegs(guard); |
| 5345 | volatileRegs.takeUnchecked(scratch); |
| 5346 | |
| 5347 | Label bail; |
| 5348 | masm.guardSpecificAtom(str, guard->mir()->atom(), scratch, volatileRegs, |
| 5349 | &bail); |
| 5350 | bailoutFrom(&bail, guard->snapshot()); |
| 5351 | } |
| 5352 | |
| 5353 | void CodeGenerator::visitGuardSpecificSymbol(LGuardSpecificSymbol* guard) { |
| 5354 | Register symbol = ToRegister(guard->symbol()); |
| 5355 | |
| 5356 | bailoutCmpPtr(Assembler::NotEqual, symbol, ImmGCPtr(guard->mir()->expected()), |
| 5357 | guard->snapshot()); |
| 5358 | } |
| 5359 | |
| 5360 | void CodeGenerator::visitGuardSpecificInt32(LGuardSpecificInt32* guard) { |
| 5361 | Register num = ToRegister(guard->num()); |
| 5362 | |
| 5363 | bailoutCmp32(Assembler::NotEqual, num, Imm32(guard->mir()->expected()), |
| 5364 | guard->snapshot()); |
| 5365 | } |
| 5366 | |
| 5367 | void CodeGenerator::visitGuardStringToIndex(LGuardStringToIndex* lir) { |
| 5368 | Register str = ToRegister(lir->string()); |
| 5369 | Register output = ToRegister(lir->output()); |
| 5370 | |
| 5371 | Label vmCall, done; |
| 5372 | masm.loadStringIndexValue(str, output, &vmCall); |
| 5373 | masm.jump(&done); |
| 5374 | |
| 5375 | { |
| 5376 | masm.bind(&vmCall); |
| 5377 | |
| 5378 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 5379 | volatileRegs.takeUnchecked(output); |
| 5380 | masm.PushRegsInMask(volatileRegs); |
| 5381 | |
| 5382 | using Fn = int32_t (*)(JSString* str); |
| 5383 | masm.setupAlignedABICall(); |
| 5384 | masm.passABIArg(str); |
| 5385 | masm.callWithABI<Fn, GetIndexFromString>(); |
| 5386 | masm.storeCallInt32Result(output); |
| 5387 | |
| 5388 | masm.PopRegsInMask(volatileRegs); |
| 5389 | |
| 5390 | // GetIndexFromString returns a negative value on failure. |
| 5391 | bailoutTest32(Assembler::Signed, output, output, lir->snapshot()); |
| 5392 | } |
| 5393 | |
| 5394 | masm.bind(&done); |
| 5395 | } |
| 5396 | |
| 5397 | void CodeGenerator::visitGuardStringToInt32(LGuardStringToInt32* lir) { |
| 5398 | Register str = ToRegister(lir->string()); |
| 5399 | Register output = ToRegister(lir->output()); |
| 5400 | Register temp = ToRegister(lir->temp0()); |
| 5401 | |
| 5402 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 5403 | |
| 5404 | Label bail; |
| 5405 | masm.guardStringToInt32(str, output, temp, volatileRegs, &bail); |
| 5406 | bailoutFrom(&bail, lir->snapshot()); |
| 5407 | } |
| 5408 | |
| 5409 | void CodeGenerator::visitGuardStringToDouble(LGuardStringToDouble* lir) { |
| 5410 | Register str = ToRegister(lir->string()); |
| 5411 | FloatRegister output = ToFloatRegister(lir->output()); |
| 5412 | Register temp0 = ToRegister(lir->temp0()); |
| 5413 | Register temp1 = ToRegister(lir->temp1()); |
| 5414 | |
| 5415 | Label vmCall, done; |
| 5416 | // Use indexed value as fast path if possible. |
| 5417 | masm.loadStringIndexValue(str, temp0, &vmCall); |
| 5418 | masm.convertInt32ToDouble(temp0, output); |
| 5419 | masm.jump(&done); |
| 5420 | { |
| 5421 | masm.bind(&vmCall); |
| 5422 | |
| 5423 | // Reserve stack for holding the result value of the call. |
| 5424 | masm.reserveStack(sizeof(double)); |
| 5425 | masm.moveStackPtrTo(temp0); |
| 5426 | |
| 5427 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 5428 | volatileRegs.takeUnchecked(temp0); |
| 5429 | volatileRegs.takeUnchecked(temp1); |
| 5430 | masm.PushRegsInMask(volatileRegs); |
| 5431 | |
| 5432 | using Fn = bool (*)(JSContext* cx, JSString* str, double* result); |
| 5433 | masm.setupAlignedABICall(); |
| 5434 | masm.loadJSContext(temp1); |
| 5435 | masm.passABIArg(temp1); |
| 5436 | masm.passABIArg(str); |
| 5437 | masm.passABIArg(temp0); |
| 5438 | masm.callWithABI<Fn, StringToNumberPure>(); |
| 5439 | masm.storeCallPointerResult(temp0); |
| 5440 | |
| 5441 | masm.PopRegsInMask(volatileRegs); |
| 5442 | |
| 5443 | Label ok; |
| 5444 | masm.branchIfTrueBool(temp0, &ok); |
| 5445 | { |
| 5446 | // OOM path, recovered by StringToNumberPure. |
| 5447 | // |
| 5448 | // Use addToStackPtr instead of freeStack as freeStack tracks stack height |
| 5449 | // flow-insensitively, and using it here would confuse the stack height |
| 5450 | // tracking. |
| 5451 | masm.addToStackPtr(Imm32(sizeof(double))); |
| 5452 | bailout(lir->snapshot()); |
| 5453 | } |
| 5454 | masm.bind(&ok); |
| 5455 | masm.Pop(output); |
| 5456 | } |
| 5457 | masm.bind(&done); |
| 5458 | } |
| 5459 | |
| 5460 | void CodeGenerator::visitGuardNoDenseElements(LGuardNoDenseElements* guard) { |
| 5461 | Register obj = ToRegister(guard->object()); |
| 5462 | Register temp = ToRegister(guard->temp0()); |
| 5463 | |
| 5464 | // Load obj->elements. |
| 5465 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp); |
| 5466 | |
| 5467 | // Make sure there are no dense elements. |
| 5468 | Address initLength(temp, ObjectElements::offsetOfInitializedLength()); |
| 5469 | bailoutCmp32(Assembler::NotEqual, initLength, Imm32(0), guard->snapshot()); |
| 5470 | } |
| 5471 | |
| 5472 | void CodeGenerator::visitBooleanToInt64(LBooleanToInt64* lir) { |
| 5473 | Register input = ToRegister(lir->input()); |
| 5474 | Register64 output = ToOutRegister64(lir); |
| 5475 | |
| 5476 | masm.move32To64ZeroExtend(input, output); |
| 5477 | } |
| 5478 | |
| 5479 | void CodeGenerator::emitStringToInt64(LInstruction* lir, Register input, |
| 5480 | Register64 output) { |
| 5481 | Register temp = output.scratchReg(); |
| 5482 | |
| 5483 | saveLive(lir); |
| 5484 | |
| 5485 | masm.reserveStack(sizeof(uint64_t)); |
| 5486 | masm.moveStackPtrTo(temp); |
| 5487 | pushArg(temp); |
| 5488 | pushArg(input); |
| 5489 | |
| 5490 | using Fn = bool (*)(JSContext*, HandleString, uint64_t*); |
| 5491 | callVM<Fn, DoStringToInt64>(lir); |
| 5492 | |
| 5493 | masm.load64(Address(masm.getStackPointer(), 0), output); |
| 5494 | masm.freeStack(sizeof(uint64_t)); |
| 5495 | |
| 5496 | restoreLiveIgnore(lir, StoreValueTo(output).clobbered()); |
| 5497 | } |
| 5498 | |
| 5499 | void CodeGenerator::visitStringToInt64(LStringToInt64* lir) { |
| 5500 | Register input = ToRegister(lir->input()); |
| 5501 | Register64 output = ToOutRegister64(lir); |
| 5502 | |
| 5503 | emitStringToInt64(lir, input, output); |
| 5504 | } |
| 5505 | |
| 5506 | void CodeGenerator::visitValueToInt64(LValueToInt64* lir) { |
| 5507 | ValueOperand input = ToValue(lir->input()); |
| 5508 | Register temp = ToRegister(lir->temp0()); |
| 5509 | Register64 output = ToOutRegister64(lir); |
| 5510 | |
| 5511 | int checks = 3; |
| 5512 | |
| 5513 | Label fail, done; |
| 5514 | // Jump to fail if this is the last check and we fail it, |
| 5515 | // otherwise to the next test. |
| 5516 | auto emitTestAndUnbox = [&](auto testAndUnbox) { |
| 5517 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 5517); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "checks > 0" ")"); do { MOZ_CrashSequence (__null, 5517); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5518 | |
| 5519 | checks--; |
| 5520 | Label notType; |
| 5521 | Label* target = checks ? ¬Type : &fail; |
| 5522 | |
| 5523 | testAndUnbox(target); |
| 5524 | |
| 5525 | if (checks) { |
| 5526 | masm.jump(&done); |
| 5527 | masm.bind(¬Type); |
| 5528 | } |
| 5529 | }; |
| 5530 | |
| 5531 | Register tag = masm.extractTag(input, temp); |
| 5532 | |
| 5533 | // BigInt. |
| 5534 | emitTestAndUnbox([&](Label* target) { |
| 5535 | masm.branchTestBigInt(Assembler::NotEqual, tag, target); |
| 5536 | masm.unboxBigInt(input, temp); |
| 5537 | masm.loadBigInt64(temp, output); |
| 5538 | }); |
| 5539 | |
| 5540 | // Boolean |
| 5541 | emitTestAndUnbox([&](Label* target) { |
| 5542 | masm.branchTestBoolean(Assembler::NotEqual, tag, target); |
| 5543 | masm.unboxBoolean(input, temp); |
| 5544 | masm.move32To64ZeroExtend(temp, output); |
| 5545 | }); |
| 5546 | |
| 5547 | // String |
| 5548 | emitTestAndUnbox([&](Label* target) { |
| 5549 | masm.branchTestString(Assembler::NotEqual, tag, target); |
| 5550 | masm.unboxString(input, temp); |
| 5551 | emitStringToInt64(lir, temp, output); |
| 5552 | }); |
| 5553 | |
| 5554 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 5554); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checks == 0" ")"); do { MOZ_CrashSequence(__null, 5554); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5555 | |
| 5556 | bailoutFrom(&fail, lir->snapshot()); |
| 5557 | masm.bind(&done); |
| 5558 | } |
| 5559 | |
| 5560 | void CodeGenerator::visitTruncateBigIntToInt64(LTruncateBigIntToInt64* lir) { |
| 5561 | Register operand = ToRegister(lir->input()); |
| 5562 | Register64 output = ToOutRegister64(lir); |
| 5563 | |
| 5564 | masm.loadBigInt64(operand, output); |
| 5565 | } |
| 5566 | |
| 5567 | OutOfLineCode* CodeGenerator::createBigIntOutOfLine(LInstruction* lir, |
| 5568 | Scalar::Type type, |
| 5569 | Register64 input, |
| 5570 | Register output) { |
| 5571 | #if JS_BITS_PER_WORD64 == 32 |
| 5572 | using Fn = BigInt* (*)(JSContext*, uint32_t, uint32_t); |
| 5573 | auto args = ArgList(input.low, input.high); |
| 5574 | #else |
| 5575 | using Fn = BigInt* (*)(JSContext*, uint64_t); |
| 5576 | auto args = ArgList(input); |
| 5577 | #endif |
| 5578 | |
| 5579 | if (type == Scalar::BigInt64) { |
| 5580 | return oolCallVM<Fn, jit::CreateBigIntFromInt64>(lir, args, |
| 5581 | StoreRegisterTo(output)); |
| 5582 | } |
| 5583 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 5583); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == Scalar::BigUint64" ")"); do { MOZ_CrashSequence (__null, 5583); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5584 | return oolCallVM<Fn, jit::CreateBigIntFromUint64>(lir, args, |
| 5585 | StoreRegisterTo(output)); |
| 5586 | } |
| 5587 | |
| 5588 | void CodeGenerator::emitCreateBigInt(LInstruction* lir, Scalar::Type type, |
| 5589 | Register64 input, Register output, |
| 5590 | Register maybeTemp, |
| 5591 | Register64 maybeTemp64) { |
| 5592 | OutOfLineCode* ool = createBigIntOutOfLine(lir, type, input, output); |
| 5593 | |
| 5594 | if (maybeTemp != InvalidReg) { |
| 5595 | masm.newGCBigInt(output, maybeTemp, initialBigIntHeap(), ool->entry()); |
| 5596 | } else { |
| 5597 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 5598 | regs.take(input); |
| 5599 | regs.take(output); |
| 5600 | |
| 5601 | Register temp = regs.takeAny(); |
| 5602 | |
| 5603 | masm.push(temp); |
| 5604 | |
| 5605 | Label fail, ok; |
| 5606 | masm.newGCBigInt(output, temp, initialBigIntHeap(), &fail); |
| 5607 | masm.pop(temp); |
| 5608 | masm.jump(&ok); |
| 5609 | masm.bind(&fail); |
| 5610 | masm.pop(temp); |
| 5611 | masm.jump(ool->entry()); |
| 5612 | masm.bind(&ok); |
| 5613 | } |
| 5614 | masm.initializeBigInt64(type, output, input, maybeTemp64); |
| 5615 | masm.bind(ool->rejoin()); |
| 5616 | } |
| 5617 | |
| 5618 | void CodeGenerator::emitCallMegamorphicGetter( |
| 5619 | LInstruction* lir, ValueOperand accessorAndOutput, Register obj, |
| 5620 | Register calleeScratch, Register argcScratch, Label* nullGetter) { |
| 5621 | MOZ_ASSERT(calleeScratch == IonGenericCallCalleeReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(calleeScratch == IonGenericCallCalleeReg)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(calleeScratch == IonGenericCallCalleeReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("calleeScratch == IonGenericCallCalleeReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 5621); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "calleeScratch == IonGenericCallCalleeReg" ")" ); do { MOZ_CrashSequence(__null, 5621); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5622 | MOZ_ASSERT(argcScratch == IonGenericCallArgcReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(argcScratch == IonGenericCallArgcReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(argcScratch == IonGenericCallArgcReg ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "argcScratch == IonGenericCallArgcReg", "./../../../../js/src/jit/CodeGenerator.cpp" , 5622); AnnotateMozCrashReason("MOZ_ASSERT" "(" "argcScratch == IonGenericCallArgcReg" ")"); do { MOZ_CrashSequence(__null, 5622); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5623 | |
| 5624 | masm.unboxNonDouble(accessorAndOutput, calleeScratch, |
| 5625 | JSVAL_TYPE_PRIVATE_GCTHING); |
| 5626 | |
| 5627 | masm.loadPtr(Address(calleeScratch, GetterSetter::offsetOfGetter()), |
| 5628 | calleeScratch); |
| 5629 | masm.branchTestPtr(Assembler::Zero, calleeScratch, calleeScratch, nullGetter); |
| 5630 | |
| 5631 | if (JitStackValueAlignment > 1) { |
| 5632 | masm.reserveStack(sizeof(Value) * (JitStackValueAlignment - 1)); |
| 5633 | } |
| 5634 | masm.pushValue(JSVAL_TYPE_OBJECT, obj); |
| 5635 | |
| 5636 | masm.checkStackAlignment(); |
| 5637 | |
| 5638 | masm.move32(Imm32(0), argcScratch); |
| 5639 | ensureOsiSpace(); |
| 5640 | |
| 5641 | TrampolinePtr genericCallStub = |
| 5642 | gen->jitRuntime()->getIonGenericCallStub(IonGenericCallKind::Call); |
| 5643 | uint32_t callOffset = masm.callJit(genericCallStub); |
| 5644 | markSafepointAt(callOffset, lir); |
| 5645 | |
| 5646 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 5647 | |
| 5648 | masm.moveValue(JSReturnOperand, accessorAndOutput); |
| 5649 | |
| 5650 | masm.setFramePushed(frameSize()); |
| 5651 | emitRestoreStackPointerFromFP(); |
| 5652 | } |
| 5653 | |
| 5654 | void CodeGenerator::visitInt64ToBigInt(LInt64ToBigInt* lir) { |
| 5655 | Register64 input = ToRegister64(lir->input()); |
| 5656 | Register64 temp = ToRegister64(lir->temp0()); |
| 5657 | Register output = ToRegister(lir->output()); |
| 5658 | |
| 5659 | emitCreateBigInt(lir, Scalar::BigInt64, input, output, temp.scratchReg(), |
| 5660 | temp); |
| 5661 | } |
| 5662 | |
| 5663 | void CodeGenerator::visitUint64ToBigInt(LUint64ToBigInt* lir) { |
| 5664 | Register64 input = ToRegister64(lir->input()); |
| 5665 | Register temp = ToRegister(lir->temp0()); |
| 5666 | Register output = ToRegister(lir->output()); |
| 5667 | |
| 5668 | emitCreateBigInt(lir, Scalar::BigUint64, input, output, temp); |
| 5669 | } |
| 5670 | |
| 5671 | void CodeGenerator::visitInt64ToIntPtr(LInt64ToIntPtr* lir) { |
| 5672 | Register64 input = ToRegister64(lir->input()); |
| 5673 | #ifdef JS_64BIT1 |
| 5674 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 5674); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "input.reg == ToRegister(lir->output())" ")"); do { MOZ_CrashSequence(__null, 5674); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 5675 | #else |
| 5676 | Register output = ToRegister(lir->output()); |
| 5677 | #endif |
| 5678 | |
| 5679 | Label bail; |
| 5680 | if (lir->mir()->isSigned()) { |
| 5681 | masm.branchInt64NotInPtrRange(input, &bail); |
| 5682 | } else { |
| 5683 | masm.branchUInt64NotInPtrRange(input, &bail); |
| 5684 | } |
| 5685 | bailoutFrom(&bail, lir->snapshot()); |
| 5686 | |
| 5687 | #ifndef JS_64BIT1 |
| 5688 | masm.move64To32(input, output); |
| 5689 | #endif |
| 5690 | } |
| 5691 | |
| 5692 | void CodeGenerator::visitIntPtrToInt64(LIntPtrToInt64* lir) { |
| 5693 | #ifdef JS_64BIT1 |
| 5694 | MOZ_CRASH("Not used on 64-bit platforms")do { do { } while (false); MOZ_ReportCrash("" "Not used on 64-bit platforms" , "./../../../../js/src/jit/CodeGenerator.cpp", 5694); AnnotateMozCrashReason ("MOZ_CRASH(" "Not used on 64-bit platforms" ")"); do { MOZ_CrashSequence (__null, 5694); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 5695 | #else |
| 5696 | Register input = ToRegister(lir->input()); |
| 5697 | Register64 output = ToOutRegister64(lir); |
| 5698 | |
| 5699 | masm.move32To64SignExtend(input, output); |
| 5700 | #endif |
| 5701 | } |
| 5702 | |
| 5703 | Address CodeGenerator::getNurseryValueAddress(ValueOrNurseryValueIndex val, |
| 5704 | Register reg) { |
| 5705 | // Move the address of the Value stored in the IonScript into |reg|. |
| 5706 | uint32_t nurseryIndex = val.toNurseryValueIndex(); |
| 5707 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), reg); |
| 5708 | masm.propagateOOM(nurseryValueLabels_.emplaceBack(label, nurseryIndex)); |
| 5709 | return Address(reg, 0); |
| 5710 | } |
| 5711 | |
| 5712 | void CodeGenerator::visitGuardValue(LGuardValue* lir) { |
| 5713 | ValueOperand input = ToValue(lir->input()); |
| 5714 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 5715 | ValueOrNurseryValueIndex expected = lir->mir()->expected(); |
| 5716 | |
| 5717 | Label bail; |
| 5718 | if (expected.isValue()) { |
| 5719 | Value expectedVal = expected.toValue(); |
| 5720 | if (expectedVal.isNaN()) { |
| 5721 | MOZ_ASSERT(temp != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp != InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp != InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp != InvalidReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 5721); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp != InvalidReg" ")"); do { MOZ_CrashSequence (__null, 5721); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5722 | masm.branchTestNaNValue(Assembler::NotEqual, input, temp, &bail); |
| 5723 | } else { |
| 5724 | MOZ_ASSERT(temp == InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp == InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp == InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp == InvalidReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 5724); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp == InvalidReg" ")"); do { MOZ_CrashSequence (__null, 5724); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5725 | masm.branchTestValue(Assembler::NotEqual, input, expectedVal, &bail); |
| 5726 | } |
| 5727 | } else { |
| 5728 | // Compare to the Value stored in IonScript's nursery values list. |
| 5729 | MOZ_ASSERT(temp != InvalidReg)do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp != InvalidReg)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp != InvalidReg))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp != InvalidReg" , "./../../../../js/src/jit/CodeGenerator.cpp", 5729); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp != InvalidReg" ")"); do { MOZ_CrashSequence (__null, 5729); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 5730 | Address valueAddr = getNurseryValueAddress(expected, temp); |
| 5731 | masm.branchTestValue(Assembler::NotEqual, valueAddr, input, &bail); |
| 5732 | } |
| 5733 | |
| 5734 | bailoutFrom(&bail, lir->snapshot()); |
| 5735 | } |
| 5736 | |
| 5737 | void CodeGenerator::visitGuardNullOrUndefined(LGuardNullOrUndefined* lir) { |
| 5738 | ValueOperand input = ToValue(lir->value()); |
| 5739 | |
| 5740 | ScratchTagScope tag(masm, input); |
| 5741 | masm.splitTagForTest(input, tag); |
| 5742 | |
| 5743 | Label done; |
| 5744 | masm.branchTestNull(Assembler::Equal, tag, &done); |
| 5745 | |
| 5746 | Label bail; |
| 5747 | masm.branchTestUndefined(Assembler::NotEqual, tag, &bail); |
| 5748 | bailoutFrom(&bail, lir->snapshot()); |
| 5749 | |
| 5750 | masm.bind(&done); |
| 5751 | } |
| 5752 | |
| 5753 | void CodeGenerator::visitGuardIsNotObject(LGuardIsNotObject* lir) { |
| 5754 | ValueOperand input = ToValue(lir->value()); |
| 5755 | |
| 5756 | Label bail; |
| 5757 | masm.branchTestObject(Assembler::Equal, input, &bail); |
| 5758 | bailoutFrom(&bail, lir->snapshot()); |
| 5759 | } |
| 5760 | |
| 5761 | void CodeGenerator::visitGuardFunctionFlags(LGuardFunctionFlags* lir) { |
| 5762 | Register function = ToRegister(lir->function()); |
| 5763 | |
| 5764 | Label bail; |
| 5765 | if (uint16_t flags = lir->mir()->expectedFlags()) { |
| 5766 | masm.branchTestFunctionFlags(function, flags, Assembler::Zero, &bail); |
| 5767 | } |
| 5768 | if (uint16_t flags = lir->mir()->unexpectedFlags()) { |
| 5769 | masm.branchTestFunctionFlags(function, flags, Assembler::NonZero, &bail); |
| 5770 | } |
| 5771 | bailoutFrom(&bail, lir->snapshot()); |
| 5772 | } |
| 5773 | |
| 5774 | void CodeGenerator::visitGuardFunctionIsNonBuiltinCtor( |
| 5775 | LGuardFunctionIsNonBuiltinCtor* lir) { |
| 5776 | Register function = ToRegister(lir->function()); |
| 5777 | Register temp = ToRegister(lir->temp0()); |
| 5778 | |
| 5779 | Label bail; |
| 5780 | masm.branchIfNotFunctionIsNonBuiltinCtor(function, temp, &bail); |
| 5781 | bailoutFrom(&bail, lir->snapshot()); |
| 5782 | } |
| 5783 | |
| 5784 | void CodeGenerator::visitGuardFunctionKind(LGuardFunctionKind* lir) { |
| 5785 | Register function = ToRegister(lir->function()); |
| 5786 | Register temp = ToRegister(lir->temp0()); |
| 5787 | |
| 5788 | Assembler::Condition cond = |
| 5789 | lir->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual; |
| 5790 | |
| 5791 | Label bail; |
| 5792 | masm.branchFunctionKind(cond, lir->mir()->expected(), function, temp, &bail); |
| 5793 | bailoutFrom(&bail, lir->snapshot()); |
| 5794 | } |
| 5795 | |
| 5796 | void CodeGenerator::visitGuardFunctionScript(LGuardFunctionScript* lir) { |
| 5797 | Register function = ToRegister(lir->function()); |
| 5798 | |
| 5799 | Address scriptAddr(function, JSFunction::offsetOfJitInfoOrScript()); |
| 5800 | bailoutCmpPtr(Assembler::NotEqual, scriptAddr, |
| 5801 | ImmGCPtr(lir->mir()->expected()), lir->snapshot()); |
| 5802 | } |
| 5803 | |
| 5804 | // Out-of-line path to update the store buffer. |
| 5805 | class OutOfLineCallPostWriteBarrier : public OutOfLineCodeBase<CodeGenerator> { |
| 5806 | LInstruction* lir_; |
| 5807 | const LAllocation* object_; |
| 5808 | |
| 5809 | public: |
| 5810 | OutOfLineCallPostWriteBarrier(LInstruction* lir, const LAllocation* object) |
| 5811 | : lir_(lir), object_(object) {} |
| 5812 | |
| 5813 | void accept(CodeGenerator* codegen) override { |
| 5814 | codegen->visitOutOfLineCallPostWriteBarrier(this); |
| 5815 | } |
| 5816 | |
| 5817 | LInstruction* lir() const { return lir_; } |
| 5818 | const LAllocation* object() const { return object_; } |
| 5819 | }; |
| 5820 | |
| 5821 | static void EmitStoreBufferCheckForConstant(MacroAssembler& masm, |
| 5822 | const gc::TenuredCell* cell, |
| 5823 | AllocatableGeneralRegisterSet& regs, |
| 5824 | Label* exit, Label* callVM) { |
| 5825 | Register temp = regs.takeAny(); |
| 5826 | |
| 5827 | gc::Arena* arena = cell->arena(); |
| 5828 | |
| 5829 | Register cells = temp; |
| 5830 | masm.loadPtr(AbsoluteAddress(&arena->bufferedCells()), cells); |
| 5831 | |
| 5832 | size_t index = gc::ArenaCellSet::getCellIndex(cell); |
| 5833 | auto [word, mask] = gc::ArenaCellSet::getWordIndexAndMask(index); |
| 5834 | size_t offset = gc::ArenaCellSet::offsetOfBits() + word * sizeof(uint32_t); |
| 5835 | |
| 5836 | masm.branchTest32(Assembler::NonZero, Address(cells, offset), Imm32(mask), |
| 5837 | exit); |
| 5838 | |
| 5839 | // Check whether this is the sentinel set and if so call the VM to allocate |
| 5840 | // one for this arena. |
| 5841 | masm.branchPtr(Assembler::Equal, |
| 5842 | Address(cells, gc::ArenaCellSet::offsetOfArena()), |
| 5843 | ImmPtr(nullptr), callVM); |
| 5844 | |
| 5845 | // Add the cell to the set. |
| 5846 | masm.or32(Imm32(mask), Address(cells, offset)); |
| 5847 | masm.jump(exit); |
| 5848 | |
| 5849 | regs.add(temp); |
| 5850 | } |
| 5851 | |
| 5852 | static void EmitPostWriteBarrier(MacroAssembler& masm, CompileRuntime* runtime, |
| 5853 | Register objreg, JSObject* maybeConstant, |
| 5854 | bool isGlobal, |
| 5855 | AllocatableGeneralRegisterSet& regs) { |
| 5856 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 5856); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "maybeConstant" ")"); do { MOZ_CrashSequence (__null, 5856); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false); |
| 5857 | |
| 5858 | Label callVM; |
| 5859 | Label exit; |
| 5860 | |
| 5861 | Register temp = regs.takeAny(); |
| 5862 | |
| 5863 | // We already have a fast path to check whether a global is in the store |
| 5864 | // buffer. |
| 5865 | if (!isGlobal) { |
| 5866 | if (maybeConstant) { |
| 5867 | // Check store buffer bitmap directly for known object. |
| 5868 | EmitStoreBufferCheckForConstant(masm, &maybeConstant->asTenured(), regs, |
| 5869 | &exit, &callVM); |
| 5870 | } else { |
| 5871 | // Check one element cache to avoid VM call. |
| 5872 | masm.branchPtr(Assembler::Equal, |
| 5873 | AbsoluteAddress(runtime->addressOfLastBufferedWholeCell()), |
| 5874 | objreg, &exit); |
| 5875 | } |
| 5876 | } |
| 5877 | |
| 5878 | // Call into the VM to barrier the write. |
| 5879 | masm.bind(&callVM); |
| 5880 | |
| 5881 | Register runtimereg = temp; |
| 5882 | masm.mov(ImmPtr(runtime), runtimereg); |
| 5883 | |
| 5884 | masm.setupAlignedABICall(); |
| 5885 | masm.passABIArg(runtimereg); |
| 5886 | masm.passABIArg(objreg); |
| 5887 | if (isGlobal) { |
| 5888 | using Fn = void (*)(JSRuntime* rt, GlobalObject* obj); |
| 5889 | masm.callWithABI<Fn, PostGlobalWriteBarrier>(); |
| 5890 | } else { |
| 5891 | using Fn = void (*)(JSRuntime* rt, js::gc::Cell* obj); |
| 5892 | masm.callWithABI<Fn, PostWriteBarrier>(); |
| 5893 | } |
| 5894 | |
| 5895 | masm.bind(&exit); |
| 5896 | } |
| 5897 | |
| 5898 | void CodeGenerator::emitPostWriteBarrier(const LAllocation* obj) { |
| 5899 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 5900 | |
| 5901 | Register objreg; |
| 5902 | JSObject* object = nullptr; |
| 5903 | bool isGlobal = false; |
| 5904 | if (obj->isConstant()) { |
| 5905 | object = &obj->toConstant()->toObject(); |
| 5906 | isGlobal = isGlobalObject(object); |
| 5907 | objreg = regs.takeAny(); |
| 5908 | masm.movePtr(ImmGCPtr(object), objreg); |
| 5909 | } else { |
| 5910 | objreg = ToRegister(obj); |
| 5911 | regs.takeUnchecked(objreg); |
| 5912 | } |
| 5913 | |
| 5914 | EmitPostWriteBarrier(masm, gen->runtime, objreg, object, isGlobal, regs); |
| 5915 | } |
| 5916 | |
| 5917 | // Returns true if `def` might be allocated in the nursery. |
| 5918 | static bool ValueNeedsPostBarrier(MDefinition* def) { |
| 5919 | if (def->isBox()) { |
| 5920 | def = def->toBox()->input(); |
| 5921 | } |
| 5922 | if (def->type() == MIRType::Value) { |
| 5923 | return true; |
| 5924 | } |
| 5925 | return NeedsPostBarrier(def->type()); |
| 5926 | } |
| 5927 | |
| 5928 | void CodeGenerator::emitElementPostWriteBarrier( |
| 5929 | MInstruction* mir, const LiveRegisterSet& liveVolatileRegs, Register obj, |
| 5930 | Register index, Register scratch, const ConstantOrRegister& val, |
| 5931 | int32_t indexDiff) { |
| 5932 | if (val.constant()) { |
| 5933 | 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())", "./../../../../js/src/jit/CodeGenerator.cpp" , 5934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())" ")"); do { MOZ_CrashSequence(__null, 5934); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
| 5934 | !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())", "./../../../../js/src/jit/CodeGenerator.cpp" , 5934); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsInsideNursery(val.value().toGCThing())" ")"); do { MOZ_CrashSequence(__null, 5934); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 5935 | return; |
| 5936 | } |
| 5937 | |
| 5938 | TypedOrValueRegister reg = val.reg(); |
| 5939 | if (reg.hasTyped() && !NeedsPostBarrier(reg.type())) { |
| 5940 | return; |
| 5941 | } |
| 5942 | |
| 5943 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 5944 | masm.PushRegsInMask(liveVolatileRegs); |
| 5945 | |
| 5946 | if (indexDiff != 0) { |
| 5947 | masm.add32(Imm32(indexDiff), index); |
| 5948 | } |
| 5949 | |
| 5950 | masm.setupUnalignedABICall(scratch); |
| 5951 | masm.movePtr(ImmPtr(gen->runtime), scratch); |
| 5952 | masm.passABIArg(scratch); |
| 5953 | masm.passABIArg(obj); |
| 5954 | masm.passABIArg(index); |
| 5955 | using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index); |
| 5956 | masm.callWithABI<Fn, PostWriteElementBarrier>(); |
| 5957 | |
| 5958 | // We don't need a sub32 here because index must be in liveVolatileRegs |
| 5959 | // if indexDiff is not zero, so it will be restored below. |
| 5960 | MOZ_ASSERT_IF(indexDiff != 0, liveVolatileRegs.has(index))do { if (indexDiff != 0) { do { static_assert( mozilla::detail ::AssertionConditionType<decltype(liveVolatileRegs.has(index ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(liveVolatileRegs.has(index)))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("liveVolatileRegs.has(index)" , "./../../../../js/src/jit/CodeGenerator.cpp", 5960); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "liveVolatileRegs.has(index)" ")"); do { MOZ_CrashSequence (__null, 5960); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false); |
| 5961 | |
| 5962 | masm.PopRegsInMask(liveVolatileRegs); |
| 5963 | |
| 5964 | masm.jump(ool.rejoin()); |
| 5965 | }); |
| 5966 | addOutOfLineCode(ool, mir); |
| 5967 | |
| 5968 | if (reg.hasValue()) { |
| 5969 | masm.branchValueIsNurseryCell(Assembler::NotEqual, reg.valueReg(), scratch, |
| 5970 | ool->rejoin()); |
| 5971 | } else { |
| 5972 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, reg.typedReg().gpr(), |
| 5973 | scratch, ool->rejoin()); |
| 5974 | } |
| 5975 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, scratch, ool->entry()); |
| 5976 | |
| 5977 | masm.bind(ool->rejoin()); |
| 5978 | } |
| 5979 | |
| 5980 | void CodeGenerator::emitPostWriteBarrier(Register objreg) { |
| 5981 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 5982 | regs.takeUnchecked(objreg); |
| 5983 | EmitPostWriteBarrier(masm, gen->runtime, objreg, nullptr, false, regs); |
| 5984 | } |
| 5985 | |
| 5986 | void CodeGenerator::visitOutOfLineCallPostWriteBarrier( |
| 5987 | OutOfLineCallPostWriteBarrier* ool) { |
| 5988 | saveLiveVolatile(ool->lir()); |
| 5989 | const LAllocation* obj = ool->object(); |
| 5990 | emitPostWriteBarrier(obj); |
| 5991 | restoreLiveVolatile(ool->lir()); |
| 5992 | |
| 5993 | masm.jump(ool->rejoin()); |
| 5994 | } |
| 5995 | |
| 5996 | void CodeGenerator::maybeEmitGlobalBarrierCheck(const LAllocation* maybeGlobal, |
| 5997 | OutOfLineCode* ool) { |
| 5998 | // Check whether an object is a global that we have already barriered before |
| 5999 | // calling into the VM. |
| 6000 | // |
| 6001 | // We only check for the script's global, not other globals within the same |
| 6002 | // compartment, because we bake in a pointer to realm->globalWriteBarriered |
| 6003 | // and doing that would be invalid for other realms because they could be |
| 6004 | // collected before the Ion code is discarded. |
| 6005 | |
| 6006 | if (!maybeGlobal->isConstant()) { |
| 6007 | return; |
| 6008 | } |
| 6009 | |
| 6010 | JSObject* obj = &maybeGlobal->toConstant()->toObject(); |
| 6011 | if (gen->realm->maybeGlobal() != obj) { |
| 6012 | return; |
| 6013 | } |
| 6014 | |
| 6015 | const uint32_t* addr = gen->realm->addressOfGlobalWriteBarriered(); |
| 6016 | masm.branch32(Assembler::NotEqual, AbsoluteAddress(addr), Imm32(0), |
| 6017 | ool->rejoin()); |
| 6018 | } |
| 6019 | |
| 6020 | template <class LPostBarrierType, MIRType nurseryType> |
| 6021 | void CodeGenerator::visitPostWriteBarrierCommon(LPostBarrierType* lir, |
| 6022 | OutOfLineCode* ool) { |
| 6023 | static_assert(NeedsPostBarrier(nurseryType)); |
| 6024 | |
| 6025 | addOutOfLineCode(ool, lir->mir()); |
| 6026 | |
| 6027 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 6028 | |
| 6029 | if (lir->object()->isConstant()) { |
| 6030 | // The object must be tenured because MIR and LIR can't contain nursery |
| 6031 | // pointers. |
| 6032 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 6032); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())" ")"); do { MOZ_CrashSequence(__null, 6032); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6033 | } else { |
| 6034 | masm.branchPtrInNurseryChunk(Assembler::Equal, ToRegister(lir->object()), |
| 6035 | temp, ool->rejoin()); |
| 6036 | } |
| 6037 | |
| 6038 | maybeEmitGlobalBarrierCheck(lir->object(), ool); |
| 6039 | |
| 6040 | Register value = ToRegister(lir->value()); |
| 6041 | if constexpr (nurseryType == MIRType::Object) { |
| 6042 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 6042); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::Object" ")"); do { MOZ_CrashSequence(__null, 6042); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6043 | } else if constexpr (nurseryType == MIRType::String) { |
| 6044 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 6044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::String" ")"); do { MOZ_CrashSequence(__null, 6044); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6045 | } else { |
| 6046 | static_assert(nurseryType == MIRType::BigInt); |
| 6047 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 6047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->value()->type() == MIRType::BigInt" ")"); do { MOZ_CrashSequence(__null, 6047); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6048 | } |
| 6049 | masm.branchPtrInNurseryChunk(Assembler::Equal, value, temp, ool->entry()); |
| 6050 | |
| 6051 | masm.bind(ool->rejoin()); |
| 6052 | } |
| 6053 | |
| 6054 | template <class LPostBarrierType> |
| 6055 | void CodeGenerator::visitPostWriteBarrierCommonV(LPostBarrierType* lir, |
| 6056 | OutOfLineCode* ool) { |
| 6057 | addOutOfLineCode(ool, lir->mir()); |
| 6058 | |
| 6059 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 6060 | |
| 6061 | maybeEmitGlobalBarrierCheck(lir->object(), ool); |
| 6062 | |
| 6063 | ValueOperand value = ToValue(lir->value()); |
| 6064 | if (lir->object()->isConstant()) { |
| 6065 | // The object must be tenured because MIR and LIR can't contain nursery |
| 6066 | // pointers. |
| 6067 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 6067); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!IsInsideNursery(&lir->object()->toConstant()->toObject())" ")"); do { MOZ_CrashSequence(__null, 6067); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6068 | masm.branchValueIsNurseryCell(Assembler::Equal, value, temp, ool->entry()); |
| 6069 | } else { |
| 6070 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, |
| 6071 | ool->rejoin()); |
| 6072 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, ToRegister(lir->object()), |
| 6073 | temp, ool->entry()); |
| 6074 | } |
| 6075 | |
| 6076 | masm.bind(ool->rejoin()); |
| 6077 | } |
| 6078 | |
| 6079 | void CodeGenerator::visitPostWriteBarrierO(LPostWriteBarrierO* lir) { |
| 6080 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
| 6081 | visitPostWriteBarrierCommon<LPostWriteBarrierO, MIRType::Object>(lir, ool); |
| 6082 | } |
| 6083 | |
| 6084 | void CodeGenerator::visitPostWriteBarrierS(LPostWriteBarrierS* lir) { |
| 6085 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
| 6086 | visitPostWriteBarrierCommon<LPostWriteBarrierS, MIRType::String>(lir, ool); |
| 6087 | } |
| 6088 | |
| 6089 | void CodeGenerator::visitPostWriteBarrierBI(LPostWriteBarrierBI* lir) { |
| 6090 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
| 6091 | visitPostWriteBarrierCommon<LPostWriteBarrierBI, MIRType::BigInt>(lir, ool); |
| 6092 | } |
| 6093 | |
| 6094 | void CodeGenerator::visitPostWriteBarrierV(LPostWriteBarrierV* lir) { |
| 6095 | auto ool = new (alloc()) OutOfLineCallPostWriteBarrier(lir, lir->object()); |
| 6096 | visitPostWriteBarrierCommonV(lir, ool); |
| 6097 | } |
| 6098 | |
| 6099 | // Out-of-line path to update the store buffer. |
| 6100 | class OutOfLineCallPostWriteElementBarrier |
| 6101 | : public OutOfLineCodeBase<CodeGenerator> { |
| 6102 | LInstruction* lir_; |
| 6103 | const LAllocation* object_; |
| 6104 | const LAllocation* index_; |
| 6105 | |
| 6106 | public: |
| 6107 | OutOfLineCallPostWriteElementBarrier(LInstruction* lir, |
| 6108 | const LAllocation* object, |
| 6109 | const LAllocation* index) |
| 6110 | : lir_(lir), object_(object), index_(index) {} |
| 6111 | |
| 6112 | void accept(CodeGenerator* codegen) override { |
| 6113 | codegen->visitOutOfLineCallPostWriteElementBarrier(this); |
| 6114 | } |
| 6115 | |
| 6116 | LInstruction* lir() const { return lir_; } |
| 6117 | |
| 6118 | const LAllocation* object() const { return object_; } |
| 6119 | |
| 6120 | const LAllocation* index() const { return index_; } |
| 6121 | }; |
| 6122 | |
| 6123 | void CodeGenerator::visitOutOfLineCallPostWriteElementBarrier( |
| 6124 | OutOfLineCallPostWriteElementBarrier* ool) { |
| 6125 | saveLiveVolatile(ool->lir()); |
| 6126 | |
| 6127 | const LAllocation* obj = ool->object(); |
| 6128 | const LAllocation* index = ool->index(); |
| 6129 | |
| 6130 | Register objreg = obj->isConstant() ? InvalidReg : ToRegister(obj); |
| 6131 | Register indexreg = ToRegister(index); |
| 6132 | |
| 6133 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 6134 | regs.takeUnchecked(indexreg); |
| 6135 | |
| 6136 | if (obj->isConstant()) { |
| 6137 | objreg = regs.takeAny(); |
| 6138 | masm.movePtr(ImmGCPtr(&obj->toConstant()->toObject()), objreg); |
| 6139 | } else { |
| 6140 | regs.takeUnchecked(objreg); |
| 6141 | } |
| 6142 | |
| 6143 | Register runtimereg = regs.takeAny(); |
| 6144 | using Fn = void (*)(JSRuntime* rt, JSObject* obj, int32_t index); |
| 6145 | masm.setupAlignedABICall(); |
| 6146 | masm.mov(ImmPtr(gen->runtime), runtimereg); |
| 6147 | masm.passABIArg(runtimereg); |
| 6148 | masm.passABIArg(objreg); |
| 6149 | masm.passABIArg(indexreg); |
| 6150 | masm.callWithABI<Fn, PostWriteElementBarrier>(); |
| 6151 | |
| 6152 | restoreLiveVolatile(ool->lir()); |
| 6153 | |
| 6154 | masm.jump(ool->rejoin()); |
| 6155 | } |
| 6156 | |
| 6157 | void CodeGenerator::visitPostWriteElementBarrierO( |
| 6158 | LPostWriteElementBarrierO* lir) { |
| 6159 | auto ool = new (alloc()) |
| 6160 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
| 6161 | visitPostWriteBarrierCommon<LPostWriteElementBarrierO, MIRType::Object>(lir, |
| 6162 | ool); |
| 6163 | } |
| 6164 | |
| 6165 | void CodeGenerator::visitPostWriteElementBarrierS( |
| 6166 | LPostWriteElementBarrierS* lir) { |
| 6167 | auto ool = new (alloc()) |
| 6168 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
| 6169 | visitPostWriteBarrierCommon<LPostWriteElementBarrierS, MIRType::String>(lir, |
| 6170 | ool); |
| 6171 | } |
| 6172 | |
| 6173 | void CodeGenerator::visitPostWriteElementBarrierBI( |
| 6174 | LPostWriteElementBarrierBI* lir) { |
| 6175 | auto ool = new (alloc()) |
| 6176 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
| 6177 | visitPostWriteBarrierCommon<LPostWriteElementBarrierBI, MIRType::BigInt>(lir, |
| 6178 | ool); |
| 6179 | } |
| 6180 | |
| 6181 | void CodeGenerator::visitPostWriteElementBarrierV( |
| 6182 | LPostWriteElementBarrierV* lir) { |
| 6183 | auto ool = new (alloc()) |
| 6184 | OutOfLineCallPostWriteElementBarrier(lir, lir->object(), lir->index()); |
| 6185 | visitPostWriteBarrierCommonV(lir, ool); |
| 6186 | } |
| 6187 | |
| 6188 | void CodeGenerator::visitAssertCanElidePostWriteBarrier( |
| 6189 | LAssertCanElidePostWriteBarrier* lir) { |
| 6190 | Register object = ToRegister(lir->object()); |
| 6191 | ValueOperand value = ToValue(lir->value()); |
| 6192 | Register temp = ToRegister(lir->temp0()); |
| 6193 | |
| 6194 | Label ok; |
| 6195 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp, &ok); |
| 6196 | masm.branchPtrInNurseryChunk(Assembler::Equal, object, temp, &ok); |
| 6197 | |
| 6198 | masm.assumeUnreachable("Unexpected missing post write barrier"); |
| 6199 | |
| 6200 | masm.bind(&ok); |
| 6201 | } |
| 6202 | |
| 6203 | template <typename LCallIns> |
| 6204 | void CodeGenerator::emitCallNative(LCallIns* call, JSNative native, |
| 6205 | Register argContextReg, Register argUintNReg, |
| 6206 | Register argVpReg, Register tempReg, |
| 6207 | uint32_t unusedStack) { |
| 6208 | masm.checkStackAlignment(); |
| 6209 | |
| 6210 | // Native functions have the signature: |
| 6211 | // bool (*)(JSContext*, unsigned, Value* vp) |
| 6212 | // Where vp[0] is space for an outparam, vp[1] is |this|, and vp[2] onward |
| 6213 | // are the function arguments. |
| 6214 | |
| 6215 | // Allocate space for the outparam, moving the StackPointer to what will be |
| 6216 | // &vp[1]. |
| 6217 | masm.adjustStack(unusedStack); |
| 6218 | |
| 6219 | // Push a Value containing the callee object: natives are allowed to access |
| 6220 | // their callee before setting the return value. The StackPointer is moved |
| 6221 | // to &vp[0]. |
| 6222 | // |
| 6223 | // Also reserves the space for |NativeExitFrameLayout::{lo,hi}CalleeResult_|. |
| 6224 | if constexpr (std::is_same_v<LCallIns, LCallClassHook>) { |
| 6225 | Register calleeReg = ToRegister(call->getCallee()); |
| 6226 | masm.Push(TypedOrValueRegister(MIRType::Object, AnyRegister(calleeReg))); |
| 6227 | |
| 6228 | // Enter the callee realm. |
| 6229 | if (call->mir()->maybeCrossRealm()) { |
| 6230 | masm.switchToObjectRealm(calleeReg, tempReg); |
| 6231 | } |
| 6232 | } else { |
| 6233 | WrappedFunction* target = call->mir()->getSingleTarget(); |
| 6234 | masm.Push(ObjectValue(*target->rawNativeJSFunction())); |
| 6235 | |
| 6236 | // Enter the callee realm. |
| 6237 | if (call->mir()->maybeCrossRealm()) { |
| 6238 | masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), tempReg); |
| 6239 | masm.switchToObjectRealm(tempReg, tempReg); |
| 6240 | } |
| 6241 | } |
| 6242 | |
| 6243 | // Preload arguments into registers. |
| 6244 | masm.loadJSContext(argContextReg); |
| 6245 | masm.moveStackPtrTo(argVpReg); |
| 6246 | |
| 6247 | // Initialize |NativeExitFrameLayout::argc_|. |
| 6248 | masm.Push(argUintNReg); |
| 6249 | |
| 6250 | // Construct native exit frame. |
| 6251 | // |
| 6252 | // |buildFakeExitFrame| initializes |NativeExitFrameLayout::exit_| and |
| 6253 | // |enterFakeExitFrameForNative| initializes |NativeExitFrameLayout::footer_|. |
| 6254 | // |
| 6255 | // The NativeExitFrameLayout is now fully initialized. |
| 6256 | uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg); |
| 6257 | masm.enterFakeExitFrameForNative(argContextReg, tempReg, |
| 6258 | call->mir()->isConstructing()); |
| 6259 | |
| 6260 | markSafepointAt(safepointOffset, call); |
| 6261 | |
| 6262 | // Construct and execute call. |
| 6263 | masm.setupAlignedABICall(); |
| 6264 | masm.passABIArg(argContextReg); |
| 6265 | masm.passABIArg(argUintNReg); |
| 6266 | masm.passABIArg(argVpReg); |
| 6267 | |
| 6268 | ensureOsiSpace(); |
| 6269 | // If we're using a simulator build, `native` will already point to the |
| 6270 | // simulator's call-redirection code for LCallClassHook. Load the address in |
| 6271 | // a register first so that we don't try to redirect it a second time. |
| 6272 | bool emittedCall = false; |
| 6273 | #ifdef JS_SIMULATOR |
| 6274 | if constexpr (std::is_same_v<LCallIns, LCallClassHook>) { |
| 6275 | masm.movePtr(ImmPtr(native), tempReg); |
| 6276 | masm.callWithABI(tempReg); |
| 6277 | emittedCall = true; |
| 6278 | } |
| 6279 | #endif |
| 6280 | if (!emittedCall) { |
| 6281 | masm.callWithABI(DynamicFunction<JSNative>(native), ABIType::General, |
| 6282 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 6283 | } |
| 6284 | |
| 6285 | // Test for failure. |
| 6286 | masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); |
| 6287 | |
| 6288 | // Exit the callee realm. |
| 6289 | if (call->mir()->maybeCrossRealm()) { |
| 6290 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 6291 | } |
| 6292 | |
| 6293 | // Load the outparam vp[0] into output register(s). |
| 6294 | masm.loadValue( |
| 6295 | Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), |
| 6296 | JSReturnOperand); |
| 6297 | |
| 6298 | // Until C++ code is instrumented against Spectre, prevent speculative |
| 6299 | // execution from returning any private data. |
| 6300 | if (JitOptions.spectreJitToCxxCalls && !call->mir()->ignoresReturnValue() && |
| 6301 | call->mir()->hasLiveDefUses()) { |
| 6302 | masm.speculationBarrier(); |
| 6303 | } |
| 6304 | |
| 6305 | #ifdef DEBUG1 |
| 6306 | // Native constructors are guaranteed to return an Object value. |
| 6307 | if (call->mir()->isConstructing()) { |
| 6308 | Label notPrimitive; |
| 6309 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 6310 | ¬Primitive); |
| 6311 | masm.assumeUnreachable("native constructors don't return primitives"); |
| 6312 | masm.bind(¬Primitive); |
| 6313 | } |
| 6314 | #endif |
| 6315 | } |
| 6316 | |
| 6317 | template <typename LCallIns> |
| 6318 | void CodeGenerator::emitCallNative(LCallIns* call, JSNative native) { |
| 6319 | uint32_t unusedStack = |
| 6320 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
| 6321 | |
| 6322 | // Registers used for callWithABI() argument-passing. |
| 6323 | const Register argContextReg = ToRegister(call->getArgContextReg()); |
| 6324 | const Register argUintNReg = ToRegister(call->getArgUintNReg()); |
| 6325 | const Register argVpReg = ToRegister(call->getArgVpReg()); |
| 6326 | |
| 6327 | // Misc. temporary registers. |
| 6328 | const Register tempReg = ToRegister(call->getTempReg()); |
| 6329 | |
| 6330 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
| 6331 | |
| 6332 | // Initialize the argc register. |
| 6333 | masm.move32(Imm32(call->mir()->numActualArgs()), argUintNReg); |
| 6334 | |
| 6335 | // Create the exit frame and call the native. |
| 6336 | emitCallNative(call, native, argContextReg, argUintNReg, argVpReg, tempReg, |
| 6337 | unusedStack); |
| 6338 | |
| 6339 | // The next instruction is removing the footer of the exit frame, so there |
| 6340 | // is no need for leaveFakeExitFrame. |
| 6341 | |
| 6342 | // Move the StackPointer back to its original location, unwinding the native |
| 6343 | // exit frame. |
| 6344 | masm.adjustStack(NativeExitFrameLayout::Size() - unusedStack); |
| 6345 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 6345); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { MOZ_CrashSequence(__null, 6345); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6346 | } |
| 6347 | |
| 6348 | void CodeGenerator::visitCallNative(LCallNative* call) { |
| 6349 | WrappedFunction* target = call->getSingleTarget(); |
| 6350 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 6350); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")" ); do { MOZ_CrashSequence(__null, 6350); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6351 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 6351); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { MOZ_CrashSequence(__null, 6351); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6352 | |
| 6353 | JSNative native = target->native(); |
| 6354 | if (call->ignoresReturnValue() && target->hasJitInfo()) { |
| 6355 | const JSJitInfo* jitInfo = target->jitInfo(); |
| 6356 | if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) { |
| 6357 | native = jitInfo->ignoresReturnValueMethod; |
| 6358 | } |
| 6359 | } |
| 6360 | emitCallNative(call, native); |
| 6361 | } |
| 6362 | |
| 6363 | void CodeGenerator::visitCallClassHook(LCallClassHook* call) { |
| 6364 | emitCallNative(call, call->mir()->target()); |
| 6365 | } |
| 6366 | |
| 6367 | static void LoadDOMPrivate(MacroAssembler& masm, Register obj, Register priv, |
| 6368 | DOMObjectKind kind) { |
| 6369 | // Load the value in DOM_OBJECT_SLOT for a native or proxy DOM object. This |
| 6370 | // will be in the first slot but may be fixed or non-fixed. |
| 6371 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 6371); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj != priv" ")"); do { MOZ_CrashSequence(__null, 6371); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6372 | |
| 6373 | switch (kind) { |
| 6374 | case DOMObjectKind::Native: |
| 6375 | // If it's a native object, the value must be in a fixed slot. |
| 6376 | // See CanAttachDOMCall in CacheIR.cpp. |
| 6377 | masm.debugAssertObjHasFixedSlots(obj, priv); |
| 6378 | masm.loadPrivate(Address(obj, NativeObject::getFixedSlotOffset(0)), priv); |
| 6379 | break; |
| 6380 | case DOMObjectKind::Proxy: { |
| 6381 | #ifdef DEBUG1 |
| 6382 | // Sanity check: it must be a DOM proxy. |
| 6383 | Label isDOMProxy; |
| 6384 | masm.branchTestProxyHandlerFamily( |
| 6385 | Assembler::Equal, obj, priv, GetDOMProxyHandlerFamily(), &isDOMProxy); |
| 6386 | masm.assumeUnreachable("Expected a DOM proxy"); |
| 6387 | masm.bind(&isDOMProxy); |
| 6388 | #endif |
| 6389 | masm.loadPrivate(Address(obj, ProxyObject::offsetOfReservedSlot(0)), |
| 6390 | priv); |
| 6391 | break; |
| 6392 | } |
| 6393 | } |
| 6394 | } |
| 6395 | |
| 6396 | void CodeGenerator::visitCallDOMNative(LCallDOMNative* call) { |
| 6397 | WrappedFunction* target = call->getSingleTarget(); |
| 6398 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 6398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")" ); do { MOZ_CrashSequence(__null, 6398); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6399 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 6399); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { MOZ_CrashSequence(__null, 6399); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6400 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 6400); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "target->hasJitInfo()" ")"); do { MOZ_CrashSequence (__null, 6400); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 6401 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 6401); AnnotateMozCrashReason("MOZ_ASSERT" "(" "call->mir()->isCallDOMNative()" ")"); do { MOZ_CrashSequence(__null, 6401); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6402 | |
| 6403 | int unusedStack = UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
| 6404 | |
| 6405 | // Registers used for callWithABI() argument-passing. |
| 6406 | const Register argJSContext = ToRegister(call->getArgJSContext()); |
| 6407 | const Register argObj = ToRegister(call->getArgObj()); |
| 6408 | const Register argPrivate = ToRegister(call->getArgPrivate()); |
| 6409 | const Register argArgs = ToRegister(call->getArgArgs()); |
| 6410 | |
| 6411 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
| 6412 | |
| 6413 | masm.checkStackAlignment(); |
| 6414 | |
| 6415 | // DOM methods have the signature: |
| 6416 | // bool (*)(JSContext*, HandleObject, void* private, const |
| 6417 | // JSJitMethodCallArgs& args) |
| 6418 | // Where args is initialized from an argc and a vp, vp[0] is space for an |
| 6419 | // outparam and the callee, vp[1] is |this|, and vp[2] onward are the |
| 6420 | // function arguments. Note that args stores the argv, not the vp, and |
| 6421 | // argv == vp + 2. |
| 6422 | |
| 6423 | // Nestle the stack up against the pushed arguments, leaving StackPointer at |
| 6424 | // &vp[1] |
| 6425 | masm.adjustStack(unusedStack); |
| 6426 | // argObj is filled with the extracted object, then returned. |
| 6427 | Register obj = masm.extractObject(Address(masm.getStackPointer(), 0), argObj); |
| 6428 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 6428); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "obj == argObj" ")"); do { MOZ_CrashSequence (__null, 6428); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 6429 | |
| 6430 | // Push a Value containing the callee object: natives are allowed to access |
| 6431 | // their callee before setting the return value. After this the StackPointer |
| 6432 | // points to &vp[0]. |
| 6433 | masm.Push(ObjectValue(*target->rawNativeJSFunction())); |
| 6434 | |
| 6435 | // Now compute the argv value. Since StackPointer is pointing to &vp[0] and |
| 6436 | // argv is &vp[2] we just need to add 2*sizeof(Value) to the current |
| 6437 | // StackPointer. |
| 6438 | static_assert(JSJitMethodCallArgsTraits::offsetOfArgv == 0); |
| 6439 | static_assert(JSJitMethodCallArgsTraits::offsetOfArgc == |
| 6440 | IonDOMMethodExitFrameLayoutTraits::offsetOfArgcFromArgv); |
| 6441 | masm.computeEffectiveAddress( |
| 6442 | Address(masm.getStackPointer(), 2 * sizeof(Value)), argArgs); |
| 6443 | |
| 6444 | LoadDOMPrivate(masm, obj, argPrivate, |
| 6445 | static_cast<MCallDOMNative*>(call->mir())->objectKind()); |
| 6446 | |
| 6447 | // Push argc from the call instruction into what will become the IonExitFrame |
| 6448 | masm.Push(Imm32(call->numActualArgs())); |
| 6449 | |
| 6450 | // Push our argv onto the stack |
| 6451 | masm.Push(argArgs); |
| 6452 | // And store our JSJitMethodCallArgs* in argArgs. |
| 6453 | masm.moveStackPtrTo(argArgs); |
| 6454 | |
| 6455 | // Push |this| object for passing HandleObject. We push after argc to |
| 6456 | // maintain the same sp-relative location of the object pointer with other |
| 6457 | // DOMExitFrames. |
| 6458 | masm.Push(argObj); |
| 6459 | masm.moveStackPtrTo(argObj); |
| 6460 | |
| 6461 | if (call->mir()->maybeCrossRealm()) { |
| 6462 | // We use argJSContext as scratch register here. |
| 6463 | masm.movePtr(ImmGCPtr(target->rawNativeJSFunction()), argJSContext); |
| 6464 | masm.switchToObjectRealm(argJSContext, argJSContext); |
| 6465 | } |
| 6466 | |
| 6467 | bool preTenureWrapperAllocation = |
| 6468 | call->mir()->to<MCallDOMNative>()->initialHeap() == gc::Heap::Tenured; |
| 6469 | if (preTenureWrapperAllocation) { |
| 6470 | auto ptr = ImmPtr(mirGen().realm->zone()->tenuringAllocSite()); |
| 6471 | masm.storeLocalAllocSite(ptr, argJSContext); |
| 6472 | } |
| 6473 | |
| 6474 | // Construct native exit frame. |
| 6475 | uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext); |
| 6476 | |
| 6477 | masm.loadJSContext(argJSContext); |
| 6478 | masm.enterFakeExitFrame(argJSContext, argJSContext, |
| 6479 | ExitFrameType::IonDOMMethod); |
| 6480 | |
| 6481 | markSafepointAt(safepointOffset, call); |
| 6482 | |
| 6483 | // Construct and execute call. |
| 6484 | masm.setupAlignedABICall(); |
| 6485 | masm.loadJSContext(argJSContext); |
| 6486 | masm.passABIArg(argJSContext); |
| 6487 | masm.passABIArg(argObj); |
| 6488 | masm.passABIArg(argPrivate); |
| 6489 | masm.passABIArg(argArgs); |
| 6490 | ensureOsiSpace(); |
| 6491 | masm.callWithABI(DynamicFunction<JSJitMethodOp>(target->jitInfo()->method), |
| 6492 | ABIType::General, |
| 6493 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 6494 | |
| 6495 | if (target->jitInfo()->isInfallible) { |
| 6496 | masm.loadValue(Address(masm.getStackPointer(), |
| 6497 | IonDOMMethodExitFrameLayout::offsetOfResult()), |
| 6498 | JSReturnOperand); |
| 6499 | } else { |
| 6500 | // Test for failure. |
| 6501 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
| 6502 | |
| 6503 | // Load the outparam vp[0] into output register(s). |
| 6504 | masm.loadValue(Address(masm.getStackPointer(), |
| 6505 | IonDOMMethodExitFrameLayout::offsetOfResult()), |
| 6506 | JSReturnOperand); |
| 6507 | } |
| 6508 | |
| 6509 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 6510 | "Clobbering ReturnReg should not affect the return value"); |
| 6511 | |
| 6512 | // Switch back to the current realm if needed. Note: if the DOM method threw |
| 6513 | // an exception, the exception handler will do this. |
| 6514 | if (call->mir()->maybeCrossRealm()) { |
| 6515 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 6516 | } |
| 6517 | |
| 6518 | // Wipe out the preTenuring bit from the local alloc site |
| 6519 | // On exception we handle this in C++ |
| 6520 | if (preTenureWrapperAllocation) { |
| 6521 | masm.storeLocalAllocSite(ImmPtr(nullptr), ReturnReg); |
| 6522 | } |
| 6523 | |
| 6524 | // Until C++ code is instrumented against Spectre, prevent speculative |
| 6525 | // execution from returning any private data. |
| 6526 | if (JitOptions.spectreJitToCxxCalls && call->mir()->hasLiveDefUses()) { |
| 6527 | masm.speculationBarrier(); |
| 6528 | } |
| 6529 | |
| 6530 | // The next instruction is removing the footer of the exit frame, so there |
| 6531 | // is no need for leaveFakeExitFrame. |
| 6532 | |
| 6533 | // Move the StackPointer back to its original location, unwinding the native |
| 6534 | // exit frame. |
| 6535 | masm.adjustStack(IonDOMMethodExitFrameLayout::Size() - unusedStack); |
| 6536 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 6536); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { MOZ_CrashSequence(__null, 6536); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6537 | } |
| 6538 | |
| 6539 | void CodeGenerator::visitCallGetIntrinsicValue(LCallGetIntrinsicValue* lir) { |
| 6540 | pushArg(ImmGCPtr(lir->mir()->name())); |
| 6541 | |
| 6542 | using Fn = bool (*)(JSContext* cx, Handle<PropertyName*>, MutableHandleValue); |
| 6543 | callVM<Fn, GetIntrinsicValue>(lir); |
| 6544 | } |
| 6545 | |
| 6546 | void CodeGenerator::emitCallInvokeFunction( |
| 6547 | LInstruction* call, Register calleereg, bool constructing, |
| 6548 | bool ignoresReturnValue, uint32_t argc, uint32_t unusedStack) { |
| 6549 | // Nestle %esp up to the argument vector. |
| 6550 | // Each path must account for framePushed_ separately, for callVM to be valid. |
| 6551 | masm.freeStack(unusedStack); |
| 6552 | |
| 6553 | pushArg(masm.getStackPointer()); // argv. |
| 6554 | pushArg(Imm32(argc)); // argc. |
| 6555 | pushArg(Imm32(ignoresReturnValue)); |
| 6556 | pushArg(Imm32(constructing)); // constructing. |
| 6557 | pushArg(calleereg); // JSFunction*. |
| 6558 | |
| 6559 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
| 6560 | MutableHandleValue); |
| 6561 | callVM<Fn, jit::InvokeFunction>(call); |
| 6562 | |
| 6563 | // Un-nestle %esp from the argument vector. No prefix was pushed. |
| 6564 | masm.reserveStack(unusedStack); |
| 6565 | } |
| 6566 | |
| 6567 | void CodeGenerator::visitCallGeneric(LCallGeneric* call) { |
| 6568 | // The callee is passed straight through to the trampoline. |
| 6569 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 6569); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(call->getCallee()) == IonGenericCallCalleeReg" ")"); do { MOZ_CrashSequence(__null, 6569); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 6570 | |
| 6571 | Register argcReg = ToRegister(call->getArgc()); |
| 6572 | uint32_t unusedStack = |
| 6573 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
| 6574 | |
| 6575 | // Known-target case is handled by LCallKnown. |
| 6576 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 6576); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!call->hasSingleTarget()" ")"); do { MOZ_CrashSequence (__null, 6576); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 6577 | |
| 6578 | masm.checkStackAlignment(); |
| 6579 | |
| 6580 | masm.move32(Imm32(call->numActualArgs()), argcReg); |
| 6581 | |
| 6582 | // Nestle the StackPointer up to the argument vector. |
| 6583 | masm.freeStack(unusedStack); |
| 6584 | ensureOsiSpace(); |
| 6585 | |
| 6586 | auto kind = call->mir()->isConstructing() ? IonGenericCallKind::Construct |
| 6587 | : IonGenericCallKind::Call; |
| 6588 | |
| 6589 | TrampolinePtr genericCallStub = |
| 6590 | gen->jitRuntime()->getIonGenericCallStub(kind); |
| 6591 | uint32_t callOffset = masm.callJit(genericCallStub); |
| 6592 | markSafepointAt(callOffset, call); |
| 6593 | |
| 6594 | if (call->mir()->maybeCrossRealm()) { |
| 6595 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 6596 | "ReturnReg available as scratch after scripted calls"); |
| 6597 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 6598 | } |
| 6599 | |
| 6600 | // If the return value of the constructing function is Primitive, |
| 6601 | // replace the return value with the Object from CreateThis. |
| 6602 | if (call->mir()->isConstructing()) { |
| 6603 | Label notPrimitive; |
| 6604 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 6605 | ¬Primitive); |
| 6606 | size_t thisvOffset = |
| 6607 | JitFrameLayout::offsetOfThis() - JitFrameLayout::bytesPoppedAfterCall(); |
| 6608 | masm.loadValue(Address(masm.getStackPointer(), thisvOffset), |
| 6609 | JSReturnOperand); |
| 6610 | #ifdef DEBUG1 |
| 6611 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 6612 | ¬Primitive); |
| 6613 | masm.assumeUnreachable("CreateThis creates an object"); |
| 6614 | #endif |
| 6615 | masm.bind(¬Primitive); |
| 6616 | } |
| 6617 | |
| 6618 | // Restore stack pointer. |
| 6619 | masm.setFramePushed(frameSize()); |
| 6620 | emitRestoreStackPointerFromFP(); |
| 6621 | } |
| 6622 | |
| 6623 | void JitRuntime::generateIonGenericCallArgumentsShift( |
| 6624 | MacroAssembler& masm, Register argc, Register curr, Register end, |
| 6625 | Register scratch, Label* done) { |
| 6626 | static_assert(sizeof(Value) == 8); |
| 6627 | // There are |argc| Values on the stack. Shift them all down by 8 bytes, |
| 6628 | // overwriting the first value. |
| 6629 | |
| 6630 | // Initialize `curr` to the destination of the first copy, and `end` to the |
| 6631 | // final value of curr. |
| 6632 | masm.moveStackPtrTo(curr); |
| 6633 | masm.computeEffectiveAddress(BaseValueIndex(curr, argc), end); |
| 6634 | |
| 6635 | Label loop; |
| 6636 | masm.bind(&loop); |
| 6637 | masm.branchPtr(Assembler::Equal, curr, end, done); |
| 6638 | masm.loadPtr(Address(curr, 8), scratch); |
| 6639 | masm.storePtr(scratch, Address(curr, 0)); |
| 6640 | masm.addPtr(Imm32(sizeof(uintptr_t)), curr); |
| 6641 | masm.jump(&loop); |
| 6642 | } |
| 6643 | |
| 6644 | void JitRuntime::generateIonGenericCallStub(MacroAssembler& masm, |
| 6645 | IonGenericCallKind kind) { |
| 6646 | AutoCreatedBy acb(masm, "JitRuntime::generateIonGenericCallStub"); |
| 6647 | ionGenericCallStubOffset_[kind] = startTrampolineCode(masm); |
| 6648 | |
| 6649 | // This code is tightly coupled with visitCallGeneric. |
| 6650 | // |
| 6651 | // Upon entry: |
| 6652 | // IonGenericCallCalleeReg contains a pointer to the callee object. |
| 6653 | // IonGenericCallArgcReg contains the number of actual args. |
| 6654 | // The arguments have been pushed onto the stack: |
| 6655 | // [newTarget] (iff isConstructing) |
| 6656 | // [argN] |
| 6657 | // ... |
| 6658 | // [arg1] |
| 6659 | // [arg0] |
| 6660 | // [this] |
| 6661 | // <return address> (if not JS_USE_LINK_REGISTER) |
| 6662 | // |
| 6663 | // This trampoline is responsible for entering the callee's realm, |
| 6664 | // massaging the stack into the right shape, and then performing a |
| 6665 | // tail call. We will return directly to the Ion code from the |
| 6666 | // callee. |
| 6667 | // |
| 6668 | // To do a tail call, we keep the return address in a register, even |
| 6669 | // on platforms that don't normally use a link register, and push it |
| 6670 | // just before jumping to the callee, after we are done setting up |
| 6671 | // the stack. |
| 6672 | // |
| 6673 | // The caller is responsible for switching back to the caller's |
| 6674 | // realm and cleaning up the stack. |
| 6675 | |
| 6676 | Register calleeReg = IonGenericCallCalleeReg; |
| 6677 | Register argcReg = IonGenericCallArgcReg; |
| 6678 | AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs()); |
| 6679 | Register scratch = regs.takeAny(); |
| 6680 | Register scratch2 = regs.takeAny(); |
| 6681 | |
| 6682 | #ifndef JS_USE_LINK_REGISTER |
| 6683 | Register returnAddrReg = IonGenericCallReturnAddrReg; |
| 6684 | masm.pop(returnAddrReg); |
| 6685 | #endif |
| 6686 | |
| 6687 | #ifdef JS_CODEGEN_ARM |
| 6688 | // The default second scratch register on arm is lr, which we need |
| 6689 | // preserved for tail calls. |
| 6690 | AutoNonDefaultSecondScratchRegister andssr(masm, IonGenericSecondScratchReg); |
| 6691 | #endif |
| 6692 | |
| 6693 | bool isConstructing = kind == IonGenericCallKind::Construct; |
| 6694 | |
| 6695 | Label entry, notFunction, noJitEntry, vmCall; |
| 6696 | masm.bind(&entry); |
| 6697 | |
| 6698 | // Guard that the callee is actually a function. |
| 6699 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch, |
| 6700 | calleeReg, ¬Function); |
| 6701 | |
| 6702 | // Guard that the callee supports the [[Call]] or [[Construct]] operation. |
| 6703 | // If these tests fail, we will call into the VM to throw an exception. |
| 6704 | if (isConstructing) { |
| 6705 | masm.branchTestFunctionFlags(calleeReg, FunctionFlags::CONSTRUCTOR, |
| 6706 | Assembler::Zero, &vmCall); |
| 6707 | } else { |
| 6708 | masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor, |
| 6709 | calleeReg, scratch, &vmCall); |
| 6710 | } |
| 6711 | |
| 6712 | if (isConstructing) { |
| 6713 | // Use the slow path if CreateThis was unable to create the |this| object. |
| 6714 | Address thisAddr(masm.getStackPointer(), 0); |
| 6715 | masm.branchTestNull(Assembler::Equal, thisAddr, &vmCall); |
| 6716 | } |
| 6717 | |
| 6718 | masm.switchToObjectRealm(calleeReg, scratch); |
| 6719 | |
| 6720 | // Load jitCodeRaw for callee if it exists. |
| 6721 | masm.branchIfFunctionHasNoJitEntry(calleeReg, &noJitEntry); |
| 6722 | |
| 6723 | // **************************** |
| 6724 | // * Functions with jit entry * |
| 6725 | // **************************** |
| 6726 | |
| 6727 | generateIonGenericHandleUnderflow(masm, isConstructing, &vmCall); |
| 6728 | |
| 6729 | masm.loadJitCodeRaw(calleeReg, scratch2); |
| 6730 | |
| 6731 | // Construct the JitFrameLayout. |
| 6732 | masm.PushCalleeToken(calleeReg, isConstructing); |
| 6733 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcReg, scratch); |
| 6734 | #ifndef JS_USE_LINK_REGISTER |
| 6735 | masm.push(returnAddrReg); |
| 6736 | #endif |
| 6737 | |
| 6738 | // Tail call the jit entry. |
| 6739 | masm.jump(scratch2); |
| 6740 | |
| 6741 | // ******************** |
| 6742 | // * Native functions * |
| 6743 | // ******************** |
| 6744 | masm.bind(&noJitEntry); |
| 6745 | if (!isConstructing) { |
| 6746 | generateIonGenericCallFunCall(masm, &entry, &vmCall); |
| 6747 | } |
| 6748 | generateIonGenericCallNativeFunction(masm, isConstructing); |
| 6749 | |
| 6750 | // ******************* |
| 6751 | // * Bound functions * |
| 6752 | // ******************* |
| 6753 | // TODO: support class hooks? |
| 6754 | masm.bind(¬Function); |
| 6755 | if (!isConstructing) { |
| 6756 | // TODO: support generic bound constructors? |
| 6757 | generateIonGenericCallBoundFunction(masm, &entry, &vmCall); |
| 6758 | } |
| 6759 | |
| 6760 | // ******************** |
| 6761 | // * Fallback VM call * |
| 6762 | // ******************** |
| 6763 | masm.bind(&vmCall); |
| 6764 | |
| 6765 | masm.push(masm.getStackPointer()); // argv |
| 6766 | masm.push(argcReg); // argc |
| 6767 | masm.push(Imm32(false)); // ignores return value |
| 6768 | masm.push(Imm32(isConstructing)); // constructing |
| 6769 | masm.push(calleeReg); // callee |
| 6770 | |
| 6771 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
| 6772 | MutableHandleValue); |
| 6773 | VMFunctionId id = VMFunctionToId<Fn, jit::InvokeFunction>::id; |
| 6774 | uint32_t invokeFunctionOffset = functionWrapperOffsets_[size_t(id)]; |
| 6775 | Label invokeFunctionVMEntry; |
| 6776 | bindLabelToOffset(&invokeFunctionVMEntry, invokeFunctionOffset); |
| 6777 | |
| 6778 | masm.push(FrameDescriptor(FrameType::IonJS)); |
| 6779 | #ifndef JS_USE_LINK_REGISTER |
| 6780 | masm.push(returnAddrReg); |
| 6781 | #endif |
| 6782 | masm.jump(&invokeFunctionVMEntry); |
| 6783 | } |
| 6784 | |
| 6785 | void JitRuntime::generateMegamorphicLoadStub(MacroAssembler& masm) { |
| 6786 | AutoCreatedBy acb(masm, "JitRuntime::generateMegamorphicLoadStub"); |
| 6787 | megamorphicLoadStubOffset_ = startTrampolineCode(masm); |
| 6788 | |
| 6789 | Register obj = CallTempReg3; |
| 6790 | Register id = CallTempReg0; |
| 6791 | Register idHash = CallTempReg1; |
| 6792 | Register outEntryPtr = CallTempReg2; |
| 6793 | |
| 6794 | #if defined(JS_NUNBOX32) |
| 6795 | auto output = ValueOperand(JSReturnReg_Type, JSReturnReg_Data); |
| 6796 | static_assert(!JSReturnReg_Type.aliases(CallTempReg2)); |
| 6797 | static_assert(!JSReturnReg_Data.aliases(CallTempReg2)); |
| 6798 | #else |
| 6799 | auto output = ValueOperand(JSReturnReg); |
| 6800 | static_assert(!JSReturnReg.aliases(CallTempReg2)); |
| 6801 | #endif |
| 6802 | |
| 6803 | Label cacheHit; |
| 6804 | masm.emitMegamorphicCacheLookupByValue(obj, id, idHash, outEntryPtr, output, |
| 6805 | &cacheHit); |
| 6806 | |
| 6807 | masm.abiret(); |
| 6808 | |
| 6809 | // Given we don't need the entry if we got a cache hit, and the entry pointer |
| 6810 | // will never point into the first code page, we can use the low bits of the |
| 6811 | // outEntryPtr to indicate success |
| 6812 | masm.bind(&cacheHit); |
| 6813 | masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHit)), outEntryPtr); |
| 6814 | masm.abiret(); |
| 6815 | } |
| 6816 | |
| 6817 | void JitRuntime::generateMegamorphicLoadStubPermissive(MacroAssembler& masm) { |
| 6818 | AutoCreatedBy acb(masm, "JitRuntime::generateMegamorphicLoadStubPermissive"); |
| 6819 | megamorphicLoadStubPermissiveOffset_ = startTrampolineCode(masm); |
| 6820 | |
| 6821 | Register obj = CallTempReg3; |
| 6822 | Register id = CallTempReg0; |
| 6823 | Register idHash = CallTempReg1; |
| 6824 | Register outEntryPtr = CallTempReg2; |
| 6825 | |
| 6826 | #if defined(JS_NUNBOX32) |
| 6827 | auto output = ValueOperand(JSReturnReg_Type, JSReturnReg_Data); |
| 6828 | static_assert(!JSReturnReg_Type.aliases(CallTempReg2)); |
| 6829 | static_assert(!JSReturnReg_Data.aliases(CallTempReg2)); |
| 6830 | #else |
| 6831 | auto output = ValueOperand(JSReturnReg); |
| 6832 | static_assert(!JSReturnReg.aliases(CallTempReg2)); |
| 6833 | #endif |
| 6834 | |
| 6835 | Label cacheHit, cacheHitGetter; |
| 6836 | masm.emitMegamorphicCacheLookupByValue(obj, id, idHash, outEntryPtr, output, |
| 6837 | &cacheHit, &cacheHitGetter); |
| 6838 | |
| 6839 | masm.abiret(); |
| 6840 | |
| 6841 | // Given we don't need the entry if we got a cache hit, and the entry pointer |
| 6842 | // will never point into the first code page, we can use the low bits of the |
| 6843 | // outEntryPtr to indicate success or that output holds a getter. |
| 6844 | masm.bind(&cacheHit); |
| 6845 | masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHit)), outEntryPtr); |
| 6846 | masm.abiret(); |
| 6847 | |
| 6848 | masm.bind(&cacheHitGetter); |
| 6849 | masm.movePtr(ImmPtr((void*)(MegamorphicLoadStubCacheHitGetter)), outEntryPtr); |
| 6850 | masm.abiret(); |
| 6851 | } |
| 6852 | |
| 6853 | void JitRuntime::generateIonGenericHandleUnderflow(MacroAssembler& masm, |
| 6854 | bool isConstructing, |
| 6855 | Label* vmCall) { |
| 6856 | Register calleeReg = IonGenericCallCalleeReg; |
| 6857 | Register argcReg = IonGenericCallArgcReg; |
| 6858 | AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs()); |
| 6859 | Register numMissing = regs.takeAny(); |
| 6860 | Register src = regs.takeAny(); |
| 6861 | Register dest = regs.takeAny(); |
| 6862 | |
| 6863 | // On x86 we have fewer registers than we'd like, so we generate |
| 6864 | // slightly less efficient code. |
| 6865 | Register srcEnd, scratch; |
| 6866 | bool mustSpill = false; |
| 6867 | if (regs.empty()) { |
| 6868 | srcEnd = numMissing; |
| 6869 | scratch = calleeReg; |
| 6870 | mustSpill = true; |
| 6871 | } else { |
| 6872 | srcEnd = regs.takeAny(); |
| 6873 | scratch = regs.takeAny(); |
| 6874 | } |
| 6875 | |
| 6876 | // Compute fun->nargs - argc. If it's positive, it's the number of |
| 6877 | // undefined args we must push. |
| 6878 | Label noUnderflow; |
| 6879 | masm.loadFunctionArgCount(calleeReg, numMissing); |
| 6880 | masm.sub32(argcReg, numMissing); |
| 6881 | masm.branch32(Assembler::LessThanOrEqual, numMissing, Imm32(0), &noUnderflow); |
| 6882 | |
| 6883 | // Ensure that we don't adjust the stack pointer by more than a page. |
| 6884 | masm.branch32(Assembler::Above, numMissing, Imm32(JIT_ARGS_LENGTH_MAX), |
| 6885 | vmCall); |
| 6886 | |
| 6887 | // If numMissing is even, we want to make the following transformation: |
| 6888 | // |
| 6889 | // INITIAL FINAL |
| 6890 | // [newTarget] (iff isConstructing) [newTarget] (iff isConstructing) |
| 6891 | // [argN] [undefined] |
| 6892 | // ... [undefined] (...) |
| 6893 | // [arg1] [argN] |
| 6894 | // [arg0] ... |
| 6895 | // [this] <- sp aligned [arg1] |
| 6896 | // [arg0] |
| 6897 | // [this] -> moved down numMissing |
| 6898 | // slots |
| 6899 | // |
| 6900 | // If numMissing is odd, we must also insert padding: |
| 6901 | // [newTarget] (iff isConstructing) (padding) |
| 6902 | // [argN] [newTarget] (iff isConstructing) |
| 6903 | // ... [undefined] |
| 6904 | // [arg1] [argN] |
| 6905 | // [arg0] ... |
| 6906 | // [this] <- sp aligned [arg1] |
| 6907 | // [arg0] |
| 6908 | // [this] -> moved down numMissing+1 |
| 6909 | // slots |
| 6910 | // |
| 6911 | // Note that |newTarget|, if it exists, must be between the padding and the |
| 6912 | // undefined args. It does not move down along with the actual args. |
| 6913 | |
| 6914 | // The first step is to copy the memory from [this] through [argN] into the |
| 6915 | // correct position. The source of the copy is the current stack pointer. |
| 6916 | masm.moveStackPtrTo(src); |
| 6917 | |
| 6918 | // Compute how far the args must be moved and adjust the stack pointer. |
| 6919 | // If numMissing is even, this is numMissing slots. If numMissing is odd, |
| 6920 | // this is numMissing+1 slots. We can compute this as (numMissing + 1) & ~1. |
| 6921 | masm.add32(Imm32(1), numMissing, dest); |
| 6922 | masm.and32(Imm32(~1), dest); |
| 6923 | masm.lshift32(Imm32(3), dest); |
| 6924 | masm.subFromStackPtr(dest); |
| 6925 | masm.moveStackPtrTo(dest); |
| 6926 | |
| 6927 | // We also set up a register pointing to the last copied argument. On x86 |
| 6928 | // we don't have enough registers, so we spill the calleeReg and numMissing. |
| 6929 | if (mustSpill) { |
| 6930 | masm.push(calleeReg); |
| 6931 | masm.push(numMissing); |
| 6932 | } |
| 6933 | masm.computeEffectiveAddress(BaseValueIndex(src, argcReg), srcEnd); |
| 6934 | |
| 6935 | // The stack currently looks like this: |
| 6936 | // |
| 6937 | // [newTarget] |
| 6938 | // [argN] <-- srcEnd |
| 6939 | // ... |
| 6940 | // [arg0] |
| 6941 | // [this] <-- src |
| 6942 | // ... |
| 6943 | // ... <-- dest |
| 6944 | // [spill?] |
| 6945 | // [spill?] |
| 6946 | |
| 6947 | // Loop to move the arguments. |
| 6948 | Label argLoop; |
| 6949 | masm.bind(&argLoop); |
| 6950 | masm.copy64(Address(src, 0), Address(dest, 0), scratch); |
| 6951 | masm.addPtr(Imm32(sizeof(Value)), src); |
| 6952 | masm.addPtr(Imm32(sizeof(Value)), dest); |
| 6953 | masm.branchPtr(Assembler::BelowOrEqual, src, srcEnd, &argLoop); |
| 6954 | |
| 6955 | if (mustSpill) { |
| 6956 | // We must restore numMissing now, so that we can test if it's odd. |
| 6957 | // The copy64 below still needs calleeReg as a scratch register. |
| 6958 | masm.pop(numMissing); |
| 6959 | } |
| 6960 | |
| 6961 | if (isConstructing) { |
| 6962 | // If numMissing is odd, we must move newTarget down by one slot. |
| 6963 | Label skip; |
| 6964 | masm.branchTest32(Assembler::Zero, numMissing, Imm32(1), &skip); |
| 6965 | Address newTargetSrc(src, 0); |
| 6966 | Address newTargetDest(src, -int32_t(sizeof(Value))); |
| 6967 | masm.copy64(newTargetSrc, newTargetDest, scratch); |
| 6968 | masm.bind(&skip); |
| 6969 | } |
| 6970 | |
| 6971 | if (mustSpill) { |
| 6972 | masm.pop(calleeReg); |
| 6973 | } |
| 6974 | |
| 6975 | // Loop to fill the remaining numMissing slots with UndefinedValue. |
| 6976 | // We do this last so that we can safely clobber numMissing. |
| 6977 | Label undefLoop; |
| 6978 | masm.bind(&undefLoop); |
| 6979 | BaseValueIndex undefSlot(dest, numMissing, -int32_t(sizeof(Value))); |
| 6980 | masm.storeValue(UndefinedValue(), undefSlot); |
| 6981 | masm.branchSub32(Assembler::NonZero, Imm32(1), numMissing, &undefLoop); |
| 6982 | |
| 6983 | masm.bind(&noUnderflow); |
| 6984 | } |
| 6985 | |
| 6986 | void JitRuntime::generateIonGenericCallNativeFunction(MacroAssembler& masm, |
| 6987 | bool isConstructing) { |
| 6988 | Register calleeReg = IonGenericCallCalleeReg; |
| 6989 | Register argcReg = IonGenericCallArgcReg; |
| 6990 | AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs()); |
| 6991 | Register scratch = regs.takeAny(); |
| 6992 | Register scratch2 = regs.takeAny(); |
| 6993 | Register contextReg = regs.takeAny(); |
| 6994 | #ifndef JS_USE_LINK_REGISTER |
| 6995 | Register returnAddrReg = IonGenericCallReturnAddrReg; |
| 6996 | #endif |
| 6997 | |
| 6998 | // Push a value containing the callee, which will become argv[0]. |
| 6999 | masm.pushValue(JSVAL_TYPE_OBJECT, calleeReg); |
| 7000 | |
| 7001 | // Load the callee address into calleeReg. |
| 7002 | #ifdef JS_SIMULATOR |
| 7003 | masm.movePtr(ImmPtr(RedirectedCallAnyNative()), calleeReg); |
| 7004 | #else |
| 7005 | masm.loadPrivate(Address(calleeReg, JSFunction::offsetOfNativeOrEnv()), |
| 7006 | calleeReg); |
| 7007 | #endif |
| 7008 | |
| 7009 | // Load argv into scratch2. |
| 7010 | masm.moveStackPtrTo(scratch2); |
| 7011 | |
| 7012 | // Push argc. |
| 7013 | masm.push(argcReg); |
| 7014 | |
| 7015 | masm.loadJSContext(contextReg); |
| 7016 | |
| 7017 | // Construct native exit frame. Note that unlike other cases in this |
| 7018 | // trampoline, this code does not use a tail call. |
| 7019 | masm.push(FrameDescriptor(FrameType::IonJS)); |
| 7020 | #ifdef JS_USE_LINK_REGISTER |
| 7021 | masm.pushReturnAddress(); |
| 7022 | #else |
| 7023 | masm.push(returnAddrReg); |
| 7024 | #endif |
| 7025 | |
| 7026 | masm.push(FramePointer); |
| 7027 | masm.moveStackPtrTo(FramePointer); |
| 7028 | masm.enterFakeExitFrameForNative(contextReg, scratch, isConstructing); |
| 7029 | |
| 7030 | masm.setupUnalignedABICall(scratch); |
| 7031 | masm.passABIArg(contextReg); // cx |
| 7032 | masm.passABIArg(argcReg); // argc |
| 7033 | masm.passABIArg(scratch2); // argv |
| 7034 | |
| 7035 | masm.callWithABI(calleeReg); |
| 7036 | |
| 7037 | // Test for failure. |
| 7038 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
| 7039 | |
| 7040 | masm.loadValue( |
| 7041 | Address(masm.getStackPointer(), NativeExitFrameLayout::offsetOfResult()), |
| 7042 | JSReturnOperand); |
| 7043 | |
| 7044 | // Leave the exit frame. |
| 7045 | masm.moveToStackPtr(FramePointer); |
| 7046 | masm.pop(FramePointer); |
| 7047 | |
| 7048 | // Return. |
| 7049 | masm.ret(); |
| 7050 | } |
| 7051 | |
| 7052 | void JitRuntime::generateIonGenericCallFunCall(MacroAssembler& masm, |
| 7053 | Label* entry, Label* vmCall) { |
| 7054 | Register calleeReg = IonGenericCallCalleeReg; |
| 7055 | Register argcReg = IonGenericCallArgcReg; |
| 7056 | AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs()); |
| 7057 | Register scratch = regs.takeAny(); |
| 7058 | Register scratch2 = regs.takeAny(); |
| 7059 | Register scratch3 = regs.takeAny(); |
| 7060 | |
| 7061 | Label notFunCall; |
| 7062 | masm.branchPtr(Assembler::NotEqual, |
| 7063 | Address(calleeReg, JSFunction::offsetOfNativeOrEnv()), |
| 7064 | ImmPtr(js::fun_call), ¬FunCall); |
| 7065 | |
| 7066 | // In general, we can implement fun_call by replacing calleeReg with |
| 7067 | // |this|, sliding all the other arguments down, and decrementing argc. |
| 7068 | // |
| 7069 | // *BEFORE* *AFTER* |
| 7070 | // [argN] argc = N+1 <padding> |
| 7071 | // ... [argN] argc = N |
| 7072 | // [arg1] ... |
| 7073 | // [arg0] [arg1] <- now arg0 |
| 7074 | // [this] <- top of stack (aligned) [arg0] <- now this |
| 7075 | // |
| 7076 | // The only exception is when argc is already 0, in which case instead |
| 7077 | // of shifting arguments down we replace [this] with UndefinedValue(): |
| 7078 | // |
| 7079 | // *BEFORE* *AFTER* |
| 7080 | // [this] argc = 0 [undef] argc = 0 |
| 7081 | // |
| 7082 | // After making this transformation, we can jump back to the beginning |
| 7083 | // of this trampoline to handle the inner call. |
| 7084 | |
| 7085 | // Guard that |this| is an object. If it is, replace calleeReg. |
| 7086 | masm.fallibleUnboxObject(Address(masm.getStackPointer(), 0), scratch, vmCall); |
| 7087 | masm.movePtr(scratch, calleeReg); |
| 7088 | |
| 7089 | Label hasArgs; |
| 7090 | masm.branch32(Assembler::NotEqual, argcReg, Imm32(0), &hasArgs); |
| 7091 | |
| 7092 | // No arguments. Replace |this| with |undefined| and start from the top. |
| 7093 | masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), 0)); |
| 7094 | masm.jump(entry); |
| 7095 | |
| 7096 | masm.bind(&hasArgs); |
| 7097 | |
| 7098 | Label doneSliding; |
| 7099 | generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2, |
| 7100 | scratch3, &doneSliding); |
| 7101 | masm.bind(&doneSliding); |
| 7102 | masm.sub32(Imm32(1), argcReg); |
| 7103 | |
| 7104 | masm.jump(entry); |
| 7105 | |
| 7106 | masm.bind(¬FunCall); |
| 7107 | } |
| 7108 | |
| 7109 | void JitRuntime::generateIonGenericCallBoundFunction(MacroAssembler& masm, |
| 7110 | Label* entry, |
| 7111 | Label* vmCall) { |
| 7112 | Register calleeReg = IonGenericCallCalleeReg; |
| 7113 | Register argcReg = IonGenericCallArgcReg; |
| 7114 | AllocatableGeneralRegisterSet regs(IonGenericCallScratchRegs()); |
| 7115 | Register scratch = regs.takeAny(); |
| 7116 | Register scratch2 = regs.takeAny(); |
| 7117 | Register scratch3 = regs.takeAny(); |
| 7118 | |
| 7119 | masm.branchTestObjClass(Assembler::NotEqual, calleeReg, |
| 7120 | &BoundFunctionObject::class_, scratch, calleeReg, |
| 7121 | vmCall); |
| 7122 | |
| 7123 | Address targetSlot(calleeReg, BoundFunctionObject::offsetOfTargetSlot()); |
| 7124 | Address flagsSlot(calleeReg, BoundFunctionObject::offsetOfFlagsSlot()); |
| 7125 | Address thisSlot(calleeReg, BoundFunctionObject::offsetOfBoundThisSlot()); |
| 7126 | Address firstInlineArgSlot( |
| 7127 | calleeReg, BoundFunctionObject::offsetOfFirstInlineBoundArg()); |
| 7128 | |
| 7129 | // Check that we won't be pushing too many arguments. |
| 7130 | masm.load32(flagsSlot, scratch); |
| 7131 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch); |
| 7132 | masm.add32(argcReg, scratch); |
| 7133 | masm.branch32(Assembler::Above, scratch, Imm32(JIT_ARGS_LENGTH_MAX), vmCall); |
| 7134 | |
| 7135 | // The stack is currently correctly aligned for a jit call. We will |
| 7136 | // be updating the `this` value and potentially adding additional |
| 7137 | // arguments. On platforms with 16-byte alignment, if the number of |
| 7138 | // bound arguments is odd, we have to move the arguments that are |
| 7139 | // currently on the stack. For example, with one bound argument: |
| 7140 | // |
| 7141 | // *BEFORE* *AFTER* |
| 7142 | // [argN] <padding> |
| 7143 | // ... [argN] | |
| 7144 | // [arg1] ... | These arguments have been |
| 7145 | // [arg0] [arg1] | shifted down 8 bytes. |
| 7146 | // [this] <- top of stack (aligned) [arg0] v |
| 7147 | // [bound0] <- one bound argument (odd) |
| 7148 | // [boundThis] <- top of stack (aligned) |
| 7149 | // |
| 7150 | Label poppedThis; |
| 7151 | if (JitStackValueAlignment > 1) { |
| 7152 | Label alreadyAligned; |
| 7153 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 7154 | Imm32(1 << BoundFunctionObject::NumBoundArgsShift), |
| 7155 | &alreadyAligned); |
| 7156 | |
| 7157 | // We have an odd number of bound arguments. Shift the existing arguments |
| 7158 | // down by 8 bytes. |
| 7159 | generateIonGenericCallArgumentsShift(masm, argcReg, scratch, scratch2, |
| 7160 | scratch3, &poppedThis); |
| 7161 | masm.bind(&alreadyAligned); |
| 7162 | } |
| 7163 | |
| 7164 | // Pop the current `this`. It will be replaced with the bound `this`. |
| 7165 | masm.freeStack(sizeof(Value)); |
| 7166 | masm.bind(&poppedThis); |
| 7167 | |
| 7168 | // Load the number of bound arguments in scratch |
| 7169 | masm.load32(flagsSlot, scratch); |
| 7170 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), scratch); |
| 7171 | |
| 7172 | Label donePushingBoundArguments; |
| 7173 | masm.branch32(Assembler::Equal, scratch, Imm32(0), |
| 7174 | &donePushingBoundArguments); |
| 7175 | |
| 7176 | // Update argc to include bound arguments. |
| 7177 | masm.add32(scratch, argcReg); |
| 7178 | |
| 7179 | // Load &boundArgs[0] in scratch2. |
| 7180 | Label outOfLineBoundArguments, haveBoundArguments; |
| 7181 | masm.branch32(Assembler::Above, scratch, |
| 7182 | Imm32(BoundFunctionObject::MaxInlineBoundArgs), |
| 7183 | &outOfLineBoundArguments); |
| 7184 | masm.computeEffectiveAddress(firstInlineArgSlot, scratch2); |
| 7185 | masm.jump(&haveBoundArguments); |
| 7186 | |
| 7187 | masm.bind(&outOfLineBoundArguments); |
| 7188 | masm.unboxObject(firstInlineArgSlot, scratch2); |
| 7189 | masm.loadPtr(Address(scratch2, NativeObject::offsetOfElements()), scratch2); |
| 7190 | |
| 7191 | masm.bind(&haveBoundArguments); |
| 7192 | |
| 7193 | // Load &boundArgs[numBoundArgs] in scratch. |
| 7194 | BaseObjectElementIndex lastBoundArg(scratch2, scratch); |
| 7195 | masm.computeEffectiveAddress(lastBoundArg, scratch); |
| 7196 | |
| 7197 | // Push the bound arguments, starting with the last one. |
| 7198 | // Copying pre-decrements scratch until scratch2 is reached. |
| 7199 | Label boundArgumentsLoop; |
| 7200 | masm.bind(&boundArgumentsLoop); |
| 7201 | masm.subPtr(Imm32(sizeof(Value)), scratch); |
| 7202 | masm.pushValue(Address(scratch, 0)); |
| 7203 | masm.branchPtr(Assembler::Above, scratch, scratch2, &boundArgumentsLoop); |
| 7204 | masm.bind(&donePushingBoundArguments); |
| 7205 | |
| 7206 | // Push the bound `this`. |
| 7207 | masm.pushValue(thisSlot); |
| 7208 | |
| 7209 | // Load the target in calleeReg. |
| 7210 | masm.unboxObject(targetSlot, calleeReg); |
| 7211 | |
| 7212 | // At this point, all preconditions for entering the trampoline are met: |
| 7213 | // - calleeReg contains a pointer to the callee object |
| 7214 | // - argcReg contains the number of actual args (now including bound args) |
| 7215 | // - the arguments are on the stack with the correct alignment. |
| 7216 | // Instead of generating more code, we can jump back to the entry point |
| 7217 | // of the trampoline to call the bound target. |
| 7218 | masm.jump(entry); |
| 7219 | } |
| 7220 | |
| 7221 | void CodeGenerator::visitCallKnown(LCallKnown* call) { |
| 7222 | Register calleereg = ToRegister(call->getFunction()); |
| 7223 | Register objreg = ToRegister(call->getTempObject()); |
| 7224 | uint32_t unusedStack = |
| 7225 | UnusedStackBytesForCall(call->mir()->paddedNumStackArgs()); |
| 7226 | WrappedFunction* target = call->getSingleTarget(); |
| 7227 | |
| 7228 | // Native single targets (except Wasm and TrampolineNative functions) are |
| 7229 | // handled by LCallNative. |
| 7230 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 7230); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "target->hasJitEntry()" ")"); do { MOZ_CrashSequence (__null, 7230); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 7231 | |
| 7232 | // Missing arguments must have been explicitly appended by WarpBuilder. |
| 7233 | DebugOnly<unsigned> numNonArgsOnStack = 1 + call->isConstructing(); |
| 7234 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 7235); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" ")"); do { MOZ_CrashSequence(__null, 7235); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 7235 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 7235); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "target->nargs() <= call->mir()->numStackArgs() - numNonArgsOnStack" ")"); do { MOZ_CrashSequence(__null, 7235); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7236 | |
| 7237 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 7237); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "target->isConstructor()" ")"); do { MOZ_CrashSequence (__null, 7237); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false); |
| 7238 | |
| 7239 | masm.checkStackAlignment(); |
| 7240 | |
| 7241 | if (target->isClassConstructor() && !call->isConstructing()) { |
| 7242 | emitCallInvokeFunction(call, calleereg, call->isConstructing(), |
| 7243 | call->ignoresReturnValue(), call->numActualArgs(), |
| 7244 | unusedStack); |
| 7245 | return; |
| 7246 | } |
| 7247 | |
| 7248 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 7248); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "call->isConstructing()" ")"); do { MOZ_CrashSequence (__null, 7248); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false); |
| 7249 | |
| 7250 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 7250); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!call->mir()->needsThisCheck()" ")"); do { MOZ_CrashSequence(__null, 7250); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7251 | |
| 7252 | if (call->mir()->maybeCrossRealm()) { |
| 7253 | masm.switchToObjectRealm(calleereg, objreg); |
| 7254 | } |
| 7255 | |
| 7256 | masm.loadJitCodeRaw(calleereg, objreg); |
| 7257 | |
| 7258 | // Nestle the StackPointer up to the argument vector. |
| 7259 | masm.freeStack(unusedStack); |
| 7260 | |
| 7261 | // Construct the JitFrameLayout. |
| 7262 | masm.PushCalleeToken(calleereg, call->mir()->isConstructing()); |
| 7263 | masm.Push(FrameDescriptor(FrameType::IonJS, call->numActualArgs())); |
| 7264 | |
| 7265 | // Finally call the function in objreg. |
| 7266 | ensureOsiSpace(); |
| 7267 | uint32_t callOffset = masm.callJit(objreg); |
| 7268 | markSafepointAt(callOffset, call); |
| 7269 | |
| 7270 | if (call->mir()->maybeCrossRealm()) { |
| 7271 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 7272 | "ReturnReg available as scratch after scripted calls"); |
| 7273 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 7274 | } |
| 7275 | |
| 7276 | // Restore stack pointer: pop JitFrameLayout fields still left on the stack |
| 7277 | // and undo the earlier |freeStack(unusedStack)|. |
| 7278 | int prefixGarbage = |
| 7279 | sizeof(JitFrameLayout) - JitFrameLayout::bytesPoppedAfterCall(); |
| 7280 | masm.adjustStack(prefixGarbage - unusedStack); |
| 7281 | |
| 7282 | // If the return value of the constructing function is Primitive, |
| 7283 | // replace the return value with the Object from CreateThis. |
| 7284 | if (call->mir()->isConstructing()) { |
| 7285 | Label notPrimitive; |
| 7286 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 7287 | ¬Primitive); |
| 7288 | masm.loadValue(Address(masm.getStackPointer(), unusedStack), |
| 7289 | JSReturnOperand); |
| 7290 | #ifdef DEBUG1 |
| 7291 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 7292 | ¬Primitive); |
| 7293 | masm.assumeUnreachable("CreateThis creates an object"); |
| 7294 | #endif |
| 7295 | masm.bind(¬Primitive); |
| 7296 | } |
| 7297 | } |
| 7298 | |
| 7299 | template <typename T> |
| 7300 | void CodeGenerator::emitCallInvokeFunction(T* apply) { |
| 7301 | pushArg(masm.getStackPointer()); // argv. |
| 7302 | pushArg(ToRegister(apply->getArgc())); // argc. |
| 7303 | pushArg(Imm32(apply->mir()->ignoresReturnValue())); // ignoresReturnValue. |
| 7304 | pushArg(Imm32(apply->mir()->isConstructing())); // isConstructing. |
| 7305 | pushArg(ToRegister(apply->getFunction())); // JSFunction*. |
| 7306 | |
| 7307 | using Fn = bool (*)(JSContext*, HandleObject, bool, bool, uint32_t, Value*, |
| 7308 | MutableHandleValue); |
| 7309 | callVM<Fn, jit::InvokeFunction>(apply); |
| 7310 | } |
| 7311 | |
| 7312 | // Do not bailout after the execution of this function since the stack no longer |
| 7313 | // corresponds to what is expected by the snapshots. |
| 7314 | template <typename T> |
| 7315 | void CodeGenerator::emitAllocateSpaceForApply(T* apply, Register calleeReg, |
| 7316 | Register argcreg, |
| 7317 | Register scratch) { |
| 7318 | Label* oolRejoin = nullptr; |
| 7319 | bool canUnderflow = |
| 7320 | !apply->hasSingleTarget() || apply->getSingleTarget()->nargs() > 0; |
| 7321 | |
| 7322 | if (canUnderflow) { |
| 7323 | auto* ool = |
| 7324 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 7325 | // Align the JitFrameLayout on the JitStackAlignment by allocating |
| 7326 | // callee->nargs() slots, possibly rounded up to the nearest odd |
| 7327 | // number (see below). Leave callee->nargs() in `scratch` for the |
| 7328 | // undef loop. |
| 7329 | if (apply->hasSingleTarget()) { |
| 7330 | uint32_t nargs = apply->getSingleTarget()->nargs(); |
| 7331 | uint32_t numSlots = JitStackValueAlignment == 1 ? nargs : nargs | 1; |
| 7332 | masm.subFromStackPtr(Imm32((numSlots) * sizeof(Value))); |
| 7333 | masm.move32(Imm32(nargs), scratch); |
| 7334 | } else { |
| 7335 | // `scratch` contains callee->nargs() |
| 7336 | if (JitStackValueAlignment > 1) { |
| 7337 | masm.orPtr(Imm32(1), scratch); |
| 7338 | } |
| 7339 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
| 7340 | masm.subFromStackPtr(scratch); |
| 7341 | |
| 7342 | // We need callee->nargs in `scratch`. If we rounded it up |
| 7343 | // above, we need to reload it. If we only shifted it, we can |
| 7344 | // simply shift it back. |
| 7345 | if (JitStackValueAlignment > 1) { |
| 7346 | masm.loadFunctionArgCount(calleeReg, scratch); |
| 7347 | } else { |
| 7348 | masm.rshiftPtr(Imm32(ValueShift), scratch); |
| 7349 | } |
| 7350 | } |
| 7351 | |
| 7352 | // Count from callee->nargs() down to argc, storing undefined values. |
| 7353 | Label loop; |
| 7354 | masm.bind(&loop); |
| 7355 | masm.sub32(Imm32(1), scratch); |
| 7356 | masm.storeValue(UndefinedValue(), |
| 7357 | BaseValueIndex(masm.getStackPointer(), scratch)); |
| 7358 | masm.branch32(Assembler::Above, scratch, argcreg, &loop); |
| 7359 | masm.jump(ool.rejoin()); |
| 7360 | }); |
| 7361 | addOutOfLineCode(ool, apply->mir()); |
| 7362 | oolRejoin = ool->rejoin(); |
| 7363 | |
| 7364 | Label noUnderflow; |
| 7365 | if (apply->hasSingleTarget()) { |
| 7366 | masm.branch32(Assembler::AboveOrEqual, argcreg, |
| 7367 | Imm32(apply->getSingleTarget()->nargs()), &noUnderflow); |
| 7368 | } else { |
| 7369 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch, |
| 7370 | calleeReg, &noUnderflow); |
| 7371 | masm.loadFunctionArgCount(calleeReg, scratch); |
| 7372 | masm.branch32(Assembler::AboveOrEqual, argcreg, scratch, &noUnderflow); |
| 7373 | } |
| 7374 | masm.branchIfFunctionHasJitEntry(calleeReg, ool->entry()); |
| 7375 | masm.bind(&noUnderflow); |
| 7376 | } |
| 7377 | |
| 7378 | // Use scratch register to calculate stack space (including padding). |
| 7379 | masm.movePtr(argcreg, scratch); |
| 7380 | |
| 7381 | // Align the JitFrameLayout on the JitStackAlignment. |
| 7382 | if (JitStackValueAlignment > 1) { |
| 7383 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 7384); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")"); do { MOZ_CrashSequence(__null, 7384); __attribute__((nomerge)) :: abort(); } while (false); } } while (false) |
| 7384 | "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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 7384); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")"); do { MOZ_CrashSequence(__null, 7384); __attribute__((nomerge)) :: abort(); } while (false); } } while (false); |
| 7385 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 7385); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence (__null, 7385); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 7386 | // If the number of arguments is odd, then we do not need any padding. |
| 7387 | // |
| 7388 | // Note: The |JitStackValueAlignment == 2| condition requires that the |
| 7389 | // overall number of values on the stack is even. When we have an odd number |
| 7390 | // of arguments, we don't need any padding, because the |thisValue| is |
| 7391 | // pushed after the arguments, so the overall number of values on the stack |
| 7392 | // is even. |
| 7393 | // |
| 7394 | // We can align by unconditionally setting the low bit. If the number of |
| 7395 | // arguments is odd, the low bit was already set, so this adds no padding. |
| 7396 | // If the number of arguments is even, the low bit was not set, so this adds |
| 7397 | // 1, as we require. |
| 7398 | masm.orPtr(Imm32(1), scratch); |
| 7399 | } |
| 7400 | |
| 7401 | // Reserve space for copying the arguments. |
| 7402 | NativeObject::elementsSizeMustNotOverflow(); |
| 7403 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
| 7404 | masm.subFromStackPtr(scratch); |
| 7405 | |
| 7406 | #ifdef DEBUG1 |
| 7407 | // Put a magic value in the space reserved for padding. Note, this code cannot |
| 7408 | // be merged with the previous test, as not all architectures can write below |
| 7409 | // their stack pointers. |
| 7410 | if (JitStackValueAlignment > 1) { |
| 7411 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 7411); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence (__null, 7411); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 7412 | Label noPaddingNeeded; |
| 7413 | // If the number of arguments is odd, then we do not need any padding. |
| 7414 | masm.branchTestPtr(Assembler::NonZero, argcreg, Imm32(1), &noPaddingNeeded); |
| 7415 | BaseValueIndex dstPtr(masm.getStackPointer(), argcreg); |
| 7416 | masm.storeValue(MagicValue(JS_ARG_POISON), dstPtr); |
| 7417 | masm.bind(&noPaddingNeeded); |
| 7418 | } |
| 7419 | #endif |
| 7420 | |
| 7421 | if (canUnderflow) { |
| 7422 | masm.bind(oolRejoin); |
| 7423 | } |
| 7424 | } |
| 7425 | |
| 7426 | // Do not bailout after the execution of this function since the stack no longer |
| 7427 | // corresponds to what is expected by the snapshots. |
| 7428 | template <typename T> |
| 7429 | void CodeGenerator::emitAllocateSpaceForConstructAndPushNewTarget( |
| 7430 | T* construct, Register calleeReg, Register argcreg, |
| 7431 | Register newTargetAndScratch) { |
| 7432 | // Push newTarget. |
| 7433 | masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch); |
| 7434 | if (JitStackValueAlignment > 1) { |
| 7435 | // x86 is short on registers. To free up newTarget for use as a scratch |
| 7436 | // register before we know if we need padding, we push newTarget twice. |
| 7437 | // If the first copy pushed is correctly aligned, we will overwrite the |
| 7438 | // second. If the second copy is correctly aligned, the first is padding. |
| 7439 | masm.pushValue(JSVAL_TYPE_OBJECT, newTargetAndScratch); |
| 7440 | } |
| 7441 | Register scratch = newTargetAndScratch; |
| 7442 | |
| 7443 | Label* oolRejoin = nullptr; |
| 7444 | bool canUnderflow = !construct->hasSingleTarget() || |
| 7445 | construct->getSingleTarget()->nargs() > 0; |
| 7446 | if (canUnderflow) { |
| 7447 | auto* ool = |
| 7448 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 7449 | // Align the JitFrameLayout on the JitStackAlignment by allocating |
| 7450 | // callee->nargs() slots, rounded down to the nearest odd number (see |
| 7451 | // below). Leave callee->nargs() in `scratch` for the undef loop. |
| 7452 | if (construct->hasSingleTarget()) { |
| 7453 | uint32_t nargs = construct->getSingleTarget()->nargs(); |
| 7454 | uint32_t numSlots = |
| 7455 | JitStackValueAlignment == 1 ? nargs : ((nargs + 1) & ~1) - 1; |
| 7456 | masm.subFromStackPtr(Imm32((numSlots) * sizeof(Value))); |
| 7457 | masm.move32(Imm32(nargs), scratch); |
| 7458 | } else { |
| 7459 | // `scratch` contains callee->nargs() |
| 7460 | if (JitStackValueAlignment > 1) { |
| 7461 | // Round down to nearest odd number. |
| 7462 | masm.addPtr(Imm32(1), scratch); |
| 7463 | masm.andPtr(Imm32(~1), scratch); |
| 7464 | masm.subPtr(Imm32(1), scratch); |
| 7465 | } |
| 7466 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
| 7467 | masm.subFromStackPtr(scratch); |
| 7468 | |
| 7469 | // We need callee->nargs in `scratch`. If we rounded it down |
| 7470 | // above, we need to reload it. If we only shifted it, we can |
| 7471 | // simply shift it back. |
| 7472 | if (JitStackValueAlignment > 1) { |
| 7473 | masm.loadFunctionArgCount(calleeReg, scratch); |
| 7474 | } else { |
| 7475 | masm.rshiftPtr(Imm32(ValueShift), scratch); |
| 7476 | } |
| 7477 | } |
| 7478 | |
| 7479 | // Count from callee->nargs() down to argc, storing undefined values. |
| 7480 | Label loop; |
| 7481 | masm.bind(&loop); |
| 7482 | masm.sub32(Imm32(1), scratch); |
| 7483 | masm.storeValue(UndefinedValue(), |
| 7484 | BaseValueIndex(masm.getStackPointer(), scratch)); |
| 7485 | masm.branch32(Assembler::Above, scratch, argcreg, &loop); |
| 7486 | masm.jump(ool.rejoin()); |
| 7487 | }); |
| 7488 | addOutOfLineCode(ool, construct->mir()); |
| 7489 | oolRejoin = ool->rejoin(); |
| 7490 | |
| 7491 | Label noUnderflow; |
| 7492 | if (construct->hasSingleTarget()) { |
| 7493 | masm.branch32(Assembler::AboveOrEqual, argcreg, |
| 7494 | Imm32(construct->getSingleTarget()->nargs()), &noUnderflow); |
| 7495 | } else { |
| 7496 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleeReg, scratch, |
| 7497 | calleeReg, &noUnderflow); |
| 7498 | masm.loadFunctionArgCount(calleeReg, scratch); |
| 7499 | masm.branch32(Assembler::AboveOrEqual, argcreg, scratch, &noUnderflow); |
| 7500 | } |
| 7501 | masm.branchIfFunctionHasJitEntry(calleeReg, ool->entry()); |
| 7502 | masm.bind(&noUnderflow); |
| 7503 | } |
| 7504 | |
| 7505 | // Use newTargetAndScratch to calculate stack space (including padding). |
| 7506 | masm.movePtr(argcreg, newTargetAndScratch); |
| 7507 | |
| 7508 | // Align the JitFrameLayout on the JitStackAlignment. |
| 7509 | if (JitStackValueAlignment > 1) { |
| 7510 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 7511); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")"); do { MOZ_CrashSequence(__null, 7511); __attribute__((nomerge)) :: abort(); } while (false); } } while (false) |
| 7511 | "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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 7511); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "frameSize() % JitStackAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")"); do { MOZ_CrashSequence(__null, 7511); __attribute__((nomerge)) :: abort(); } while (false); } } while (false); |
| 7512 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 7512); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ")"); do { MOZ_CrashSequence (__null, 7512); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 7513 | // Note: The |JitStackValueAlignment == 2| condition requires that the |
| 7514 | // overall number of values on the stack is even. We must push `newTarget`, |
| 7515 | // the args, and `this`. We've already pushed newTarget twice. Rounding |
| 7516 | // argc down to the closest odd number will give us the correct alignment: |
| 7517 | // |
| 7518 | // argc: *0* *1* *2* *3* |
| 7519 | // rounds to: -1 1 1 3 |
| 7520 | // *newTarget (newTarget) newTarget (newTarget) |
| 7521 | // curr sp --> this newTarget arg1 newTarget |
| 7522 | // *arg0 *arg0 arg2 |
| 7523 | // this this arg1 |
| 7524 | // *arg0 |
| 7525 | // this |
| 7526 | // The asterisk in each column marks the stack pointer after adding |
| 7527 | // the rounded value. In each case, pushing `this` will result in an |
| 7528 | // even number of total slots. |
| 7529 | masm.addPtr(Imm32(1), scratch); |
| 7530 | masm.andPtr(Imm32(~1), scratch); |
| 7531 | masm.subPtr(Imm32(1), scratch); |
| 7532 | } |
| 7533 | |
| 7534 | // Reserve space for copying the arguments. |
| 7535 | NativeObject::elementsSizeMustNotOverflow(); |
| 7536 | masm.lshiftPtr(Imm32(ValueShift), newTargetAndScratch); |
| 7537 | masm.subFromStackPtr(newTargetAndScratch); |
| 7538 | |
| 7539 | if (canUnderflow) { |
| 7540 | masm.bind(oolRejoin); |
| 7541 | } |
| 7542 | } |
| 7543 | |
| 7544 | // Destroys argvIndex and copyreg. |
| 7545 | void CodeGenerator::emitCopyValuesForApply(Register argvSrcBase, |
| 7546 | Register argvIndex, Register copyreg, |
| 7547 | size_t argvSrcOffset, |
| 7548 | size_t argvDstOffset) { |
| 7549 | Label loop; |
| 7550 | masm.bind(&loop); |
| 7551 | |
| 7552 | // As argvIndex is off by 1, and we use the decBranchPtr instruction to loop |
| 7553 | // back, we have to substract the size of the word which are copied. |
| 7554 | BaseValueIndex srcPtr(argvSrcBase, argvIndex, |
| 7555 | int32_t(argvSrcOffset) - sizeof(void*)); |
| 7556 | BaseValueIndex dstPtr(masm.getStackPointer(), argvIndex, |
| 7557 | int32_t(argvDstOffset) - sizeof(void*)); |
| 7558 | masm.loadPtr(srcPtr, copyreg); |
| 7559 | masm.storePtr(copyreg, dstPtr); |
| 7560 | |
| 7561 | // Handle 32 bits architectures. |
| 7562 | if (sizeof(Value) == 2 * sizeof(void*)) { |
| 7563 | BaseValueIndex srcPtrLow(argvSrcBase, argvIndex, |
| 7564 | int32_t(argvSrcOffset) - 2 * sizeof(void*)); |
| 7565 | BaseValueIndex dstPtrLow(masm.getStackPointer(), argvIndex, |
| 7566 | int32_t(argvDstOffset) - 2 * sizeof(void*)); |
| 7567 | masm.loadPtr(srcPtrLow, copyreg); |
| 7568 | masm.storePtr(copyreg, dstPtrLow); |
| 7569 | } |
| 7570 | |
| 7571 | masm.decBranchPtr(Assembler::NonZero, argvIndex, Imm32(1), &loop); |
| 7572 | } |
| 7573 | |
| 7574 | void CodeGenerator::emitRestoreStackPointerFromFP() { |
| 7575 | // This is used to restore the stack pointer after a call with a dynamic |
| 7576 | // number of arguments. |
| 7577 | |
| 7578 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 7578); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { MOZ_CrashSequence(__null, 7578); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7579 | |
| 7580 | int32_t offset = -int32_t(frameSize()); |
| 7581 | masm.computeEffectiveAddress(Address(FramePointer, offset), |
| 7582 | masm.getStackPointer()); |
| 7583 | #if JS_CODEGEN_ARM64 |
| 7584 | masm.syncStackPtr(); |
| 7585 | #endif |
| 7586 | } |
| 7587 | |
| 7588 | void CodeGenerator::emitPushArguments(Register argcreg, Register scratch, |
| 7589 | Register copyreg, uint32_t extraFormals) { |
| 7590 | Label end; |
| 7591 | |
| 7592 | // Skip the copy of arguments if there are none. |
| 7593 | masm.branchTestPtr(Assembler::Zero, argcreg, argcreg, &end); |
| 7594 | |
| 7595 | // clang-format off |
| 7596 | // |
| 7597 | // We are making a copy of the arguments which are above the JitFrameLayout |
| 7598 | // of the current Ion frame. |
| 7599 | // |
| 7600 | // [arg1] [arg0] <- src [this] [JitFrameLayout] [.. frameSize ..] [pad] [arg1] [arg0] <- dst |
| 7601 | // |
| 7602 | // clang-format on |
| 7603 | |
| 7604 | // Compute the source and destination offsets into the stack. |
| 7605 | // |
| 7606 | // The |extraFormals| parameter is used when copying rest-parameters and |
| 7607 | // allows to skip the initial parameters before the actual rest-parameters. |
| 7608 | Register argvSrcBase = FramePointer; |
| 7609 | size_t argvSrcOffset = |
| 7610 | JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value); |
| 7611 | size_t argvDstOffset = 0; |
| 7612 | |
| 7613 | Register argvIndex = scratch; |
| 7614 | masm.move32(argcreg, argvIndex); |
| 7615 | |
| 7616 | // Copy arguments. |
| 7617 | emitCopyValuesForApply(argvSrcBase, argvIndex, copyreg, argvSrcOffset, |
| 7618 | argvDstOffset); |
| 7619 | |
| 7620 | // Join with all arguments copied. |
| 7621 | masm.bind(&end); |
| 7622 | } |
| 7623 | |
| 7624 | void CodeGenerator::emitPushArguments(LApplyArgsGeneric* apply) { |
| 7625 | // Holds the function nargs. |
| 7626 | Register funcreg = ToRegister(apply->getFunction()); |
| 7627 | Register argcreg = ToRegister(apply->getArgc()); |
| 7628 | Register copyreg = ToRegister(apply->getTempObject()); |
| 7629 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7630 | uint32_t extraFormals = apply->numExtraFormals(); |
| 7631 | |
| 7632 | // Allocate space on the stack for arguments. |
| 7633 | emitAllocateSpaceForApply(apply, funcreg, argcreg, scratch); |
| 7634 | |
| 7635 | emitPushArguments(argcreg, scratch, copyreg, extraFormals); |
| 7636 | |
| 7637 | // Push |this|. |
| 7638 | masm.pushValue(ToValue(apply->thisValue())); |
| 7639 | } |
| 7640 | |
| 7641 | void CodeGenerator::emitPushArguments(LApplyArgsObj* apply) { |
| 7642 | Register function = ToRegister(apply->getFunction()); |
| 7643 | Register argsObj = ToRegister(apply->getArgsObj()); |
| 7644 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 7645 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7646 | |
| 7647 | // argc and argsObj are mapped to the same calltemp register. |
| 7648 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 7648); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "argsObj == ToRegister(apply->getArgc())" ")"); do { MOZ_CrashSequence(__null, 7648); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7649 | |
| 7650 | // Load argc into tmpArgc. |
| 7651 | masm.loadArgumentsObjectLength(argsObj, tmpArgc); |
| 7652 | |
| 7653 | // Allocate space on the stack for arguments. |
| 7654 | emitAllocateSpaceForApply(apply, function, tmpArgc, scratch); |
| 7655 | |
| 7656 | // Load arguments data. |
| 7657 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
| 7658 | argsObj); |
| 7659 | size_t argsSrcOffset = ArgumentsData::offsetOfArgs(); |
| 7660 | |
| 7661 | // This is the end of the lifetime of argsObj. |
| 7662 | // After this call, the argsObj register holds the argument count instead. |
| 7663 | emitPushArrayAsArguments(tmpArgc, argsObj, scratch, argsSrcOffset); |
| 7664 | |
| 7665 | // Push |this|. |
| 7666 | masm.pushValue(ToValue(apply->thisValue())); |
| 7667 | } |
| 7668 | |
| 7669 | void CodeGenerator::emitPushArrayAsArguments(Register tmpArgc, |
| 7670 | Register srcBaseAndArgc, |
| 7671 | Register scratch, |
| 7672 | size_t argvSrcOffset) { |
| 7673 | // Preconditions: |
| 7674 | // 1. |tmpArgc| * sizeof(Value) bytes have been allocated at the top of |
| 7675 | // the stack to hold arguments. |
| 7676 | // 2. |srcBaseAndArgc| + |srcOffset| points to an array of |tmpArgc| values. |
| 7677 | // |
| 7678 | // Postconditions: |
| 7679 | // 1. The arguments at |srcBaseAndArgc| + |srcOffset| have been copied into |
| 7680 | // the allocated space. |
| 7681 | // 2. |srcBaseAndArgc| now contains the original value of |tmpArgc|. |
| 7682 | // |
| 7683 | // |scratch| is used as a temp register within this function and clobbered. |
| 7684 | |
| 7685 | Label noCopy, epilogue; |
| 7686 | |
| 7687 | // Skip the copy of arguments if there are none. |
| 7688 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
| 7689 | { |
| 7690 | // Copy the values. This code is skipped entirely if there are no values. |
| 7691 | size_t argvDstOffset = 0; |
| 7692 | |
| 7693 | Register argvSrcBase = srcBaseAndArgc; |
| 7694 | |
| 7695 | // Stash away |tmpArgc| and adjust argvDstOffset accordingly. |
| 7696 | masm.push(tmpArgc); |
| 7697 | Register argvIndex = tmpArgc; |
| 7698 | argvDstOffset += sizeof(void*); |
| 7699 | |
| 7700 | // Copy |
| 7701 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
| 7702 | argvDstOffset); |
| 7703 | |
| 7704 | // Restore. |
| 7705 | masm.pop(srcBaseAndArgc); // srcBaseAndArgc now contains argc. |
| 7706 | masm.jump(&epilogue); |
| 7707 | } |
| 7708 | masm.bind(&noCopy); |
| 7709 | { |
| 7710 | // Clear argc if we skipped the copy step. |
| 7711 | masm.movePtr(ImmWord(0), srcBaseAndArgc); |
| 7712 | } |
| 7713 | |
| 7714 | // Join with all arguments copied. |
| 7715 | // Note, "srcBase" has become "argc". |
| 7716 | masm.bind(&epilogue); |
| 7717 | } |
| 7718 | |
| 7719 | void CodeGenerator::emitPushArguments(LApplyArrayGeneric* apply) { |
| 7720 | Register function = ToRegister(apply->getFunction()); |
| 7721 | Register elements = ToRegister(apply->getElements()); |
| 7722 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 7723 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7724 | |
| 7725 | // argc and elements are mapped to the same calltemp register. |
| 7726 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 7726); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "elements == ToRegister(apply->getArgc())" ")"); do { MOZ_CrashSequence(__null, 7726); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7727 | |
| 7728 | // Invariants guarded in the caller: |
| 7729 | // - the array is not too long |
| 7730 | // - the array length equals its initialized length |
| 7731 | |
| 7732 | // The array length is our argc for the purposes of allocating space. |
| 7733 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
| 7734 | |
| 7735 | // Allocate space for the values. |
| 7736 | emitAllocateSpaceForApply(apply, function, tmpArgc, scratch); |
| 7737 | |
| 7738 | // After this call "elements" has become "argc". |
| 7739 | size_t elementsOffset = 0; |
| 7740 | emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset); |
| 7741 | |
| 7742 | // Push |this|. |
| 7743 | masm.pushValue(ToValue(apply->thisValue())); |
| 7744 | } |
| 7745 | |
| 7746 | void CodeGenerator::emitPushArguments(LConstructArgsGeneric* construct) { |
| 7747 | // Holds the function nargs. |
| 7748 | Register argcreg = ToRegister(construct->getArgc()); |
| 7749 | Register function = ToRegister(construct->getFunction()); |
| 7750 | Register copyreg = ToRegister(construct->getTempObject()); |
| 7751 | Register scratch = ToRegister(construct->getTempForArgCopy()); |
| 7752 | uint32_t extraFormals = construct->numExtraFormals(); |
| 7753 | |
| 7754 | // newTarget and scratch are mapped to the same calltemp register. |
| 7755 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 7755); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())" ")"); do { MOZ_CrashSequence(__null, 7755); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7756 | |
| 7757 | // Allocate space for the values. |
| 7758 | // After this call "newTarget" has become "scratch". |
| 7759 | emitAllocateSpaceForConstructAndPushNewTarget(construct, function, argcreg, |
| 7760 | scratch); |
| 7761 | |
| 7762 | emitPushArguments(argcreg, scratch, copyreg, extraFormals); |
| 7763 | |
| 7764 | // Push |this|. |
| 7765 | masm.pushValue(ToValue(construct->thisValue())); |
| 7766 | } |
| 7767 | |
| 7768 | void CodeGenerator::emitPushArguments(LConstructArrayGeneric* construct) { |
| 7769 | Register function = ToRegister(construct->getFunction()); |
| 7770 | Register elements = ToRegister(construct->getElements()); |
| 7771 | Register tmpArgc = ToRegister(construct->getTempObject()); |
| 7772 | Register scratch = ToRegister(construct->getTempForArgCopy()); |
| 7773 | |
| 7774 | // argc and elements are mapped to the same calltemp register. |
| 7775 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 7775); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "elements == ToRegister(construct->getArgc())" ")"); do { MOZ_CrashSequence(__null, 7775); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7776 | |
| 7777 | // newTarget and scratch are mapped to the same calltemp register. |
| 7778 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 7778); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "scratch == ToRegister(construct->getNewTarget())" ")"); do { MOZ_CrashSequence(__null, 7778); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7779 | |
| 7780 | // Invariants guarded in the caller: |
| 7781 | // - the array is not too long |
| 7782 | // - the array length equals its initialized length |
| 7783 | |
| 7784 | // The array length is our argc for the purposes of allocating space. |
| 7785 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
| 7786 | |
| 7787 | // Allocate space for the values. |
| 7788 | // After this call "newTarget" has become "scratch". |
| 7789 | emitAllocateSpaceForConstructAndPushNewTarget(construct, function, tmpArgc, |
| 7790 | scratch); |
| 7791 | |
| 7792 | // After this call "elements" has become "argc". |
| 7793 | size_t elementsOffset = 0; |
| 7794 | emitPushArrayAsArguments(tmpArgc, elements, scratch, elementsOffset); |
| 7795 | |
| 7796 | // Push |this|. |
| 7797 | masm.pushValue(ToValue(construct->thisValue())); |
| 7798 | } |
| 7799 | |
| 7800 | template <typename T> |
| 7801 | void CodeGenerator::emitApplyGeneric(T* apply) { |
| 7802 | // Holds the function object. |
| 7803 | Register calleereg = ToRegister(apply->getFunction()); |
| 7804 | |
| 7805 | // Temporary register for modifying the function object. |
| 7806 | Register objreg = ToRegister(apply->getTempObject()); |
| 7807 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7808 | |
| 7809 | // Holds the function nargs, computed in the invoker or (for ApplyArray, |
| 7810 | // ConstructArray, or ApplyArgsObj) in the argument pusher. |
| 7811 | Register argcreg = ToRegister(apply->getArgc()); |
| 7812 | |
| 7813 | // Copy the arguments of the current function. |
| 7814 | // |
| 7815 | // In the case of ApplyArray, ConstructArray, or ApplyArgsObj, also compute |
| 7816 | // argc. The argc register and the elements/argsObj register are the same; |
| 7817 | // argc must not be referenced before the call to emitPushArguments() and |
| 7818 | // elements/argsObj must not be referenced after it returns. |
| 7819 | // |
| 7820 | // In the case of ConstructArray or ConstructArgs, also overwrite newTarget; |
| 7821 | // newTarget must not be referenced after this point. |
| 7822 | // |
| 7823 | // objreg is dead across this call. |
| 7824 | emitPushArguments(apply); |
| 7825 | |
| 7826 | masm.checkStackAlignment(); |
| 7827 | |
| 7828 | bool constructing = apply->mir()->isConstructing(); |
| 7829 | |
| 7830 | // If the function is native, the call is compiled through emitApplyNative. |
| 7831 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 7832); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()" ")"); do { MOZ_CrashSequence(__null, 7832); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false) |
| 7832 | !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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 7832); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!apply->getSingleTarget()->isNativeWithoutJitEntry()" ")"); do { MOZ_CrashSequence(__null, 7832); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 7833 | |
| 7834 | Label end, invoke; |
| 7835 | |
| 7836 | // Unless already known, guard that calleereg is actually a function object. |
| 7837 | if (!apply->hasSingleTarget()) { |
| 7838 | masm.branchTestObjIsFunction(Assembler::NotEqual, calleereg, objreg, |
| 7839 | calleereg, &invoke); |
| 7840 | } |
| 7841 | |
| 7842 | // Guard that calleereg is an interpreted function with a JSScript. |
| 7843 | masm.branchIfFunctionHasNoJitEntry(calleereg, &invoke); |
| 7844 | |
| 7845 | // Guard that callee allows the [[Call]] or [[Construct]] operation required. |
| 7846 | if (constructing) { |
| 7847 | masm.branchTestFunctionFlags(calleereg, FunctionFlags::CONSTRUCTOR, |
| 7848 | Assembler::Zero, &invoke); |
| 7849 | } else { |
| 7850 | masm.branchFunctionKind(Assembler::Equal, FunctionFlags::ClassConstructor, |
| 7851 | calleereg, objreg, &invoke); |
| 7852 | } |
| 7853 | |
| 7854 | // Use the slow path if CreateThis was unable to create the |this| object. |
| 7855 | if (constructing) { |
| 7856 | Address thisAddr(masm.getStackPointer(), 0); |
| 7857 | masm.branchTestNull(Assembler::Equal, thisAddr, &invoke); |
| 7858 | } |
| 7859 | |
| 7860 | // Call with an Ion frame |
| 7861 | { |
| 7862 | if (apply->mir()->maybeCrossRealm()) { |
| 7863 | masm.switchToObjectRealm(calleereg, objreg); |
| 7864 | } |
| 7865 | |
| 7866 | // Knowing that calleereg is a non-native function, load jitcode. |
| 7867 | masm.loadJitCodeRaw(calleereg, objreg); |
| 7868 | |
| 7869 | masm.PushCalleeToken(calleereg, constructing); |
| 7870 | masm.PushFrameDescriptorForJitCall(FrameType::IonJS, argcreg, scratch); |
| 7871 | |
| 7872 | // Call the function. |
| 7873 | ensureOsiSpace(); |
| 7874 | uint32_t callOffset = masm.callJit(objreg); |
| 7875 | markSafepointAt(callOffset, apply); |
| 7876 | |
| 7877 | if (apply->mir()->maybeCrossRealm()) { |
| 7878 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 7879 | "ReturnReg available as scratch after scripted calls"); |
| 7880 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 7881 | } |
| 7882 | |
| 7883 | // Discard JitFrameLayout fields still left on the stack. |
| 7884 | masm.freeStack(sizeof(JitFrameLayout) - |
| 7885 | JitFrameLayout::bytesPoppedAfterCall()); |
| 7886 | masm.jump(&end); |
| 7887 | } |
| 7888 | |
| 7889 | // Handle uncompiled or native functions. |
| 7890 | { |
| 7891 | masm.bind(&invoke); |
| 7892 | emitCallInvokeFunction(apply); |
| 7893 | } |
| 7894 | |
| 7895 | masm.bind(&end); |
| 7896 | |
| 7897 | // If the return value of the constructing function is Primitive, replace the |
| 7898 | // return value with the Object from CreateThis. |
| 7899 | if (constructing) { |
| 7900 | Label notPrimitive; |
| 7901 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 7902 | ¬Primitive); |
| 7903 | masm.loadValue(Address(masm.getStackPointer(), 0), JSReturnOperand); |
| 7904 | |
| 7905 | #ifdef DEBUG1 |
| 7906 | masm.branchTestPrimitive(Assembler::NotEqual, JSReturnOperand, |
| 7907 | ¬Primitive); |
| 7908 | masm.assumeUnreachable("CreateThis creates an object"); |
| 7909 | #endif |
| 7910 | |
| 7911 | masm.bind(¬Primitive); |
| 7912 | } |
| 7913 | |
| 7914 | // Pop arguments and continue. |
| 7915 | emitRestoreStackPointerFromFP(); |
| 7916 | } |
| 7917 | |
| 7918 | template <typename T> |
| 7919 | void CodeGenerator::emitAlignStackForApplyNative(T* apply, Register argc) { |
| 7920 | static_assert(JitStackAlignment % ABIStackAlignment == 0, |
| 7921 | "aligning on JIT stack subsumes ABI alignment"); |
| 7922 | |
| 7923 | // Align the arguments on the JitStackAlignment. |
| 7924 | if (JitStackValueAlignment > 1) { |
| 7925 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp" , 7926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ") (" "Stack padding adds exactly one Value" ")"); do { MOZ_CrashSequence (__null, 7926); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false) |
| 7926 | "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" ")", "./../../../../js/src/jit/CodeGenerator.cpp" , 7926); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitStackValueAlignment == 2" ") (" "Stack padding adds exactly one Value" ")"); do { MOZ_CrashSequence (__null, 7926); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 7927 | 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" ")" , "./../../../../js/src/jit/CodeGenerator.cpp", 7928); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { MOZ_CrashSequence(__null, 7928); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 7928 | "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" ")" , "./../../../../js/src/jit/CodeGenerator.cpp", 7928); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "frameSize() % JitStackValueAlignment == 0" ") (" "Stack padding assumes that the frameSize is correct" ")" ); do { MOZ_CrashSequence(__null, 7928); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 7929 | |
| 7930 | Assembler::Condition cond; |
| 7931 | if constexpr (T::isConstructing()) { |
| 7932 | // If the number of arguments is even, then we do not need any padding. |
| 7933 | // |
| 7934 | // Also see emitAllocateSpaceForApply(). |
| 7935 | cond = Assembler::Zero; |
| 7936 | } else { |
| 7937 | // If the number of arguments is odd, then we do not need any padding. |
| 7938 | // |
| 7939 | // Also see emitAllocateSpaceForConstructAndPushNewTarget(). |
| 7940 | cond = Assembler::NonZero; |
| 7941 | } |
| 7942 | |
| 7943 | Label noPaddingNeeded; |
| 7944 | masm.branchTestPtr(cond, argc, Imm32(1), &noPaddingNeeded); |
| 7945 | masm.pushValue(MagicValue(JS_ARG_POISON)); |
| 7946 | masm.bind(&noPaddingNeeded); |
| 7947 | } |
| 7948 | } |
| 7949 | |
| 7950 | template <typename T> |
| 7951 | void CodeGenerator::emitPushNativeArguments(T* apply) { |
| 7952 | Register argc = ToRegister(apply->getArgc()); |
| 7953 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 7954 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 7955 | uint32_t extraFormals = apply->numExtraFormals(); |
| 7956 | |
| 7957 | // Align stack. |
| 7958 | emitAlignStackForApplyNative(apply, argc); |
| 7959 | |
| 7960 | // Push newTarget. |
| 7961 | if constexpr (T::isConstructing()) { |
| 7962 | masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget())); |
| 7963 | } |
| 7964 | |
| 7965 | // Push arguments. |
| 7966 | Label noCopy; |
| 7967 | masm.branchTestPtr(Assembler::Zero, argc, argc, &noCopy); |
| 7968 | { |
| 7969 | // Use scratch register to calculate stack space. |
| 7970 | masm.movePtr(argc, scratch); |
| 7971 | |
| 7972 | // Reserve space for copying the arguments. |
| 7973 | NativeObject::elementsSizeMustNotOverflow(); |
| 7974 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
| 7975 | masm.subFromStackPtr(scratch); |
| 7976 | |
| 7977 | // Compute the source and destination offsets into the stack. |
| 7978 | Register argvSrcBase = FramePointer; |
| 7979 | size_t argvSrcOffset = |
| 7980 | JitFrameLayout::offsetOfActualArgs() + extraFormals * sizeof(JS::Value); |
| 7981 | size_t argvDstOffset = 0; |
| 7982 | |
| 7983 | Register argvIndex = tmpArgc; |
| 7984 | masm.move32(argc, argvIndex); |
| 7985 | |
| 7986 | // Copy arguments. |
| 7987 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
| 7988 | argvDstOffset); |
| 7989 | } |
| 7990 | masm.bind(&noCopy); |
| 7991 | |
| 7992 | // Push |this|. |
| 7993 | if constexpr (T::isConstructing()) { |
| 7994 | masm.pushValue(MagicValue(JS_IS_CONSTRUCTING)); |
| 7995 | } else { |
| 7996 | masm.pushValue(ToValue(apply->thisValue())); |
| 7997 | } |
| 7998 | } |
| 7999 | |
| 8000 | template <typename T> |
| 8001 | void CodeGenerator::emitPushArrayAsNativeArguments(T* apply) { |
| 8002 | Register argc = ToRegister(apply->getArgc()); |
| 8003 | Register elements = ToRegister(apply->getElements()); |
| 8004 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 8005 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 8006 | |
| 8007 | // NB: argc and elements are mapped to the same register. |
| 8008 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 8008); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "argc == elements" ")"); do { MOZ_CrashSequence (__null, 8008); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 8009 | |
| 8010 | // Invariants guarded in the caller: |
| 8011 | // - the array is not too long |
| 8012 | // - the array length equals its initialized length |
| 8013 | |
| 8014 | // The array length is our argc. |
| 8015 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), tmpArgc); |
| 8016 | |
| 8017 | // Align stack. |
| 8018 | emitAlignStackForApplyNative(apply, tmpArgc); |
| 8019 | |
| 8020 | // Push newTarget. |
| 8021 | if constexpr (T::isConstructing()) { |
| 8022 | masm.pushValue(JSVAL_TYPE_OBJECT, ToRegister(apply->getNewTarget())); |
| 8023 | } |
| 8024 | |
| 8025 | // Skip the copy of arguments if there are none. |
| 8026 | Label noCopy; |
| 8027 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
| 8028 | { |
| 8029 | // |tmpArgc| is off-by-one, so adjust the offset accordingly. |
| 8030 | BaseObjectElementIndex srcPtr(elements, tmpArgc, |
| 8031 | -int32_t(sizeof(JS::Value))); |
| 8032 | |
| 8033 | Label loop; |
| 8034 | masm.bind(&loop); |
| 8035 | masm.pushValue(srcPtr, scratch); |
| 8036 | masm.decBranchPtr(Assembler::NonZero, tmpArgc, Imm32(1), &loop); |
| 8037 | } |
| 8038 | masm.bind(&noCopy); |
| 8039 | |
| 8040 | // Set argc in preparation for calling the native function. |
| 8041 | masm.load32(Address(elements, ObjectElements::offsetOfLength()), argc); |
| 8042 | |
| 8043 | // Push |this|. |
| 8044 | if constexpr (T::isConstructing()) { |
| 8045 | masm.pushValue(MagicValue(JS_IS_CONSTRUCTING)); |
| 8046 | } else { |
| 8047 | masm.pushValue(ToValue(apply->thisValue())); |
| 8048 | } |
| 8049 | } |
| 8050 | |
| 8051 | void CodeGenerator::emitPushArguments(LApplyArgsNative* apply) { |
| 8052 | emitPushNativeArguments(apply); |
| 8053 | } |
| 8054 | |
| 8055 | void CodeGenerator::emitPushArguments(LApplyArrayNative* apply) { |
| 8056 | emitPushArrayAsNativeArguments(apply); |
| 8057 | } |
| 8058 | |
| 8059 | void CodeGenerator::emitPushArguments(LConstructArgsNative* construct) { |
| 8060 | emitPushNativeArguments(construct); |
| 8061 | } |
| 8062 | |
| 8063 | void CodeGenerator::emitPushArguments(LConstructArrayNative* construct) { |
| 8064 | emitPushArrayAsNativeArguments(construct); |
| 8065 | } |
| 8066 | |
| 8067 | void CodeGenerator::emitPushArguments(LApplyArgsObjNative* apply) { |
| 8068 | Register argc = ToRegister(apply->getArgc()); |
| 8069 | Register argsObj = ToRegister(apply->getArgsObj()); |
| 8070 | Register tmpArgc = ToRegister(apply->getTempObject()); |
| 8071 | Register scratch = ToRegister(apply->getTempForArgCopy()); |
| 8072 | Register scratch2 = ToRegister(apply->getTempExtra()); |
| 8073 | |
| 8074 | // NB: argc and argsObj are mapped to the same register. |
| 8075 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 8075); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "argc == argsObj" ")"); do { MOZ_CrashSequence (__null, 8075); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 8076 | |
| 8077 | // Load argc into tmpArgc. |
| 8078 | masm.loadArgumentsObjectLength(argsObj, tmpArgc); |
| 8079 | |
| 8080 | // Align stack. |
| 8081 | emitAlignStackForApplyNative(apply, tmpArgc); |
| 8082 | |
| 8083 | // Push arguments. |
| 8084 | Label noCopy, epilogue; |
| 8085 | masm.branchTestPtr(Assembler::Zero, tmpArgc, tmpArgc, &noCopy); |
| 8086 | { |
| 8087 | // Use scratch register to calculate stack space. |
| 8088 | masm.movePtr(tmpArgc, scratch); |
| 8089 | |
| 8090 | // Reserve space for copying the arguments. |
| 8091 | NativeObject::elementsSizeMustNotOverflow(); |
| 8092 | masm.lshiftPtr(Imm32(ValueShift), scratch); |
| 8093 | masm.subFromStackPtr(scratch); |
| 8094 | |
| 8095 | // Load arguments data. |
| 8096 | Register argvSrcBase = argsObj; |
| 8097 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
| 8098 | argvSrcBase); |
| 8099 | size_t argvSrcOffset = ArgumentsData::offsetOfArgs(); |
| 8100 | size_t argvDstOffset = 0; |
| 8101 | |
| 8102 | Register argvIndex = scratch2; |
| 8103 | masm.move32(tmpArgc, argvIndex); |
| 8104 | |
| 8105 | // Copy the values. |
| 8106 | emitCopyValuesForApply(argvSrcBase, argvIndex, scratch, argvSrcOffset, |
| 8107 | argvDstOffset); |
| 8108 | } |
| 8109 | masm.bind(&noCopy); |
| 8110 | |
| 8111 | // Set argc in preparation for calling the native function. |
| 8112 | masm.movePtr(tmpArgc, argc); |
| 8113 | |
| 8114 | // Push |this|. |
| 8115 | masm.pushValue(ToValue(apply->thisValue())); |
| 8116 | } |
| 8117 | |
| 8118 | template <typename T> |
| 8119 | void CodeGenerator::emitApplyNative(T* apply) { |
| 8120 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp" , 8121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()" ") (" "isConstructing condition must be consistent" ")"); do { MOZ_CrashSequence(__null, 8121); __attribute__((nomerge)) :: abort(); } while (false); } } while (false) |
| 8121 | "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" ")", "./../../../../js/src/jit/CodeGenerator.cpp" , 8121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "T::isConstructing() == apply->mir()->isConstructing()" ") (" "isConstructing condition must be consistent" ")"); do { MOZ_CrashSequence(__null, 8121); __attribute__((nomerge)) :: abort(); } while (false); } } while (false); |
| 8122 | |
| 8123 | WrappedFunction* target = apply->mir()->getSingleTarget(); |
| 8124 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 8124); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target->isNativeWithoutJitEntry()" ")"); do { MOZ_CrashSequence(__null, 8124); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8125 | |
| 8126 | JSNative native = target->native(); |
| 8127 | if (apply->mir()->ignoresReturnValue() && target->hasJitInfo()) { |
| 8128 | const JSJitInfo* jitInfo = target->jitInfo(); |
| 8129 | if (jitInfo->type() == JSJitInfo::IgnoresReturnValueNative) { |
| 8130 | native = jitInfo->ignoresReturnValueMethod; |
| 8131 | } |
| 8132 | } |
| 8133 | |
| 8134 | // Push arguments, including newTarget and |this|. |
| 8135 | emitPushArguments(apply); |
| 8136 | |
| 8137 | // Registers used for callWithABI() argument-passing. |
| 8138 | Register argContextReg = ToRegister(apply->getTempObject()); |
| 8139 | Register argUintNReg = ToRegister(apply->getArgc()); |
| 8140 | Register argVpReg = ToRegister(apply->getTempForArgCopy()); |
| 8141 | Register tempReg = ToRegister(apply->getTempExtra()); |
| 8142 | |
| 8143 | // No unused stack for variadic calls. |
| 8144 | uint32_t unusedStack = 0; |
| 8145 | |
| 8146 | // Pushed arguments don't change the pushed frames amount. |
| 8147 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 8147); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { MOZ_CrashSequence(__null, 8147); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8148 | |
| 8149 | // Create the exit frame and call the native. |
| 8150 | emitCallNative(apply, native, argContextReg, argUintNReg, argVpReg, tempReg, |
| 8151 | unusedStack); |
| 8152 | |
| 8153 | // The exit frame is still on the stack. |
| 8154 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 8154); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "masm.framePushed() == frameSize() + NativeExitFrameLayout::Size()" ")"); do { MOZ_CrashSequence(__null, 8154); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8155 | |
| 8156 | // The next instruction is removing the exit frame, so there is no need for |
| 8157 | // leaveFakeExitFrame. |
| 8158 | |
| 8159 | // Pop arguments and continue. |
| 8160 | masm.setFramePushed(frameSize()); |
| 8161 | emitRestoreStackPointerFromFP(); |
| 8162 | } |
| 8163 | |
| 8164 | template <typename T> |
| 8165 | void CodeGenerator::emitApplyArgsGuard(T* apply) { |
| 8166 | LSnapshot* snapshot = apply->snapshot(); |
| 8167 | Register argcreg = ToRegister(apply->getArgc()); |
| 8168 | |
| 8169 | // Ensure that we have a reasonable number of arguments. |
| 8170 | bailoutCmp32(Assembler::Above, argcreg, Imm32(JIT_ARGS_LENGTH_MAX), snapshot); |
| 8171 | } |
| 8172 | |
| 8173 | template <typename T> |
| 8174 | void CodeGenerator::emitApplyArgsObjGuard(T* apply) { |
| 8175 | Register argsObj = ToRegister(apply->getArgsObj()); |
| 8176 | Register temp = ToRegister(apply->getTempObject()); |
| 8177 | |
| 8178 | Label bail; |
| 8179 | masm.loadArgumentsObjectLength(argsObj, temp, &bail); |
| 8180 | masm.branch32(Assembler::Above, temp, Imm32(JIT_ARGS_LENGTH_MAX), &bail); |
| 8181 | bailoutFrom(&bail, apply->snapshot()); |
| 8182 | } |
| 8183 | |
| 8184 | template <typename T> |
| 8185 | void CodeGenerator::emitApplyArrayGuard(T* apply) { |
| 8186 | LSnapshot* snapshot = apply->snapshot(); |
| 8187 | Register elements = ToRegister(apply->getElements()); |
| 8188 | Register tmp = ToRegister(apply->getTempObject()); |
| 8189 | |
| 8190 | Address length(elements, ObjectElements::offsetOfLength()); |
| 8191 | masm.load32(length, tmp); |
| 8192 | |
| 8193 | // Ensure that we have a reasonable number of arguments. |
| 8194 | bailoutCmp32(Assembler::Above, tmp, Imm32(JIT_ARGS_LENGTH_MAX), snapshot); |
| 8195 | |
| 8196 | // Ensure that the array does not contain an uninitialized tail. |
| 8197 | |
| 8198 | Address initializedLength(elements, |
| 8199 | ObjectElements::offsetOfInitializedLength()); |
| 8200 | masm.sub32(initializedLength, tmp); |
| 8201 | bailoutCmp32(Assembler::NotEqual, tmp, Imm32(0), snapshot); |
| 8202 | } |
| 8203 | |
| 8204 | void CodeGenerator::visitApplyArgsGeneric(LApplyArgsGeneric* apply) { |
| 8205 | emitApplyArgsGuard(apply); |
| 8206 | emitApplyGeneric(apply); |
| 8207 | } |
| 8208 | |
| 8209 | void CodeGenerator::visitApplyArgsObj(LApplyArgsObj* apply) { |
| 8210 | emitApplyArgsObjGuard(apply); |
| 8211 | emitApplyGeneric(apply); |
| 8212 | } |
| 8213 | |
| 8214 | void CodeGenerator::visitApplyArrayGeneric(LApplyArrayGeneric* apply) { |
| 8215 | emitApplyArrayGuard(apply); |
| 8216 | emitApplyGeneric(apply); |
| 8217 | } |
| 8218 | |
| 8219 | void CodeGenerator::visitConstructArgsGeneric(LConstructArgsGeneric* lir) { |
| 8220 | emitApplyArgsGuard(lir); |
| 8221 | emitApplyGeneric(lir); |
| 8222 | } |
| 8223 | |
| 8224 | void CodeGenerator::visitConstructArrayGeneric(LConstructArrayGeneric* lir) { |
| 8225 | emitApplyArrayGuard(lir); |
| 8226 | emitApplyGeneric(lir); |
| 8227 | } |
| 8228 | |
| 8229 | void CodeGenerator::visitApplyArgsNative(LApplyArgsNative* lir) { |
| 8230 | emitApplyArgsGuard(lir); |
| 8231 | emitApplyNative(lir); |
| 8232 | } |
| 8233 | |
| 8234 | void CodeGenerator::visitApplyArgsObjNative(LApplyArgsObjNative* lir) { |
| 8235 | emitApplyArgsObjGuard(lir); |
| 8236 | emitApplyNative(lir); |
| 8237 | } |
| 8238 | |
| 8239 | void CodeGenerator::visitApplyArrayNative(LApplyArrayNative* lir) { |
| 8240 | emitApplyArrayGuard(lir); |
| 8241 | emitApplyNative(lir); |
| 8242 | } |
| 8243 | |
| 8244 | void CodeGenerator::visitConstructArgsNative(LConstructArgsNative* lir) { |
| 8245 | emitApplyArgsGuard(lir); |
| 8246 | emitApplyNative(lir); |
| 8247 | } |
| 8248 | |
| 8249 | void CodeGenerator::visitConstructArrayNative(LConstructArrayNative* lir) { |
| 8250 | emitApplyArrayGuard(lir); |
| 8251 | emitApplyNative(lir); |
| 8252 | } |
| 8253 | |
| 8254 | void CodeGenerator::visitBail(LBail* lir) { bailout(lir->snapshot()); } |
| 8255 | |
| 8256 | void CodeGenerator::visitUnreachable(LUnreachable* lir) { |
| 8257 | masm.assumeUnreachable("end-of-block assumed unreachable"); |
| 8258 | } |
| 8259 | |
| 8260 | void CodeGenerator::visitEncodeSnapshot(LEncodeSnapshot* lir) { |
| 8261 | encode(lir->snapshot()); |
| 8262 | } |
| 8263 | |
| 8264 | void CodeGenerator::visitUnreachableResultV(LUnreachableResultV* lir) { |
| 8265 | masm.assumeUnreachable("must be unreachable"); |
| 8266 | } |
| 8267 | |
| 8268 | void CodeGenerator::visitUnreachableResultT(LUnreachableResultT* lir) { |
| 8269 | masm.assumeUnreachable("must be unreachable"); |
| 8270 | } |
| 8271 | |
| 8272 | void CodeGenerator::visitCheckOverRecursed(LCheckOverRecursed* lir) { |
| 8273 | // If we don't push anything on the stack, skip the check. |
| 8274 | if (omitOverRecursedStackCheck()) { |
| 8275 | return; |
| 8276 | } |
| 8277 | |
| 8278 | // Ensure that this frame will not cross the stack limit. |
| 8279 | // This is a weak check, justified by Ion using the C stack: we must always |
| 8280 | // be some distance away from the actual limit, since if the limit is |
| 8281 | // crossed, an error must be thrown, which requires more frames. |
| 8282 | // |
| 8283 | // It must always be possible to trespass past the stack limit. |
| 8284 | // Ion may legally place frames very close to the limit. Calling additional |
| 8285 | // C functions may then violate the limit without any checking. |
| 8286 | // |
| 8287 | // Since Ion frames exist on the C stack, the stack limit may be |
| 8288 | // dynamically set by JS_SetThreadStackLimit() and JS_SetNativeStackQuota(). |
| 8289 | |
| 8290 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 8291 | // The OOL path is hit if the recursion depth has been exceeded. |
| 8292 | // Throw an InternalError for over-recursion. |
| 8293 | |
| 8294 | // LFunctionEnvironment can appear before LCheckOverRecursed, so we have |
| 8295 | // to save all live registers to avoid crashes if CheckOverRecursed triggers |
| 8296 | // a GC. |
| 8297 | saveLive(lir); |
| 8298 | |
| 8299 | using Fn = bool (*)(JSContext*); |
| 8300 | if (lir->mir()->isResumingGenerator()) { |
| 8301 | callVM<Fn, CheckOverRecursedResumingGenerator>(lir); |
| 8302 | } else { |
| 8303 | callVM<Fn, CheckOverRecursed>(lir); |
| 8304 | } |
| 8305 | |
| 8306 | restoreLive(lir); |
| 8307 | masm.jump(ool.rejoin()); |
| 8308 | }); |
| 8309 | addOutOfLineCode(ool, lir->mir()); |
| 8310 | |
| 8311 | // When resuming a generator we check the no-interrupt limit, because the VM |
| 8312 | // function must not handle interrupts for a half-initialized frame. |
| 8313 | const void* limitAddr = |
| 8314 | lir->mir()->isResumingGenerator() |
| 8315 | ? gen->runtime->addressOfJitStackLimitNoInterrupt() |
| 8316 | : gen->runtime->addressOfJitStackLimit(); |
| 8317 | |
| 8318 | // Conditional forward (unlikely) branch to failure. |
| 8319 | masm.branchStackPtrRhs(Assembler::AboveOrEqual, AbsoluteAddress(limitAddr), |
| 8320 | ool->entry()); |
| 8321 | masm.bind(ool->rejoin()); |
| 8322 | } |
| 8323 | |
| 8324 | IonScriptCounts* CodeGenerator::maybeCreateScriptCounts() { |
| 8325 | // If scripts are being profiled, create a new IonScriptCounts for the |
| 8326 | // profiling data, which will be attached to the associated JSScript or |
| 8327 | // wasm module after code generation finishes. |
| 8328 | if (!gen->hasProfilingScripts()) { |
| 8329 | return nullptr; |
| 8330 | } |
| 8331 | |
| 8332 | // This test inhibits IonScriptCount creation for wasm code which is |
| 8333 | // currently incompatible with wasm codegen for two reasons: (1) wasm code |
| 8334 | // must be serializable and script count codegen bakes in absolute |
| 8335 | // addresses, (2) wasm code does not have a JSScript with which to associate |
| 8336 | // code coverage data. |
| 8337 | JSScript* script = gen->outerInfo().script(); |
| 8338 | if (!script) { |
| 8339 | return nullptr; |
| 8340 | } |
| 8341 | |
| 8342 | auto counts = MakeUnique<IonScriptCounts>(); |
| 8343 | if (!counts || !counts->init(graph.numBlocks())) { |
| 8344 | return nullptr; |
| 8345 | } |
| 8346 | |
| 8347 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
| 8348 | MBasicBlock* block = graph.getBlock(i)->mir(); |
| 8349 | |
| 8350 | uint32_t offset = 0; |
| 8351 | char* description = nullptr; |
| 8352 | if (MResumePoint* resume = block->entryResumePoint()) { |
| 8353 | // Find a PC offset in the outermost script to use. If this |
| 8354 | // block is from an inlined script, find a location in the |
| 8355 | // outer script to associate information about the inlining |
| 8356 | // with. |
| 8357 | while (resume->caller()) { |
| 8358 | resume = resume->caller(); |
| 8359 | } |
| 8360 | offset = script->pcToOffset(resume->pc()); |
| 8361 | |
| 8362 | if (block->entryResumePoint()->caller()) { |
| 8363 | // Get the filename and line number of the inner script. |
| 8364 | JSScript* innerScript = block->info().script(); |
| 8365 | description = js_pod_calloc<char>(200); |
| 8366 | if (description) { |
| 8367 | snprintf(description, 200, "%s:%u", innerScript->filename(), |
| 8368 | innerScript->lineno()); |
| 8369 | } |
| 8370 | } |
| 8371 | } |
| 8372 | |
| 8373 | if (!counts->block(i).init(block->id(), offset, description, |
| 8374 | block->numSuccessors())) { |
| 8375 | return nullptr; |
| 8376 | } |
| 8377 | |
| 8378 | for (size_t j = 0; j < block->numSuccessors(); j++) { |
| 8379 | counts->block(i).setSuccessor( |
| 8380 | j, skipTrivialBlocks(block->getSuccessor(j))->id()); |
| 8381 | } |
| 8382 | } |
| 8383 | |
| 8384 | scriptCounts_ = counts.release(); |
| 8385 | return scriptCounts_; |
| 8386 | } |
| 8387 | |
| 8388 | // Structure for managing the state tracked for a block by script counters. |
| 8389 | struct ScriptCountBlockState { |
| 8390 | IonBlockCounts& block; |
| 8391 | MacroAssembler& masm; |
| 8392 | |
| 8393 | Sprinter printer; |
| 8394 | |
| 8395 | public: |
| 8396 | ScriptCountBlockState(IonBlockCounts* block, MacroAssembler* masm) |
| 8397 | : block(*block), masm(*masm), printer(GetJitContext()->cx, false) {} |
| 8398 | |
| 8399 | bool init() { |
| 8400 | if (!printer.init()) { |
| 8401 | return false; |
| 8402 | } |
| 8403 | |
| 8404 | // Bump the hit count for the block at the start. This code is not |
| 8405 | // included in either the text for the block or the instruction byte |
| 8406 | // counts. |
| 8407 | masm.inc64(AbsoluteAddress(block.addressOfHitCount())); |
| 8408 | |
| 8409 | // Collect human readable assembly for the code generated in the block. |
| 8410 | masm.setPrinter(&printer); |
| 8411 | |
| 8412 | return true; |
| 8413 | } |
| 8414 | |
| 8415 | void visitInstruction(LInstruction* ins) { |
| 8416 | #ifdef JS_JITSPEW1 |
| 8417 | // Prefix stream of assembly instructions with their LIR instruction |
| 8418 | // name and any associated high level info. |
| 8419 | if (const char* extra = ins->getExtraName()) { |
| 8420 | printer.printf("[%s:%s]\n", ins->opName(), extra); |
| 8421 | } else { |
| 8422 | printer.printf("[%s]\n", ins->opName()); |
| 8423 | } |
| 8424 | #endif |
| 8425 | } |
| 8426 | |
| 8427 | ~ScriptCountBlockState() { |
| 8428 | masm.setPrinter(nullptr); |
| 8429 | |
| 8430 | if (JS::UniqueChars str = printer.release()) { |
| 8431 | block.setCode(str.get()); |
| 8432 | } |
| 8433 | } |
| 8434 | }; |
| 8435 | |
| 8436 | void CodeGenerator::branchIfInvalidated(Register temp, Label* invalidated) { |
| 8437 | CodeOffset label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp); |
| 8438 | masm.propagateOOM(ionScriptLabels_.append(label)); |
| 8439 | |
| 8440 | // If IonScript::invalidationCount_ != 0, the script has been invalidated. |
| 8441 | masm.branch32(Assembler::NotEqual, |
| 8442 | Address(temp, IonScript::offsetOfInvalidationCount()), Imm32(0), |
| 8443 | invalidated); |
| 8444 | } |
| 8445 | |
| 8446 | #ifdef DEBUG1 |
| 8447 | void CodeGenerator::emitAssertGCThingResult(Register input, |
| 8448 | const MDefinition* mir) { |
| 8449 | MIRType type = mir->type(); |
| 8450 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 8451); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" ")"); do { MOZ_CrashSequence(__null, 8451); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 8451 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 8451); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == MIRType::Object || type == MIRType::String || type == MIRType::Symbol || type == MIRType::BigInt" ")"); do { MOZ_CrashSequence(__null, 8451); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8452 | |
| 8453 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 8454 | regs.take(input); |
| 8455 | |
| 8456 | Register temp = regs.takeAny(); |
| 8457 | masm.push(temp); |
| 8458 | |
| 8459 | // Don't check if the script has been invalidated. In that case invalid |
| 8460 | // types are expected (until we reach the OsiPoint and bailout). |
| 8461 | Label done; |
| 8462 | branchIfInvalidated(temp, &done); |
| 8463 | |
| 8464 | # ifndef JS_SIMULATOR |
| 8465 | // Check that we have a valid GC pointer. |
| 8466 | // Disable for wasm because we don't have a context on wasm compilation |
| 8467 | // threads and this needs a context. |
| 8468 | // Also disable for simulator builds because the C++ call is a lot slower |
| 8469 | // there than on actual hardware. |
| 8470 | if (JitOptions.fullDebugChecks && !IsCompilingWasm()) { |
| 8471 | saveVolatile(); |
| 8472 | masm.setupUnalignedABICall(temp); |
| 8473 | masm.loadJSContext(temp); |
| 8474 | masm.passABIArg(temp); |
| 8475 | masm.passABIArg(input); |
| 8476 | |
| 8477 | switch (type) { |
| 8478 | case MIRType::Object: { |
| 8479 | using Fn = void (*)(JSContext* cx, JSObject* obj); |
| 8480 | masm.callWithABI<Fn, AssertValidObjectPtr>(); |
| 8481 | break; |
| 8482 | } |
| 8483 | case MIRType::String: { |
| 8484 | using Fn = void (*)(JSContext* cx, JSString* str); |
| 8485 | masm.callWithABI<Fn, AssertValidStringPtr>(); |
| 8486 | break; |
| 8487 | } |
| 8488 | case MIRType::Symbol: { |
| 8489 | using Fn = void (*)(JSContext* cx, JS::Symbol* sym); |
| 8490 | masm.callWithABI<Fn, AssertValidSymbolPtr>(); |
| 8491 | break; |
| 8492 | } |
| 8493 | case MIRType::BigInt: { |
| 8494 | using Fn = void (*)(JSContext* cx, JS::BigInt* bi); |
| 8495 | masm.callWithABI<Fn, AssertValidBigIntPtr>(); |
| 8496 | break; |
| 8497 | } |
| 8498 | default: |
| 8499 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp" , 8499); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence (__null, 8499); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 8500 | } |
| 8501 | |
| 8502 | restoreVolatile(); |
| 8503 | } |
| 8504 | # endif |
| 8505 | |
| 8506 | masm.bind(&done); |
| 8507 | masm.pop(temp); |
| 8508 | } |
| 8509 | |
| 8510 | void CodeGenerator::emitAssertResultV(const ValueOperand input, |
| 8511 | const MDefinition* mir) { |
| 8512 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 8513 | regs.take(input); |
| 8514 | |
| 8515 | Register temp1 = regs.takeAny(); |
| 8516 | Register temp2 = regs.takeAny(); |
| 8517 | masm.push(temp1); |
| 8518 | masm.push(temp2); |
| 8519 | |
| 8520 | // Don't check if the script has been invalidated. In that case invalid |
| 8521 | // types are expected (until we reach the OsiPoint and bailout). |
| 8522 | Label done; |
| 8523 | branchIfInvalidated(temp1, &done); |
| 8524 | |
| 8525 | // Check that we have a valid GC pointer. |
| 8526 | if (JitOptions.fullDebugChecks) { |
| 8527 | saveVolatile(); |
| 8528 | |
| 8529 | masm.pushValue(input); |
| 8530 | masm.moveStackPtrTo(temp1); |
| 8531 | |
| 8532 | using Fn = void (*)(JSContext* cx, Value* v); |
| 8533 | masm.setupUnalignedABICall(temp2); |
| 8534 | masm.loadJSContext(temp2); |
| 8535 | masm.passABIArg(temp2); |
| 8536 | masm.passABIArg(temp1); |
| 8537 | masm.callWithABI<Fn, AssertValidValue>(); |
| 8538 | masm.popValue(input); |
| 8539 | restoreVolatile(); |
| 8540 | } |
| 8541 | |
| 8542 | masm.bind(&done); |
| 8543 | masm.pop(temp2); |
| 8544 | masm.pop(temp1); |
| 8545 | } |
| 8546 | |
| 8547 | void CodeGenerator::emitGCThingResultChecks(LInstruction* lir, |
| 8548 | MDefinition* mir) { |
| 8549 | if (lir->numDefs() == 0) { |
| 8550 | return; |
| 8551 | } |
| 8552 | |
| 8553 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 8553); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { MOZ_CrashSequence (__null, 8553); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 8554 | if (lir->getDef(0)->isBogusTemp()) { |
| 8555 | return; |
| 8556 | } |
| 8557 | |
| 8558 | Register output = ToRegister(lir->getDef(0)); |
| 8559 | emitAssertGCThingResult(output, mir); |
| 8560 | } |
| 8561 | |
| 8562 | void CodeGenerator::emitValueResultChecks(LInstruction* lir, MDefinition* mir) { |
| 8563 | if (lir->numDefs() == 0) { |
| 8564 | return; |
| 8565 | } |
| 8566 | |
| 8567 | MOZ_ASSERT(lir->numDefs() == BOX_PIECES)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->numDefs() == BOX_PIECES)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->numDefs() == BOX_PIECES ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "lir->numDefs() == BOX_PIECES", "./../../../../js/src/jit/CodeGenerator.cpp" , 8567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->numDefs() == BOX_PIECES" ")"); do { MOZ_CrashSequence(__null, 8567); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8568 | if (!lir->getDef(0)->output()->isGeneralReg()) { |
| 8569 | return; |
| 8570 | } |
| 8571 | |
| 8572 | ValueOperand output = ToOutValue(lir); |
| 8573 | |
| 8574 | emitAssertResultV(output, mir); |
| 8575 | } |
| 8576 | |
| 8577 | void CodeGenerator::emitWasmAnyrefResultChecks(LInstruction* lir, |
| 8578 | MDefinition* mir) { |
| 8579 | MOZ_ASSERT(mir->type() == MIRType::WasmAnyRef)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mir->type() == MIRType::WasmAnyRef)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(mir->type() == MIRType::WasmAnyRef))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mir->type() == MIRType::WasmAnyRef" , "./../../../../js/src/jit/CodeGenerator.cpp", 8579); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "mir->type() == MIRType::WasmAnyRef" ")" ); do { MOZ_CrashSequence(__null, 8579); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8580 | |
| 8581 | if (!JitOptions.fullDebugChecks) { |
| 8582 | return; |
| 8583 | } |
| 8584 | |
| 8585 | wasm::MaybeRefType destType = mir->wasmRefType(); |
| 8586 | if (!destType || !destType.value().isCastable()) { |
| 8587 | return; |
| 8588 | } |
| 8589 | |
| 8590 | if (lir->numDefs() == 0) { |
| 8591 | return; |
| 8592 | } |
| 8593 | |
| 8594 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 8594); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->numDefs() == 1" ")"); do { MOZ_CrashSequence (__null, 8594); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 8595 | if (lir->getDef(0)->isBogusTemp()) { |
| 8596 | return; |
| 8597 | } |
| 8598 | |
| 8599 | if (lir->getDef(0)->output()->isMemory()) { |
| 8600 | return; |
| 8601 | } |
| 8602 | Register output = ToRegister(lir->getDef(0)); |
| 8603 | |
| 8604 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 8605 | regs.take(output); |
| 8606 | |
| 8607 | BranchWasmRefIsSubtypeRegisters needs = |
| 8608 | MacroAssembler::regsForBranchWasmRefIsSubtype(destType.value()); |
| 8609 | |
| 8610 | Register temp1; |
| 8611 | Register temp2; |
| 8612 | Register temp3; |
| 8613 | if (needs.needSuperSTV) { |
| 8614 | temp1 = regs.takeAny(); |
| 8615 | masm.push(temp1); |
| 8616 | } |
| 8617 | if (needs.needScratch1) { |
| 8618 | temp2 = regs.takeAny(); |
| 8619 | masm.push(temp2); |
| 8620 | } |
| 8621 | if (needs.needScratch2) { |
| 8622 | temp3 = regs.takeAny(); |
| 8623 | masm.push(temp3); |
| 8624 | } |
| 8625 | |
| 8626 | if (needs.needSuperSTV) { |
| 8627 | uint32_t typeIndex = |
| 8628 | wasmCodeMeta()->types->indexOf(*destType.value().typeDef()); |
| 8629 | |
| 8630 | // When full debug checks are enabled, we always write the callee instance |
| 8631 | // pointer into its usual slot in the frame in our function prologue, so |
| 8632 | // that we can get it even if the InstanceReg is currently being used for |
| 8633 | // something else. |
| 8634 | masm.loadPtr( |
| 8635 | Address(FramePointer, wasm::FrameWithInstances::calleeInstanceOffset()), |
| 8636 | temp1); |
| 8637 | masm.loadPtr( |
| 8638 | Address(temp1, wasm::Instance::offsetInData( |
| 8639 | wasmCodeMeta()->offsetOfSuperTypeVector(typeIndex))), |
| 8640 | temp1); |
| 8641 | } |
| 8642 | |
| 8643 | Label ok; |
| 8644 | masm.branchWasmRefIsSubtype(output, wasm::MaybeRefType(), destType.value(), |
| 8645 | &ok, /*onSuccess=*/true, |
| 8646 | /*signalNullChecks=*/false, temp1, temp2, temp3); |
| 8647 | masm.breakpoint(); |
| 8648 | masm.bind(&ok); |
| 8649 | |
| 8650 | if (needs.needScratch2) { |
| 8651 | masm.pop(temp3); |
| 8652 | } |
| 8653 | if (needs.needScratch1) { |
| 8654 | masm.pop(temp2); |
| 8655 | } |
| 8656 | if (needs.needSuperSTV) { |
| 8657 | masm.pop(temp1); |
| 8658 | } |
| 8659 | |
| 8660 | # ifdef JS_CODEGEN_ARM64 |
| 8661 | masm.syncStackPtr(); |
| 8662 | # endif |
| 8663 | } |
| 8664 | |
| 8665 | void CodeGenerator::emitDebugResultChecks(LInstruction* ins) { |
| 8666 | // In debug builds, check that LIR instructions return valid values. |
| 8667 | |
| 8668 | MDefinition* mir = ins->mirRaw(); |
| 8669 | if (!mir) { |
| 8670 | return; |
| 8671 | } |
| 8672 | |
| 8673 | switch (mir->type()) { |
| 8674 | case MIRType::Object: |
| 8675 | case MIRType::String: |
| 8676 | case MIRType::Symbol: |
| 8677 | case MIRType::BigInt: |
| 8678 | emitGCThingResultChecks(ins, mir); |
| 8679 | break; |
| 8680 | case MIRType::Value: |
| 8681 | emitValueResultChecks(ins, mir); |
| 8682 | break; |
| 8683 | case MIRType::WasmAnyRef: |
| 8684 | emitWasmAnyrefResultChecks(ins, mir); |
| 8685 | break; |
| 8686 | default: |
| 8687 | break; |
| 8688 | } |
| 8689 | } |
| 8690 | |
| 8691 | void CodeGenerator::emitDebugForceBailing(LInstruction* lir) { |
| 8692 | if (MOZ_LIKELY(!gen->options.ionBailAfterEnabled())(__builtin_expect(!!(!gen->options.ionBailAfterEnabled()), 1))) { |
| 8693 | return; |
| 8694 | } |
| 8695 | if (!lir->snapshot()) { |
| 8696 | return; |
| 8697 | } |
| 8698 | if (lir->isOsiPoint()) { |
| 8699 | return; |
| 8700 | } |
| 8701 | |
| 8702 | masm.comment("emitDebugForceBailing"); |
| 8703 | const void* bailAfterCounterAddr = |
| 8704 | gen->runtime->addressOfIonBailAfterCounter(); |
| 8705 | |
| 8706 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All()); |
| 8707 | |
| 8708 | Label done, notBail; |
| 8709 | masm.branch32(Assembler::Equal, AbsoluteAddress(bailAfterCounterAddr), |
| 8710 | Imm32(0), &done); |
| 8711 | { |
| 8712 | Register temp = regs.takeAny(); |
| 8713 | |
| 8714 | masm.push(temp); |
| 8715 | masm.load32(AbsoluteAddress(bailAfterCounterAddr), temp); |
| 8716 | masm.sub32(Imm32(1), temp); |
| 8717 | masm.store32(temp, AbsoluteAddress(bailAfterCounterAddr)); |
| 8718 | |
| 8719 | masm.branch32(Assembler::NotEqual, temp, Imm32(0), ¬Bail); |
| 8720 | { |
| 8721 | masm.pop(temp); |
| 8722 | bailout(lir->snapshot()); |
| 8723 | } |
| 8724 | masm.bind(¬Bail); |
| 8725 | masm.pop(temp); |
| 8726 | } |
| 8727 | masm.bind(&done); |
| 8728 | } |
| 8729 | #endif // DEBUG |
| 8730 | |
| 8731 | bool CodeGenerator::generateBody() { |
| 8732 | JitSpew(JitSpew_Codegen, "\n"); |
| 8733 | AutoCreatedBy acb(masm, "CodeGenerator::generateBody"); |
| 8734 | |
| 8735 | JitSpew(JitSpew_Codegen, "==== BEGIN CodeGenerator::generateBody ===="); |
| 8736 | counts_ = maybeCreateScriptCounts(); |
| 8737 | |
| 8738 | const bool compilingWasm = gen->compilingWasm(); |
| 8739 | |
| 8740 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
| 8741 | current = graph.getBlock(i); |
| 8742 | |
| 8743 | // Don't emit any code for trivial blocks, containing just a goto. Such |
| 8744 | // blocks are created to split critical edges, and if we didn't end up |
| 8745 | // putting any instructions in them, we can skip them. |
| 8746 | if (current->isTrivial()) { |
| 8747 | continue; |
| 8748 | } |
| 8749 | |
| 8750 | if (gen->shouldCancel("Generate Code (block loop)")) { |
| 8751 | return false; |
| 8752 | } |
| 8753 | |
| 8754 | // Skip out of line blocks for now. They will be emitted in |
| 8755 | // generateOutOfLineBlocks. |
| 8756 | if (current->isOutOfLine()) { |
| 8757 | continue; |
| 8758 | } |
| 8759 | |
| 8760 | // Generate a basic block |
| 8761 | if (!generateBlock(current, i, counts_, compilingWasm)) { |
| 8762 | return false; |
| 8763 | } |
| 8764 | } |
| 8765 | |
| 8766 | JitSpew(JitSpew_Codegen, "==== END CodeGenerator::generateBody ====\n"); |
| 8767 | return true; |
| 8768 | } |
| 8769 | |
| 8770 | bool CodeGenerator::generateBlock(LBlock* current, size_t blockNumber, |
| 8771 | IonScriptCounts* counts, bool compilingWasm) { |
| 8772 | #ifdef JS_JITSPEW1 |
| 8773 | const char* filename = nullptr; |
| 8774 | size_t lineNumber = 0; |
| 8775 | JS::LimitedColumnNumberOneOrigin columnNumber; |
| 8776 | if (current->mir()->info().script()) { |
| 8777 | filename = current->mir()->info().script()->filename(); |
| 8778 | if (current->mir()->pc()) { |
| 8779 | lineNumber = PCToLineNumber(current->mir()->info().script(), |
| 8780 | current->mir()->pc(), &columnNumber); |
| 8781 | } |
| 8782 | } |
| 8783 | JitSpew(JitSpew_Codegen, "--------------------------------"); |
| 8784 | JitSpew(JitSpew_Codegen, "# block%zu %s:%zu:%u%s:", blockNumber, |
| 8785 | filename ? filename : "?", lineNumber, columnNumber.oneOriginValue(), |
| 8786 | current->mir()->isLoopHeader() ? " (loop header)" : ""); |
| 8787 | #endif |
| 8788 | |
| 8789 | if (current->mir()->isLoopHeader() && compilingWasm) { |
| 8790 | masm.nopAlign(CodeAlignment); |
| 8791 | } |
| 8792 | |
| 8793 | masm.bind(current->label()); |
| 8794 | |
| 8795 | mozilla::Maybe<ScriptCountBlockState> blockCounts; |
| 8796 | if (counts) { |
| 8797 | blockCounts.emplace(&counts->block(blockNumber), &masm); |
| 8798 | if (!blockCounts->init()) { |
| 8799 | return false; |
| 8800 | } |
| 8801 | } |
| 8802 | |
| 8803 | for (LInstructionIterator iter = current->begin(); iter != current->end(); |
| 8804 | iter++) { |
| 8805 | if (gen->shouldCancel("Generate Code (instruction loop)")) { |
| 8806 | return false; |
| 8807 | } |
| 8808 | if (!alloc().ensureBallast()) { |
| 8809 | return false; |
| 8810 | } |
| 8811 | |
| 8812 | perfSpewer().recordInstruction(masm, *iter); |
| 8813 | #ifdef JS_JITSPEW1 |
| 8814 | { |
| 8815 | AutoJitSpewMessage msg(JitSpew_Codegen, |
| 8816 | " # LIR=%s", |
| 8817 | iter->opName()); |
| 8818 | if (const char* extra = iter->getExtraName()) { |
| 8819 | msg.append(":%s", extra); |
| 8820 | } |
| 8821 | } |
| 8822 | #endif |
| 8823 | |
| 8824 | if (counts) { |
| 8825 | blockCounts->visitInstruction(*iter); |
| 8826 | } |
| 8827 | |
| 8828 | #ifdef CHECK_OSIPOINT_REGISTERS1 |
| 8829 | if (iter->safepoint() && !compilingWasm) { |
| 8830 | resetOsiPointRegs(iter->safepoint()); |
| 8831 | } |
| 8832 | #endif |
| 8833 | |
| 8834 | if (!compilingWasm) { |
| 8835 | if (MDefinition* mir = iter->mirRaw()) { |
| 8836 | if (!addNativeToBytecodeEntry(mir->trackedSite())) { |
| 8837 | return false; |
| 8838 | } |
| 8839 | } |
| 8840 | } |
| 8841 | |
| 8842 | setElement(*iter); // needed to encode correct snapshot location. |
| 8843 | |
| 8844 | #ifdef DEBUG1 |
| 8845 | emitDebugForceBailing(*iter); |
| 8846 | #endif |
| 8847 | |
| 8848 | switch (iter->op()) { |
| 8849 | #ifndef JS_CODEGEN_NONE |
| 8850 | # define LIROP(op) \ |
| 8851 | case LNode::Opcode::op: \ |
| 8852 | visit##op(iter->to##op()); \ |
| 8853 | break; |
| 8854 | 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(Parameter)LIROP(Goto)LIROP(NewArray)LIROP(NewIterator )LIROP(NewBoundFunction)LIROP(NewTypedArray)LIROP(NewTypedArrayInline )LIROP(BindFunction)LIROP(NewObject)LIROP(NewPlainObject)LIROP (NewArrayObject)LIROP(NewCallObject)LIROP(NewMapObject)LIROP( NewSetObject)LIROP(NewStringObject)LIROP(WasmRefAsNonNull)LIROP (WasmRefTestConcrete)LIROP(WasmRefTestAbstract)LIROP(WasmRefTestConcreteAndBranch )LIROP(WasmRefTestAbstractAndBranch)LIROP(WasmRefCastConcrete )LIROP(WasmRefCastAbstract)LIROP(WasmNewStructObject)LIROP(WasmNewArrayObject )LIROP(ReinterpretCast)LIROP(ReinterpretCastFromI64)LIROP(ReinterpretCastToI64 )LIROP(Rotate)LIROP(RotateI64)LIROP(WasmInterruptCheck)LIROP( TypeOfV)LIROP(TypeOfO)LIROP(TypeOfIsNonPrimitiveV)LIROP(TypeOfIsNonPrimitiveO )LIROP(TypeOfIsPrimitive)LIROP(CreateInlinedArgumentsObject)LIROP (GetInlinedArgument)LIROP(GetInlinedArgumentHole)LIROP(BoxNonStrictThis )LIROP(StackArgT)LIROP(StackArgV)LIROP(CallGeneric)LIROP(CallKnown )LIROP(CallNative)LIROP(CallDOMNative)LIROP(CallClassHook)LIROP (Unreachable)LIROP(UnreachableResultV)LIROP(UnreachableResultT )LIROP(GetDOMProperty)LIROP(GetDOMMemberV)LIROP(GetDOMMemberT )LIROP(SetDOMProperty)LIROP(LoadDOMExpandoValueGuardGeneration )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(StrictConstantCompareInt32AndBranch)LIROP(StrictConstantCompareBooleanAndBranch )LIROP(BitAndAndBranch)LIROP(BitAnd64AndBranch)LIROP(IsNullOrLikeUndefinedV )LIROP(IsNullOrLikeUndefinedT)LIROP(IsNull)LIROP(IsUndefined) LIROP(IsNullOrLikeUndefinedAndBranchV)LIROP(IsNullOrLikeUndefinedAndBranchT )LIROP(IsNullAndBranch)LIROP(IsUndefinedAndBranch)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(ShiftIntPtr)LIROP(ShiftI64 )LIROP(SignExtendInt32)LIROP(SignExtendIntPtr)LIROP(SignExtendInt64 )LIROP(UrshD)LIROP(Return)LIROP(MinMaxI)LIROP(MinMaxIntPtr)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(SignID)LIROP(MathFunctionD)LIROP(MathFunctionF )LIROP(AddI)LIROP(AddI64)LIROP(AddIntPtr)LIROP(SubI)LIROP(SubI64 )LIROP(SubIntPtr)LIROP(MulI64)LIROP(MulIntPtr)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(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(Concat)LIROP( StringIncludesSIMD)LIROP(StringIndexOfSIMD)LIROP(StringStartsWithInline )LIROP(StringEndsWithInline)LIROP(StringToLowerCase)LIROP(CharCodeToLowerCase )LIROP(StringToUpperCase)LIROP(CharCodeToUpperCase)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(BooleanToString)LIROP(IntToString)LIROP(DoubleToString )LIROP(ValueToString)LIROP(PowHalfD)LIROP(NaNToZero)LIROP(OsrEntry )LIROP(OsrValue)LIROP(OsrEnvironmentChain)LIROP(OsrReturnValue )LIROP(OsrArgumentsObject)LIROP(RegExp)LIROP(RegExpHasCaptureGroups )LIROP(GetFirstDollarIndex)LIROP(StringReplace)LIROP(BinaryValueCache )LIROP(BinaryBoolCache)LIROP(Lambda)LIROP(FunctionWithProto)LIROP (GetNextEntryForIterator)LIROP(GuardNumberToIntPtrIndex)LIROP (BoundsCheck)LIROP(BoundsCheckRange)LIROP(BoundsCheckLower)LIROP (SpectreMaskIndex)LIROP(LoadElementV)LIROP(InArray)LIROP(LoadElementHole )LIROP(StoreElementV)LIROP(StoreElementT)LIROP(StoreHoleValueElement )LIROP(StoreElementHoleV)LIROP(StoreElementHoleT)LIROP(ArrayPopShift )LIROP(FrameArgumentsSlice)LIROP(InlineArgumentsSlice)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(TypedArraySubarray )LIROP(TypedArrayFill)LIROP(TypedArrayFill64)LIROP(AtomicIsLockFree )LIROP(CompareExchangeTypedArrayElement)LIROP(AtomicExchangeTypedArrayElement )LIROP(AtomicTypedArrayElementBinop)LIROP(AtomicTypedArrayElementBinopForEffect )LIROP(AtomicLoad64)LIROP(AtomicStore64)LIROP(CompareExchangeTypedArrayElement64 )LIROP(AtomicExchangeTypedArrayElement64)LIROP(AtomicTypedArrayElementBinop64 )LIROP(AtomicTypedArrayElementBinopForEffect64)LIROP(EffectiveAddress3 )LIROP(EffectiveAddress2)LIROP(ClampIToUint8)LIROP(ClampDToUint8 )LIROP(ClampVToUint8)LIROP(LoadFixedSlotV)LIROP(LoadFixedSlotAndAtomize )LIROP(LoadFixedSlotT)LIROP(LoadFixedSlotAndUnbox)LIROP(LoadDynamicSlotAndUnbox )LIROP(LoadElementAndUnbox)LIROP(LoadFixedSlotUnboxAndAtomize )LIROP(LoadDynamicSlotUnboxAndAtomize)LIROP(AddAndStoreSlot)LIROP (AddAndStoreSlotPreserveWrapper)LIROP(StoreFixedSlotV)LIROP(StoreFixedSlotT )LIROP(StoreFixedSlotFromOffsetV)LIROP(StoreFixedSlotFromOffsetT )LIROP(GetPropSuperCache)LIROP(GetPropertyCache)LIROP(LoadDynamicSlotV )LIROP(LoadDynamicSlotAndAtomize)LIROP(StoreDynamicSlotV)LIROP (StoreDynamicSlotT)LIROP(StoreDynamicSlotFromOffsetV)LIROP(StoreDynamicSlotFromOffsetT )LIROP(Floor)LIROP(FloorF)LIROP(Ceil)LIROP(CeilF)LIROP(Round) LIROP(RoundF)LIROP(Trunc)LIROP(TruncF)LIROP(NearbyInt)LIROP(NearbyIntF )LIROP(RoundToDouble)LIROP(RoundToFloat32)LIROP(NewLexicalEnvironmentObject )LIROP(NewClassBodyEnvironmentObject)LIROP(NewVarEnvironmentObject )LIROP(MegamorphicSetElement)LIROP(ObjectToIterator)LIROP(IteratorHasIndicesAndBranch )LIROP(IteratorsMatchAndHaveIndicesAndBranch)LIROP(SetPropertyCache )LIROP(GetIteratorCache)LIROP(IsNoIterAndBranch)LIROP(IsResumingGeneratorAndBranch )LIROP(CloseIterCache)LIROP(Int32ToIntPtr)LIROP(AdjustDataViewLength )LIROP(BooleanToInt64)LIROP(StringToInt64)LIROP(ValueToInt64) LIROP(Uint64ToBigInt)LIROP(PostWriteBarrierO)LIROP(PostWriteBarrierS )LIROP(PostWriteBarrierBI)LIROP(PostWriteBarrierV)LIROP(PostWriteElementBarrierO )LIROP(PostWriteElementBarrierS)LIROP(PostWriteElementBarrierBI )LIROP(PostWriteElementBarrierV)LIROP(GuardObjectIdentity)LIROP (GuardSpecificFunction)LIROP(GuardSpecificSymbol)LIROP(GuardMultipleShapesToOffset )LIROP(GuardProto)LIROP(GuardNullProto)LIROP(ProxyHasProp)LIROP (InCache)LIROP(HasOwnCache)LIROP(CheckPrivateFieldCache)LIROP (InstanceOfO)LIROP(InstanceOfV)LIROP(IsCallableO)LIROP(IsCallableV )LIROP(IsArrayO)LIROP(IsArrayV)LIROP(IsTypedArray)LIROP(IsObjectAndBranch )LIROP(IsNullOrUndefinedAndBranch)LIROP(IsSuspendedGeneratorAndBranch )LIROP(HasClass)LIROP(GuardToClass)LIROP(GuardToFunction)LIROP (WasmSelect)LIROP(WasmSelectI64)LIROP(WasmCompareAndSelect)LIROP (WasmAddOffset)LIROP(WasmAddOffset64)LIROP(WasmBoundsCheck)LIROP (WasmBoundsCheck64)LIROP(WasmBoundsCheckInstanceField)LIROP(WasmBoundsCheckInstanceField64 )LIROP(WasmExtendU32Index)LIROP(WasmWrapU32Index)LIROP(WasmAlignmentCheck )LIROP(WasmAlignmentCheck64)LIROP(WasmLoadInstance)LIROP(WasmLoadInstance64 )LIROP(WasmHeapReg)LIROP(WasmLoad)LIROP(WasmLoadI64)LIROP(WasmStore )LIROP(WasmStoreI64)LIROP(WasmCompareExchangeHeap)LIROP(WasmAtomicExchangeHeap )LIROP(WasmAtomicBinopHeap)LIROP(WasmAtomicBinopHeapForEffect )LIROP(WasmLoadSlot)LIROP(WasmLoadElement)LIROP(WasmLoadSlotI64 )LIROP(WasmLoadElementI64)LIROP(WasmStoreSlot)LIROP(WasmStoreSlotI64 )LIROP(WasmStoreStackResult)LIROP(WasmStoreStackResultI64)LIROP (WasmStoreElement)LIROP(WasmStoreElementI64)LIROP(WasmStoreElementRef )LIROP(WasmLoadTableElement)LIROP(WasmDerivedPointer)LIROP(WasmDerivedIndexPointer )LIROP(WasmStoreRef)LIROP(WasmPostWriteBarrierWholeCell)LIROP (WasmPostWriteBarrierEdgeAtIndex)LIROP(WasmParameter)LIROP(WasmParameterI64 )LIROP(WasmReturn)LIROP(WasmReturnI64)LIROP(WasmReturnVoid)LIROP (WasmStackArg)LIROP(WasmStackArgI64)LIROP(WasmNullConstant)LIROP (WasmCall)LIROP(WasmRegisterResult)LIROP(WasmRegisterPairResult )LIROP(WasmSystemFloatRegisterResult)LIROP(WasmStackResultArea )LIROP(WasmStackResult)LIROP(WasmStackResult64)LIROP(WasmSuspend )LIROP(WasmPrepareResume)LIROP(WasmResume)LIROP(AssertRangeI) LIROP(AssertRangeD)LIROP(AssertRangeF)LIROP(AssertRangeV)LIROP (AssertClass)LIROP(GuardValue)LIROP(GuardFunctionFlags)LIROP( LexicalCheck)LIROP(MemoryBarrier)LIROP(Debugger)LIROP(Generator )LIROP(MaybeExtractAwaitValue)LIROP(ObjectStaticProto)LIROP(GuardTagNotEqual )LIROP(CanonicalizeNaND)LIROP(CanonicalizeNaNF)LIROP(IonToWasmCall )LIROP(IonToWasmCallV)LIROP(IonToWasmCallI64)LIROP(WasmNewI31Ref )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(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(TimeClipCall)LIROP(DivConstantI )LIROP(ModConstantI)LIROP(UDivConstant)LIROP(UModConstant)LIROP (UDiv)LIROP(UMod)LIROP(DivI64)LIROP(ModI64)LIROP(UDivI64)LIROP (UModI64)LIROP(DivPowTwoI64)LIROP(ModPowTwoI64)LIROP(DivConstantI64 )LIROP(ModConstantI64)LIROP(UDivConstantI64)LIROP(UModConstantI64 )LIROP(WasmTruncateToInt64)LIROP(Int64ToFloatingPoint)LIROP(WasmAddSubI128HI64 )LIROP(WasmMulI64WideHI64)LIROP(Callee)LIROP(IsConstructing)LIROP (Throw)LIROP(ThrowWithStack)LIROP(NewArrayDynamicLength)LIROP (NewTypedArrayDynamicLength)LIROP(NewTypedArrayFromArray)LIROP (NewTypedArrayFromArrayBuffer)LIROP(NewMapObjectFromIterable) LIROP(NewSetObjectFromIterable)LIROP(BoundFunctionNumArgs)LIROP (GuardBoundFunctionIsConstructor)LIROP(MutateProto)LIROP(InitPropGetterSetter )LIROP(InitElemGetterSetter)LIROP(Bail)LIROP(EncodeSnapshot)LIROP (StrictConstantCompareInt32)LIROP(StrictConstantCompareBoolean )LIROP(StrictConstantCompareString)LIROP(StrictConstantCompareObject )LIROP(SameValueDouble)LIROP(SameValue)LIROP(AssertShape)LIROP (CreateThis)LIROP(CreateArgumentsObject)LIROP(GetArgumentsObjectArg )LIROP(SetArgumentsObjectArg)LIROP(LoadArgumentsObjectArg)LIROP (LoadArgumentsObjectArgHole)LIROP(InArgumentsObjectArg)LIROP( ArgumentsObjectLength)LIROP(ArrayFromArgumentsObject)LIROP(GuardArgumentsObjectFlags )LIROP(GuardObjectHasSameRealm)LIROP(LoadScriptedProxyHandler )LIROP(CheckScriptedProxyGetResult)LIROP(IdToStringOrSymbol)LIROP (ReturnFromCtor)LIROP(WrapInt64ToInt32)LIROP(ExtendInt32ToInt64 )LIROP(WasmAnyRefFromJSValue)LIROP(WasmAnyRefFromJSObject)LIROP (WasmAnyRefFromJSString)LIROP(WasmAnyRefIsJSString)LIROP(WasmTrapIfAnyRefIsNotJSString )LIROP(WasmAnyRefJSStringLength)LIROP(WasmI31RefGet)LIROP(NonNegativeIntPtrToInt32 )LIROP(IntPtrToDouble)LIROP(TruncateBigIntToInt64)LIROP(Int64ToBigInt )LIROP(Int64ToIntPtr)LIROP(IntPtrToInt64)LIROP(TypeOfName)LIROP (ToAsyncIter)LIROP(ToPropertyKeyCache)LIROP(Random)LIROP(BigIntToIntPtr )LIROP(IntPtrToBigInt)LIROP(Int32ToStringWithBase)LIROP(NumberParseInt )LIROP(DoubleParseInt)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(StringIndexOf )LIROP(StringLastIndexOf)LIROP(StringStartsWith)LIROP(StringEndsWith )LIROP(StringTrimStartIndex)LIROP(StringTrimEndIndex)LIROP(StringSplit )LIROP(ImplicitThis)LIROP(UnaryCache)LIROP(CheckOverRecursed) LIROP(InterruptCheck)LIROP(WasmTrap)LIROP(ThrowRuntimeLexicalError )LIROP(ThrowMsg)LIROP(GlobalDeclInstantiation)LIROP(RegExpMatcher )LIROP(RegExpSearcher)LIROP(RegExpSearcherLastLimit)LIROP(RegExpExecMatch )LIROP(RegExpExecTest)LIROP(Substr)LIROP(ModuleMetadata)LIROP (DynamicImport)LIROP(SetFunName)LIROP(Slots)LIROP(Elements)LIROP (InitializedLength)LIROP(SetInitializedLength)LIROP(ArrayLength )LIROP(SetArrayLength)LIROP(FunctionLength)LIROP(FunctionName )LIROP(ArrayBufferByteLength)LIROP(ArrayBufferViewLength)LIROP (ArrayBufferViewByteOffset)LIROP(ArrayBufferViewElements)LIROP (ResizableTypedArrayLength)LIROP(ResizableDataViewByteLength) LIROP(GrowableSharedArrayBufferByteLength)LIROP(TypedArrayElementSize )LIROP(GuardHasAttachedArrayBuffer)LIROP(GuardResizableArrayBufferViewInBounds )LIROP(GuardResizableArrayBufferViewInBoundsOrDetached)LIROP( TypedArraySet)LIROP(TypedArraySetFromSubarray)LIROP(GuardTypedArraySetOffset )LIROP(ToIntegerIndex)LIROP(KeepAliveObject)LIROP(DebugEnterGCUnsafeRegion )LIROP(DebugLeaveGCUnsafeRegion)LIROP(ArrayPush)LIROP(ArraySlice )LIROP(ArgumentsSlice)LIROP(NormalizeSliceTerm)LIROP(ArrayJoin )LIROP(ObjectKeys)LIROP(LoadFixedSlotFromOffset)LIROP(HomeObjectSuperBase )LIROP(BindNameCache)LIROP(GuardShape)LIROP(HasShape)LIROP(GuardFuse )LIROP(GuardMultipleShapes)LIROP(GuardShapeList)LIROP(GuardShapeListToOffset )LIROP(GuardIsNativeObject)LIROP(GuardGlobalGeneration)LIROP( GuardIsProxy)LIROP(GuardIsNotDOMProxy)LIROP(GuardIsNotProxy)LIROP (ProxyGet)LIROP(ProxyGetByValue)LIROP(ProxySet)LIROP(ProxySetByValue )LIROP(CallSetArrayLength)LIROP(MegamorphicLoadSlot)LIROP(MegamorphicLoadSlotPermissive )LIROP(MegamorphicLoadSlotByValue)LIROP(MegamorphicLoadSlotByValuePermissive )LIROP(MegamorphicStoreSlot)LIROP(MegamorphicHasProp)LIROP(SmallObjectVariableKeyHasProp )LIROP(GuardToArrayBuffer)LIROP(GuardToSharedArrayBuffer)LIROP (GuardIsNotArrayBufferMaybeShared)LIROP(GuardIsNonResizableTypedArray )LIROP(GuardIsResizableTypedArray)LIROP(GuardHasProxyHandler) LIROP(NurseryObject)LIROP(GuardNullOrUndefined)LIROP(GuardIsNotObject )LIROP(GuardFunctionIsNonBuiltinCtor)LIROP(GuardFunctionKind) LIROP(GuardFunctionScript)LIROP(GuardSpecificAtom)LIROP(GuardSpecificInt32 )LIROP(GuardStringToIndex)LIROP(GuardStringToInt32)LIROP(GuardStringToDouble )LIROP(GuardNoDenseElements)LIROP(LoadDynamicSlotFromOffset)LIROP (FunctionEnvironment)LIROP(HomeObject)LIROP(AllocateAndStoreSlot )LIROP(GetNameCache)LIROP(CallGetIntrinsicValue)LIROP(DeleteProperty )LIROP(DeleteElement)LIROP(IteratorLength)LIROP(LoadIteratorElement )LIROP(ValueToIterator)LIROP(LoadSlotByIteratorIndex)LIROP(StoreSlotByIteratorIndex )LIROP(LoadSlotByIteratorIndexIndexed)LIROP(StoreSlotByIteratorIndexIndexed )LIROP(LoadDOMExpandoValue)LIROP(LoadDOMExpandoValueIgnoreGeneration )LIROP(GuardDOMExpandoMissingOrGuardShape)LIROP(StringLength) LIROP(OptimizeSpreadCallCache)LIROP(IteratorMore)LIROP(IteratorEnd )LIROP(OptimizeGetIteratorCache)LIROP(GuardElementNotHole)LIROP (NewPrivateName)LIROP(InstanceOfCache)LIROP(ArgumentsLength)LIROP (GetFrameArgument)LIROP(GetFrameArgumentHole)LIROP(NewTarget) LIROP(Rest)LIROP(AssertCanElidePostWriteBarrier)LIROP(NewNamedLambdaObject )LIROP(IsConstructor)LIROP(IsCrossRealmArrayConstructor)LIROP (IsObject)LIROP(IsSuspendedGenerator)LIROP(IsNullOrUndefined) LIROP(ObjectClassToString)LIROP(CheckReturn)LIROP(CheckThis)LIROP (AsyncResolve)LIROP(AsyncAwait)LIROP(GeneratorResume)LIROP(IsResumingGenerator )LIROP(ResumeFrameArg)LIROP(ClearResumingGeneratorFlag)LIROP( CheckThisReinit)LIROP(CanSkipAwait)LIROP(IncrementWarmUpCounter )LIROP(AtomicPause)LIROP(CheckIsObj)LIROP(CheckObjCoercible)LIROP (CheckClassHeritage)LIROP(DebugCheckSelfHosted)LIROP(IsPackedArray )LIROP(GuardArrayIsPacked)LIROP(GuardElementsArePacked)LIROP( GetPrototypeOf)LIROP(ObjectWithProto)LIROP(BuiltinObject)LIROP (SuperFunction)LIROP(SuperFunctionAndUnbox)LIROP(InitHomeObject )LIROP(IsTypedArrayConstructor)LIROP(LoadValueTag)LIROP(LoadWrapperTarget )LIROP(LoadGetterSetterFunction)LIROP(GuardHasGetterSetter)LIROP (GuardIsExtensible)LIROP(GuardInt32IsNonNegative)LIROP(GuardIntPtrIsNonNegative )LIROP(GuardInt32Range)LIROP(GuardIndexIsNotDenseElement)LIROP (GuardIndexIsValidUpdateOrAdd)LIROP(CallAddOrUpdateSparseElement )LIROP(CallGetSparseElement)LIROP(CallNativeGetElement)LIROP( CallNativeGetElementSuper)LIROP(CallObjectHasSparseElement)LIROP (BigIntAsIntN)LIROP(BigIntAsUintN)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(SetObjectDelete)LIROP(SetObjectAdd)LIROP(SetObjectSize )LIROP(MapObjectHasNonBigInt)LIROP(MapObjectHasBigInt)LIROP(MapObjectHasValue )LIROP(MapObjectHasValueVMCall)LIROP(MapObjectGetNonBigInt)LIROP (MapObjectGetBigInt)LIROP(MapObjectGetValue)LIROP(MapObjectGetValueVMCall )LIROP(MapObjectDelete)LIROP(MapObjectSet)LIROP(MapObjectSize )LIROP(WeakMapGetObject)LIROP(WeakMapHasObject)LIROP(WeakSetHasObject )LIROP(DateFillLocalTimeSlots)LIROP(DateHoursFromSecondsIntoYear )LIROP(DateMinutesFromSecondsIntoYear)LIROP(DateSecondsFromSecondsIntoYear )LIROP(DateNow)LIROP(DateParse)LIROP(TimeClip)LIROP(LocalTimeToUTC )LIROP(YearFromTime)LIROP(MonthFromTime)LIROP(DateFromTime)LIROP (NewDateObject)LIROP(WasmBoundsCheckRange32)LIROP(WasmClampTable64Address )LIROP(WasmFence)LIROP(WasmCallLandingPrePad)LIROP(WasmFindHandler )LIROP(WasmResumeBarrier)LIROP(WasmUnarySimd128)LIROP(AddDisposableResource )LIROP(TakeDisposeCapability) |
| 8855 | # undef LIROP |
| 8856 | #endif |
| 8857 | case LNode::Opcode::Invalid: |
| 8858 | default: |
| 8859 | MOZ_CRASH("Invalid LIR op")do { do { } while (false); MOZ_ReportCrash("" "Invalid LIR op" , "./../../../../js/src/jit/CodeGenerator.cpp", 8859); AnnotateMozCrashReason ("MOZ_CRASH(" "Invalid LIR op" ")"); do { MOZ_CrashSequence(__null , 8859); __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
| 8860 | } |
| 8861 | |
| 8862 | #ifdef DEBUG1 |
| 8863 | if (!counts) { |
| 8864 | emitDebugResultChecks(*iter); |
| 8865 | } |
| 8866 | #endif |
| 8867 | |
| 8868 | // To reduce the blast radius of OOM during codegen, bail out early if we've |
| 8869 | // OOM'ed. |
| 8870 | if (masm.oom()) { |
| 8871 | return false; |
| 8872 | } |
| 8873 | } |
| 8874 | |
| 8875 | return true; |
| 8876 | } |
| 8877 | |
| 8878 | bool CodeGenerator::generateOutOfLineBlocks() { |
| 8879 | AutoCreatedBy acb(masm, "CodeGeneratorShared::generateOutOfLineBlocks"); |
| 8880 | |
| 8881 | // Generate out of line basic blocks. |
| 8882 | // If we are generated some blocks at the end of the function, we need |
| 8883 | // to adjust the frame depth. |
| 8884 | if (!gen->branchHintingEnabled()) { |
| 8885 | return true; |
| 8886 | } |
| 8887 | masm.setFramePushed(frameDepth_); |
| 8888 | |
| 8889 | const bool compilingWasm = gen->compilingWasm(); |
| 8890 | |
| 8891 | for (size_t i = 0; i < graph.numBlocks(); i++) { |
| 8892 | current = graph.getBlock(i); |
| 8893 | |
| 8894 | if (gen->shouldCancel("Generate Code (block loop)")) { |
| 8895 | return false; |
| 8896 | } |
| 8897 | |
| 8898 | if (current->isTrivial()) { |
| 8899 | continue; |
| 8900 | } |
| 8901 | |
| 8902 | // If this block is marked as out of line, we need to generate it now. |
| 8903 | if (!current->isOutOfLine()) { |
| 8904 | continue; |
| 8905 | } |
| 8906 | |
| 8907 | if (!generateBlock(current, i, counts_, compilingWasm)) { |
| 8908 | return false; |
| 8909 | } |
| 8910 | } |
| 8911 | |
| 8912 | return !masm.oom(); |
| 8913 | } |
| 8914 | |
| 8915 | void CodeGenerator::visitNewArrayCallVM(LNewArray* lir) { |
| 8916 | Register objReg = ToRegister(lir->output()); |
| 8917 | |
| 8918 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 8918); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { MOZ_CrashSequence (__null, 8918); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 8919 | saveLive(lir); |
| 8920 | |
| 8921 | JSObject* templateObject = lir->mir()->templateObject(); |
| 8922 | |
| 8923 | if (templateObject) { |
| 8924 | pushArg(ImmGCPtr(templateObject->shape())); |
| 8925 | pushArg(Imm32(lir->mir()->length())); |
| 8926 | |
| 8927 | using Fn = ArrayObject* (*)(JSContext*, uint32_t, Handle<Shape*>); |
| 8928 | callVM<Fn, NewArrayWithShape>(lir); |
| 8929 | } else { |
| 8930 | pushArg(Imm32(GenericObject)); |
| 8931 | pushArg(Imm32(lir->mir()->length())); |
| 8932 | |
| 8933 | using Fn = ArrayObject* (*)(JSContext*, uint32_t, NewObjectKind); |
| 8934 | callVM<Fn, NewArrayOperation>(lir); |
| 8935 | } |
| 8936 | |
| 8937 | masm.storeCallPointerResult(objReg); |
| 8938 | |
| 8939 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 8939); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)" ")"); do { MOZ_CrashSequence(__null, 8939); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8940 | restoreLive(lir); |
| 8941 | } |
| 8942 | |
| 8943 | void CodeGenerator::visitAtan2D(LAtan2D* lir) { |
| 8944 | FloatRegister y = ToFloatRegister(lir->y()); |
| 8945 | FloatRegister x = ToFloatRegister(lir->x()); |
| 8946 | |
| 8947 | using Fn = double (*)(double x, double y); |
| 8948 | masm.setupAlignedABICall(); |
| 8949 | masm.passABIArg(y, ABIType::Float64); |
| 8950 | masm.passABIArg(x, ABIType::Float64); |
| 8951 | masm.callWithABI<Fn, ecmaAtan2>(ABIType::Float64); |
| 8952 | |
| 8953 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 8953); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 8953); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8954 | } |
| 8955 | |
| 8956 | void CodeGenerator::visitHypot(LHypot* lir) { |
| 8957 | uint32_t numArgs = lir->numArgs(); |
| 8958 | masm.setupAlignedABICall(); |
| 8959 | |
| 8960 | for (uint32_t i = 0; i < numArgs; ++i) { |
| 8961 | masm.passABIArg(ToFloatRegister(lir->getOperand(i)), ABIType::Float64); |
| 8962 | } |
| 8963 | |
| 8964 | switch (numArgs) { |
| 8965 | case 2: { |
| 8966 | using Fn = double (*)(double x, double y); |
| 8967 | masm.callWithABI<Fn, ecmaHypot>(ABIType::Float64); |
| 8968 | break; |
| 8969 | } |
| 8970 | case 3: { |
| 8971 | using Fn = double (*)(double x, double y, double z); |
| 8972 | masm.callWithABI<Fn, hypot3>(ABIType::Float64); |
| 8973 | break; |
| 8974 | } |
| 8975 | case 4: { |
| 8976 | using Fn = double (*)(double x, double y, double z, double w); |
| 8977 | masm.callWithABI<Fn, hypot4>(ABIType::Float64); |
| 8978 | break; |
| 8979 | } |
| 8980 | default: |
| 8981 | MOZ_CRASH("Unexpected number of arguments to hypot function.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected number of arguments to hypot function." , "./../../../../js/src/jit/CodeGenerator.cpp", 8981); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected number of arguments to hypot function." ")"); do { MOZ_CrashSequence(__null, 8981); __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 8982 | } |
| 8983 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 8983); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 8983); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8984 | } |
| 8985 | |
| 8986 | void CodeGenerator::visitNewArray(LNewArray* lir) { |
| 8987 | Register objReg = ToRegister(lir->output()); |
| 8988 | Register tempReg = ToRegister(lir->temp0()); |
| 8989 | DebugOnly<uint32_t> length = lir->mir()->length(); |
| 8990 | |
| 8991 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 8991); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "length <= NativeObject::MAX_DENSE_ELEMENTS_COUNT" ")"); do { MOZ_CrashSequence(__null, 8991); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 8992 | |
| 8993 | if (lir->mir()->isVMCall()) { |
| 8994 | visitNewArrayCallVM(lir); |
| 8995 | return; |
| 8996 | } |
| 8997 | |
| 8998 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 8999 | visitNewArrayCallVM(lir); |
| 9000 | masm.jump(ool.rejoin()); |
| 9001 | }); |
| 9002 | addOutOfLineCode(ool, lir->mir()); |
| 9003 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 9004 | #ifdef DEBUG1 |
| 9005 | size_t numInlineElements = gc::GetGCKindSlots(templateObject.getAllocKind()) - |
| 9006 | ObjectElements::VALUES_PER_HEADER; |
| 9007 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 9008); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements" ")"); do { MOZ_CrashSequence(__null, 9008); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 9008 | "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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 9008); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "length <= numInlineElements" ") (" "Inline allocation only supports inline elements" ")"); do { MOZ_CrashSequence(__null, 9008); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9009 | #endif |
| 9010 | masm.createGCObject(objReg, tempReg, templateObject, |
| 9011 | lir->mir()->initialHeap(), ool->entry()); |
| 9012 | |
| 9013 | masm.bind(ool->rejoin()); |
| 9014 | } |
| 9015 | |
| 9016 | void CodeGenerator::visitNewArrayDynamicLength(LNewArrayDynamicLength* lir) { |
| 9017 | Register lengthReg = ToRegister(lir->length()); |
| 9018 | Register objReg = ToRegister(lir->output()); |
| 9019 | Register tempReg = ToRegister(lir->temp0()); |
| 9020 | |
| 9021 | JSObject* templateObject = lir->mir()->templateObject(); |
| 9022 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
| 9023 | |
| 9024 | using Fn = ArrayObject* (*)(JSContext*, Handle<ArrayObject*>, int32_t length, |
| 9025 | gc::AllocSite*); |
| 9026 | OutOfLineCode* ool = oolCallVM<Fn, ArrayConstructorOneArg>( |
| 9027 | lir, ArgList(ImmGCPtr(templateObject), lengthReg, ImmPtr(nullptr)), |
| 9028 | StoreRegisterTo(objReg)); |
| 9029 | |
| 9030 | bool canInline = true; |
| 9031 | size_t inlineLength = 0; |
| 9032 | if (templateObject->as<ArrayObject>().hasFixedElements()) { |
| 9033 | size_t numSlots = |
| 9034 | gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()); |
| 9035 | inlineLength = numSlots - ObjectElements::VALUES_PER_HEADER; |
| 9036 | } else { |
| 9037 | canInline = false; |
| 9038 | } |
| 9039 | |
| 9040 | if (canInline) { |
| 9041 | // Try to do the allocation inline if the template object is big enough |
| 9042 | // for the length in lengthReg. If the length is bigger we could still |
| 9043 | // use the template object and not allocate the elements, but it's more |
| 9044 | // efficient to do a single big allocation than (repeatedly) reallocating |
| 9045 | // the array later on when filling it. |
| 9046 | masm.branch32(Assembler::Above, lengthReg, Imm32(inlineLength), |
| 9047 | ool->entry()); |
| 9048 | |
| 9049 | TemplateObject templateObj(templateObject); |
| 9050 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, |
| 9051 | ool->entry()); |
| 9052 | |
| 9053 | size_t lengthOffset = NativeObject::offsetOfFixedElements() + |
| 9054 | ObjectElements::offsetOfLength(); |
| 9055 | masm.store32(lengthReg, Address(objReg, lengthOffset)); |
| 9056 | } else { |
| 9057 | masm.jump(ool->entry()); |
| 9058 | } |
| 9059 | |
| 9060 | masm.bind(ool->rejoin()); |
| 9061 | } |
| 9062 | |
| 9063 | void CodeGenerator::visitNewIterator(LNewIterator* lir) { |
| 9064 | Register objReg = ToRegister(lir->output()); |
| 9065 | Register tempReg = ToRegister(lir->temp0()); |
| 9066 | |
| 9067 | OutOfLineCode* ool; |
| 9068 | switch (lir->mir()->type()) { |
| 9069 | case MNewIterator::ArrayIterator: { |
| 9070 | using Fn = ArrayIteratorObject* (*)(JSContext*); |
| 9071 | ool = oolCallVM<Fn, NewArrayIterator>(lir, ArgList(), |
| 9072 | StoreRegisterTo(objReg)); |
| 9073 | break; |
| 9074 | } |
| 9075 | case MNewIterator::StringIterator: { |
| 9076 | using Fn = StringIteratorObject* (*)(JSContext*); |
| 9077 | ool = oolCallVM<Fn, NewStringIterator>(lir, ArgList(), |
| 9078 | StoreRegisterTo(objReg)); |
| 9079 | break; |
| 9080 | } |
| 9081 | case MNewIterator::RegExpStringIterator: { |
| 9082 | using Fn = RegExpStringIteratorObject* (*)(JSContext*); |
| 9083 | ool = oolCallVM<Fn, NewRegExpStringIterator>(lir, ArgList(), |
| 9084 | StoreRegisterTo(objReg)); |
| 9085 | break; |
| 9086 | } |
| 9087 | default: |
| 9088 | MOZ_CRASH("unexpected iterator type")do { do { } while (false); MOZ_ReportCrash("" "unexpected iterator type" , "./../../../../js/src/jit/CodeGenerator.cpp", 9088); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected iterator type" ")"); do { MOZ_CrashSequence (__null, 9088); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 9089 | } |
| 9090 | |
| 9091 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 9092 | masm.createGCObject(objReg, tempReg, templateObject, gc::Heap::Default, |
| 9093 | ool->entry()); |
| 9094 | |
| 9095 | masm.bind(ool->rejoin()); |
| 9096 | } |
| 9097 | |
| 9098 | void CodeGenerator::visitNewTypedArrayInline(LNewTypedArrayInline* lir) { |
| 9099 | Register objReg = ToRegister(lir->output()); |
| 9100 | Register tempReg = ToRegister(lir->temp0()); |
| 9101 | |
| 9102 | auto* templateObject = lir->mir()->templateObject(); |
| 9103 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
| 9104 | |
| 9105 | size_t n = templateObject->length(); |
| 9106 | 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" ")" , "./../../../../js/src/jit/CodeGenerator.cpp", 9107); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")"); do { MOZ_CrashSequence(__null, 9107); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 9107 | "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" ")" , "./../../../../js/src/jit/CodeGenerator.cpp", 9107); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")"); do { MOZ_CrashSequence(__null, 9107); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9108 | |
| 9109 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t); |
| 9110 | auto* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
| 9111 | lir, ArgList(ImmGCPtr(templateObject), Imm32(n)), |
| 9112 | StoreRegisterTo(objReg)); |
| 9113 | |
| 9114 | TemplateObject templateObj(templateObject); |
| 9115 | masm.createGCObject(objReg, tempReg, templateObj, initialHeap, ool->entry()); |
| 9116 | |
| 9117 | masm.initTypedArraySlotsInline(objReg, tempReg, templateObject); |
| 9118 | |
| 9119 | masm.bind(ool->rejoin()); |
| 9120 | } |
| 9121 | |
| 9122 | void CodeGenerator::visitNewTypedArray(LNewTypedArray* lir) { |
| 9123 | Register output = ToRegister(lir->output()); |
| 9124 | Register temp1Reg = ToRegister(lir->temp0()); |
| 9125 | Register temp2Reg = ToRegister(lir->temp1()); |
| 9126 | Register lengthReg = ToRegister(lir->temp2()); |
| 9127 | Register temp4Reg = ToRegister(lir->temp3()); |
| 9128 | |
| 9129 | auto* templateObject = lir->mir()->templateObject(); |
| 9130 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
| 9131 | |
| 9132 | size_t n = templateObject->length(); |
| 9133 | 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" ")" , "./../../../../js/src/jit/CodeGenerator.cpp", 9134); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")"); do { MOZ_CrashSequence(__null, 9134); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 9134 | "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" ")" , "./../../../../js/src/jit/CodeGenerator.cpp", 9134); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "n <= (2147483647)" ") (" "Template objects are only created for int32 lengths" ")"); do { MOZ_CrashSequence(__null, 9134); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9135 | |
| 9136 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length); |
| 9137 | OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
| 9138 | lir, ArgList(ImmGCPtr(templateObject), Imm32(n)), |
| 9139 | StoreRegisterTo(output)); |
| 9140 | |
| 9141 | TemplateObject templateObj(templateObject); |
| 9142 | masm.createGCObject(temp4Reg, temp1Reg, templateObj, initialHeap, |
| 9143 | ool->entry()); |
| 9144 | |
| 9145 | masm.move32(Imm32(n), lengthReg); |
| 9146 | |
| 9147 | masm.initTypedArraySlots(temp4Reg, lengthReg, temp1Reg, temp2Reg, |
| 9148 | ool->entry(), templateObject); |
| 9149 | masm.mov(temp4Reg, output); |
| 9150 | |
| 9151 | masm.bind(ool->rejoin()); |
| 9152 | } |
| 9153 | |
| 9154 | void CodeGenerator::visitNewTypedArrayDynamicLength( |
| 9155 | LNewTypedArrayDynamicLength* lir) { |
| 9156 | Register lengthReg = ToRegister(lir->length()); |
| 9157 | Register output = ToRegister(lir->output()); |
| 9158 | Register temp1Reg = ToRegister(lir->temp0()); |
| 9159 | Register temp2Reg = ToRegister(lir->temp1()); |
| 9160 | Register temp3Reg = ToRegister(lir->temp2()); |
| 9161 | |
| 9162 | JSObject* templateObject = lir->mir()->templateObject(); |
| 9163 | gc::Heap initialHeap = lir->mir()->initialHeap(); |
| 9164 | |
| 9165 | auto* ttemplate = &templateObject->as<FixedLengthTypedArrayObject>(); |
| 9166 | |
| 9167 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, int32_t length); |
| 9168 | OutOfLineCode* ool = oolCallVM<Fn, NewTypedArrayWithTemplateAndLength>( |
| 9169 | lir, ArgList(ImmGCPtr(templateObject), lengthReg), |
| 9170 | StoreRegisterTo(output)); |
| 9171 | |
| 9172 | TemplateObject templateObj(templateObject); |
| 9173 | masm.createGCObject(temp3Reg, temp1Reg, templateObj, initialHeap, |
| 9174 | ool->entry()); |
| 9175 | |
| 9176 | masm.initTypedArraySlots(temp3Reg, lengthReg, temp1Reg, temp2Reg, |
| 9177 | ool->entry(), ttemplate); |
| 9178 | masm.mov(temp3Reg, output); |
| 9179 | |
| 9180 | masm.bind(ool->rejoin()); |
| 9181 | } |
| 9182 | |
| 9183 | void CodeGenerator::visitNewTypedArrayFromArray(LNewTypedArrayFromArray* lir) { |
| 9184 | pushArg(ToRegister(lir->array())); |
| 9185 | pushArg(ImmGCPtr(lir->mir()->templateObject())); |
| 9186 | |
| 9187 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject); |
| 9188 | callVM<Fn, js::NewTypedArrayWithTemplateAndArray>(lir); |
| 9189 | } |
| 9190 | |
| 9191 | void CodeGenerator::visitNewTypedArrayFromArrayBuffer( |
| 9192 | LNewTypedArrayFromArrayBuffer* lir) { |
| 9193 | pushArg(ToValue(lir->length())); |
| 9194 | pushArg(ToValue(lir->byteOffset())); |
| 9195 | pushArg(ToRegister(lir->arrayBuffer())); |
| 9196 | pushArg(ImmGCPtr(lir->mir()->templateObject())); |
| 9197 | |
| 9198 | using Fn = TypedArrayObject* (*)(JSContext*, HandleObject, HandleObject, |
| 9199 | HandleValue, HandleValue); |
| 9200 | callVM<Fn, js::NewTypedArrayWithTemplateAndBuffer>(lir); |
| 9201 | } |
| 9202 | |
| 9203 | void CodeGenerator::visitBindFunction(LBindFunction* lir) { |
| 9204 | Register target = ToRegister(lir->target()); |
| 9205 | Register temp1 = ToRegister(lir->temp0()); |
| 9206 | Register temp2 = ToRegister(lir->temp1()); |
| 9207 | |
| 9208 | // Try to allocate a new BoundFunctionObject we can pass to the VM function. |
| 9209 | // If this fails, we set temp1 to nullptr so we do the allocation in C++. |
| 9210 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 9211 | Label allocOk, allocFailed; |
| 9212 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
| 9213 | &allocFailed); |
| 9214 | masm.jump(&allocOk); |
| 9215 | |
| 9216 | masm.bind(&allocFailed); |
| 9217 | masm.movePtr(ImmWord(0), temp1); |
| 9218 | |
| 9219 | masm.bind(&allocOk); |
| 9220 | |
| 9221 | // Set temp2 to the address of the first argument on the stack. |
| 9222 | // Note that the Value slots used for arguments are currently aligned for a |
| 9223 | // JIT call, even though that's not strictly necessary for calling into C++. |
| 9224 | uint32_t argc = lir->mir()->numStackArgs(); |
| 9225 | if (JitStackValueAlignment > 1) { |
| 9226 | argc = AlignBytes(argc, JitStackValueAlignment); |
| 9227 | } |
| 9228 | uint32_t unusedStack = UnusedStackBytesForCall(argc); |
| 9229 | masm.computeEffectiveAddress(Address(masm.getStackPointer(), unusedStack), |
| 9230 | temp2); |
| 9231 | |
| 9232 | pushArg(temp1); |
| 9233 | pushArg(Imm32(lir->mir()->numStackArgs())); |
| 9234 | pushArg(temp2); |
| 9235 | pushArg(target); |
| 9236 | |
| 9237 | using Fn = BoundFunctionObject* (*)(JSContext*, Handle<JSObject*>, Value*, |
| 9238 | uint32_t, Handle<BoundFunctionObject*>); |
| 9239 | callVM<Fn, js::BoundFunctionObject::functionBindImpl>(lir); |
| 9240 | } |
| 9241 | |
| 9242 | void CodeGenerator::visitNewBoundFunction(LNewBoundFunction* lir) { |
| 9243 | Register output = ToRegister(lir->output()); |
| 9244 | Register temp = ToRegister(lir->temp0()); |
| 9245 | |
| 9246 | JSObject* templateObj = lir->mir()->templateObj(); |
| 9247 | |
| 9248 | using Fn = BoundFunctionObject* (*)(JSContext*, Handle<BoundFunctionObject*>); |
| 9249 | OutOfLineCode* ool = oolCallVM<Fn, BoundFunctionObject::createWithTemplate>( |
| 9250 | lir, ArgList(ImmGCPtr(templateObj)), StoreRegisterTo(output)); |
| 9251 | |
| 9252 | TemplateObject templateObject(templateObj); |
| 9253 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 9254 | ool->entry()); |
| 9255 | |
| 9256 | masm.bind(ool->rejoin()); |
| 9257 | } |
| 9258 | |
| 9259 | void CodeGenerator::visitNewObjectVMCall(LNewObject* lir) { |
| 9260 | Register objReg = ToRegister(lir->output()); |
| 9261 | |
| 9262 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 9262); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!lir->isCall()" ")"); do { MOZ_CrashSequence (__null, 9262); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 9263 | saveLive(lir); |
| 9264 | |
| 9265 | JSObject* templateObject = lir->mir()->templateObject(); |
| 9266 | |
| 9267 | // If we're making a new object with a class prototype (that is, an object |
| 9268 | // that derives its class from its prototype instead of being |
| 9269 | // PlainObject::class_'d) from self-hosted code, we need a different init |
| 9270 | // function. |
| 9271 | switch (lir->mir()->mode()) { |
| 9272 | case MNewObject::ObjectLiteral: { |
| 9273 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 9273); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!templateObject" ")"); do { MOZ_CrashSequence (__null, 9273); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 9274 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
| 9275 | pushArg(ImmGCPtr(lir->mir()->block()->info().script())); |
| 9276 | |
| 9277 | using Fn = JSObject* (*)(JSContext*, HandleScript, const jsbytecode* pc); |
| 9278 | callVM<Fn, NewObjectOperation>(lir); |
| 9279 | break; |
| 9280 | } |
| 9281 | case MNewObject::ObjectCreate: { |
| 9282 | pushArg(ImmGCPtr(templateObject)); |
| 9283 | |
| 9284 | using Fn = PlainObject* (*)(JSContext*, Handle<PlainObject*>); |
| 9285 | callVM<Fn, ObjectCreateWithTemplate>(lir); |
| 9286 | break; |
| 9287 | } |
| 9288 | } |
| 9289 | |
| 9290 | masm.storeCallPointerResult(objReg); |
| 9291 | |
| 9292 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 9292); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!lir->safepoint()->liveRegs().has(objReg)" ")"); do { MOZ_CrashSequence(__null, 9292); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9293 | restoreLive(lir); |
| 9294 | } |
| 9295 | |
| 9296 | static bool ShouldInitFixedSlots(MIRGenerator* gen, LNewPlainObject* lir, |
| 9297 | const Shape* shape, uint32_t nfixed) { |
| 9298 | // Look for StoreFixedSlot instructions following an object allocation |
| 9299 | // that write to this object before a GC is triggered or this object is |
| 9300 | // passed to a VM call. If all fixed slots will be initialized, the |
| 9301 | // allocation code doesn't need to set the slots to |undefined|. |
| 9302 | |
| 9303 | if (nfixed == 0) { |
| 9304 | return false; |
| 9305 | } |
| 9306 | |
| 9307 | #ifdef DEBUG1 |
| 9308 | // The bailAfter testing function can trigger a bailout between allocating the |
| 9309 | // object and initializing the slots. |
| 9310 | if (gen->options.ionBailAfterEnabled()) { |
| 9311 | return true; |
| 9312 | } |
| 9313 | #endif |
| 9314 | |
| 9315 | // Keep track of the fixed slots that are initialized. initializedSlots is |
| 9316 | // a bit mask with a bit for each slot. |
| 9317 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 9317); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "nfixed <= NativeObject::MAX_FIXED_SLOTS" ")"); do { MOZ_CrashSequence(__null, 9317); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9318 | static_assert(NativeObject::MAX_FIXED_SLOTS <= 32, |
| 9319 | "Slot bits must fit in 32 bits"); |
| 9320 | uint32_t initializedSlots = 0; |
| 9321 | uint32_t numInitialized = 0; |
| 9322 | |
| 9323 | MInstruction* allocMir = lir->mir(); |
| 9324 | MBasicBlock* block = allocMir->block(); |
| 9325 | |
| 9326 | // Skip the allocation instruction. |
| 9327 | MInstructionIterator iter = block->begin(allocMir); |
| 9328 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 9328); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "*iter == allocMir" ")"); do { MOZ_CrashSequence (__null, 9328); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 9329 | iter++; |
| 9330 | |
| 9331 | // Handle the leading shape guard, if present. |
| 9332 | for (; iter != block->end(); iter++) { |
| 9333 | if (iter->isConstant()) { |
| 9334 | // This instruction won't trigger a GC or read object slots. |
| 9335 | continue; |
| 9336 | } |
| 9337 | if (iter->isGuardShape()) { |
| 9338 | auto* guard = iter->toGuardShape(); |
| 9339 | if (guard->object() != allocMir || guard->shape() != shape) { |
| 9340 | return true; |
| 9341 | } |
| 9342 | allocMir = guard; |
| 9343 | iter++; |
| 9344 | } |
| 9345 | break; |
| 9346 | } |
| 9347 | |
| 9348 | for (; iter != block->end(); iter++) { |
| 9349 | if (iter->isConstant() || iter->isPostWriteBarrier()) { |
| 9350 | // These instructions won't trigger a GC or read object slots. |
| 9351 | continue; |
| 9352 | } |
| 9353 | |
| 9354 | if (iter->isStoreFixedSlot()) { |
| 9355 | MStoreFixedSlot* store = iter->toStoreFixedSlot(); |
| 9356 | if (store->object() != allocMir) { |
| 9357 | return true; |
| 9358 | } |
| 9359 | |
| 9360 | // We may not initialize this object slot on allocation, so the |
| 9361 | // pre-barrier could read uninitialized memory. Simply disable |
| 9362 | // the barrier for this store: the object was just initialized |
| 9363 | // so the barrier is not necessary. |
| 9364 | store->setNeedsBarrier(false); |
| 9365 | |
| 9366 | uint32_t slot = store->slot(); |
| 9367 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 9367); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "slot < nfixed" ")"); do { MOZ_CrashSequence (__null, 9367); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 9368 | if ((initializedSlots & (1 << slot)) == 0) { |
| 9369 | numInitialized++; |
| 9370 | initializedSlots |= (1 << slot); |
| 9371 | |
| 9372 | if (numInitialized == nfixed) { |
| 9373 | // All fixed slots will be initialized. |
| 9374 | MOZ_ASSERT(uint32_t(std::popcount(initializedSlots)) == nfixed)do { static_assert( mozilla::detail::AssertionConditionType< decltype(uint32_t(std::popcount(initializedSlots)) == nfixed) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(uint32_t(std::popcount(initializedSlots)) == nfixed) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("uint32_t(std::popcount(initializedSlots)) == nfixed" , "./../../../../js/src/jit/CodeGenerator.cpp", 9374); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "uint32_t(std::popcount(initializedSlots)) == nfixed" ")"); do { MOZ_CrashSequence(__null, 9374); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9375 | return false; |
| 9376 | } |
| 9377 | } |
| 9378 | continue; |
| 9379 | } |
| 9380 | |
| 9381 | // Unhandled instruction, assume it bails or reads object slots. |
| 9382 | return true; |
| 9383 | } |
| 9384 | |
| 9385 | MOZ_CRASH("Shouldn't get here")do { do { } while (false); MOZ_ReportCrash("" "Shouldn't get here" , "./../../../../js/src/jit/CodeGenerator.cpp", 9385); AnnotateMozCrashReason ("MOZ_CRASH(" "Shouldn't get here" ")"); do { MOZ_CrashSequence (__null, 9385); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 9386 | } |
| 9387 | |
| 9388 | void CodeGenerator::visitNewObject(LNewObject* lir) { |
| 9389 | Register objReg = ToRegister(lir->output()); |
| 9390 | Register tempReg = ToRegister(lir->temp0()); |
| 9391 | |
| 9392 | if (lir->mir()->isVMCall()) { |
| 9393 | visitNewObjectVMCall(lir); |
| 9394 | return; |
| 9395 | } |
| 9396 | |
| 9397 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 9398 | visitNewObjectVMCall(lir); |
| 9399 | masm.jump(ool.rejoin()); |
| 9400 | }); |
| 9401 | addOutOfLineCode(ool, lir->mir()); |
| 9402 | |
| 9403 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 9404 | |
| 9405 | masm.createGCObject(objReg, tempReg, templateObject, |
| 9406 | lir->mir()->initialHeap(), ool->entry()); |
| 9407 | |
| 9408 | masm.bind(ool->rejoin()); |
| 9409 | } |
| 9410 | |
| 9411 | void CodeGenerator::visitNewPlainObject(LNewPlainObject* lir) { |
| 9412 | Register objReg = ToRegister(lir->output()); |
| 9413 | Register temp0Reg = ToRegister(lir->temp0()); |
| 9414 | Register temp1Reg = ToRegister(lir->temp1()); |
| 9415 | Register shapeReg = ToRegister(lir->temp2()); |
| 9416 | |
| 9417 | auto* mir = lir->mir(); |
| 9418 | const Shape* shape = mir->shape(); |
| 9419 | gc::Heap initialHeap = mir->initialHeap(); |
| 9420 | gc::AllocKind allocKind = mir->allocKind(); |
| 9421 | |
| 9422 | using Fn = |
| 9423 | JSObject* (*)(JSContext*, Handle<SharedShape*>, gc::AllocKind, gc::Heap); |
| 9424 | OutOfLineCode* ool = oolCallVM<Fn, NewPlainObjectOptimizedFallback>( |
| 9425 | lir, |
| 9426 | ArgList(ImmGCPtr(shape), Imm32(int32_t(allocKind)), |
| 9427 | Imm32(int32_t(initialHeap))), |
| 9428 | StoreRegisterTo(objReg)); |
| 9429 | |
| 9430 | bool initContents = |
| 9431 | ShouldInitFixedSlots(gen, lir, shape, mir->numFixedSlots()); |
| 9432 | |
| 9433 | masm.movePtr(ImmGCPtr(shape), shapeReg); |
| 9434 | masm.createPlainGCObject( |
| 9435 | objReg, shapeReg, temp0Reg, temp1Reg, mir->numFixedSlots(), |
| 9436 | mir->numDynamicSlots(), allocKind, initialHeap, ool->entry(), |
| 9437 | AllocSiteInput(gc::CatchAllAllocSite::Optimized), initContents); |
| 9438 | |
| 9439 | #ifdef DEBUG1 |
| 9440 | // ShouldInitFixedSlots expects that the leading GuardShape will never fail, |
| 9441 | // so ensure the newly created object has the correct shape. Should the guard |
| 9442 | // ever fail, we may end up with uninitialized fixed slots, which can confuse |
| 9443 | // the GC. |
| 9444 | Label ok; |
| 9445 | masm.branchTestObjShape(Assembler::Equal, objReg, shape, temp0Reg, objReg, |
| 9446 | &ok); |
| 9447 | masm.assumeUnreachable("Newly created object has the correct shape"); |
| 9448 | masm.bind(&ok); |
| 9449 | #endif |
| 9450 | |
| 9451 | masm.bind(ool->rejoin()); |
| 9452 | } |
| 9453 | |
| 9454 | void CodeGenerator::visitNewArrayObject(LNewArrayObject* lir) { |
| 9455 | Register objReg = ToRegister(lir->output()); |
| 9456 | Register temp0Reg = ToRegister(lir->temp0()); |
| 9457 | Register shapeReg = ToRegister(lir->temp1()); |
| 9458 | |
| 9459 | auto* mir = lir->mir(); |
| 9460 | uint32_t arrayLength = mir->length(); |
| 9461 | |
| 9462 | gc::AllocKind allocKind = GuessArrayGCKind(arrayLength); |
| 9463 | MOZ_ASSERT(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(& ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" , "./../../../../js/src/jit/CodeGenerator.cpp", 9464); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" ")"); do { MOZ_CrashSequence(__null, 9464); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 9464 | gc::FinalizeKind::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(& ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" , "./../../../../js/src/jit/CodeGenerator.cpp", 9464); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" ")"); do { MOZ_CrashSequence(__null, 9464); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9465 | MOZ_ASSERT(!IsFinalizedKind(allocKind))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsFinalizedKind(allocKind))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsFinalizedKind(allocKind)) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsFinalizedKind(allocKind)" , "./../../../../js/src/jit/CodeGenerator.cpp", 9465); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!IsFinalizedKind(allocKind)" ")"); do { MOZ_CrashSequence (__null, 9465); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 9466 | |
| 9467 | uint32_t slotCount = GetGCKindSlots(allocKind); |
| 9468 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 9468); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "slotCount >= ObjectElements::VALUES_PER_HEADER" ")"); do { MOZ_CrashSequence(__null, 9468); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9469 | uint32_t arrayCapacity = slotCount - ObjectElements::VALUES_PER_HEADER; |
| 9470 | |
| 9471 | const Shape* shape = mir->shape(); |
| 9472 | |
| 9473 | NewObjectKind objectKind = |
| 9474 | mir->initialHeap() == gc::Heap::Tenured ? TenuredObject : GenericObject; |
| 9475 | |
| 9476 | using Fn = |
| 9477 | ArrayObject* (*)(JSContext*, uint32_t, gc::AllocKind, NewObjectKind); |
| 9478 | OutOfLineCode* ool = oolCallVM<Fn, NewArrayObjectOptimizedFallback>( |
| 9479 | lir, |
| 9480 | ArgList(Imm32(arrayLength), Imm32(int32_t(allocKind)), Imm32(objectKind)), |
| 9481 | StoreRegisterTo(objReg)); |
| 9482 | |
| 9483 | masm.movePtr(ImmGCPtr(shape), shapeReg); |
| 9484 | masm.createArrayWithFixedElements( |
| 9485 | objReg, shapeReg, temp0Reg, InvalidReg, arrayLength, arrayCapacity, 0, 0, |
| 9486 | allocKind, mir->initialHeap(), ool->entry(), |
| 9487 | AllocSiteInput(gc::CatchAllAllocSite::Optimized)); |
| 9488 | masm.bind(ool->rejoin()); |
| 9489 | } |
| 9490 | |
| 9491 | void CodeGenerator::visitNewNamedLambdaObject(LNewNamedLambdaObject* lir) { |
| 9492 | Register objReg = ToRegister(lir->output()); |
| 9493 | Register tempReg = ToRegister(lir->temp0()); |
| 9494 | const CompileInfo& info = lir->mir()->block()->info(); |
| 9495 | gc::Heap heap = lir->mir()->initialHeap(); |
| 9496 | |
| 9497 | using Fn = js::NamedLambdaObject* (*)(JSContext*, HandleFunction, gc::Heap); |
| 9498 | OutOfLineCode* ool = oolCallVM<Fn, NamedLambdaObject::createWithoutEnclosing>( |
| 9499 | lir, ArgList(info.funMaybeLazy(), Imm32(uint32_t(heap))), |
| 9500 | StoreRegisterTo(objReg)); |
| 9501 | |
| 9502 | TemplateObject templateObject(lir->mir()->templateObj()); |
| 9503 | |
| 9504 | masm.createGCObject(objReg, tempReg, templateObject, heap, ool->entry(), |
| 9505 | /* initContents = */ true, |
| 9506 | AllocSiteInput(gc::CatchAllAllocSite::Optimized)); |
| 9507 | |
| 9508 | masm.bind(ool->rejoin()); |
| 9509 | } |
| 9510 | |
| 9511 | void CodeGenerator::visitNewCallObject(LNewCallObject* lir) { |
| 9512 | Register objReg = ToRegister(lir->output()); |
| 9513 | Register tempReg = ToRegister(lir->temp0()); |
| 9514 | |
| 9515 | CallObject* templateObj = lir->mir()->templateObject(); |
| 9516 | gc::Heap heap = lir->mir()->initialHeap(); |
| 9517 | |
| 9518 | // todo: should get a specialized fallback that passes site |
| 9519 | using Fn = CallObject* (*)(JSContext*, Handle<SharedShape*>, gc::Heap); |
| 9520 | OutOfLineCode* ool = oolCallVM<Fn, CallObject::createWithShape>( |
| 9521 | lir, ArgList(ImmGCPtr(templateObj->sharedShape()), Imm32(uint32_t(heap))), |
| 9522 | StoreRegisterTo(objReg)); |
| 9523 | |
| 9524 | // Inline call object creation, using the OOL path only for tricky cases. |
| 9525 | TemplateObject templateObject(templateObj); |
| 9526 | |
| 9527 | masm.createGCObject(objReg, tempReg, templateObject, heap, ool->entry(), |
| 9528 | /* initContents = */ true, |
| 9529 | AllocSiteInput(gc::CatchAllAllocSite::Optimized)); |
| 9530 | |
| 9531 | masm.bind(ool->rejoin()); |
| 9532 | } |
| 9533 | |
| 9534 | void CodeGenerator::visitNewMapObject(LNewMapObject* lir) { |
| 9535 | Register output = ToRegister(lir->output()); |
| 9536 | Register temp = ToRegister(lir->temp0()); |
| 9537 | |
| 9538 | // Note: pass nullptr for |proto| to use |Map.prototype|. |
| 9539 | using Fn = MapObject* (*)(JSContext*, HandleObject); |
| 9540 | auto* ool = oolCallVM<Fn, MapObject::create>(lir, ArgList(ImmPtr(nullptr)), |
| 9541 | StoreRegisterTo(output)); |
| 9542 | |
| 9543 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 9544 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 9545 | ool->entry()); |
| 9546 | masm.bind(ool->rejoin()); |
| 9547 | } |
| 9548 | |
| 9549 | void CodeGenerator::visitNewSetObject(LNewSetObject* lir) { |
| 9550 | Register output = ToRegister(lir->output()); |
| 9551 | Register temp = ToRegister(lir->temp0()); |
| 9552 | |
| 9553 | // Note: pass nullptr for |proto| to use |Set.prototype|. |
| 9554 | using Fn = SetObject* (*)(JSContext*, HandleObject); |
| 9555 | auto* ool = oolCallVM<Fn, SetObject::create>(lir, ArgList(ImmPtr(nullptr)), |
| 9556 | StoreRegisterTo(output)); |
| 9557 | |
| 9558 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 9559 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 9560 | ool->entry()); |
| 9561 | masm.bind(ool->rejoin()); |
| 9562 | } |
| 9563 | |
| 9564 | void CodeGenerator::visitNewMapObjectFromIterable( |
| 9565 | LNewMapObjectFromIterable* lir) { |
| 9566 | ValueOperand iterable = ToValue(lir->iterable()); |
| 9567 | Register output = ToRegister(lir->output()); |
| 9568 | Register temp1 = ToRegister(lir->temp0()); |
| 9569 | Register temp2 = ToRegister(lir->temp1()); |
| 9570 | |
| 9571 | // Allocate a new MapObject. If this fails we pass nullptr for |
| 9572 | // allocatedFromJit. |
| 9573 | Label failedAlloc, vmCall, done; |
| 9574 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 9575 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
| 9576 | &failedAlloc); |
| 9577 | |
| 9578 | // We're done if |iterable| is null or undefined. |
| 9579 | masm.branchIfNotNullOrUndefined(iterable, &vmCall); |
| 9580 | masm.movePtr(temp1, output); |
| 9581 | masm.jump(&done); |
| 9582 | |
| 9583 | masm.bind(&failedAlloc); |
| 9584 | masm.movePtr(ImmPtr(nullptr), temp1); |
| 9585 | |
| 9586 | masm.bind(&vmCall); |
| 9587 | |
| 9588 | pushArg(temp1); // allocatedFromJit |
| 9589 | pushArg(iterable); |
| 9590 | pushArg(ImmPtr(nullptr)); // proto |
| 9591 | |
| 9592 | using Fn = MapObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>, |
| 9593 | Handle<MapObject*>); |
| 9594 | callVM<Fn, MapObject::createFromIterable>(lir); |
| 9595 | |
| 9596 | masm.bind(&done); |
| 9597 | } |
| 9598 | |
| 9599 | void CodeGenerator::visitNewSetObjectFromIterable( |
| 9600 | LNewSetObjectFromIterable* lir) { |
| 9601 | ValueOperand iterable = ToValue(lir->iterable()); |
| 9602 | Register output = ToRegister(lir->output()); |
| 9603 | Register temp1 = ToRegister(lir->temp0()); |
| 9604 | Register temp2 = ToRegister(lir->temp1()); |
| 9605 | |
| 9606 | // Allocate a new SetObject. If this fails we pass nullptr for |
| 9607 | // allocatedFromJit. |
| 9608 | Label failedAlloc, vmCall, done; |
| 9609 | TemplateObject templateObject(lir->mir()->templateObject()); |
| 9610 | masm.createGCObject(temp1, temp2, templateObject, gc::Heap::Default, |
| 9611 | &failedAlloc); |
| 9612 | |
| 9613 | // We're done if |iterable| is null or undefined. |
| 9614 | masm.branchIfNotNullOrUndefined(iterable, &vmCall); |
| 9615 | masm.movePtr(temp1, output); |
| 9616 | masm.jump(&done); |
| 9617 | |
| 9618 | masm.bind(&failedAlloc); |
| 9619 | masm.movePtr(ImmPtr(nullptr), temp1); |
| 9620 | |
| 9621 | masm.bind(&vmCall); |
| 9622 | |
| 9623 | pushArg(temp1); // allocatedFromJit |
| 9624 | pushArg(iterable); |
| 9625 | pushArg(ImmPtr(nullptr)); // proto |
| 9626 | |
| 9627 | using Fn = SetObject* (*)(JSContext*, Handle<JSObject*>, Handle<Value>, |
| 9628 | Handle<SetObject*>); |
| 9629 | callVM<Fn, SetObject::createFromIterable>(lir); |
| 9630 | |
| 9631 | masm.bind(&done); |
| 9632 | } |
| 9633 | |
| 9634 | void CodeGenerator::visitNewStringObject(LNewStringObject* lir) { |
| 9635 | Register input = ToRegister(lir->input()); |
| 9636 | Register output = ToRegister(lir->output()); |
| 9637 | Register temp = ToRegister(lir->temp0()); |
| 9638 | |
| 9639 | StringObject* templateObj = lir->mir()->templateObj(); |
| 9640 | |
| 9641 | using Fn = JSObject* (*)(JSContext*, HandleString); |
| 9642 | OutOfLineCode* ool = oolCallVM<Fn, NewStringObject>(lir, ArgList(input), |
| 9643 | StoreRegisterTo(output)); |
| 9644 | |
| 9645 | TemplateObject templateObject(templateObj); |
| 9646 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 9647 | ool->entry()); |
| 9648 | |
| 9649 | masm.loadStringLength(input, temp); |
| 9650 | |
| 9651 | masm.storeValue(JSVAL_TYPE_STRING, input, |
| 9652 | Address(output, StringObject::offsetOfPrimitiveValue())); |
| 9653 | masm.storeValue(JSVAL_TYPE_INT32, temp, |
| 9654 | Address(output, StringObject::offsetOfLength())); |
| 9655 | |
| 9656 | masm.bind(ool->rejoin()); |
| 9657 | } |
| 9658 | |
| 9659 | void CodeGenerator::visitInitElemGetterSetter(LInitElemGetterSetter* lir) { |
| 9660 | Register obj = ToRegister(lir->object()); |
| 9661 | Register value = ToRegister(lir->value()); |
| 9662 | |
| 9663 | pushArg(value); |
| 9664 | pushArg(ToValue(lir->id())); |
| 9665 | pushArg(obj); |
| 9666 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
| 9667 | |
| 9668 | using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, HandleValue, |
| 9669 | HandleObject); |
| 9670 | callVM<Fn, InitElemGetterSetterOperation>(lir); |
| 9671 | } |
| 9672 | |
| 9673 | void CodeGenerator::visitMutateProto(LMutateProto* lir) { |
| 9674 | Register objReg = ToRegister(lir->object()); |
| 9675 | |
| 9676 | pushArg(ToValue(lir->value())); |
| 9677 | pushArg(objReg); |
| 9678 | |
| 9679 | using Fn = |
| 9680 | bool (*)(JSContext* cx, Handle<PlainObject*> obj, HandleValue value); |
| 9681 | callVM<Fn, MutatePrototype>(lir); |
| 9682 | } |
| 9683 | |
| 9684 | void CodeGenerator::visitInitPropGetterSetter(LInitPropGetterSetter* lir) { |
| 9685 | Register obj = ToRegister(lir->object()); |
| 9686 | Register value = ToRegister(lir->value()); |
| 9687 | |
| 9688 | pushArg(value); |
| 9689 | pushArg(ImmGCPtr(lir->mir()->name())); |
| 9690 | pushArg(obj); |
| 9691 | pushArg(ImmPtr(lir->mir()->resumePoint()->pc())); |
| 9692 | |
| 9693 | using Fn = bool (*)(JSContext*, jsbytecode*, HandleObject, |
| 9694 | Handle<PropertyName*>, HandleObject); |
| 9695 | callVM<Fn, InitPropGetterSetterOperation>(lir); |
| 9696 | } |
| 9697 | |
| 9698 | void CodeGenerator::visitCreateThis(LCreateThis* lir) { |
| 9699 | const LAllocation* callee = lir->callee(); |
| 9700 | const LAllocation* newTarget = lir->newTarget(); |
| 9701 | |
| 9702 | if (newTarget->isConstant()) { |
| 9703 | pushArg(ImmGCPtr(&newTarget->toConstant()->toObject())); |
| 9704 | } else { |
| 9705 | pushArg(ToRegister(newTarget)); |
| 9706 | } |
| 9707 | |
| 9708 | if (callee->isConstant()) { |
| 9709 | pushArg(ImmGCPtr(&callee->toConstant()->toObject())); |
| 9710 | } else { |
| 9711 | pushArg(ToRegister(callee)); |
| 9712 | } |
| 9713 | |
| 9714 | using Fn = bool (*)(JSContext* cx, HandleObject callee, |
| 9715 | HandleObject newTarget, MutableHandleValue rval); |
| 9716 | callVM<Fn, jit::CreateThisFromIon>(lir); |
| 9717 | } |
| 9718 | |
| 9719 | void CodeGenerator::visitCreateArgumentsObject(LCreateArgumentsObject* lir) { |
| 9720 | #ifdef DEBUG1 |
| 9721 | // This should be getting constructed in the first block only, and not any OSR |
| 9722 | // entry blocks. Warp builds it in block 0 except in a generator or async |
| 9723 | // function, where block 0 forks between the fresh call and a resume. |
| 9724 | MBasicBlock* block = lir->mir()->block(); |
| 9725 | JSScript* script = block->info().script(); |
| 9726 | MOZ_ASSERT(block != block->graph().osrBlock())do { static_assert( mozilla::detail::AssertionConditionType< decltype(block != block->graph().osrBlock())>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(! !(block != block->graph().osrBlock()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("block != block->graph().osrBlock()" , "./../../../../js/src/jit/CodeGenerator.cpp", 9726); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "block != block->graph().osrBlock()" ")" ); do { MOZ_CrashSequence(__null, 9726); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9727 | MOZ_ASSERT_IF(!script->isGenerator() && !script->isAsync(), block->id() == 0)do { if (!script->isGenerator() && !script->isAsync ()) { do { static_assert( mozilla::detail::AssertionConditionType <decltype(block->id() == 0)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(block->id() == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("block->id() == 0" , "./../../../../js/src/jit/CodeGenerator.cpp", 9727); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "block->id() == 0" ")"); do { MOZ_CrashSequence (__null, 9727); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false); |
| 9728 | #endif |
| 9729 | |
| 9730 | Register callObj = ToRegister(lir->callObject()); |
| 9731 | Register temp0 = ToRegister(lir->temp0()); |
| 9732 | Label done; |
| 9733 | |
| 9734 | if (ArgumentsObject* templateObj = lir->mir()->templateObject()) { |
| 9735 | Register objTemp = ToRegister(lir->temp1()); |
| 9736 | Register cxTemp = ToRegister(lir->temp2()); |
| 9737 | |
| 9738 | masm.Push(callObj); |
| 9739 | |
| 9740 | // Try to allocate an arguments object. This will leave the reserved |
| 9741 | // slots uninitialized, so it's important we don't GC until we |
| 9742 | // initialize these slots in ArgumentsObject::finishForIonPure. |
| 9743 | Label failure; |
| 9744 | TemplateObject templateObject(templateObj); |
| 9745 | masm.createGCObject(objTemp, temp0, templateObject, gc::Heap::Default, |
| 9746 | &failure, |
| 9747 | /* initContents = */ false); |
| 9748 | |
| 9749 | masm.moveStackPtrTo(temp0); |
| 9750 | masm.addPtr(Imm32(masm.framePushed()), temp0); |
| 9751 | |
| 9752 | using Fn = |
| 9753 | ArgumentsObject* (*)(JSContext * cx, jit::JitFrameLayout * frame, |
| 9754 | JSObject * scopeChain, ArgumentsObject * obj); |
| 9755 | masm.setupAlignedABICall(); |
| 9756 | masm.loadJSContext(cxTemp); |
| 9757 | masm.passABIArg(cxTemp); |
| 9758 | masm.passABIArg(temp0); |
| 9759 | masm.passABIArg(callObj); |
| 9760 | masm.passABIArg(objTemp); |
| 9761 | |
| 9762 | masm.callWithABI<Fn, ArgumentsObject::finishForIonPure>(); |
| 9763 | masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure); |
| 9764 | |
| 9765 | // Discard saved callObj on the stack. |
| 9766 | masm.addToStackPtr(Imm32(sizeof(uintptr_t))); |
| 9767 | masm.jump(&done); |
| 9768 | |
| 9769 | masm.bind(&failure); |
| 9770 | masm.Pop(callObj); |
| 9771 | } |
| 9772 | |
| 9773 | masm.moveStackPtrTo(temp0); |
| 9774 | masm.addPtr(Imm32(frameSize()), temp0); |
| 9775 | |
| 9776 | pushArg(callObj); |
| 9777 | pushArg(temp0); |
| 9778 | |
| 9779 | using Fn = ArgumentsObject* (*)(JSContext*, JitFrameLayout*, HandleObject); |
| 9780 | callVM<Fn, ArgumentsObject::createForIon>(lir); |
| 9781 | |
| 9782 | masm.bind(&done); |
| 9783 | } |
| 9784 | |
| 9785 | void CodeGenerator::visitCreateInlinedArgumentsObject( |
| 9786 | LCreateInlinedArgumentsObject* lir) { |
| 9787 | Register callObj = ToRegister(lir->getCallObject()); |
| 9788 | Register callee = ToRegister(lir->getCallee()); |
| 9789 | Register argsAddress = ToRegister(lir->temp1()); |
| 9790 | Register argsObj = ToRegister(lir->temp2()); |
| 9791 | |
| 9792 | // TODO: Do we have to worry about alignment here? |
| 9793 | |
| 9794 | // Create a contiguous array of values for ArgumentsObject::create |
| 9795 | // by pushing the arguments onto the stack in reverse order. |
| 9796 | uint32_t argc = lir->mir()->numActuals(); |
| 9797 | for (uint32_t i = 0; i < argc; i++) { |
| 9798 | uint32_t argNum = argc - i - 1; |
| 9799 | uint32_t index = LCreateInlinedArgumentsObject::ArgIndex(argNum); |
| 9800 | ConstantOrRegister arg = |
| 9801 | toConstantOrRegister(lir, index, lir->mir()->getArg(argNum)->type()); |
| 9802 | masm.Push(arg); |
| 9803 | } |
| 9804 | masm.moveStackPtrTo(argsAddress); |
| 9805 | |
| 9806 | Label done; |
| 9807 | if (ArgumentsObject* templateObj = lir->mir()->templateObject()) { |
| 9808 | LiveRegisterSet liveRegs; |
| 9809 | liveRegs.add(callObj); |
| 9810 | liveRegs.add(callee); |
| 9811 | |
| 9812 | masm.PushRegsInMask(liveRegs); |
| 9813 | |
| 9814 | // We are free to clobber all registers, as LCreateInlinedArgumentsObject is |
| 9815 | // a call instruction. |
| 9816 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 9817 | allRegs.take(callObj); |
| 9818 | allRegs.take(callee); |
| 9819 | allRegs.take(argsObj); |
| 9820 | allRegs.take(argsAddress); |
| 9821 | |
| 9822 | Register temp3 = allRegs.takeAny(); |
| 9823 | Register temp4 = allRegs.takeAny(); |
| 9824 | |
| 9825 | // Try to allocate an arguments object. This will leave the reserved slots |
| 9826 | // uninitialized, so it's important we don't GC until we initialize these |
| 9827 | // slots in ArgumentsObject::finishForIonPure. |
| 9828 | Label failure; |
| 9829 | TemplateObject templateObject(templateObj); |
| 9830 | masm.createGCObject(argsObj, temp3, templateObject, gc::Heap::Default, |
| 9831 | &failure, |
| 9832 | /* initContents = */ false); |
| 9833 | |
| 9834 | Register numActuals = temp3; |
| 9835 | masm.move32(Imm32(argc), numActuals); |
| 9836 | |
| 9837 | using Fn = ArgumentsObject* (*)(JSContext*, JSObject*, JSFunction*, Value*, |
| 9838 | uint32_t, ArgumentsObject*); |
| 9839 | masm.setupAlignedABICall(); |
| 9840 | masm.loadJSContext(temp4); |
| 9841 | masm.passABIArg(temp4); |
| 9842 | masm.passABIArg(callObj); |
| 9843 | masm.passABIArg(callee); |
| 9844 | masm.passABIArg(argsAddress); |
| 9845 | masm.passABIArg(numActuals); |
| 9846 | masm.passABIArg(argsObj); |
| 9847 | |
| 9848 | masm.callWithABI<Fn, ArgumentsObject::finishInlineForIonPure>(); |
| 9849 | masm.branchTestPtr(Assembler::Zero, ReturnReg, ReturnReg, &failure); |
| 9850 | |
| 9851 | // Discard saved callObj, callee, and values array on the stack. |
| 9852 | masm.addToStackPtr( |
| 9853 | Imm32(MacroAssembler::PushRegsInMaskSizeInBytes(liveRegs) + |
| 9854 | argc * sizeof(Value))); |
| 9855 | masm.jump(&done); |
| 9856 | |
| 9857 | masm.bind(&failure); |
| 9858 | masm.PopRegsInMask(liveRegs); |
| 9859 | |
| 9860 | // Reload argsAddress because it may have been overridden. |
| 9861 | masm.moveStackPtrTo(argsAddress); |
| 9862 | } |
| 9863 | |
| 9864 | pushArg(Imm32(argc)); |
| 9865 | pushArg(callObj); |
| 9866 | pushArg(callee); |
| 9867 | pushArg(argsAddress); |
| 9868 | |
| 9869 | using Fn = ArgumentsObject* (*)(JSContext*, Value*, HandleFunction, |
| 9870 | HandleObject, uint32_t); |
| 9871 | callVM<Fn, ArgumentsObject::createForInlinedIon>(lir); |
| 9872 | |
| 9873 | // Discard the array of values. |
| 9874 | masm.freeStack(argc * sizeof(Value)); |
| 9875 | |
| 9876 | masm.bind(&done); |
| 9877 | } |
| 9878 | |
| 9879 | template <class GetInlinedArgument> |
| 9880 | void CodeGenerator::emitGetInlinedArgument(GetInlinedArgument* lir, |
| 9881 | Register index, |
| 9882 | ValueOperand output) { |
| 9883 | uint32_t numActuals = lir->mir()->numActuals(); |
| 9884 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 9884); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "numActuals <= ArgumentsObject::MaxInlinedArgs" ")"); do { MOZ_CrashSequence(__null, 9884); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 9885 | |
| 9886 | // The index has already been bounds-checked, so the code we |
| 9887 | // generate here should be unreachable. We can end up in this |
| 9888 | // situation in self-hosted code using GetArgument(), or in a |
| 9889 | // monomorphically inlined function if we've inlined some CacheIR |
| 9890 | // that was created for a different caller. |
| 9891 | if (numActuals == 0) { |
| 9892 | masm.assumeUnreachable("LGetInlinedArgument: invalid index"); |
| 9893 | return; |
| 9894 | } |
| 9895 | |
| 9896 | // Check the first n-1 possible indices. |
| 9897 | Label done; |
| 9898 | for (uint32_t i = 0; i < numActuals - 1; i++) { |
| 9899 | Label skip; |
| 9900 | ConstantOrRegister arg = toConstantOrRegister( |
| 9901 | lir, GetInlinedArgument::ArgIndex(i), lir->mir()->getArg(i)->type()); |
| 9902 | masm.branch32(Assembler::NotEqual, index, Imm32(i), &skip); |
| 9903 | masm.moveValue(arg, output); |
| 9904 | |
| 9905 | masm.jump(&done); |
| 9906 | masm.bind(&skip); |
| 9907 | } |
| 9908 | |
| 9909 | #ifdef DEBUG1 |
| 9910 | Label skip; |
| 9911 | masm.branch32(Assembler::Equal, index, Imm32(numActuals - 1), &skip); |
| 9912 | masm.assumeUnreachable("LGetInlinedArgument: invalid index"); |
| 9913 | masm.bind(&skip); |
| 9914 | #endif |
| 9915 | |
| 9916 | // The index has already been bounds-checked, so load the last argument. |
| 9917 | uint32_t lastIdx = numActuals - 1; |
| 9918 | ConstantOrRegister arg = |
| 9919 | toConstantOrRegister(lir, GetInlinedArgument::ArgIndex(lastIdx), |
| 9920 | lir->mir()->getArg(lastIdx)->type()); |
| 9921 | masm.moveValue(arg, output); |
| 9922 | masm.bind(&done); |
| 9923 | } |
| 9924 | |
| 9925 | void CodeGenerator::visitGetInlinedArgument(LGetInlinedArgument* lir) { |
| 9926 | Register index = ToRegister(lir->getIndex()); |
| 9927 | ValueOperand output = ToOutValue(lir); |
| 9928 | |
| 9929 | emitGetInlinedArgument(lir, index, output); |
| 9930 | } |
| 9931 | |
| 9932 | void CodeGenerator::visitGetInlinedArgumentHole(LGetInlinedArgumentHole* lir) { |
| 9933 | Register index = ToRegister(lir->getIndex()); |
| 9934 | ValueOperand output = ToOutValue(lir); |
| 9935 | |
| 9936 | uint32_t numActuals = lir->mir()->numActuals(); |
| 9937 | |
| 9938 | if (numActuals == 0) { |
| 9939 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 9940 | masm.moveValue(UndefinedValue(), output); |
| 9941 | return; |
| 9942 | } |
| 9943 | |
| 9944 | Label outOfBounds, done; |
| 9945 | masm.branch32(Assembler::AboveOrEqual, index, Imm32(numActuals), |
| 9946 | &outOfBounds); |
| 9947 | |
| 9948 | emitGetInlinedArgument(lir, index, output); |
| 9949 | masm.jump(&done); |
| 9950 | |
| 9951 | masm.bind(&outOfBounds); |
| 9952 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 9953 | masm.moveValue(UndefinedValue(), output); |
| 9954 | |
| 9955 | masm.bind(&done); |
| 9956 | } |
| 9957 | |
| 9958 | void CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg* lir) { |
| 9959 | Register temp = ToRegister(lir->temp0()); |
| 9960 | Register argsObj = ToRegister(lir->argsObject()); |
| 9961 | ValueOperand out = ToOutValue(lir); |
| 9962 | |
| 9963 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
| 9964 | temp); |
| 9965 | Address argAddr(temp, ArgumentsData::offsetOfArgs() + |
| 9966 | lir->mir()->argno() * sizeof(Value)); |
| 9967 | masm.loadValue(argAddr, out); |
| 9968 | #ifdef DEBUG1 |
| 9969 | Label success; |
| 9970 | masm.branchTestMagic(Assembler::NotEqual, out, &success); |
| 9971 | masm.assumeUnreachable( |
| 9972 | "Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); |
| 9973 | masm.bind(&success); |
| 9974 | #endif |
| 9975 | } |
| 9976 | |
| 9977 | void CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg* lir) { |
| 9978 | Register temp = ToRegister(lir->temp0()); |
| 9979 | Register argsObj = ToRegister(lir->argsObject()); |
| 9980 | ValueOperand value = ToValue(lir->value()); |
| 9981 | |
| 9982 | masm.loadPrivate(Address(argsObj, ArgumentsObject::getDataSlotOffset()), |
| 9983 | temp); |
| 9984 | Address argAddr(temp, ArgumentsData::offsetOfArgs() + |
| 9985 | lir->mir()->argno() * sizeof(Value)); |
| 9986 | emitPreBarrier(argAddr); |
| 9987 | #ifdef DEBUG1 |
| 9988 | Label success; |
| 9989 | masm.branchTestMagic(Assembler::NotEqual, argAddr, &success); |
| 9990 | masm.assumeUnreachable( |
| 9991 | "Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); |
| 9992 | masm.bind(&success); |
| 9993 | #endif |
| 9994 | masm.storeValue(value, argAddr); |
| 9995 | } |
| 9996 | |
| 9997 | void CodeGenerator::visitLoadArgumentsObjectArg(LLoadArgumentsObjectArg* lir) { |
| 9998 | Register temp = ToRegister(lir->temp0()); |
| 9999 | Register argsObj = ToRegister(lir->argsObject()); |
| 10000 | Register index = ToRegister(lir->index()); |
| 10001 | ValueOperand out = ToOutValue(lir); |
| 10002 | |
| 10003 | Label bail; |
| 10004 | masm.loadArgumentsObjectElement(argsObj, index, out, temp, &bail); |
| 10005 | bailoutFrom(&bail, lir->snapshot()); |
| 10006 | } |
| 10007 | |
| 10008 | void CodeGenerator::visitLoadArgumentsObjectArgHole( |
| 10009 | LLoadArgumentsObjectArgHole* lir) { |
| 10010 | Register temp = ToRegister(lir->temp0()); |
| 10011 | Register argsObj = ToRegister(lir->argsObject()); |
| 10012 | Register index = ToRegister(lir->index()); |
| 10013 | ValueOperand out = ToOutValue(lir); |
| 10014 | |
| 10015 | Label bail; |
| 10016 | masm.loadArgumentsObjectElementHole(argsObj, index, out, temp, &bail); |
| 10017 | bailoutFrom(&bail, lir->snapshot()); |
| 10018 | } |
| 10019 | |
| 10020 | void CodeGenerator::visitInArgumentsObjectArg(LInArgumentsObjectArg* lir) { |
| 10021 | Register temp = ToRegister(lir->temp0()); |
| 10022 | Register argsObj = ToRegister(lir->argsObject()); |
| 10023 | Register index = ToRegister(lir->index()); |
| 10024 | Register out = ToRegister(lir->output()); |
| 10025 | |
| 10026 | Label bail; |
| 10027 | masm.loadArgumentsObjectElementExists(argsObj, index, out, temp, &bail); |
| 10028 | bailoutFrom(&bail, lir->snapshot()); |
| 10029 | } |
| 10030 | |
| 10031 | void CodeGenerator::visitArgumentsObjectLength(LArgumentsObjectLength* lir) { |
| 10032 | Register argsObj = ToRegister(lir->argsObject()); |
| 10033 | Register out = ToRegister(lir->output()); |
| 10034 | |
| 10035 | Label bail; |
| 10036 | masm.loadArgumentsObjectLength(argsObj, out, &bail); |
| 10037 | bailoutFrom(&bail, lir->snapshot()); |
| 10038 | } |
| 10039 | |
| 10040 | void CodeGenerator::visitArrayFromArgumentsObject( |
| 10041 | LArrayFromArgumentsObject* lir) { |
| 10042 | pushArg(ToRegister(lir->argsObject())); |
| 10043 | |
| 10044 | using Fn = ArrayObject* (*)(JSContext*, Handle<ArgumentsObject*>); |
| 10045 | callVM<Fn, js::ArrayFromArgumentsObject>(lir); |
| 10046 | } |
| 10047 | |
| 10048 | void CodeGenerator::visitGuardArgumentsObjectFlags( |
| 10049 | LGuardArgumentsObjectFlags* lir) { |
| 10050 | Register argsObj = ToRegister(lir->argsObject()); |
| 10051 | Register temp = ToRegister(lir->temp0()); |
| 10052 | |
| 10053 | Label bail; |
| 10054 | masm.branchTestArgumentsObjectFlags(argsObj, temp, lir->mir()->flags(), |
| 10055 | Assembler::NonZero, &bail); |
| 10056 | bailoutFrom(&bail, lir->snapshot()); |
| 10057 | } |
| 10058 | |
| 10059 | void CodeGenerator::visitGuardObjectHasSameRealm( |
| 10060 | LGuardObjectHasSameRealm* lir) { |
| 10061 | Register obj = ToRegister(lir->object()); |
| 10062 | Register temp = ToRegister(lir->temp0()); |
| 10063 | |
| 10064 | Label bail; |
| 10065 | masm.guardObjectHasSameRealm(obj, temp, &bail); |
| 10066 | bailoutFrom(&bail, lir->snapshot()); |
| 10067 | } |
| 10068 | |
| 10069 | void CodeGenerator::visitBoundFunctionNumArgs(LBoundFunctionNumArgs* lir) { |
| 10070 | Register obj = ToRegister(lir->object()); |
| 10071 | Register output = ToRegister(lir->output()); |
| 10072 | |
| 10073 | masm.unboxInt32(Address(obj, BoundFunctionObject::offsetOfFlagsSlot()), |
| 10074 | output); |
| 10075 | masm.rshift32(Imm32(BoundFunctionObject::NumBoundArgsShift), output); |
| 10076 | } |
| 10077 | |
| 10078 | void CodeGenerator::visitGuardBoundFunctionIsConstructor( |
| 10079 | LGuardBoundFunctionIsConstructor* lir) { |
| 10080 | Register obj = ToRegister(lir->object()); |
| 10081 | |
| 10082 | Label bail; |
| 10083 | Address flagsSlot(obj, BoundFunctionObject::offsetOfFlagsSlot()); |
| 10084 | masm.branchTest32(Assembler::Zero, flagsSlot, |
| 10085 | Imm32(BoundFunctionObject::IsConstructorFlag), &bail); |
| 10086 | bailoutFrom(&bail, lir->snapshot()); |
| 10087 | } |
| 10088 | |
| 10089 | void CodeGenerator::visitReturnFromCtor(LReturnFromCtor* lir) { |
| 10090 | ValueOperand value = ToValue(lir->value()); |
| 10091 | Register obj = ToRegister(lir->object()); |
| 10092 | Register output = ToRegister(lir->output()); |
| 10093 | |
| 10094 | Label valueIsObject, end; |
| 10095 | |
| 10096 | masm.branchTestObject(Assembler::Equal, value, &valueIsObject); |
| 10097 | |
| 10098 | // Value is not an object. Return that other object. |
| 10099 | masm.movePtr(obj, output); |
| 10100 | masm.jump(&end); |
| 10101 | |
| 10102 | // Value is an object. Return unbox(Value). |
| 10103 | masm.bind(&valueIsObject); |
| 10104 | Register payload = masm.extractObject(value, output); |
| 10105 | if (payload != output) { |
| 10106 | masm.movePtr(payload, output); |
| 10107 | } |
| 10108 | |
| 10109 | masm.bind(&end); |
| 10110 | } |
| 10111 | |
| 10112 | void CodeGenerator::visitBoxNonStrictThis(LBoxNonStrictThis* lir) { |
| 10113 | ValueOperand value = ToValue(lir->value()); |
| 10114 | Register output = ToRegister(lir->output()); |
| 10115 | |
| 10116 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 10117 | Label notNullOrUndefined; |
| 10118 | { |
| 10119 | Label isNullOrUndefined; |
| 10120 | ScratchTagScope tag(masm, value); |
| 10121 | masm.splitTagForTest(value, tag); |
| 10122 | masm.branchTestUndefined(Assembler::Equal, tag, &isNullOrUndefined); |
| 10123 | masm.branchTestNull(Assembler::NotEqual, tag, ¬NullOrUndefined); |
| 10124 | masm.bind(&isNullOrUndefined); |
| 10125 | masm.movePtr(ImmGCPtr(lir->mir()->globalThis()), output); |
| 10126 | masm.jump(ool.rejoin()); |
| 10127 | } |
| 10128 | |
| 10129 | masm.bind(¬NullOrUndefined); |
| 10130 | |
| 10131 | saveLive(lir); |
| 10132 | |
| 10133 | pushArg(value); |
| 10134 | using Fn = JSObject* (*)(JSContext*, HandleValue); |
| 10135 | callVM<Fn, BoxNonStrictThis>(lir); |
| 10136 | |
| 10137 | StoreRegisterTo(output).generate(this); |
| 10138 | restoreLiveIgnore(lir, StoreRegisterTo(output).clobbered()); |
| 10139 | |
| 10140 | masm.jump(ool.rejoin()); |
| 10141 | }); |
| 10142 | addOutOfLineCode(ool, lir->mir()); |
| 10143 | |
| 10144 | masm.fallibleUnboxObject(value, output, ool->entry()); |
| 10145 | masm.bind(ool->rejoin()); |
| 10146 | } |
| 10147 | |
| 10148 | void CodeGenerator::visitImplicitThis(LImplicitThis* lir) { |
| 10149 | Register env = ToRegister(lir->env()); |
| 10150 | ValueOperand output = ToOutValue(lir); |
| 10151 | |
| 10152 | using Fn = void (*)(JSContext*, HandleObject, MutableHandleValue); |
| 10153 | auto* ool = oolCallVM<Fn, ImplicitThisOperation>(lir, ArgList(env), |
| 10154 | StoreValueTo(output)); |
| 10155 | |
| 10156 | masm.computeImplicitThis(env, output, ool->entry()); |
| 10157 | masm.bind(ool->rejoin()); |
| 10158 | } |
| 10159 | |
| 10160 | void CodeGenerator::visitArrayLength(LArrayLength* lir) { |
| 10161 | Register elements = ToRegister(lir->elements()); |
| 10162 | Register output = ToRegister(lir->output()); |
| 10163 | |
| 10164 | Address length(elements, ObjectElements::offsetOfLength()); |
| 10165 | masm.load32(length, output); |
| 10166 | |
| 10167 | bool intact = hasSeenArrayExceedsInt32LengthFuseIntactAndDependencyNoted(); |
| 10168 | |
| 10169 | if (intact) { |
| 10170 | #ifdef DEBUG1 |
| 10171 | Label done; |
| 10172 | masm.branchTest32(Assembler::NotSigned, output, output, &done); |
| 10173 | masm.assumeUnreachable("Unexpected array with length > INT32_MAX"); |
| 10174 | masm.bind(&done); |
| 10175 | #endif |
| 10176 | } else { |
| 10177 | // Bail out if the length doesn't fit in int32. |
| 10178 | bailoutTest32(Assembler::Signed, output, output, lir->snapshot()); |
| 10179 | } |
| 10180 | } |
| 10181 | |
| 10182 | void CodeGenerator::visitSetArrayLength(LSetArrayLength* lir) { |
| 10183 | Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); |
| 10184 | masm.store32(Imm32(lir->mir()->length()), length); |
| 10185 | } |
| 10186 | |
| 10187 | void CodeGenerator::visitFunctionLength(LFunctionLength* lir) { |
| 10188 | Register function = ToRegister(lir->function()); |
| 10189 | Register output = ToRegister(lir->output()); |
| 10190 | |
| 10191 | Label bail; |
| 10192 | |
| 10193 | // Get the JSFunction flags. |
| 10194 | masm.load32(Address(function, JSFunction::offsetOfFlagsAndArgCount()), |
| 10195 | output); |
| 10196 | |
| 10197 | // Functions with a SelfHostedLazyScript must be compiled with the slow-path |
| 10198 | // before the function length is known. If the length was previously resolved, |
| 10199 | // the length property may be shadowed. |
| 10200 | masm.branchTest32( |
| 10201 | Assembler::NonZero, output, |
| 10202 | Imm32(FunctionFlags::SELFHOSTLAZY | FunctionFlags::RESOLVED_LENGTH), |
| 10203 | &bail); |
| 10204 | |
| 10205 | masm.loadFunctionLength(function, output, output, &bail); |
| 10206 | |
| 10207 | bailoutFrom(&bail, lir->snapshot()); |
| 10208 | } |
| 10209 | |
| 10210 | void CodeGenerator::visitFunctionName(LFunctionName* lir) { |
| 10211 | Register function = ToRegister(lir->function()); |
| 10212 | Register output = ToRegister(lir->output()); |
| 10213 | |
| 10214 | Label bail; |
| 10215 | |
| 10216 | const JSAtomState& names = gen->runtime->names(); |
| 10217 | masm.loadFunctionName(function, output, ImmGCPtr(names.empty_), &bail); |
| 10218 | |
| 10219 | bailoutFrom(&bail, lir->snapshot()); |
| 10220 | } |
| 10221 | |
| 10222 | template <class TableObject> |
| 10223 | static void TableIteratorLoadEntry(MacroAssembler&, Register, Register, |
| 10224 | Register); |
| 10225 | |
| 10226 | template <> |
| 10227 | void TableIteratorLoadEntry<MapObject>(MacroAssembler& masm, Register iter, |
| 10228 | Register i, Register front) { |
| 10229 | masm.unboxObject(Address(iter, MapIteratorObject::offsetOfTarget()), front); |
| 10230 | masm.loadPrivate(Address(front, MapObject::offsetOfData()), front); |
| 10231 | |
| 10232 | static_assert(MapObject::Table::offsetOfImplDataElement() == 0, |
| 10233 | "offsetof(Data, element) is 0"); |
| 10234 | static_assert(MapObject::Table::sizeofImplData() == 24, "sizeof(Data) is 24"); |
| 10235 | masm.mulBy3(i, i); |
| 10236 | masm.lshiftPtr(Imm32(3), i); |
| 10237 | masm.addPtr(i, front); |
| 10238 | } |
| 10239 | |
| 10240 | template <> |
| 10241 | void TableIteratorLoadEntry<SetObject>(MacroAssembler& masm, Register iter, |
| 10242 | Register i, Register front) { |
| 10243 | masm.unboxObject(Address(iter, SetIteratorObject::offsetOfTarget()), front); |
| 10244 | masm.loadPrivate(Address(front, SetObject::offsetOfData()), front); |
| 10245 | |
| 10246 | static_assert(SetObject::Table::offsetOfImplDataElement() == 0, |
| 10247 | "offsetof(Data, element) is 0"); |
| 10248 | static_assert(SetObject::Table::sizeofImplData() == 16, "sizeof(Data) is 16"); |
| 10249 | masm.lshiftPtr(Imm32(4), i); |
| 10250 | masm.addPtr(i, front); |
| 10251 | } |
| 10252 | |
| 10253 | template <class TableObject> |
| 10254 | static void TableIteratorAdvance(MacroAssembler& masm, Register iter, |
| 10255 | Register front, Register dataLength, |
| 10256 | Register temp) { |
| 10257 | Register i = temp; |
| 10258 | |
| 10259 | // Note: |count| and |index| are stored as PrivateUint32Value. We use add32 |
| 10260 | // and store32 to change the payload. |
| 10261 | masm.add32(Imm32(1), Address(iter, TableIteratorObject::offsetOfCount())); |
| 10262 | |
| 10263 | masm.unboxInt32(Address(iter, TableIteratorObject::offsetOfIndex()), i); |
| 10264 | |
| 10265 | Label done, seek; |
| 10266 | masm.bind(&seek); |
| 10267 | masm.add32(Imm32(1), i); |
| 10268 | masm.branch32(Assembler::AboveOrEqual, i, dataLength, &done); |
| 10269 | |
| 10270 | // We can add sizeof(Data) to |front| to select the next element, because |
| 10271 | // |front| and |mapOrSetObject.data[i]| point to the same location. |
| 10272 | static_assert(TableObject::Table::offsetOfImplDataElement() == 0, |
| 10273 | "offsetof(Data, element) is 0"); |
| 10274 | masm.addPtr(Imm32(TableObject::Table::sizeofImplData()), front); |
| 10275 | |
| 10276 | masm.branchTestMagic(Assembler::Equal, |
| 10277 | Address(front, TableObject::Table::offsetOfEntryKey()), |
| 10278 | JS_HASH_KEY_EMPTY, &seek); |
| 10279 | |
| 10280 | masm.bind(&done); |
| 10281 | masm.store32(i, Address(iter, TableIteratorObject::offsetOfIndex())); |
| 10282 | } |
| 10283 | |
| 10284 | // Corresponds to TableIteratorObject::finish. |
| 10285 | static void TableIteratorFinish(MacroAssembler& masm, Register iter, |
| 10286 | Register temp0, Register temp1) { |
| 10287 | Register next = temp0; |
| 10288 | Register prevp = temp1; |
| 10289 | masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfNext()), next); |
| 10290 | masm.loadPrivate(Address(iter, TableIteratorObject::offsetOfPrevPtr()), |
| 10291 | prevp); |
| 10292 | masm.storePtr(next, Address(prevp, 0)); |
| 10293 | |
| 10294 | Label hasNoNext; |
| 10295 | masm.branchTestPtr(Assembler::Zero, next, next, &hasNoNext); |
| 10296 | masm.storePrivateValue(prevp, |
| 10297 | Address(next, TableIteratorObject::offsetOfPrevPtr())); |
| 10298 | masm.bind(&hasNoNext); |
| 10299 | |
| 10300 | // Mark iterator inactive. |
| 10301 | Address targetAddr(iter, TableIteratorObject::offsetOfTarget()); |
| 10302 | masm.guardedCallPreBarrier(targetAddr, MIRType::Value); |
| 10303 | masm.storeValue(UndefinedValue(), targetAddr); |
| 10304 | } |
| 10305 | |
| 10306 | template <> |
| 10307 | void CodeGenerator::emitLoadIteratorValues<MapObject>(Register result, |
| 10308 | Register temp, |
| 10309 | Register front) { |
| 10310 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
| 10311 | |
| 10312 | Address keyAddress(front, MapObject::Table::Entry::offsetOfKey()); |
| 10313 | Address valueAddress(front, MapObject::Table::Entry::offsetOfValue()); |
| 10314 | Address keyElemAddress(result, elementsOffset); |
| 10315 | Address valueElemAddress(result, elementsOffset + sizeof(Value)); |
| 10316 | masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value); |
| 10317 | masm.guardedCallPreBarrier(valueElemAddress, MIRType::Value); |
| 10318 | masm.storeValue(keyAddress, keyElemAddress, temp); |
| 10319 | masm.storeValue(valueAddress, valueElemAddress, temp); |
| 10320 | |
| 10321 | Label emitBarrier, skipBarrier; |
| 10322 | masm.branchValueIsNurseryCell(Assembler::Equal, keyAddress, temp, |
| 10323 | &emitBarrier); |
| 10324 | masm.branchValueIsNurseryCell(Assembler::NotEqual, valueAddress, temp, |
| 10325 | &skipBarrier); |
| 10326 | { |
| 10327 | masm.bind(&emitBarrier); |
| 10328 | saveVolatile(temp); |
| 10329 | emitPostWriteBarrier(result); |
| 10330 | restoreVolatile(temp); |
| 10331 | } |
| 10332 | masm.bind(&skipBarrier); |
| 10333 | } |
| 10334 | |
| 10335 | template <> |
| 10336 | void CodeGenerator::emitLoadIteratorValues<SetObject>(Register result, |
| 10337 | Register temp, |
| 10338 | Register front) { |
| 10339 | size_t elementsOffset = NativeObject::offsetOfFixedElements(); |
| 10340 | |
| 10341 | Address keyAddress(front, SetObject::Table::offsetOfEntryKey()); |
| 10342 | Address keyElemAddress(result, elementsOffset); |
| 10343 | masm.guardedCallPreBarrier(keyElemAddress, MIRType::Value); |
| 10344 | masm.storeValue(keyAddress, keyElemAddress, temp); |
| 10345 | |
| 10346 | Label skipBarrier; |
| 10347 | masm.branchValueIsNurseryCell(Assembler::NotEqual, keyAddress, temp, |
| 10348 | &skipBarrier); |
| 10349 | { |
| 10350 | saveVolatile(temp); |
| 10351 | emitPostWriteBarrier(result); |
| 10352 | restoreVolatile(temp); |
| 10353 | } |
| 10354 | masm.bind(&skipBarrier); |
| 10355 | } |
| 10356 | |
| 10357 | template <class IteratorObject, class TableObject> |
| 10358 | void CodeGenerator::emitGetNextEntryForIterator(LGetNextEntryForIterator* lir) { |
| 10359 | Register iter = ToRegister(lir->iter()); |
| 10360 | Register result = ToRegister(lir->result()); |
| 10361 | Register temp = ToRegister(lir->temp0()); |
| 10362 | Register dataLength = ToRegister(lir->temp1()); |
| 10363 | Register front = ToRegister(lir->temp2()); |
| 10364 | Register output = ToRegister(lir->output()); |
| 10365 | |
| 10366 | #ifdef DEBUG1 |
| 10367 | // Self-hosted code is responsible for ensuring GetNextEntryForIterator is |
| 10368 | // only called with the correct iterator class. Assert here all self- |
| 10369 | // hosted callers of GetNextEntryForIterator perform this class check. |
| 10370 | // No Spectre mitigations are needed because this is DEBUG-only code. |
| 10371 | Label success; |
| 10372 | masm.branchTestObjClassNoSpectreMitigations( |
| 10373 | Assembler::Equal, iter, &IteratorObject::class_, temp, &success); |
| 10374 | masm.assumeUnreachable("Iterator object should have the correct class."); |
| 10375 | masm.bind(&success); |
| 10376 | #endif |
| 10377 | |
| 10378 | // If the iterator has no target, it's already done. |
| 10379 | // See TableIteratorObject::isActive. |
| 10380 | Label iterAlreadyDone, iterDone, done; |
| 10381 | masm.branchTestUndefined(Assembler::Equal, |
| 10382 | Address(iter, IteratorObject::offsetOfTarget()), |
| 10383 | &iterAlreadyDone); |
| 10384 | |
| 10385 | // Load |iter->index| in |temp| and |iter->target->dataLength| in |
| 10386 | // |dataLength|. Both values are stored as PrivateUint32Value. |
| 10387 | masm.unboxInt32(Address(iter, IteratorObject::offsetOfIndex()), temp); |
| 10388 | masm.unboxObject(Address(iter, IteratorObject::offsetOfTarget()), dataLength); |
| 10389 | masm.unboxInt32(Address(dataLength, TableObject::offsetOfDataLength()), |
| 10390 | dataLength); |
| 10391 | masm.branch32(Assembler::AboveOrEqual, temp, dataLength, &iterDone); |
| 10392 | { |
| 10393 | TableIteratorLoadEntry<TableObject>(masm, iter, temp, front); |
| 10394 | |
| 10395 | emitLoadIteratorValues<TableObject>(result, temp, front); |
| 10396 | |
| 10397 | TableIteratorAdvance<TableObject>(masm, iter, front, dataLength, temp); |
| 10398 | |
| 10399 | masm.move32(Imm32(0), output); |
| 10400 | masm.jump(&done); |
| 10401 | } |
| 10402 | { |
| 10403 | masm.bind(&iterDone); |
| 10404 | TableIteratorFinish(masm, iter, temp, dataLength); |
| 10405 | |
| 10406 | masm.bind(&iterAlreadyDone); |
| 10407 | masm.move32(Imm32(1), output); |
| 10408 | } |
| 10409 | masm.bind(&done); |
| 10410 | } |
| 10411 | |
| 10412 | void CodeGenerator::visitGetNextEntryForIterator( |
| 10413 | LGetNextEntryForIterator* lir) { |
| 10414 | if (lir->mir()->mode() == MGetNextEntryForIterator::Map) { |
| 10415 | emitGetNextEntryForIterator<MapIteratorObject, MapObject>(lir); |
| 10416 | } else { |
| 10417 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 10417); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->mode() == MGetNextEntryForIterator::Set" ")"); do { MOZ_CrashSequence(__null, 10417); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10418 | emitGetNextEntryForIterator<SetIteratorObject, SetObject>(lir); |
| 10419 | } |
| 10420 | } |
| 10421 | |
| 10422 | // The point of these is to inform Ion of where these values already are; they |
| 10423 | // don't normally generate (much) code. |
| 10424 | void CodeGenerator::visitWasmRegisterPairResult(LWasmRegisterPairResult* lir) {} |
| 10425 | void CodeGenerator::visitWasmStackResult(LWasmStackResult* lir) {} |
| 10426 | void CodeGenerator::visitWasmStackResult64(LWasmStackResult64* lir) {} |
| 10427 | |
| 10428 | void CodeGenerator::visitWasmStackResultArea(LWasmStackResultArea* lir) { |
| 10429 | LAllocation* output = lir->getDef(0)->output(); |
| 10430 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 10430); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output->isStackArea()" ")"); do { MOZ_CrashSequence (__null, 10430); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 10431 | bool tempInit = false; |
| 10432 | for (auto iter = output->toStackArea()->results(); iter; iter.next()) { |
| 10433 | // Zero out ref stack results. |
| 10434 | if (iter.isWasmAnyRef()) { |
| 10435 | Register temp = ToRegister(lir->temp0()); |
| 10436 | if (!tempInit) { |
| 10437 | masm.xorPtr(temp, temp); |
| 10438 | tempInit = true; |
| 10439 | } |
| 10440 | masm.storePtr(temp, ToAddress(iter.alloc())); |
| 10441 | } |
| 10442 | } |
| 10443 | } |
| 10444 | |
| 10445 | void CodeGenerator::visitWasmRegisterResult(LWasmRegisterResult* lir) { |
| 10446 | #ifdef JS_64BIT1 |
| 10447 | if (MWasmRegisterResult* mir = lir->mir()) { |
| 10448 | if (mir->type() == MIRType::Int32) { |
| 10449 | masm.widenInt32(ToRegister(lir->output())); |
| 10450 | } |
| 10451 | } |
| 10452 | #endif |
| 10453 | } |
| 10454 | |
| 10455 | void CodeGenerator::visitWasmSystemFloatRegisterResult( |
| 10456 | LWasmSystemFloatRegisterResult* lir) { |
| 10457 | MOZ_ASSERT(lir->mir()->type() == MIRType::Float32 ||do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Float32 || lir-> mir()->type() == MIRType::Double)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->type() == MIRType ::Float32 || lir->mir()->type() == MIRType::Double))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double" , "./../../../../js/src/jit/CodeGenerator.cpp", 10458); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double" ")"); do { MOZ_CrashSequence(__null, 10458); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 10458 | lir->mir()->type() == MIRType::Double)do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->type() == MIRType::Float32 || lir-> mir()->type() == MIRType::Double)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->type() == MIRType ::Float32 || lir->mir()->type() == MIRType::Double))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double" , "./../../../../js/src/jit/CodeGenerator.cpp", 10458); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32 || lir->mir()->type() == MIRType::Double" ")"); do { MOZ_CrashSequence(__null, 10458); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10459 | MOZ_ASSERT_IF(lir->mir()->type() == MIRType::Float32,do { if (lir->mir()->type() == MIRType::Float32) { 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 10460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg" ")"); do { MOZ_CrashSequence(__null, 10460); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 10460 | ToFloatRegister(lir->output()) == ReturnFloat32Reg)do { if (lir->mir()->type() == MIRType::Float32) { 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 10460); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg" ")"); do { MOZ_CrashSequence(__null, 10460); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 10461 | MOZ_ASSERT_IF(lir->mir()->type() == MIRType::Double,do { if (lir->mir()->type() == MIRType::Double) { 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 10462); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 10462); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 10462 | ToFloatRegister(lir->output()) == ReturnDoubleReg)do { if (lir->mir()->type() == MIRType::Double) { 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 10462); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 10462); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 10463 | |
| 10464 | #ifdef JS_CODEGEN_ARM |
| 10465 | MWasmSystemFloatRegisterResult* mir = lir->mir(); |
| 10466 | if (!mir->hardFP()) { |
| 10467 | if (mir->type() == MIRType::Float32) { |
| 10468 | // Move float32 from r0 to ReturnFloatReg. |
| 10469 | masm.ma_vxfer(r0, ReturnFloat32Reg); |
| 10470 | } else if (mir->type() == MIRType::Double) { |
| 10471 | // Move double from r0/r1 to ReturnDoubleReg. |
| 10472 | masm.ma_vxfer(r0, r1, ReturnDoubleReg); |
| 10473 | } else { |
| 10474 | MOZ_CRASH("SIMD type not supported")do { do { } while (false); MOZ_ReportCrash("" "SIMD type not supported" , "./../../../../js/src/jit/CodeGenerator.cpp", 10474); AnnotateMozCrashReason ("MOZ_CRASH(" "SIMD type not supported" ")"); do { MOZ_CrashSequence (__null, 10474); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 10475 | } |
| 10476 | } |
| 10477 | #elif JS_CODEGEN_X86 |
| 10478 | MWasmSystemFloatRegisterResult* mir = lir->mir(); |
| 10479 | if (mir->type() == MIRType::Double) { |
| 10480 | masm.reserveStack(sizeof(double)); |
| 10481 | masm.fstp(Operand(esp, 0)); |
| 10482 | masm.loadDouble(Operand(esp, 0), ReturnDoubleReg); |
| 10483 | masm.freeStack(sizeof(double)); |
| 10484 | } else if (mir->type() == MIRType::Float32) { |
| 10485 | masm.reserveStack(sizeof(float)); |
| 10486 | masm.fstp32(Operand(esp, 0)); |
| 10487 | masm.loadFloat32(Operand(esp, 0), ReturnFloat32Reg); |
| 10488 | masm.freeStack(sizeof(float)); |
| 10489 | } |
| 10490 | #endif |
| 10491 | } |
| 10492 | |
| 10493 | void CodeGenerator::visitWasmCall(LWasmCall* lir) { |
| 10494 | const MWasmCallBase* callBase = lir->callBase(); |
| 10495 | bool isReturnCall = lir->isReturnCall(); |
| 10496 | |
| 10497 | // If this call is in Wasm try code block, initialise a wasm::TryNote for this |
| 10498 | // call. |
| 10499 | bool inTry = callBase->inTry(); |
| 10500 | if (inTry) { |
| 10501 | size_t tryNoteIndex = callBase->tryNoteIndex(); |
| 10502 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
| 10503 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
| 10504 | tryNote.setTryBodyBegin(masm.currentOffset()); |
| 10505 | } |
| 10506 | |
| 10507 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 10508); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" ")"); do { MOZ_CrashSequence(__null, 10508); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 10508 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 10508); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "(sizeof(wasm::Frame) + masm.framePushed()) % WasmStackAlignment == 0" ")"); do { MOZ_CrashSequence(__null, 10508); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10509 | static_assert( |
| 10510 | WasmStackAlignment >= ABIStackAlignment && |
| 10511 | WasmStackAlignment % ABIStackAlignment == 0, |
| 10512 | "The wasm stack alignment should subsume the ABI-required alignment"); |
| 10513 | |
| 10514 | #ifdef DEBUG1 |
| 10515 | Label ok; |
| 10516 | masm.branchTestStackPtr(Assembler::Zero, Imm32(WasmStackAlignment - 1), &ok); |
| 10517 | masm.breakpoint(); |
| 10518 | masm.bind(&ok); |
| 10519 | #endif |
| 10520 | |
| 10521 | // LWasmCallBase::isCallPreserved() assumes that all MWasmCalls preserve the |
| 10522 | // instance and pinned regs. The only case where where we don't have to |
| 10523 | // reload the instance and pinned regs is when the callee preserves them. |
| 10524 | bool reloadInstance = true; |
| 10525 | bool reloadPinnedRegs = true; |
| 10526 | bool switchRealm = true; |
| 10527 | |
| 10528 | const wasm::CallSiteDesc& desc = callBase->desc(); |
| 10529 | const wasm::CalleeDesc& callee = callBase->callee(); |
| 10530 | CodeOffset retOffset; |
| 10531 | CodeOffset secondRetOffset; |
| 10532 | switch (callee.which()) { |
| 10533 | case wasm::CalleeDesc::Func: |
| 10534 | if (isReturnCall) { |
| 10535 | ReturnCallAdjustmentInfo retCallInfo( |
| 10536 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
| 10537 | masm.wasmReturnCall(desc, callee.funcIndex(), retCallInfo); |
| 10538 | // The rest of the method is unnecessary for a return call. |
| 10539 | return; |
| 10540 | } |
| 10541 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 10541); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence (__null, 10541); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 10542 | retOffset = masm.call(desc, callee.funcIndex()); |
| 10543 | reloadInstance = false; |
| 10544 | reloadPinnedRegs = false; |
| 10545 | switchRealm = false; |
| 10546 | break; |
| 10547 | case wasm::CalleeDesc::Import: |
| 10548 | if (isReturnCall) { |
| 10549 | ReturnCallAdjustmentInfo retCallInfo( |
| 10550 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
| 10551 | masm.wasmReturnCallImport(desc, callee, retCallInfo); |
| 10552 | // The rest of the method is unnecessary for a return call. |
| 10553 | return; |
| 10554 | } |
| 10555 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 10555); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence (__null, 10555); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 10556 | retOffset = masm.wasmCallImport(desc, callee); |
| 10557 | break; |
| 10558 | case wasm::CalleeDesc::WasmTable: { |
| 10559 | Label* nullCheckFailed = nullptr; |
| 10560 | #ifndef WASM_HAS_HEAPREG1 |
| 10561 | { |
| 10562 | auto* ool = new ( |
| 10563 | alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 10564 | masm.wasmTrap(wasm::Trap::IndirectCallToNull, desc.toTrapSiteDesc()); |
| 10565 | }); |
| 10566 | if (lir->isCatchable()) { |
| 10567 | addOutOfLineCode(ool, lir->mirCatchable()); |
| 10568 | } else if (isReturnCall) { |
| 10569 | addOutOfLineCode(ool, lir->mirReturnCall()); |
| 10570 | } else { |
| 10571 | addOutOfLineCode(ool, lir->mirUncatchable()); |
| 10572 | } |
| 10573 | nullCheckFailed = ool->entry(); |
| 10574 | } |
| 10575 | #endif |
| 10576 | if (isReturnCall) { |
| 10577 | ReturnCallAdjustmentInfo retCallInfo( |
| 10578 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
| 10579 | masm.wasmReturnCallIndirect(desc, callee, nullCheckFailed, retCallInfo); |
| 10580 | // The rest of the method is unnecessary for a return call. |
| 10581 | return; |
| 10582 | } |
| 10583 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 10583); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence (__null, 10583); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 10584 | masm.wasmCallIndirect(desc, callee, nullCheckFailed, &retOffset, |
| 10585 | &secondRetOffset); |
| 10586 | // Register reloading and realm switching are handled dynamically inside |
| 10587 | // wasmCallIndirect. There are two return offsets, one for each call |
| 10588 | // instruction (fast path and slow path). |
| 10589 | reloadInstance = false; |
| 10590 | reloadPinnedRegs = false; |
| 10591 | switchRealm = false; |
| 10592 | break; |
| 10593 | } |
| 10594 | case wasm::CalleeDesc::Builtin: |
| 10595 | retOffset = masm.call(desc, callee.builtin()); |
| 10596 | // The builtin ABI preserves the instance and pinned registers. However, |
| 10597 | // builtins may grow the memory which requires us to reload the pinned |
| 10598 | // registers. |
| 10599 | reloadInstance = false; |
| 10600 | reloadPinnedRegs = true; |
| 10601 | switchRealm = false; |
| 10602 | break; |
| 10603 | case wasm::CalleeDesc::BuiltinInstanceMethod: { |
| 10604 | CodeOffset unused_trapStackMapKey; |
| 10605 | masm.wasmCallBuiltinInstanceMethod(desc, callBase->instanceArg(), |
| 10606 | callee.builtin(), |
| 10607 | callBase->builtinMethodFailureMode(), |
| 10608 | callBase->builtinMethodFailureTrap(), |
| 10609 | &retOffset, &unused_trapStackMapKey); |
| 10610 | // The builtin ABI preserves the instance and pinned registers. However, |
| 10611 | // builtins may grow the memory which requires us to reload the pinned |
| 10612 | // registers. |
| 10613 | reloadInstance = false; |
| 10614 | reloadPinnedRegs = true; |
| 10615 | switchRealm = false; |
| 10616 | break; |
| 10617 | } |
| 10618 | case wasm::CalleeDesc::FuncRef: |
| 10619 | if (isReturnCall) { |
| 10620 | ReturnCallAdjustmentInfo retCallInfo( |
| 10621 | callBase->stackArgAreaSizeUnaligned(), inboundStackArgBytes_); |
| 10622 | masm.wasmReturnCallRef(desc, callee, retCallInfo); |
| 10623 | // The rest of the method is unnecessary for a return call. |
| 10624 | return; |
| 10625 | } |
| 10626 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 10626); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence (__null, 10626); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 10627 | // Register reloading and realm switching are handled dynamically inside |
| 10628 | // wasmCallRef. There are two return offsets, one for each call |
| 10629 | // instruction (fast path and slow path). |
| 10630 | masm.wasmCallRef(desc, callee, &retOffset, &secondRetOffset); |
| 10631 | reloadInstance = false; |
| 10632 | reloadPinnedRegs = false; |
| 10633 | switchRealm = false; |
| 10634 | break; |
| 10635 | } |
| 10636 | |
| 10637 | // Note the assembler offset for the associated LSafePoint. |
| 10638 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 10638); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!isReturnCall" ")"); do { MOZ_CrashSequence (__null, 10638); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 10639 | markSafepointAt(retOffset.offset(), lir); |
| 10640 | |
| 10641 | // Now that all the outbound in-memory args are on the stack, note the |
| 10642 | // required lower boundary point of the associated StackMap. |
| 10643 | uint32_t framePushedAtStackMapBase = |
| 10644 | masm.framePushed() - |
| 10645 | wasm::AlignStackArgAreaSize(callBase->stackArgAreaSizeUnaligned()); |
| 10646 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAtStackMapBase); |
| 10647 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 10648); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" ")"); do { MOZ_CrashSequence(__null, 10648); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 10648 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 10648); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->safepoint()->wasmSafepointKind() == WasmSafepointKind::LirCall" ")"); do { MOZ_CrashSequence(__null, 10648); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10649 | |
| 10650 | // Indirect calls (WasmTable/FuncRef) emit two call instructions (fast and |
| 10651 | // slow path) with two distinct return offsets. The two paths are mutually |
| 10652 | // exclusive and rejoin with the same live references and frame layout, so a |
| 10653 | // single stackmap serves both: register this call's LSafepoint a second time |
| 10654 | // at the slow-path return offset. |
| 10655 | if (callee.which() == wasm::CalleeDesc::WasmTable || |
| 10656 | callee.which() == wasm::CalleeDesc::FuncRef) { |
| 10657 | markSafepointAt(secondRetOffset.offset(), lir); |
| 10658 | } |
| 10659 | |
| 10660 | if (reloadInstance) { |
| 10661 | masm.loadPtr( |
| 10662 | Address(masm.getStackPointer(), WasmCallerInstanceOffsetBeforeCall), |
| 10663 | InstanceReg); |
| 10664 | if (switchRealm) { |
| 10665 | masm.switchToWasmInstanceRealm(ABINonArgReturnReg0, ABINonArgReturnReg1); |
| 10666 | } |
| 10667 | } else { |
| 10668 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 10668); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!switchRealm" ")"); do { MOZ_CrashSequence(__null, 10668); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10669 | } |
| 10670 | if (reloadPinnedRegs) { |
| 10671 | masm.loadWasmPinnedRegsFromInstance(mozilla::Nothing()); |
| 10672 | } |
| 10673 | |
| 10674 | switch (callee.which()) { |
| 10675 | case wasm::CalleeDesc::Func: |
| 10676 | case wasm::CalleeDesc::Import: |
| 10677 | case wasm::CalleeDesc::WasmTable: |
| 10678 | case wasm::CalleeDesc::FuncRef: |
| 10679 | // Stack allocation could change during Wasm (return) calls, |
| 10680 | // recover pre-call state. |
| 10681 | masm.freeStackTo(masm.framePushed()); |
| 10682 | break; |
| 10683 | default: |
| 10684 | break; |
| 10685 | } |
| 10686 | |
| 10687 | if (inTry) { |
| 10688 | // Set the end of the try note range |
| 10689 | size_t tryNoteIndex = callBase->tryNoteIndex(); |
| 10690 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
| 10691 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
| 10692 | |
| 10693 | // Don't set the end of the try note if we've OOM'ed, as the above |
| 10694 | // instructions may not have been emitted, which will trigger an assert |
| 10695 | // about zero-length try-notes. This is okay as this compilation will be |
| 10696 | // thrown away. |
| 10697 | if (!masm.oom()) { |
| 10698 | tryNote.setTryBodyEnd(masm.currentOffset()); |
| 10699 | } |
| 10700 | |
| 10701 | // This instruction must be the last instruction in the block. No other |
| 10702 | // instructions may be inserted. |
| 10703 | LBlock* block = lir->block(); |
| 10704 | MOZ_RELEASE_ASSERT(*block->rbegin() == lir)do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir" , "./../../../../js/src/jit/CodeGenerator.cpp", 10704); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir" ")"); do { MOZ_CrashSequence(__null, 10704); __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
| 10705 | |
| 10706 | // Jump to the fallthrough block |
| 10707 | jumpToBlock(lir->mirCatchable()->getSuccessor( |
| 10708 | MWasmCallCatchable::FallthroughBranchIndex)); |
| 10709 | } |
| 10710 | } |
| 10711 | |
| 10712 | #ifdef ENABLE_WASM_JSPI1 |
| 10713 | void CodeGenerator::visitWasmFindHandler(LWasmFindHandler* lir) { |
| 10714 | MWasmFindHandler* mir = lir->mir(); |
| 10715 | Register instance = ToRegister(lir->instance()); |
| 10716 | Register tag = ToRegister(lir->tag()); |
| 10717 | Register output = ToRegister(lir->output()); |
| 10718 | Register scratch1 = ToRegister(lir->temp0()); |
| 10719 | Register scratch2 = ToRegister(lir->temp1()); |
| 10720 | Register scratch3 = ToRegister(lir->temp2()); |
| 10721 | Register scratch4 = ToRegister(lir->temp3()); |
| 10722 | const wasm::Trap& trap = mir->trap(); |
| 10723 | const wasm::TrapSiteDesc& trapSiteDesc = mir->trapSiteDesc(); |
| 10724 | |
| 10725 | auto* ool = new (alloc()) |
| 10726 | LambdaOutOfLineCode([this, trap, trapSiteDesc](OutOfLineCode& ool) { |
| 10727 | masm.wasmTrap(trap, trapSiteDesc); |
| 10728 | }); |
| 10729 | addOutOfLineCode(ool, (const BytecodeSite*)nullptr); |
| 10730 | wasm::EmitFindHandler(masm, instance, tag, output, scratch1, scratch2, |
| 10731 | scratch3, scratch4, ool->entry()); |
| 10732 | } |
| 10733 | |
| 10734 | void CodeGenerator::visitWasmSuspend(LWasmSuspend* lir) { |
| 10735 | Register instance = ToRegister(lir->instance()); |
| 10736 | Register suspendedCont = ToRegister(lir->suspendedCont()); |
| 10737 | Register handler = ToRegister(lir->handler()); |
| 10738 | Register scratch1 = ToRegister(lir->temp0()); |
| 10739 | Register scratch2 = ToRegister(lir->temp1()); |
| 10740 | Register scratch3 = ToRegister(lir->temp2()); |
| 10741 | |
| 10742 | uint32_t suspendResultsAreaBase = |
| 10743 | lir->mir()->suspendResultsArea()->toWasmStackResultArea()->base(); |
| 10744 | |
| 10745 | CodeOffset suspendedCodeOffset; |
| 10746 | uint32_t suspendedFramePushed; |
| 10747 | wasm::EmitSuspend(masm, instance, suspendedCont, handler, scratch1, scratch2, |
| 10748 | scratch3, lir->mir()->callSiteDesc(), &suspendedCodeOffset, |
| 10749 | &suspendedFramePushed, suspendResultsAreaBase); |
| 10750 | |
| 10751 | if (masm.oom()) { |
| 10752 | return; |
| 10753 | } |
| 10754 | |
| 10755 | markSafepointAt(suspendedCodeOffset.offset(), lir); |
| 10756 | lir->safepoint()->setFramePushedAtStackMapBase(suspendedFramePushed); |
| 10757 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
| 10758 | } |
| 10759 | |
| 10760 | void CodeGenerator::visitWasmPrepareResume(LWasmPrepareResume* lir) { |
| 10761 | MWasmPrepareResume* mir = lir->mir(); |
| 10762 | Register cont = ToRegister(lir->cont()); |
| 10763 | Register output = ToRegister(lir->output()); |
| 10764 | Register scratch1 = ToRegister(lir->temp0()); |
| 10765 | Register scratch2 = ToRegister(lir->temp1()); |
| 10766 | uint32_t resumeParamsAreaBase = |
| 10767 | mir->resumeParamsArea()->toWasmStackResultArea()->base(); |
| 10768 | wasm::TrapSiteDesc trapSiteDesc = mir->trapSiteDesc(); |
| 10769 | |
| 10770 | auto* ool = new (alloc()) |
| 10771 | LambdaOutOfLineCode([this, trapSiteDesc](OutOfLineCode& ool) { |
| 10772 | masm.wasmTrap(wasm::Trap::NullPointerDereference, trapSiteDesc); |
| 10773 | }); |
| 10774 | addOutOfLineCode(ool, (const BytecodeSite*)nullptr); |
| 10775 | |
| 10776 | wasm::EmitPrepareResume(masm, cont, resumeParamsAreaBase, output, scratch1, |
| 10777 | scratch2, ool->entry()); |
| 10778 | } |
| 10779 | |
| 10780 | void CodeGenerator::visitWasmResume(LWasmResume* lir) { |
| 10781 | MWasmResume* mir = lir->mir(); |
| 10782 | Register instance = ToRegister(lir->instance()); |
| 10783 | Register cont = ToRegister(lir->cont()); |
| 10784 | uint32_t handlersParamsAreaBase = mir->handlersParamsArea()->base(); |
| 10785 | uint32_t contResultsAreaBase = mir->contResultsArea()->base(); |
| 10786 | Register scratch1 = ToRegister(lir->temp0()); |
| 10787 | Register scratch2 = ToRegister(lir->temp1()); |
| 10788 | Register scratch3 = ToRegister(lir->temp2()); |
| 10789 | |
| 10790 | // If this resume is in a wasm try code block, initialise a wasm::TryNote for |
| 10791 | // this resume. |
| 10792 | bool inTry = mir->hasTryNote(); |
| 10793 | if (inTry) { |
| 10794 | size_t tryNoteIndex = mir->tryNoteIndex().value(); |
| 10795 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
| 10796 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
| 10797 | tryNote.setTryBodyBegin(masm.currentOffset()); |
| 10798 | } |
| 10799 | |
| 10800 | mozilla::Vector<jit::Label*, 2, JitAllocPolicy> handlerLabels(alloc()); |
| 10801 | if (!handlerLabels.reserve(mir->numHandlers())) { |
| 10802 | masm.setOOM(); |
| 10803 | return; |
| 10804 | } |
| 10805 | for (size_t i = 0; i < mir->numHandlers(); i++) { |
| 10806 | handlerLabels.infallibleAppend(getJumpLabelForBranch(mir->handlerBlock(i))); |
| 10807 | } |
| 10808 | |
| 10809 | CodeOffset resumeCodeOffset; |
| 10810 | uint32_t resumeFramePushed; |
| 10811 | wasm::EmitResume(masm, instance, cont, handlersParamsAreaBase, scratch1, |
| 10812 | scratch2, scratch3, mir->handlers(), handlerLabels, |
| 10813 | mir->callSiteDesc(), &resumeCodeOffset, &resumeFramePushed, |
| 10814 | contResultsAreaBase); |
| 10815 | |
| 10816 | if (masm.oom()) { |
| 10817 | return; |
| 10818 | } |
| 10819 | |
| 10820 | markSafepointAt(resumeCodeOffset.offset(), lir); |
| 10821 | lir->safepoint()->setFramePushedAtStackMapBase(resumeFramePushed); |
| 10822 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::StackSwitch); |
| 10823 | |
| 10824 | if (inTry) { |
| 10825 | // Set the end of the try note range |
| 10826 | size_t tryNoteIndex = mir->tryNoteIndex().value(); |
| 10827 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
| 10828 | wasm::TryNote& tryNote = tryNotes[tryNoteIndex]; |
| 10829 | |
| 10830 | // Don't set the end of the try note if we've OOM'ed, as the above |
| 10831 | // instructions may not have been emitted, which will trigger an assert |
| 10832 | // about zero-length try-notes. This is okay as this compilation will be |
| 10833 | // thrown away. |
| 10834 | if (!masm.oom()) { |
| 10835 | tryNote.setTryBodyEnd(masm.currentOffset()); |
| 10836 | } |
| 10837 | |
| 10838 | // This instruction must be the last instruction in the block. No other |
| 10839 | // instructions may be inserted. |
| 10840 | LBlock* block = lir->block(); |
| 10841 | MOZ_RELEASE_ASSERT(*block->rbegin() == lir)do { static_assert( mozilla::detail::AssertionConditionType< decltype(*block->rbegin() == lir)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(*block->rbegin() == lir)) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("*block->rbegin() == lir" , "./../../../../js/src/jit/CodeGenerator.cpp", 10841); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "*block->rbegin() == lir" ")"); do { MOZ_CrashSequence(__null, 10841); __attribute__((nomerge)) ::abort(); } while (false); } } while (false); |
| 10842 | } |
| 10843 | |
| 10844 | // Jump to the fallthrough block |
| 10845 | jumpToBlock(mir->fallthroughBlock()); |
| 10846 | } |
| 10847 | #endif // ENABLE_WASM_JSPI |
| 10848 | |
| 10849 | void CodeGenerator::visitWasmCallLandingPrePad(LWasmCallLandingPrePad* lir) { |
| 10850 | LBlock* block = lir->block(); |
| 10851 | MWasmCallLandingPrePad* mir = lir->mir(); |
| 10852 | MBasicBlock* mirBlock = mir->block(); |
| 10853 | MBasicBlock* callMirBlock = mir->callBlock(); |
| 10854 | |
| 10855 | // This block must be the pre-pad successor of the call block. No blocks may |
| 10856 | // be inserted between us, such as for critical edge splitting. |
| 10857 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 10858); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" ")"); do { MOZ_CrashSequence(__null, 10858); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 10858 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 10858); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "mirBlock == callMirBlock->getSuccessor( MWasmCallCatchable::PrePadBranchIndex)" ")"); do { MOZ_CrashSequence(__null, 10858); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10859 | |
| 10860 | // This instruction or a move group must be the first instruction in the |
| 10861 | // block. No other instructions may be inserted. |
| 10862 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 10863); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" ")"); do { MOZ_CrashSequence(__null, 10863); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 10863 | *(++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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 10863); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "*block->begin() == lir || (block->begin()->isMoveGroup() && *(++block->begin()) == lir)" ")"); do { MOZ_CrashSequence(__null, 10863); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10864 | |
| 10865 | wasm::TryNoteVector& tryNotes = masm.tryNotes(); |
| 10866 | wasm::TryNote& tryNote = tryNotes[mir->tryNoteIndex()]; |
| 10867 | // Set the entry point for the call try note to be the beginning of this |
| 10868 | // block. The above assertions (and assertions in visitWasmCall) guarantee |
| 10869 | // that we are not skipping over instructions that should be executed. |
| 10870 | tryNote.setLandingPad(block->label()->offset(), masm.framePushed()); |
| 10871 | } |
| 10872 | |
| 10873 | template <typename InstructionWithMaybeTrapSite> |
| 10874 | void EmitSignalNullCheckTrapSite(MacroAssembler& masm, |
| 10875 | InstructionWithMaybeTrapSite* ins, |
| 10876 | FaultingCodeRange fcr, |
| 10877 | wasm::TrapMachineInsn tmi) { |
| 10878 | if (!ins->maybeTrap()) { |
| 10879 | return; |
| 10880 | } |
| 10881 | masm.appendAndVerify(wasm::Trap::NullPointerDereference, tmi, fcr, |
| 10882 | *ins->maybeTrap()); |
| 10883 | } |
| 10884 | |
| 10885 | template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndexT> |
| 10886 | void CodeGenerator::emitWasmValueLoad(InstructionWithMaybeTrapSite* ins, |
| 10887 | MIRType type, MWideningOp wideningOp, |
| 10888 | AddressOrBaseIndexT addr, |
| 10889 | AnyRegister dst) { |
| 10890 | FaultingCodeRange fcr; |
| 10891 | switch (type) { |
| 10892 | case MIRType::Int32: |
| 10893 | switch (wideningOp) { |
| 10894 | case MWideningOp::None: |
| 10895 | fcr = masm.load32(addr, dst.gpr()); |
| 10896 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10897 | wasm::TrapMachineInsn::Load32); |
| 10898 | break; |
| 10899 | case MWideningOp::FromU16: |
| 10900 | fcr = masm.load16ZeroExtend(addr, dst.gpr()); |
| 10901 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10902 | wasm::TrapMachineInsn::Load16); |
| 10903 | break; |
| 10904 | case MWideningOp::FromS16: |
| 10905 | fcr = masm.load16SignExtend(addr, dst.gpr()); |
| 10906 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10907 | wasm::TrapMachineInsn::Load16); |
| 10908 | break; |
| 10909 | case MWideningOp::FromU8: |
| 10910 | fcr = masm.load8ZeroExtend(addr, dst.gpr()); |
| 10911 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10912 | wasm::TrapMachineInsn::Load8); |
| 10913 | break; |
| 10914 | case MWideningOp::FromS8: |
| 10915 | fcr = masm.load8SignExtend(addr, dst.gpr()); |
| 10916 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10917 | wasm::TrapMachineInsn::Load8); |
| 10918 | break; |
| 10919 | default: |
| 10920 | MOZ_CRASH("unexpected widening op in ::visitWasmLoadElement")do { do { } while (false); MOZ_ReportCrash("" "unexpected widening op in ::visitWasmLoadElement" , "./../../../../js/src/jit/CodeGenerator.cpp", 10920); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected widening op in ::visitWasmLoadElement" ")"); do { MOZ_CrashSequence(__null, 10920); __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 10921 | } |
| 10922 | break; |
| 10923 | case MIRType::Float32: |
| 10924 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 10924); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { MOZ_CrashSequence(__null, 10924); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10925 | fcr = masm.loadFloat32(addr, dst.fpu()); |
| 10926 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10927 | wasm::TrapMachineInsn::Load32); |
| 10928 | break; |
| 10929 | case MIRType::Double: |
| 10930 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 10930); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { MOZ_CrashSequence(__null, 10930); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10931 | fcr = masm.loadDouble(addr, dst.fpu()); |
| 10932 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10933 | wasm::TrapMachineInsn::Load64); |
| 10934 | break; |
| 10935 | case MIRType::Pointer: |
| 10936 | case MIRType::WasmAnyRef: |
| 10937 | case MIRType::WasmStructData: |
| 10938 | case MIRType::WasmArrayData: |
| 10939 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 10939); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { MOZ_CrashSequence(__null, 10939); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 10940 | fcr = masm.loadPtr(addr, dst.gpr()); |
| 10941 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10942 | wasm::TrapMachineInsnForLoadWord()); |
| 10943 | break; |
| 10944 | default: |
| 10945 | MOZ_CRASH("unexpected type in ::emitWasmValueLoad")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueLoad" , "./../../../../js/src/jit/CodeGenerator.cpp", 10945); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected type in ::emitWasmValueLoad" ")"); do { MOZ_CrashSequence(__null, 10945); __attribute__((nomerge)) ::abort(); } while (false); } while (false); |
| 10946 | } |
| 10947 | } |
| 10948 | |
| 10949 | template <typename InstructionWithMaybeTrapSite, class AddressOrBaseIndexT> |
| 10950 | void CodeGenerator::emitWasmValueStore(InstructionWithMaybeTrapSite* ins, |
| 10951 | MIRType type, MNarrowingOp narrowingOp, |
| 10952 | AnyRegister src, |
| 10953 | AddressOrBaseIndexT addr) { |
| 10954 | FaultingCodeRange fcr; |
| 10955 | switch (type) { |
| 10956 | case MIRType::Int32: |
| 10957 | switch (narrowingOp) { |
| 10958 | case MNarrowingOp::None: |
| 10959 | fcr = masm.store32(src.gpr(), addr); |
| 10960 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10961 | wasm::TrapMachineInsn::Store32); |
| 10962 | break; |
| 10963 | case MNarrowingOp::To16: |
| 10964 | fcr = masm.store16(src.gpr(), addr); |
| 10965 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10966 | wasm::TrapMachineInsn::Store16); |
| 10967 | break; |
| 10968 | case MNarrowingOp::To8: |
| 10969 | fcr = masm.store8(src.gpr(), addr); |
| 10970 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10971 | wasm::TrapMachineInsn::Store8); |
| 10972 | break; |
| 10973 | default: |
| 10974 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp" , 10974); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence (__null, 10974); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 10975 | } |
| 10976 | break; |
| 10977 | case MIRType::Float32: |
| 10978 | fcr = masm.storeFloat32(src.fpu(), addr); |
| 10979 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10980 | wasm::TrapMachineInsn::Store32); |
| 10981 | break; |
| 10982 | case MIRType::Double: |
| 10983 | fcr = masm.storeDouble(src.fpu(), addr); |
| 10984 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 10985 | wasm::TrapMachineInsn::Store64); |
| 10986 | break; |
| 10987 | case MIRType::Pointer: |
| 10988 | // This could be correct, but it would be a new usage, so check carefully. |
| 10989 | MOZ_CRASH("Unexpected type in ::emitWasmValueStore.")do { do { } while (false); MOZ_ReportCrash("" "Unexpected type in ::emitWasmValueStore." , "./../../../../js/src/jit/CodeGenerator.cpp", 10989); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected type in ::emitWasmValueStore." ")") ; do { MOZ_CrashSequence(__null, 10989); __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 10990 | case MIRType::WasmAnyRef: |
| 10991 | MOZ_CRASH("Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef.")do { do { } while (false); MOZ_ReportCrash("" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef." , "./../../../../js/src/jit/CodeGenerator.cpp", 10991); AnnotateMozCrashReason ("MOZ_CRASH(" "Bad type in ::emitWasmValueStore. Use LWasmStoreElementRef." ")"); do { MOZ_CrashSequence(__null, 10991); __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 10992 | default: |
| 10993 | MOZ_CRASH("unexpected type in ::emitWasmValueStore")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::emitWasmValueStore" , "./../../../../js/src/jit/CodeGenerator.cpp", 10993); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected type in ::emitWasmValueStore" ")"); do { MOZ_CrashSequence(__null, 10993); __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 10994 | } |
| 10995 | } |
| 10996 | |
| 10997 | void CodeGenerator::visitWasmLoadSlot(LWasmLoadSlot* ins) { |
| 10998 | MIRType type = ins->type(); |
| 10999 | MWideningOp wideningOp = ins->wideningOp(); |
| 11000 | Register container = ToRegister(ins->containerRef()); |
| 11001 | Address addr(container, ins->offset()); |
| 11002 | AnyRegister dst = ToAnyRegister(ins->output()); |
| 11003 | |
| 11004 | #ifdef ENABLE_WASM_SIMD1 |
| 11005 | if (type == MIRType::Simd128) { |
| 11006 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11006); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { MOZ_CrashSequence(__null, 11006); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11007 | FaultingCodeRange fcr = masm.loadUnalignedSimd128(addr, dst.fpu()); |
| 11008 | EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load128); |
| 11009 | return; |
| 11010 | } |
| 11011 | #endif |
| 11012 | emitWasmValueLoad(ins, type, wideningOp, addr, dst); |
| 11013 | } |
| 11014 | |
| 11015 | void CodeGenerator::visitWasmLoadElement(LWasmLoadElement* ins) { |
| 11016 | MIRType type = ins->type(); |
| 11017 | MWideningOp wideningOp = ins->wideningOp(); |
| 11018 | Scale scale = ins->scale(); |
| 11019 | Register base = ToRegister(ins->base()); |
| 11020 | Register index = ToRegister(ins->index()); |
| 11021 | AnyRegister dst = ToAnyRegister(ins->output()); |
| 11022 | |
| 11023 | #ifdef ENABLE_WASM_SIMD1 |
| 11024 | if (type == MIRType::Simd128) { |
| 11025 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11025); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wideningOp == MWideningOp::None" ")"); do { MOZ_CrashSequence(__null, 11025); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11026 | FaultingCodeRange fcr; |
| 11027 | Register temp = ToRegister(ins->temp0()); |
| 11028 | masm.lshiftPtr(Imm32(4), index, temp); |
| 11029 | fcr = masm.loadUnalignedSimd128(BaseIndex(base, temp, Scale::TimesOne), |
| 11030 | dst.fpu()); |
| 11031 | EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load128); |
| 11032 | return; |
| 11033 | } |
| 11034 | #endif |
| 11035 | emitWasmValueLoad(ins, type, wideningOp, BaseIndex(base, index, scale), dst); |
| 11036 | } |
| 11037 | |
| 11038 | void CodeGenerator::visitWasmStoreSlot(LWasmStoreSlot* ins) { |
| 11039 | MIRType type = ins->type(); |
| 11040 | MNarrowingOp narrowingOp = ins->narrowingOp(); |
| 11041 | Register container = ToRegister(ins->containerRef()); |
| 11042 | Address addr(container, ins->offset()); |
| 11043 | AnyRegister src = ToAnyRegister(ins->value()); |
| 11044 | if (type != MIRType::Int32) { |
| 11045 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11045); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None" ")"); do { MOZ_CrashSequence(__null, 11045); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11046 | } |
| 11047 | |
| 11048 | #ifdef ENABLE_WASM_SIMD1 |
| 11049 | if (type == MIRType::Simd128) { |
| 11050 | FaultingCodeRange fcr = masm.storeUnalignedSimd128(src.fpu(), addr); |
| 11051 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 11052 | wasm::TrapMachineInsn::Store128); |
| 11053 | return; |
| 11054 | } |
| 11055 | #endif |
| 11056 | emitWasmValueStore(ins, type, narrowingOp, src, addr); |
| 11057 | } |
| 11058 | |
| 11059 | void CodeGenerator::visitWasmStoreStackResult(LWasmStoreStackResult* ins) { |
| 11060 | const LAllocation* value = ins->value(); |
| 11061 | Address addr(ToRegister(ins->stackResultsArea()), ins->offset()); |
| 11062 | |
| 11063 | switch (ins->type()) { |
| 11064 | case MIRType::Int32: |
| 11065 | masm.storePtr(ToRegister(value), addr); |
| 11066 | break; |
| 11067 | case MIRType::Float32: |
| 11068 | masm.storeFloat32(ToFloatRegister(value), addr); |
| 11069 | break; |
| 11070 | case MIRType::Double: |
| 11071 | masm.storeDouble(ToFloatRegister(value), addr); |
| 11072 | break; |
| 11073 | #ifdef ENABLE_WASM_SIMD1 |
| 11074 | case MIRType::Simd128: |
| 11075 | masm.storeUnalignedSimd128(ToFloatRegister(value), addr); |
| 11076 | break; |
| 11077 | #endif |
| 11078 | case MIRType::WasmAnyRef: |
| 11079 | masm.storePtr(ToRegister(value), addr); |
| 11080 | break; |
| 11081 | default: |
| 11082 | MOZ_CRASH("unexpected type in ::visitWasmStoreStackResult")do { do { } while (false); MOZ_ReportCrash("" "unexpected type in ::visitWasmStoreStackResult" , "./../../../../js/src/jit/CodeGenerator.cpp", 11082); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected type in ::visitWasmStoreStackResult" ")"); do { MOZ_CrashSequence(__null, 11082); __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 11083 | } |
| 11084 | } |
| 11085 | |
| 11086 | void CodeGenerator::visitWasmStoreStackResultI64( |
| 11087 | LWasmStoreStackResultI64* ins) { |
| 11088 | masm.store64(ToRegister64(ins->value()), |
| 11089 | Address(ToRegister(ins->stackResultsArea()), ins->offset())); |
| 11090 | } |
| 11091 | |
| 11092 | void CodeGenerator::visitWasmStoreElement(LWasmStoreElement* ins) { |
| 11093 | MIRType type = ins->type(); |
| 11094 | MNarrowingOp narrowingOp = ins->narrowingOp(); |
| 11095 | Scale scale = ins->scale(); |
| 11096 | Register base = ToRegister(ins->base()); |
| 11097 | Register index = ToRegister(ins->index()); |
| 11098 | AnyRegister src = ToAnyRegister(ins->value()); |
| 11099 | if (type != MIRType::Int32) { |
| 11100 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11100); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "narrowingOp == MNarrowingOp::None" ")"); do { MOZ_CrashSequence(__null, 11100); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11101 | } |
| 11102 | |
| 11103 | #ifdef ENABLE_WASM_SIMD1 |
| 11104 | if (type == MIRType::Simd128) { |
| 11105 | Register temp = ToRegister(ins->temp0()); |
| 11106 | masm.lshiftPtr(Imm32(4), index, temp); |
| 11107 | FaultingCodeRange fcr = masm.storeUnalignedSimd128( |
| 11108 | src.fpu(), BaseIndex(base, temp, Scale::TimesOne)); |
| 11109 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 11110 | wasm::TrapMachineInsn::Store128); |
| 11111 | return; |
| 11112 | } |
| 11113 | #endif |
| 11114 | emitWasmValueStore(ins, type, narrowingOp, src, |
| 11115 | BaseIndex(base, index, scale)); |
| 11116 | } |
| 11117 | |
| 11118 | void CodeGenerator::visitWasmLoadTableElement(LWasmLoadTableElement* ins) { |
| 11119 | Register elements = ToRegister(ins->elements()); |
| 11120 | Register index = ToRegister(ins->index()); |
| 11121 | Register output = ToRegister(ins->output()); |
| 11122 | masm.loadPtr(BaseIndex(elements, index, ScalePointer), output); |
| 11123 | } |
| 11124 | |
| 11125 | void CodeGenerator::visitWasmDerivedPointer(LWasmDerivedPointer* ins) { |
| 11126 | masm.computeEffectiveAddress( |
| 11127 | Address(ToRegister(ins->base()), int32_t(ins->mir()->offset())), |
| 11128 | ToRegister(ins->output())); |
| 11129 | } |
| 11130 | |
| 11131 | void CodeGenerator::visitWasmDerivedIndexPointer( |
| 11132 | LWasmDerivedIndexPointer* ins) { |
| 11133 | Register base = ToRegister(ins->base()); |
| 11134 | Register index = ToRegister(ins->index()); |
| 11135 | Register output = ToRegister(ins->output()); |
| 11136 | masm.computeEffectiveAddress(BaseIndex(base, index, ins->mir()->scale()), |
| 11137 | output); |
| 11138 | } |
| 11139 | |
| 11140 | void CodeGenerator::visitWasmStoreRef(LWasmStoreRef* ins) { |
| 11141 | Register instance = ToRegister(ins->instance()); |
| 11142 | Register valueBase = ToRegister(ins->valueBase()); |
| 11143 | size_t offset = ins->offset(); |
| 11144 | Register temp = ToRegister(ins->temp0()); |
| 11145 | |
| 11146 | Address addr(valueBase, offset); |
| 11147 | |
| 11148 | if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) { |
| 11149 | Label skipPreBarrier; |
| 11150 | wasm::EmitWasmPreBarrierGuard(masm, instance, temp, addr, &skipPreBarrier, |
| 11151 | ins->maybeTrap()); |
| 11152 | wasm::EmitWasmPreBarrierCallImmediate(masm, instance, temp, valueBase, |
| 11153 | offset); |
| 11154 | masm.bind(&skipPreBarrier); |
| 11155 | } |
| 11156 | |
| 11157 | FaultingCodeRange fcr; |
| 11158 | if (ins->value()->isBogus()) { |
| 11159 | fcr = masm.storePtr(ImmWord(0), addr); |
| 11160 | } else { |
| 11161 | Register value = ToRegister(ins->value()); |
| 11162 | fcr = masm.storePtr(value, addr); |
| 11163 | } |
| 11164 | |
| 11165 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 11166 | wasm::TrapMachineInsnForStoreWord()); |
| 11167 | // The postbarrier is handled separately. |
| 11168 | } |
| 11169 | |
| 11170 | void CodeGenerator::visitWasmStoreElementRef(LWasmStoreElementRef* ins) { |
| 11171 | Register instance = ToRegister(ins->instance()); |
| 11172 | Register base = ToRegister(ins->base()); |
| 11173 | Register index = ToRegister(ins->index()); |
| 11174 | Register temp0 = ToTempRegisterOrInvalid(ins->temp0()); |
| 11175 | Register temp1 = ToTempRegisterOrInvalid(ins->temp1()); |
| 11176 | |
| 11177 | BaseIndex addr(base, index, ScalePointer); |
| 11178 | |
| 11179 | if (ins->preBarrierKind() == WasmPreBarrierKind::Normal) { |
| 11180 | Label skipPreBarrier; |
| 11181 | wasm::EmitWasmPreBarrierGuard(masm, instance, temp0, addr, &skipPreBarrier, |
| 11182 | ins->maybeTrap()); |
| 11183 | wasm::EmitWasmPreBarrierCallIndex(masm, instance, temp0, temp1, addr); |
| 11184 | masm.bind(&skipPreBarrier); |
| 11185 | } |
| 11186 | |
| 11187 | FaultingCodeRange fcr; |
| 11188 | if (ins->value()->isBogus()) { |
| 11189 | fcr = masm.storePtr(ImmWord(0), addr); |
| 11190 | } else { |
| 11191 | Register value = ToRegister(ins->value()); |
| 11192 | fcr = masm.storePtr(value, addr); |
| 11193 | } |
| 11194 | |
| 11195 | EmitSignalNullCheckTrapSite(masm, ins, fcr, |
| 11196 | wasm::TrapMachineInsnForStoreWord()); |
| 11197 | // The postbarrier is handled separately. |
| 11198 | } |
| 11199 | |
| 11200 | void CodeGenerator::visitWasmPostWriteBarrierWholeCell( |
| 11201 | LWasmPostWriteBarrierWholeCell* lir) { |
| 11202 | Register object = ToRegister(lir->object()); |
| 11203 | Register value = ToRegister(lir->value()); |
| 11204 | Register temp = ToRegister(lir->temp0()); |
| 11205 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 11205); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg" ")"); do { MOZ_CrashSequence(__null, 11205); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11206 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 11207 | // Skip the barrier if this object was previously added to the store buffer. |
| 11208 | // We perform this check out of line because in practice the prior guards |
| 11209 | // eliminate most calls to the barrier. |
| 11210 | wasm::CheckWholeCellLastElementCache(masm, InstanceReg, object, temp, |
| 11211 | ool.rejoin()); |
| 11212 | |
| 11213 | saveLive(lir); |
| 11214 | masm.Push(InstanceReg); |
| 11215 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 11216 | |
| 11217 | // Call Instance::postBarrierWholeCell |
| 11218 | masm.setupWasmABICall(wasm::SymbolicAddress::PostBarrierWholeCell); |
| 11219 | masm.passABIArg(InstanceReg); |
| 11220 | masm.passABIArg(object); |
| 11221 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 11222 | masm.callWithABI(wasm::BytecodeOffset(0), |
| 11223 | wasm::SymbolicAddress::PostBarrierWholeCell, |
| 11224 | mozilla::Some(instanceOffset), ABIType::General); |
| 11225 | |
| 11226 | masm.Pop(InstanceReg); |
| 11227 | restoreLive(lir); |
| 11228 | |
| 11229 | masm.jump(ool.rejoin()); |
| 11230 | }); |
| 11231 | addOutOfLineCode(ool, lir->mir()); |
| 11232 | |
| 11233 | wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, |
| 11234 | ool->rejoin()); |
| 11235 | masm.jump(ool->entry()); |
| 11236 | masm.bind(ool->rejoin()); |
| 11237 | } |
| 11238 | |
| 11239 | void CodeGenerator::visitWasmPostWriteBarrierEdgeAtIndex( |
| 11240 | LWasmPostWriteBarrierEdgeAtIndex* lir) { |
| 11241 | Register object = ToRegister(lir->object()); |
| 11242 | Register value = ToRegister(lir->value()); |
| 11243 | Register valueBase = ToRegister(lir->valueBase()); |
| 11244 | Register index = ToRegister(lir->index()); |
| 11245 | Register temp = ToRegister(lir->temp0()); |
| 11246 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 11246); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->instance()) == InstanceReg" ")"); do { MOZ_CrashSequence(__null, 11246); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11247 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 11248 | saveLive(lir); |
| 11249 | masm.Push(InstanceReg); |
| 11250 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 11251 | |
| 11252 | // Fold the value offset into the value base |
| 11253 | if (lir->elemSize() == 16) { |
| 11254 | masm.lshiftPtr(Imm32(4), index, temp); |
| 11255 | masm.addPtr(valueBase, temp); |
| 11256 | } else { |
| 11257 | masm.computeEffectiveAddress( |
| 11258 | BaseIndex(valueBase, index, ScaleFromElemWidth(lir->elemSize())), |
| 11259 | temp); |
| 11260 | } |
| 11261 | |
| 11262 | // Call Instance::postBarrier |
| 11263 | masm.setupWasmABICall(wasm::SymbolicAddress::PostBarrierEdge); |
| 11264 | masm.passABIArg(InstanceReg); |
| 11265 | masm.passABIArg(temp); |
| 11266 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 11267 | masm.callWithABI(wasm::BytecodeOffset(0), |
| 11268 | wasm::SymbolicAddress::PostBarrierEdge, |
| 11269 | mozilla::Some(instanceOffset), ABIType::General); |
| 11270 | |
| 11271 | masm.Pop(InstanceReg); |
| 11272 | restoreLive(lir); |
| 11273 | |
| 11274 | masm.jump(ool.rejoin()); |
| 11275 | }); |
| 11276 | addOutOfLineCode(ool, lir->mir()); |
| 11277 | |
| 11278 | wasm::EmitWasmPostBarrierGuard(masm, mozilla::Some(object), temp, value, |
| 11279 | ool->rejoin()); |
| 11280 | masm.jump(ool->entry()); |
| 11281 | masm.bind(ool->rejoin()); |
| 11282 | } |
| 11283 | |
| 11284 | #ifdef ENABLE_WASM_JSPI1 |
| 11285 | void CodeGenerator::visitWasmResumeBarrier(LWasmResumeBarrier* lir) { |
| 11286 | Register instance = ToRegister(lir->instance()); |
| 11287 | Register cont = ToRegister(lir->cont()); |
| 11288 | Register scratch1 = ToRegister(lir->temp0()); |
| 11289 | |
| 11290 | auto* ool = new (alloc()) |
| 11291 | LambdaOutOfLineCode([this, lir, instance, cont](OutOfLineCode& ool) { |
| 11292 | saveLive(lir); |
| 11293 | wasm::EmitWasmResumeBarrier(masm, instance, cont); |
| 11294 | restoreLive(lir); |
| 11295 | masm.jump(ool.rejoin()); |
| 11296 | }); |
| 11297 | addOutOfLineCode(ool, (const BytecodeSite*)nullptr); |
| 11298 | |
| 11299 | wasm::EmitWasmResumeBarrierGuard(masm, instance, scratch1, ool->entry()); |
| 11300 | masm.bind(ool->rejoin()); |
| 11301 | } |
| 11302 | #endif // ENABLE_WASM_JSPI |
| 11303 | |
| 11304 | void CodeGenerator::visitWasmLoadSlotI64(LWasmLoadSlotI64* ins) { |
| 11305 | Register container = ToRegister(ins->containerRef()); |
| 11306 | Address addr(container, ins->offset()); |
| 11307 | Register64 output = ToOutRegister64(ins); |
| 11308 | // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one |
| 11309 | // transaction will always trap before the other, so it seems safest to |
| 11310 | // register both of them as potentially trapping. |
| 11311 | #ifdef JS_64BIT1 |
| 11312 | FaultingCodeRange fcr = masm.load64(addr, output); |
| 11313 | EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load64); |
| 11314 | #else |
| 11315 | FaultingCodeRangePair fcrp = masm.load64(addr, output); |
| 11316 | EmitSignalNullCheckTrapSite(masm, ins, fcrp.first, |
| 11317 | wasm::TrapMachineInsn::Load32); |
| 11318 | EmitSignalNullCheckTrapSite(masm, ins, fcrp.second, |
| 11319 | wasm::TrapMachineInsn::Load32); |
| 11320 | #endif |
| 11321 | } |
| 11322 | |
| 11323 | void CodeGenerator::visitWasmLoadElementI64(LWasmLoadElementI64* ins) { |
| 11324 | Register base = ToRegister(ins->base()); |
| 11325 | Register index = ToRegister(ins->index()); |
| 11326 | BaseIndex addr(base, index, Scale::TimesEight); |
| 11327 | Register64 output = ToOutRegister64(ins); |
| 11328 | // Either 1 or 2 words. On a 32-bit target, it is hard to argue that one |
| 11329 | // transaction will always trap before the other, so it seems safest to |
| 11330 | // register both of them as potentially trapping. |
| 11331 | #ifdef JS_64BIT1 |
| 11332 | FaultingCodeRange fcr = masm.load64(addr, output); |
| 11333 | EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Load64); |
| 11334 | #else |
| 11335 | FaultingCodeRangePair fcrp = masm.load64(addr, output); |
| 11336 | EmitSignalNullCheckTrapSite(masm, ins, fcrp.first, |
| 11337 | wasm::TrapMachineInsn::Load32); |
| 11338 | EmitSignalNullCheckTrapSite(masm, ins, fcrp.second, |
| 11339 | wasm::TrapMachineInsn::Load32); |
| 11340 | #endif |
| 11341 | } |
| 11342 | |
| 11343 | void CodeGenerator::visitWasmStoreSlotI64(LWasmStoreSlotI64* ins) { |
| 11344 | Register container = ToRegister(ins->containerRef()); |
| 11345 | Address addr(container, ins->offset()); |
| 11346 | Register64 value = ToRegister64(ins->value()); |
| 11347 | // Either 1 or 2 words. As above we register both transactions in the |
| 11348 | // 2-word case. |
| 11349 | #ifdef JS_64BIT1 |
| 11350 | FaultingCodeRange fcr = masm.store64(value, addr); |
| 11351 | EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Store64); |
| 11352 | #else |
| 11353 | FaultingCodeRangePair fcrp = masm.store64(value, addr); |
| 11354 | EmitSignalNullCheckTrapSite(masm, ins, fcrp.first, |
| 11355 | wasm::TrapMachineInsn::Store32); |
| 11356 | EmitSignalNullCheckTrapSite(masm, ins, fcrp.second, |
| 11357 | wasm::TrapMachineInsn::Store32); |
| 11358 | #endif |
| 11359 | } |
| 11360 | |
| 11361 | void CodeGenerator::visitWasmStoreElementI64(LWasmStoreElementI64* ins) { |
| 11362 | Register base = ToRegister(ins->base()); |
| 11363 | Register index = ToRegister(ins->index()); |
| 11364 | BaseIndex addr(base, index, Scale::TimesEight); |
| 11365 | Register64 value = ToRegister64(ins->value()); |
| 11366 | // Either 1 or 2 words. As above we register both transactions in the |
| 11367 | // 2-word case. |
| 11368 | #ifdef JS_64BIT1 |
| 11369 | FaultingCodeRange fcr = masm.store64(value, addr); |
| 11370 | EmitSignalNullCheckTrapSite(masm, ins, fcr, wasm::TrapMachineInsn::Store64); |
| 11371 | #else |
| 11372 | FaultingCodeRangePair fcrp = masm.store64(value, addr); |
| 11373 | EmitSignalNullCheckTrapSite(masm, ins, fcrp.first, |
| 11374 | wasm::TrapMachineInsn::Store32); |
| 11375 | EmitSignalNullCheckTrapSite(masm, ins, fcrp.second, |
| 11376 | wasm::TrapMachineInsn::Store32); |
| 11377 | #endif |
| 11378 | } |
| 11379 | |
| 11380 | void CodeGenerator::visitWasmClampTable64Address( |
| 11381 | LWasmClampTable64Address* lir) { |
| 11382 | Register64 address = ToRegister64(lir->address()); |
| 11383 | Register out = ToRegister(lir->output()); |
| 11384 | masm.wasmClampTable64Address(address, out); |
| 11385 | } |
| 11386 | |
| 11387 | void CodeGenerator::visitArrayBufferByteLength(LArrayBufferByteLength* lir) { |
| 11388 | Register obj = ToRegister(lir->object()); |
| 11389 | Register out = ToRegister(lir->output()); |
| 11390 | masm.loadArrayBufferByteLengthIntPtr(obj, out); |
| 11391 | } |
| 11392 | |
| 11393 | void CodeGenerator::visitArrayBufferViewLength(LArrayBufferViewLength* lir) { |
| 11394 | Register obj = ToRegister(lir->object()); |
| 11395 | Register out = ToRegister(lir->output()); |
| 11396 | masm.loadArrayBufferViewLengthIntPtr(obj, out); |
| 11397 | } |
| 11398 | |
| 11399 | void CodeGenerator::visitArrayBufferViewByteOffset( |
| 11400 | LArrayBufferViewByteOffset* lir) { |
| 11401 | Register obj = ToRegister(lir->object()); |
| 11402 | Register out = ToRegister(lir->output()); |
| 11403 | masm.loadArrayBufferViewByteOffsetIntPtr(obj, out); |
| 11404 | } |
| 11405 | |
| 11406 | void CodeGenerator::visitArrayBufferViewElements( |
| 11407 | LArrayBufferViewElements* lir) { |
| 11408 | Register obj = ToRegister(lir->object()); |
| 11409 | Register out = ToRegister(lir->output()); |
| 11410 | masm.loadPtr(Address(obj, ArrayBufferViewObject::dataOffset()), out); |
| 11411 | } |
| 11412 | |
| 11413 | void CodeGenerator::visitTypedArrayElementSize(LTypedArrayElementSize* lir) { |
| 11414 | Register obj = ToRegister(lir->object()); |
| 11415 | Register out = ToRegister(lir->output()); |
| 11416 | |
| 11417 | masm.typedArrayElementSize(obj, out); |
| 11418 | } |
| 11419 | |
| 11420 | void CodeGenerator::visitResizableTypedArrayLength( |
| 11421 | LResizableTypedArrayLength* lir) { |
| 11422 | Register obj = ToRegister(lir->object()); |
| 11423 | Register out = ToRegister(lir->output()); |
| 11424 | Register temp = ToRegister(lir->temp0()); |
| 11425 | |
| 11426 | auto sync = SynchronizeLoad(lir->mir()->requiresMemoryBarrier()); |
| 11427 | masm.loadResizableTypedArrayLengthIntPtr(sync, obj, out, temp); |
| 11428 | } |
| 11429 | |
| 11430 | void CodeGenerator::visitResizableDataViewByteLength( |
| 11431 | LResizableDataViewByteLength* lir) { |
| 11432 | Register obj = ToRegister(lir->object()); |
| 11433 | Register out = ToRegister(lir->output()); |
| 11434 | Register temp = ToRegister(lir->temp0()); |
| 11435 | |
| 11436 | auto sync = SynchronizeLoad(lir->mir()->requiresMemoryBarrier()); |
| 11437 | masm.loadResizableDataViewByteLengthIntPtr(sync, obj, out, temp); |
| 11438 | } |
| 11439 | |
| 11440 | void CodeGenerator::visitGrowableSharedArrayBufferByteLength( |
| 11441 | LGrowableSharedArrayBufferByteLength* lir) { |
| 11442 | Register obj = ToRegister(lir->object()); |
| 11443 | Register out = ToRegister(lir->output()); |
| 11444 | |
| 11445 | // Explicit |byteLength| accesses are seq-consistent atomic loads. |
| 11446 | auto sync = Synchronization::Load(); |
| 11447 | |
| 11448 | masm.loadGrowableSharedArrayBufferByteLengthIntPtr(sync, obj, out); |
| 11449 | } |
| 11450 | |
| 11451 | void CodeGenerator::visitGuardResizableArrayBufferViewInBounds( |
| 11452 | LGuardResizableArrayBufferViewInBounds* lir) { |
| 11453 | Register obj = ToRegister(lir->object()); |
| 11454 | Register temp = ToRegister(lir->temp0()); |
| 11455 | |
| 11456 | Label bail; |
| 11457 | masm.branchIfResizableArrayBufferViewOutOfBounds(obj, temp, &bail); |
| 11458 | bailoutFrom(&bail, lir->snapshot()); |
| 11459 | } |
| 11460 | |
| 11461 | void CodeGenerator::visitGuardResizableArrayBufferViewInBoundsOrDetached( |
| 11462 | LGuardResizableArrayBufferViewInBoundsOrDetached* lir) { |
| 11463 | Register obj = ToRegister(lir->object()); |
| 11464 | Register temp = ToRegister(lir->temp0()); |
| 11465 | |
| 11466 | Label done, bail; |
| 11467 | masm.branchIfResizableArrayBufferViewInBounds(obj, temp, &done); |
| 11468 | masm.branchIfHasAttachedArrayBuffer(obj, temp, &bail); |
| 11469 | masm.bind(&done); |
| 11470 | bailoutFrom(&bail, lir->snapshot()); |
| 11471 | } |
| 11472 | |
| 11473 | void CodeGenerator::visitGuardHasAttachedArrayBuffer( |
| 11474 | LGuardHasAttachedArrayBuffer* lir) { |
| 11475 | Register obj = ToRegister(lir->object()); |
| 11476 | Register temp = ToRegister(lir->temp0()); |
| 11477 | |
| 11478 | Label bail; |
| 11479 | masm.branchIfHasDetachedArrayBuffer(obj, temp, &bail); |
| 11480 | bailoutFrom(&bail, lir->snapshot()); |
| 11481 | } |
| 11482 | |
| 11483 | void CodeGenerator::visitGuardTypedArraySetOffset( |
| 11484 | LGuardTypedArraySetOffset* lir) { |
| 11485 | Register offset = ToRegister(lir->offset()); |
| 11486 | Register targetLength = ToRegister(lir->targetLength()); |
| 11487 | Register sourceLength = ToRegister(lir->sourceLength()); |
| 11488 | Register temp = ToRegister(lir->temp0()); |
| 11489 | |
| 11490 | Label bail; |
| 11491 | |
| 11492 | // Ensure `offset <= target.length`. |
| 11493 | masm.movePtr(targetLength, temp); |
| 11494 | masm.branchSubPtr(Assembler::Signed, offset, temp, &bail); |
| 11495 | |
| 11496 | // Ensure `source.length <= (target.length - offset)`. |
| 11497 | masm.branchPtr(Assembler::GreaterThan, sourceLength, temp, &bail); |
| 11498 | |
| 11499 | bailoutFrom(&bail, lir->snapshot()); |
| 11500 | } |
| 11501 | |
| 11502 | void CodeGenerator::visitTypedArrayFill(LTypedArrayFill* lir) { |
| 11503 | auto elementType = lir->mir()->elementType(); |
| 11504 | MOZ_ASSERT(!Scalar::isBigIntType(elementType))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!Scalar::isBigIntType(elementType))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!Scalar::isBigIntType(elementType )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!Scalar::isBigIntType(elementType)", "./../../../../js/src/jit/CodeGenerator.cpp" , 11504); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!Scalar::isBigIntType(elementType)" ")"); do { MOZ_CrashSequence(__null, 11504); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11505 | |
| 11506 | masm.setupAlignedABICall(); |
| 11507 | masm.passABIArg(ToRegister(lir->object())); |
| 11508 | if (elementType == Scalar::Float64) { |
| 11509 | masm.passABIArg(ToFloatRegister(lir->value()), ABIType::Float64); |
| 11510 | } else if (elementType == Scalar::Float32 || elementType == Scalar::Float16) { |
| 11511 | masm.passABIArg(ToFloatRegister(lir->value()), ABIType::Float32); |
| 11512 | } else { |
| 11513 | MOZ_ASSERT(!Scalar::isFloatingType(elementType))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!Scalar::isFloatingType(elementType))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!Scalar::isFloatingType(elementType )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!Scalar::isFloatingType(elementType)", "./../../../../js/src/jit/CodeGenerator.cpp" , 11513); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!Scalar::isFloatingType(elementType)" ")"); do { MOZ_CrashSequence(__null, 11513); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11514 | masm.passABIArg(ToRegister(lir->value())); |
| 11515 | } |
| 11516 | masm.passABIArg(ToRegister(lir->start())); |
| 11517 | masm.passABIArg(ToRegister(lir->end())); |
| 11518 | |
| 11519 | if (elementType == Scalar::Float64) { |
| 11520 | using Fn = void (*)(TypedArrayObject*, double, intptr_t, intptr_t); |
| 11521 | masm.callWithABI<Fn, js::TypedArrayFillDouble>(); |
| 11522 | } else if (elementType == Scalar::Float32 || elementType == Scalar::Float16) { |
| 11523 | using Fn = void (*)(TypedArrayObject*, float, intptr_t, intptr_t); |
| 11524 | masm.callWithABI<Fn, js::TypedArrayFillFloat32>(); |
| 11525 | } else { |
| 11526 | // All other types are managed using int32. |
| 11527 | MOZ_ASSERT(Scalar::byteSize(elementType) <= sizeof(int32_t))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Scalar::byteSize(elementType) <= sizeof(int32_t)) >::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(Scalar::byteSize(elementType) <= sizeof(int32_t)) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("Scalar::byteSize(elementType) <= sizeof(int32_t)" , "./../../../../js/src/jit/CodeGenerator.cpp", 11527); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "Scalar::byteSize(elementType) <= sizeof(int32_t)" ")"); do { MOZ_CrashSequence(__null, 11527); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11528 | |
| 11529 | using Fn = void (*)(TypedArrayObject*, int32_t, intptr_t, intptr_t); |
| 11530 | masm.callWithABI<Fn, js::TypedArrayFillInt32>(); |
| 11531 | } |
| 11532 | } |
| 11533 | |
| 11534 | void CodeGenerator::visitTypedArrayFill64(LTypedArrayFill64* lir) { |
| 11535 | MOZ_ASSERT(Scalar::isBigIntType(lir->mir()->elementType()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Scalar::isBigIntType(lir->mir()->elementType() ))>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(Scalar::isBigIntType(lir->mir()->elementType() )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("Scalar::isBigIntType(lir->mir()->elementType())", "./../../../../js/src/jit/CodeGenerator.cpp" , 11535); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->elementType())" ")"); do { MOZ_CrashSequence(__null, 11535); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11536 | |
| 11537 | masm.setupAlignedABICall(); |
| 11538 | masm.passABIArg(ToRegister(lir->object())); |
| 11539 | masm.passABIArg(ToRegister64(lir->value())); |
| 11540 | masm.passABIArg(ToRegister(lir->start())); |
| 11541 | masm.passABIArg(ToRegister(lir->end())); |
| 11542 | |
| 11543 | using Fn = void (*)(TypedArrayObject*, int64_t, intptr_t, intptr_t); |
| 11544 | masm.callWithABI<Fn, js::TypedArrayFillInt64>(); |
| 11545 | } |
| 11546 | |
| 11547 | void CodeGenerator::visitTypedArraySet(LTypedArraySet* lir) { |
| 11548 | Register target = ToRegister(lir->target()); |
| 11549 | Register source = ToRegister(lir->source()); |
| 11550 | Register offset = ToRegister(lir->offset()); |
| 11551 | |
| 11552 | // Bit-wise copying is infallible because it doesn't need to allocate any |
| 11553 | // temporary memory, even if the underlying buffers are the same. |
| 11554 | if (lir->mir()->canUseBitwiseCopy()) { |
| 11555 | masm.setupAlignedABICall(); |
| 11556 | masm.passABIArg(target); |
| 11557 | masm.passABIArg(source); |
| 11558 | masm.passABIArg(offset); |
| 11559 | |
| 11560 | using Fn = void (*)(TypedArrayObject*, TypedArrayObject*, intptr_t); |
| 11561 | masm.callWithABI<Fn, js::TypedArraySetInfallible>(); |
| 11562 | } else { |
| 11563 | pushArg(offset); |
| 11564 | pushArg(source); |
| 11565 | pushArg(target); |
| 11566 | |
| 11567 | using Fn = |
| 11568 | bool (*)(JSContext*, TypedArrayObject*, TypedArrayObject*, intptr_t); |
| 11569 | callVM<Fn, js::TypedArraySet>(lir); |
| 11570 | } |
| 11571 | } |
| 11572 | |
| 11573 | void CodeGenerator::visitTypedArraySetFromSubarray( |
| 11574 | LTypedArraySetFromSubarray* lir) { |
| 11575 | Register target = ToRegister(lir->target()); |
| 11576 | Register source = ToRegister(lir->source()); |
| 11577 | Register offset = ToRegister(lir->offset()); |
| 11578 | Register sourceOffset = ToRegister(lir->sourceOffset()); |
| 11579 | Register sourceLength = ToRegister(lir->sourceLength()); |
| 11580 | |
| 11581 | // Bit-wise copying is infallible because it doesn't need to allocate any |
| 11582 | // temporary memory, even if the underlying buffers are the same. |
| 11583 | if (lir->mir()->canUseBitwiseCopy()) { |
| 11584 | masm.setupAlignedABICall(); |
| 11585 | masm.passABIArg(target); |
| 11586 | masm.passABIArg(source); |
| 11587 | masm.passABIArg(offset); |
| 11588 | masm.passABIArg(sourceOffset); |
| 11589 | masm.passABIArg(sourceLength); |
| 11590 | |
| 11591 | using Fn = void (*)(TypedArrayObject*, TypedArrayObject*, intptr_t, |
| 11592 | intptr_t, intptr_t); |
| 11593 | masm.callWithABI<Fn, js::TypedArraySetFromSubarrayInfallible>(); |
| 11594 | } else { |
| 11595 | pushArg(sourceLength); |
| 11596 | pushArg(sourceOffset); |
| 11597 | pushArg(offset); |
| 11598 | pushArg(source); |
| 11599 | pushArg(target); |
| 11600 | |
| 11601 | using Fn = bool (*)(JSContext*, TypedArrayObject*, TypedArrayObject*, |
| 11602 | intptr_t, intptr_t, intptr_t); |
| 11603 | callVM<Fn, js::TypedArraySetFromSubarray>(lir); |
| 11604 | } |
| 11605 | } |
| 11606 | |
| 11607 | void CodeGenerator::visitTypedArraySubarray(LTypedArraySubarray* lir) { |
| 11608 | pushArg(ToRegister(lir->length())); |
| 11609 | pushArg(ToRegister(lir->start())); |
| 11610 | pushArg(ToRegister(lir->object())); |
| 11611 | |
| 11612 | using Fn = TypedArrayObject* (*)(JSContext*, Handle<TypedArrayObject*>, |
| 11613 | intptr_t, intptr_t); |
| 11614 | callVM<Fn, js::TypedArraySubarrayWithLength>(lir); |
| 11615 | } |
| 11616 | |
| 11617 | void CodeGenerator::visitToIntegerIndex(LToIntegerIndex* lir) { |
| 11618 | Register index = ToRegister(lir->index()); |
| 11619 | Register length = ToRegister(lir->length()); |
| 11620 | Register output = ToRegister(lir->output()); |
| 11621 | |
| 11622 | masm.movePtr(index, output); |
| 11623 | |
| 11624 | Label done, notNegative; |
| 11625 | masm.branchTestPtr(Assembler::NotSigned, index, index, ¬Negative); |
| 11626 | { |
| 11627 | masm.branchAddPtr(Assembler::NotSigned, length, output, &done); |
| 11628 | masm.movePtr(ImmWord(0), output); |
| 11629 | masm.jump(&done); |
| 11630 | } |
| 11631 | masm.bind(¬Negative); |
| 11632 | { |
| 11633 | masm.cmpPtrMovePtr(Assembler::GreaterThan, index, length, length, output); |
| 11634 | } |
| 11635 | masm.bind(&done); |
| 11636 | } |
| 11637 | |
| 11638 | void CodeGenerator::visitGuardNumberToIntPtrIndex( |
| 11639 | LGuardNumberToIntPtrIndex* lir) { |
| 11640 | FloatRegister input = ToFloatRegister(lir->input()); |
| 11641 | Register output = ToRegister(lir->output()); |
| 11642 | |
| 11643 | if (!lir->mir()->supportOOB()) { |
| 11644 | Label bail; |
| 11645 | masm.convertDoubleToPtr(input, output, &bail, false); |
| 11646 | bailoutFrom(&bail, lir->snapshot()); |
| 11647 | return; |
| 11648 | } |
| 11649 | |
| 11650 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 11651 | // Substitute the invalid index with an arbitrary out-of-bounds index. |
| 11652 | masm.movePtr(ImmWord(-1), output); |
| 11653 | masm.jump(ool.rejoin()); |
| 11654 | }); |
| 11655 | addOutOfLineCode(ool, lir->mir()); |
| 11656 | |
| 11657 | masm.convertDoubleToPtr(input, output, ool->entry(), false); |
| 11658 | masm.bind(ool->rejoin()); |
| 11659 | } |
| 11660 | |
| 11661 | void CodeGenerator::visitStringLength(LStringLength* lir) { |
| 11662 | Register input = ToRegister(lir->string()); |
| 11663 | Register output = ToRegister(lir->output()); |
| 11664 | |
| 11665 | masm.loadStringLength(input, output); |
| 11666 | } |
| 11667 | |
| 11668 | void CodeGenerator::visitMinMaxI(LMinMaxI* ins) { |
| 11669 | Register first = ToRegister(ins->first()); |
| 11670 | Register output = ToRegister(ins->output()); |
| 11671 | |
| 11672 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 11672); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "first == output" ")"); do { MOZ_CrashSequence (__null, 11672); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 11673 | |
| 11674 | if (ins->second()->isConstant()) { |
| 11675 | auto second = Imm32(ToInt32(ins->second())); |
| 11676 | |
| 11677 | if (ins->mir()->isMax()) { |
| 11678 | masm.max32(first, second, output); |
| 11679 | } else { |
| 11680 | masm.min32(first, second, output); |
| 11681 | } |
| 11682 | } else { |
| 11683 | Register second = ToRegister(ins->second()); |
| 11684 | |
| 11685 | if (ins->mir()->isMax()) { |
| 11686 | masm.max32(first, second, output); |
| 11687 | } else { |
| 11688 | masm.min32(first, second, output); |
| 11689 | } |
| 11690 | } |
| 11691 | } |
| 11692 | |
| 11693 | void CodeGenerator::visitMinMaxIntPtr(LMinMaxIntPtr* ins) { |
| 11694 | Register first = ToRegister(ins->first()); |
| 11695 | Register output = ToRegister(ins->output()); |
| 11696 | |
| 11697 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 11697); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "first == output" ")"); do { MOZ_CrashSequence (__null, 11697); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 11698 | |
| 11699 | if (ins->second()->isConstant()) { |
| 11700 | auto second = ImmWord(ToIntPtr(ins->second())); |
| 11701 | |
| 11702 | if (ins->mir()->isMax()) { |
| 11703 | masm.maxPtr(first, second, output); |
| 11704 | } else { |
| 11705 | masm.minPtr(first, second, output); |
| 11706 | } |
| 11707 | } else { |
| 11708 | Register second = ToRegister(ins->second()); |
| 11709 | |
| 11710 | if (ins->mir()->isMax()) { |
| 11711 | masm.maxPtr(first, second, output); |
| 11712 | } else { |
| 11713 | masm.minPtr(first, second, output); |
| 11714 | } |
| 11715 | } |
| 11716 | } |
| 11717 | |
| 11718 | void CodeGenerator::visitMinMaxArrayI(LMinMaxArrayI* ins) { |
| 11719 | Register array = ToRegister(ins->array()); |
| 11720 | Register output = ToRegister(ins->output()); |
| 11721 | Register temp1 = ToRegister(ins->temp0()); |
| 11722 | Register temp2 = ToRegister(ins->temp1()); |
| 11723 | Register temp3 = ToRegister(ins->temp2()); |
| 11724 | bool isMax = ins->mir()->isMax(); |
| 11725 | |
| 11726 | Label bail; |
| 11727 | masm.minMaxArrayInt32(array, output, temp1, temp2, temp3, isMax, &bail); |
| 11728 | bailoutFrom(&bail, ins->snapshot()); |
| 11729 | } |
| 11730 | |
| 11731 | void CodeGenerator::visitMinMaxArrayD(LMinMaxArrayD* ins) { |
| 11732 | Register array = ToRegister(ins->array()); |
| 11733 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11734 | FloatRegister floatTemp = ToFloatRegister(ins->temp0()); |
| 11735 | Register temp1 = ToRegister(ins->temp1()); |
| 11736 | Register temp2 = ToRegister(ins->temp2()); |
| 11737 | bool isMax = ins->mir()->isMax(); |
| 11738 | |
| 11739 | Label bail; |
| 11740 | masm.minMaxArrayNumber(array, output, floatTemp, temp1, temp2, isMax, &bail); |
| 11741 | bailoutFrom(&bail, ins->snapshot()); |
| 11742 | } |
| 11743 | |
| 11744 | // For Abs*, lowering will have tied input to output on platforms where that is |
| 11745 | // sensible, and otherwise left them untied. |
| 11746 | |
| 11747 | void CodeGenerator::visitAbsI(LAbsI* ins) { |
| 11748 | Register input = ToRegister(ins->input()); |
| 11749 | Register output = ToRegister(ins->output()); |
| 11750 | |
| 11751 | if (ins->mir()->fallible()) { |
| 11752 | Label positive; |
| 11753 | if (input != output) { |
| 11754 | masm.move32(input, output); |
| 11755 | } |
| 11756 | masm.branchTest32(Assembler::NotSigned, output, output, &positive); |
| 11757 | Label bail; |
| 11758 | masm.branchNeg32(Assembler::Overflow, output, &bail); |
| 11759 | bailoutFrom(&bail, ins->snapshot()); |
| 11760 | masm.bind(&positive); |
| 11761 | } else { |
| 11762 | masm.abs32(input, output); |
| 11763 | } |
| 11764 | } |
| 11765 | |
| 11766 | void CodeGenerator::visitAbsD(LAbsD* ins) { |
| 11767 | masm.absDouble(ToFloatRegister(ins->input()), ToFloatRegister(ins->output())); |
| 11768 | } |
| 11769 | |
| 11770 | void CodeGenerator::visitAbsF(LAbsF* ins) { |
| 11771 | masm.absFloat32(ToFloatRegister(ins->input()), |
| 11772 | ToFloatRegister(ins->output())); |
| 11773 | } |
| 11774 | |
| 11775 | void CodeGenerator::visitPowII(LPowII* ins) { |
| 11776 | Register value = ToRegister(ins->value()); |
| 11777 | Register power = ToRegister(ins->power()); |
| 11778 | Register output = ToRegister(ins->output()); |
| 11779 | Register temp0 = ToRegister(ins->temp0()); |
| 11780 | Register temp1 = ToRegister(ins->temp1()); |
| 11781 | |
| 11782 | Label bailout; |
| 11783 | masm.pow32(value, power, output, temp0, temp1, &bailout); |
| 11784 | bailoutFrom(&bailout, ins->snapshot()); |
| 11785 | } |
| 11786 | |
| 11787 | void CodeGenerator::visitPowI(LPowI* ins) { |
| 11788 | FloatRegister value = ToFloatRegister(ins->value()); |
| 11789 | Register power = ToRegister(ins->power()); |
| 11790 | |
| 11791 | using Fn = double (*)(double x, int32_t y); |
| 11792 | masm.setupAlignedABICall(); |
| 11793 | masm.passABIArg(value, ABIType::Float64); |
| 11794 | masm.passABIArg(power); |
| 11795 | |
| 11796 | masm.callWithABI<Fn, js::powi>(ABIType::Float64); |
| 11797 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11797); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 11797); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11798 | } |
| 11799 | |
| 11800 | void CodeGenerator::visitPowD(LPowD* ins) { |
| 11801 | FloatRegister value = ToFloatRegister(ins->value()); |
| 11802 | FloatRegister power = ToFloatRegister(ins->power()); |
| 11803 | |
| 11804 | using Fn = double (*)(double x, double y); |
| 11805 | masm.setupAlignedABICall(); |
| 11806 | masm.passABIArg(value, ABIType::Float64); |
| 11807 | masm.passABIArg(power, ABIType::Float64); |
| 11808 | masm.callWithABI<Fn, ecmaPow>(ABIType::Float64); |
| 11809 | |
| 11810 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11810); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 11810); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11811 | } |
| 11812 | |
| 11813 | void CodeGenerator::visitPowOfTwoI(LPowOfTwoI* ins) { |
| 11814 | Register power = ToRegister(ins->power()); |
| 11815 | Register output = ToRegister(ins->output()); |
| 11816 | |
| 11817 | uint32_t base = ins->base(); |
| 11818 | MOZ_ASSERT(std::has_single_bit(base))do { static_assert( mozilla::detail::AssertionConditionType< decltype(std::has_single_bit(base))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(std::has_single_bit(base)))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("std::has_single_bit(base)" , "./../../../../js/src/jit/CodeGenerator.cpp", 11818); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "std::has_single_bit(base)" ")"); do { MOZ_CrashSequence (__null, 11818); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 11819 | |
| 11820 | uint32_t n = mozilla::FloorLog2(base); |
| 11821 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11821); AnnotateMozCrashReason("MOZ_ASSERT" "(" "n != 0" ")" ); do { MOZ_CrashSequence(__null, 11821); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 11822 | |
| 11823 | // Hacker's Delight, 2nd edition, theorem D2. |
| 11824 | auto ceilingDiv = [](uint32_t x, uint32_t y) { return (x + y - 1) / y; }; |
| 11825 | |
| 11826 | // Take bailout if |power| is greater-or-equals |log_y(2^31)| or is negative. |
| 11827 | // |2^(n*y) < 2^31| must hold, hence |n*y < 31| resp. |y < 31/n|. |
| 11828 | // |
| 11829 | // Note: it's important for this condition to match the code in CacheIR.cpp |
| 11830 | // (CanAttachInt32Pow) to prevent failure loops. |
| 11831 | bailoutCmp32(Assembler::AboveOrEqual, power, Imm32(ceilingDiv(31, n)), |
| 11832 | ins->snapshot()); |
| 11833 | |
| 11834 | // Compute (2^n)^y as 2^(n*y) using repeated shifts. We could directly scale |
| 11835 | // |power| and perform a single shift, but due to the lack of necessary |
| 11836 | // MacroAssembler functionality, like multiplying a register with an |
| 11837 | // immediate, we restrict the number of generated shift instructions when |
| 11838 | // lowering this operation. |
| 11839 | masm.move32(Imm32(1), output); |
| 11840 | do { |
| 11841 | masm.lshift32(power, output); |
| 11842 | n--; |
| 11843 | } while (n > 0); |
| 11844 | } |
| 11845 | |
| 11846 | void CodeGenerator::visitSqrtD(LSqrtD* ins) { |
| 11847 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11848 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11849 | masm.sqrtDouble(input, output); |
| 11850 | } |
| 11851 | |
| 11852 | void CodeGenerator::visitSqrtF(LSqrtF* ins) { |
| 11853 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11854 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11855 | masm.sqrtFloat32(input, output); |
| 11856 | } |
| 11857 | |
| 11858 | void CodeGenerator::visitSignI(LSignI* ins) { |
| 11859 | Register input = ToRegister(ins->input()); |
| 11860 | Register output = ToRegister(ins->output()); |
| 11861 | masm.signInt32(input, output); |
| 11862 | } |
| 11863 | |
| 11864 | void CodeGenerator::visitSignD(LSignD* ins) { |
| 11865 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11866 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11867 | masm.signDouble(input, output); |
| 11868 | } |
| 11869 | |
| 11870 | void CodeGenerator::visitSignDI(LSignDI* ins) { |
| 11871 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11872 | FloatRegister temp = ToFloatRegister(ins->temp0()); |
| 11873 | Register output = ToRegister(ins->output()); |
| 11874 | |
| 11875 | Label bail; |
| 11876 | masm.signDoubleToInt32(input, output, temp, &bail); |
| 11877 | bailoutFrom(&bail, ins->snapshot()); |
| 11878 | } |
| 11879 | |
| 11880 | void CodeGenerator::visitSignID(LSignID* ins) { |
| 11881 | Register input = ToRegister(ins->input()); |
| 11882 | Register temp = ToRegister(ins->temp0()); |
| 11883 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11884 | |
| 11885 | masm.signInt32(input, temp); |
| 11886 | masm.convertInt32ToDouble(temp, output); |
| 11887 | } |
| 11888 | |
| 11889 | void CodeGenerator::visitMathFunctionD(LMathFunctionD* ins) { |
| 11890 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11891 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11891); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 11891); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11892 | |
| 11893 | UnaryMathFunction fun = ins->mir()->function(); |
| 11894 | UnaryMathFunctionType funPtr = GetUnaryMathFunctionPtr(fun); |
| 11895 | |
| 11896 | masm.setupAlignedABICall(); |
| 11897 | |
| 11898 | masm.passABIArg(input, ABIType::Float64); |
| 11899 | masm.callWithABI(DynamicFunction<UnaryMathFunctionType>(funPtr), |
| 11900 | ABIType::Float64); |
| 11901 | } |
| 11902 | |
| 11903 | void CodeGenerator::visitMathFunctionF(LMathFunctionF* ins) { |
| 11904 | FloatRegister input = ToFloatRegister(ins->input()); |
| 11905 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 11905); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnFloat32Reg" ")"); do { MOZ_CrashSequence(__null, 11905); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11906 | |
| 11907 | masm.setupAlignedABICall(); |
| 11908 | masm.passABIArg(input, ABIType::Float32); |
| 11909 | |
| 11910 | using Fn = float (*)(float x); |
| 11911 | Fn funptr = nullptr; |
| 11912 | CheckUnsafeCallWithABI check = CheckUnsafeCallWithABI::Check; |
| 11913 | switch (ins->mir()->function()) { |
| 11914 | case UnaryMathFunction::Floor: |
| 11915 | funptr = std::floor; |
| 11916 | check = CheckUnsafeCallWithABI::DontCheckOther; |
| 11917 | break; |
| 11918 | case UnaryMathFunction::Round: |
| 11919 | funptr = math_roundf_impl; |
| 11920 | break; |
| 11921 | case UnaryMathFunction::Trunc: |
| 11922 | funptr = std::trunc; |
| 11923 | check = CheckUnsafeCallWithABI::DontCheckOther; |
| 11924 | break; |
| 11925 | case UnaryMathFunction::Ceil: |
| 11926 | funptr = std::ceil; |
| 11927 | check = CheckUnsafeCallWithABI::DontCheckOther; |
| 11928 | break; |
| 11929 | default: |
| 11930 | MOZ_CRASH("Unknown or unsupported float32 math function")do { do { } while (false); MOZ_ReportCrash("" "Unknown or unsupported float32 math function" , "./../../../../js/src/jit/CodeGenerator.cpp", 11930); AnnotateMozCrashReason ("MOZ_CRASH(" "Unknown or unsupported float32 math function" ")" ); do { MOZ_CrashSequence(__null, 11930); __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 11931 | } |
| 11932 | |
| 11933 | masm.callWithABI(DynamicFunction<Fn>(funptr), ABIType::Float32, check); |
| 11934 | } |
| 11935 | |
| 11936 | void CodeGenerator::visitModD(LModD* ins) { |
| 11937 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 11937); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 11937); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 11938 | |
| 11939 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
| 11940 | FloatRegister rhs = ToFloatRegister(ins->rhs()); |
| 11941 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11942 | Register temp1 = ToRegister(ins->temp0()); |
| 11943 | Register temp2 = ToRegister(ins->temp1()); |
| 11944 | |
| 11945 | LiveRegisterSet liveRegisterSet = liveVolatileRegs(ins); |
| 11946 | |
| 11947 | Label call, done; |
| 11948 | // The fast path's internal integer division may itself call out to a runtime |
| 11949 | // routine (on ARM without a hardware divide), and needs the volatile live set |
| 11950 | // to know which of our registers to preserve across that call. |
| 11951 | masm.modDoubleIntegerFastPath(lhs, rhs, output, temp1, temp2, liveRegisterSet, |
| 11952 | &call); |
| 11953 | masm.jump(&done); |
| 11954 | |
| 11955 | masm.bind(&call); |
| 11956 | { |
| 11957 | LiveRegisterSet save = liveRegisterSet; |
| 11958 | save.takeUnchecked(output); |
| 11959 | |
| 11960 | masm.PushRegsInMask(save); |
| 11961 | using Fn = double (*)(double a, double b); |
| 11962 | masm.setupUnalignedABICall(temp1); |
| 11963 | masm.passABIArg(lhs, ABIType::Float64); |
| 11964 | masm.passABIArg(rhs, ABIType::Float64); |
| 11965 | masm.callWithABI<Fn, NumberMod>(ABIType::Float64); |
| 11966 | masm.storeCallFloatResult(output); |
| 11967 | masm.PopRegsInMask(save); |
| 11968 | } |
| 11969 | |
| 11970 | masm.bind(&done); |
| 11971 | } |
| 11972 | |
| 11973 | void CodeGenerator::visitModPowTwoD(LModPowTwoD* ins) { |
| 11974 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
| 11975 | uint32_t divisor = ins->divisor(); |
| 11976 | MOZ_ASSERT(std::has_single_bit(divisor))do { static_assert( mozilla::detail::AssertionConditionType< decltype(std::has_single_bit(divisor))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(std::has_single_bit(divisor) ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "std::has_single_bit(divisor)", "./../../../../js/src/jit/CodeGenerator.cpp" , 11976); AnnotateMozCrashReason("MOZ_ASSERT" "(" "std::has_single_bit(divisor)" ")"); do { MOZ_CrashSequence(__null, 11976); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 11977 | |
| 11978 | FloatRegister output = ToFloatRegister(ins->output()); |
| 11979 | |
| 11980 | // Compute |n % d| using |copysign(n - (d * trunc(n / d)), n)|. |
| 11981 | // |
| 11982 | // This doesn't work if |d| isn't a power of two, because we may lose too much |
| 11983 | // precision. For example |Number.MAX_VALUE % 3 == 2|, but |
| 11984 | // |3 * trunc(Number.MAX_VALUE / 3) == Infinity|. |
| 11985 | |
| 11986 | Label done; |
| 11987 | { |
| 11988 | ScratchDoubleScope scratch(masm); |
| 11989 | |
| 11990 | // Subnormals can lead to performance degradation, which can make calling |
| 11991 | // |fmod| faster than this inline implementation. Work around this issue by |
| 11992 | // directly returning the input for any value in the interval ]-1, +1[. |
| 11993 | Label notSubnormal; |
| 11994 | masm.loadConstantDouble(1.0, scratch); |
| 11995 | masm.loadConstantDouble(-1.0, output); |
| 11996 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, lhs, scratch, |
| 11997 | ¬Subnormal); |
| 11998 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, lhs, output, |
| 11999 | ¬Subnormal); |
| 12000 | |
| 12001 | masm.moveDouble(lhs, output); |
| 12002 | masm.jump(&done); |
| 12003 | |
| 12004 | masm.bind(¬Subnormal); |
| 12005 | |
| 12006 | if (divisor == 1) { |
| 12007 | // The pattern |n % 1 == 0| is used to detect integer numbers. We can skip |
| 12008 | // the multiplication by one in this case. |
| 12009 | masm.moveDouble(lhs, output); |
| 12010 | masm.nearbyIntDouble(RoundingMode::TowardsZero, output, scratch); |
| 12011 | masm.subDouble(scratch, output); |
| 12012 | } else { |
| 12013 | masm.loadConstantDouble(1.0 / double(divisor), scratch); |
| 12014 | masm.loadConstantDouble(double(divisor), output); |
| 12015 | |
| 12016 | masm.mulDouble(lhs, scratch); |
| 12017 | masm.nearbyIntDouble(RoundingMode::TowardsZero, scratch, scratch); |
| 12018 | masm.mulDouble(output, scratch); |
| 12019 | |
| 12020 | masm.moveDouble(lhs, output); |
| 12021 | masm.subDouble(scratch, output); |
| 12022 | } |
| 12023 | } |
| 12024 | |
| 12025 | masm.copySignDouble(output, lhs, output); |
| 12026 | masm.bind(&done); |
| 12027 | } |
| 12028 | |
| 12029 | void CodeGenerator::visitWasmBuiltinModD(LWasmBuiltinModD* ins) { |
| 12030 | masm.Push(InstanceReg); |
| 12031 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 12032 | |
| 12033 | FloatRegister lhs = ToFloatRegister(ins->lhs()); |
| 12034 | FloatRegister rhs = ToFloatRegister(ins->rhs()); |
| 12035 | |
| 12036 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 12036); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 12036); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12037 | |
| 12038 | masm.setupWasmABICall(wasm::SymbolicAddress::ModD); |
| 12039 | masm.passABIArg(lhs, ABIType::Float64); |
| 12040 | masm.passABIArg(rhs, ABIType::Float64); |
| 12041 | |
| 12042 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 12043 | masm.callWithABI(ins->mir()->bytecodeOffset(), wasm::SymbolicAddress::ModD, |
| 12044 | mozilla::Some(instanceOffset), ABIType::Float64); |
| 12045 | |
| 12046 | masm.Pop(InstanceReg); |
| 12047 | } |
| 12048 | |
| 12049 | void CodeGenerator::visitClzI(LClzI* ins) { |
| 12050 | Register input = ToRegister(ins->input()); |
| 12051 | Register output = ToRegister(ins->output()); |
| 12052 | bool knownNotZero = ins->mir()->operandIsNeverZero(); |
| 12053 | |
| 12054 | masm.clz32(input, output, knownNotZero); |
| 12055 | } |
| 12056 | |
| 12057 | void CodeGenerator::visitCtzI(LCtzI* ins) { |
| 12058 | Register input = ToRegister(ins->input()); |
| 12059 | Register output = ToRegister(ins->output()); |
| 12060 | bool knownNotZero = ins->mir()->operandIsNeverZero(); |
| 12061 | |
| 12062 | masm.ctz32(input, output, knownNotZero); |
| 12063 | } |
| 12064 | |
| 12065 | void CodeGenerator::visitPopcntI(LPopcntI* ins) { |
| 12066 | Register input = ToRegister(ins->input()); |
| 12067 | Register output = ToRegister(ins->output()); |
| 12068 | Register temp = ToRegister(ins->temp0()); |
| 12069 | |
| 12070 | masm.popcnt32(input, output, temp); |
| 12071 | } |
| 12072 | |
| 12073 | void CodeGenerator::visitClzI64(LClzI64* ins) { |
| 12074 | Register64 input = ToRegister64(ins->input()); |
| 12075 | Register64 output = ToOutRegister64(ins); |
| 12076 | |
| 12077 | masm.clz64(input, output); |
| 12078 | } |
| 12079 | |
| 12080 | void CodeGenerator::visitCtzI64(LCtzI64* ins) { |
| 12081 | Register64 input = ToRegister64(ins->input()); |
| 12082 | Register64 output = ToOutRegister64(ins); |
| 12083 | |
| 12084 | masm.ctz64(input, output); |
| 12085 | } |
| 12086 | |
| 12087 | void CodeGenerator::visitPopcntI64(LPopcntI64* ins) { |
| 12088 | Register64 input = ToRegister64(ins->input()); |
| 12089 | Register64 output = ToOutRegister64(ins); |
| 12090 | Register temp = ToRegister(ins->temp0()); |
| 12091 | |
| 12092 | masm.popcnt64(input, output, temp); |
| 12093 | } |
| 12094 | |
| 12095 | void CodeGenerator::visitBigIntAdd(LBigIntAdd* ins) { |
| 12096 | pushArg(ToRegister(ins->rhs())); |
| 12097 | pushArg(ToRegister(ins->lhs())); |
| 12098 | |
| 12099 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12100 | callVM<Fn, BigInt::add>(ins); |
| 12101 | } |
| 12102 | |
| 12103 | void CodeGenerator::visitBigIntSub(LBigIntSub* ins) { |
| 12104 | pushArg(ToRegister(ins->rhs())); |
| 12105 | pushArg(ToRegister(ins->lhs())); |
| 12106 | |
| 12107 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12108 | callVM<Fn, BigInt::sub>(ins); |
| 12109 | } |
| 12110 | |
| 12111 | void CodeGenerator::visitBigIntMul(LBigIntMul* ins) { |
| 12112 | pushArg(ToRegister(ins->rhs())); |
| 12113 | pushArg(ToRegister(ins->lhs())); |
| 12114 | |
| 12115 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12116 | callVM<Fn, BigInt::mul>(ins); |
| 12117 | } |
| 12118 | |
| 12119 | void CodeGenerator::visitBigIntDiv(LBigIntDiv* ins) { |
| 12120 | pushArg(ToRegister(ins->rhs())); |
| 12121 | pushArg(ToRegister(ins->lhs())); |
| 12122 | |
| 12123 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12124 | callVM<Fn, BigInt::div>(ins); |
| 12125 | } |
| 12126 | |
| 12127 | void CodeGenerator::visitBigIntMod(LBigIntMod* ins) { |
| 12128 | pushArg(ToRegister(ins->rhs())); |
| 12129 | pushArg(ToRegister(ins->lhs())); |
| 12130 | |
| 12131 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12132 | callVM<Fn, BigInt::mod>(ins); |
| 12133 | } |
| 12134 | |
| 12135 | void CodeGenerator::visitBigIntPow(LBigIntPow* ins) { |
| 12136 | pushArg(ToRegister(ins->rhs())); |
| 12137 | pushArg(ToRegister(ins->lhs())); |
| 12138 | |
| 12139 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12140 | callVM<Fn, BigInt::pow>(ins); |
| 12141 | } |
| 12142 | |
| 12143 | void CodeGenerator::visitBigIntBitAnd(LBigIntBitAnd* ins) { |
| 12144 | pushArg(ToRegister(ins->rhs())); |
| 12145 | pushArg(ToRegister(ins->lhs())); |
| 12146 | |
| 12147 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12148 | callVM<Fn, BigInt::bitAnd>(ins); |
| 12149 | } |
| 12150 | |
| 12151 | void CodeGenerator::visitBigIntBitOr(LBigIntBitOr* ins) { |
| 12152 | pushArg(ToRegister(ins->rhs())); |
| 12153 | pushArg(ToRegister(ins->lhs())); |
| 12154 | |
| 12155 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12156 | callVM<Fn, BigInt::bitOr>(ins); |
| 12157 | } |
| 12158 | |
| 12159 | void CodeGenerator::visitBigIntBitXor(LBigIntBitXor* ins) { |
| 12160 | pushArg(ToRegister(ins->rhs())); |
| 12161 | pushArg(ToRegister(ins->lhs())); |
| 12162 | |
| 12163 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12164 | callVM<Fn, BigInt::bitXor>(ins); |
| 12165 | } |
| 12166 | |
| 12167 | void CodeGenerator::visitBigIntLsh(LBigIntLsh* ins) { |
| 12168 | pushArg(ToRegister(ins->rhs())); |
| 12169 | pushArg(ToRegister(ins->lhs())); |
| 12170 | |
| 12171 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12172 | callVM<Fn, BigInt::lsh>(ins); |
| 12173 | } |
| 12174 | |
| 12175 | void CodeGenerator::visitBigIntRsh(LBigIntRsh* ins) { |
| 12176 | pushArg(ToRegister(ins->rhs())); |
| 12177 | pushArg(ToRegister(ins->lhs())); |
| 12178 | |
| 12179 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, HandleBigInt); |
| 12180 | callVM<Fn, BigInt::rsh>(ins); |
| 12181 | } |
| 12182 | |
| 12183 | void CodeGenerator::visitBigIntIncrement(LBigIntIncrement* ins) { |
| 12184 | pushArg(ToRegister(ins->input())); |
| 12185 | |
| 12186 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
| 12187 | callVM<Fn, BigInt::inc>(ins); |
| 12188 | } |
| 12189 | |
| 12190 | void CodeGenerator::visitBigIntDecrement(LBigIntDecrement* ins) { |
| 12191 | pushArg(ToRegister(ins->input())); |
| 12192 | |
| 12193 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
| 12194 | callVM<Fn, BigInt::dec>(ins); |
| 12195 | } |
| 12196 | |
| 12197 | void CodeGenerator::visitBigIntNegate(LBigIntNegate* ins) { |
| 12198 | Register input = ToRegister(ins->input()); |
| 12199 | Register temp = ToRegister(ins->temp0()); |
| 12200 | Register output = ToRegister(ins->output()); |
| 12201 | |
| 12202 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
| 12203 | auto* ool = |
| 12204 | oolCallVM<Fn, BigInt::neg>(ins, ArgList(input), StoreRegisterTo(output)); |
| 12205 | |
| 12206 | // -0n == 0n |
| 12207 | Label lhsNonZero; |
| 12208 | masm.branchIfBigIntIsNonZero(input, &lhsNonZero); |
| 12209 | masm.movePtr(input, output); |
| 12210 | masm.jump(ool->rejoin()); |
| 12211 | masm.bind(&lhsNonZero); |
| 12212 | |
| 12213 | // Call into the VM when the input uses heap digits. |
| 12214 | masm.copyBigIntWithInlineDigits(input, output, temp, initialBigIntHeap(), |
| 12215 | ool->entry()); |
| 12216 | |
| 12217 | // Flip the sign bit. |
| 12218 | masm.xor32(Imm32(BigInt::signBitMask()), |
| 12219 | Address(output, BigInt::offsetOfFlags())); |
| 12220 | |
| 12221 | masm.bind(ool->rejoin()); |
| 12222 | } |
| 12223 | |
| 12224 | void CodeGenerator::visitBigIntBitNot(LBigIntBitNot* ins) { |
| 12225 | pushArg(ToRegister(ins->input())); |
| 12226 | |
| 12227 | using Fn = BigInt* (*)(JSContext*, HandleBigInt); |
| 12228 | callVM<Fn, BigInt::bitNot>(ins); |
| 12229 | } |
| 12230 | |
| 12231 | void CodeGenerator::visitBigIntToIntPtr(LBigIntToIntPtr* ins) { |
| 12232 | Register input = ToRegister(ins->input()); |
| 12233 | Register output = ToRegister(ins->output()); |
| 12234 | |
| 12235 | Label bail; |
| 12236 | masm.loadBigIntPtr(input, output, &bail); |
| 12237 | bailoutFrom(&bail, ins->snapshot()); |
| 12238 | } |
| 12239 | |
| 12240 | void CodeGenerator::visitIntPtrToBigInt(LIntPtrToBigInt* ins) { |
| 12241 | Register input = ToRegister(ins->input()); |
| 12242 | Register temp = ToRegister(ins->temp0()); |
| 12243 | Register output = ToRegister(ins->output()); |
| 12244 | |
| 12245 | using Fn = BigInt* (*)(JSContext*, intptr_t); |
| 12246 | auto* ool = oolCallVM<Fn, JS::BigInt::createFromIntPtr>( |
| 12247 | ins, ArgList(input), StoreRegisterTo(output)); |
| 12248 | |
| 12249 | masm.newGCBigInt(output, temp, initialBigIntHeap(), ool->entry()); |
| 12250 | masm.movePtr(input, temp); |
| 12251 | masm.initializeBigIntPtr(output, temp); |
| 12252 | |
| 12253 | masm.bind(ool->rejoin()); |
| 12254 | } |
| 12255 | |
| 12256 | void CodeGenerator::visitBigIntPtrAdd(LBigIntPtrAdd* ins) { |
| 12257 | Register lhs = ToRegister(ins->lhs()); |
| 12258 | const LAllocation* rhs = ins->rhs(); |
| 12259 | Register output = ToRegister(ins->output()); |
| 12260 | |
| 12261 | if (rhs->isConstant()) { |
| 12262 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 12263 | } else { |
| 12264 | masm.movePtr(ToRegister(rhs), output); |
| 12265 | } |
| 12266 | |
| 12267 | Label bail; |
| 12268 | masm.branchAddPtr(Assembler::Overflow, lhs, output, &bail); |
| 12269 | bailoutFrom(&bail, ins->snapshot()); |
| 12270 | } |
| 12271 | |
| 12272 | void CodeGenerator::visitBigIntPtrSub(LBigIntPtrSub* ins) { |
| 12273 | Register lhs = ToRegister(ins->lhs()); |
| 12274 | Register rhs = ToRegister(ins->rhs()); |
| 12275 | Register output = ToRegister(ins->output()); |
| 12276 | |
| 12277 | Label bail; |
| 12278 | masm.movePtr(lhs, output); |
| 12279 | masm.branchSubPtr(Assembler::Overflow, rhs, output, &bail); |
| 12280 | bailoutFrom(&bail, ins->snapshot()); |
| 12281 | } |
| 12282 | |
| 12283 | void CodeGenerator::visitBigIntPtrMul(LBigIntPtrMul* ins) { |
| 12284 | Register lhs = ToRegister(ins->lhs()); |
| 12285 | const LAllocation* rhs = ins->rhs(); |
| 12286 | Register output = ToRegister(ins->output()); |
| 12287 | |
| 12288 | if (rhs->isConstant()) { |
| 12289 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 12290 | } else { |
| 12291 | masm.movePtr(ToRegister(rhs), output); |
| 12292 | } |
| 12293 | |
| 12294 | Label bail; |
| 12295 | masm.branchMulPtr(Assembler::Overflow, lhs, output, &bail); |
| 12296 | bailoutFrom(&bail, ins->snapshot()); |
| 12297 | } |
| 12298 | |
| 12299 | void CodeGenerator::visitBigIntPtrDiv(LBigIntPtrDiv* ins) { |
| 12300 | Register lhs = ToRegister(ins->lhs()); |
| 12301 | Register rhs = ToRegister(ins->rhs()); |
| 12302 | Register output = ToRegister(ins->output()); |
| 12303 | |
| 12304 | // x / 0 throws an error. |
| 12305 | Label bail; |
| 12306 | if (ins->mir()->canBeDivideByZero()) { |
| 12307 | masm.branchPtr(Assembler::Equal, rhs, Imm32(0), &bail); |
| 12308 | } |
| 12309 | |
| 12310 | static constexpr auto DigitMin = std::numeric_limits< |
| 12311 | mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min(); |
| 12312 | |
| 12313 | // Handle an integer overflow from INT{32,64}_MIN / -1. |
| 12314 | Label notOverflow; |
| 12315 | masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), ¬Overflow); |
| 12316 | masm.branchPtr(Assembler::Equal, rhs, Imm32(-1), &bail); |
| 12317 | masm.bind(¬Overflow); |
| 12318 | |
| 12319 | emitBigIntPtrDiv(ins, lhs, rhs, output); |
| 12320 | |
| 12321 | bailoutFrom(&bail, ins->snapshot()); |
| 12322 | } |
| 12323 | |
| 12324 | void CodeGenerator::visitBigIntPtrDivPowTwo(LBigIntPtrDivPowTwo* ins) { |
| 12325 | Register lhs = ToRegister(ins->lhs()); |
| 12326 | Register output = ToRegister(ins->output()); |
| 12327 | int32_t shift = ins->shift(); |
| 12328 | bool negativeDivisor = ins->negativeDivisor(); |
| 12329 | |
| 12330 | masm.movePtr(lhs, output); |
| 12331 | |
| 12332 | if (shift) { |
| 12333 | // Adjust the value so that shifting produces a correctly rounded result |
| 12334 | // when the numerator is negative. |
| 12335 | // See 10-1 "Signed Division by a Known Power of 2" in Henry S. Warren, |
| 12336 | // Jr.'s Hacker's Delight. |
| 12337 | |
| 12338 | constexpr size_t bits = BigInt::DigitBits; |
| 12339 | |
| 12340 | if (shift > 1) { |
| 12341 | // Copy the sign bit of the numerator. (= (2^bits - 1) or 0) |
| 12342 | masm.rshiftPtrArithmetic(Imm32(bits - 1), output); |
| 12343 | } |
| 12344 | |
| 12345 | // Divide by 2^(bits - shift) |
| 12346 | // i.e. (= (2^bits - 1) / 2^(bits - shift) or 0) |
| 12347 | // i.e. (= (2^shift - 1) or 0) |
| 12348 | masm.rshiftPtr(Imm32(bits - shift), output); |
| 12349 | |
| 12350 | // If signed, make any 1 bit below the shifted bits to bubble up, such that |
| 12351 | // once shifted the value would be rounded towards 0. |
| 12352 | masm.addPtr(lhs, output); |
| 12353 | |
| 12354 | masm.rshiftPtrArithmetic(Imm32(shift), output); |
| 12355 | |
| 12356 | if (negativeDivisor) { |
| 12357 | masm.negPtr(output); |
| 12358 | } |
| 12359 | } else if (negativeDivisor) { |
| 12360 | Label bail; |
| 12361 | masm.branchNegPtr(Assembler::Overflow, output, &bail); |
| 12362 | bailoutFrom(&bail, ins->snapshot()); |
| 12363 | } |
| 12364 | } |
| 12365 | |
| 12366 | void CodeGenerator::visitBigIntPtrMod(LBigIntPtrMod* ins) { |
| 12367 | Register lhs = ToRegister(ins->lhs()); |
| 12368 | Register rhs = ToRegister(ins->rhs()); |
| 12369 | Register output = ToRegister(ins->output()); |
| 12370 | Register temp = ToRegister(ins->temp0()); |
| 12371 | |
| 12372 | // x % 0 throws an error. |
| 12373 | if (ins->mir()->canBeDivideByZero()) { |
| 12374 | bailoutCmpPtr(Assembler::Equal, rhs, Imm32(0), ins->snapshot()); |
| 12375 | } |
| 12376 | |
| 12377 | static constexpr auto DigitMin = std::numeric_limits< |
| 12378 | mozilla::SignedStdintTypeForSize<sizeof(BigInt::Digit)>::Type>::min(); |
| 12379 | |
| 12380 | masm.movePtr(lhs, temp); |
| 12381 | |
| 12382 | // Handle an integer overflow from INT{32,64}_MIN / -1. |
| 12383 | Label notOverflow; |
| 12384 | masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(DigitMin), ¬Overflow); |
| 12385 | masm.branchPtr(Assembler::NotEqual, rhs, Imm32(-1), ¬Overflow); |
| 12386 | masm.movePtr(ImmWord(0), temp); |
| 12387 | masm.bind(¬Overflow); |
| 12388 | |
| 12389 | emitBigIntPtrMod(ins, temp, rhs, output); |
| 12390 | } |
| 12391 | |
| 12392 | void CodeGenerator::visitBigIntPtrModPowTwo(LBigIntPtrModPowTwo* ins) { |
| 12393 | Register lhs = ToRegister(ins->lhs()); |
| 12394 | Register output = ToRegister(ins->output()); |
| 12395 | Register temp = ToRegister(ins->temp0()); |
| 12396 | int32_t shift = ins->shift(); |
| 12397 | |
| 12398 | masm.movePtr(lhs, output); |
| 12399 | masm.movePtr(ImmWord((uintptr_t(1) << shift) - uintptr_t(1)), temp); |
| 12400 | |
| 12401 | // Switch based on sign of the lhs. |
| 12402 | |
| 12403 | // Positive numbers are just a bitmask. |
| 12404 | Label negative; |
| 12405 | masm.branchTestPtr(Assembler::Signed, lhs, lhs, &negative); |
| 12406 | |
| 12407 | masm.andPtr(temp, output); |
| 12408 | |
| 12409 | Label done; |
| 12410 | masm.jump(&done); |
| 12411 | |
| 12412 | // Negative numbers need a negate, bitmask, negate |
| 12413 | masm.bind(&negative); |
| 12414 | |
| 12415 | masm.negPtr(output); |
| 12416 | masm.andPtr(temp, output); |
| 12417 | masm.negPtr(output); |
| 12418 | |
| 12419 | masm.bind(&done); |
| 12420 | } |
| 12421 | |
| 12422 | void CodeGenerator::visitBigIntPtrPow(LBigIntPtrPow* ins) { |
| 12423 | Register lhs = ToRegister(ins->lhs()); |
| 12424 | Register rhs = ToRegister(ins->rhs()); |
| 12425 | Register output = ToRegister(ins->output()); |
| 12426 | Register temp0 = ToRegister(ins->temp0()); |
| 12427 | Register temp1 = ToRegister(ins->temp1()); |
| 12428 | |
| 12429 | Label bail; |
| 12430 | masm.powPtr(lhs, rhs, output, temp0, temp1, &bail); |
| 12431 | bailoutFrom(&bail, ins->snapshot()); |
| 12432 | } |
| 12433 | |
| 12434 | void CodeGenerator::visitBigIntPtrBitAnd(LBigIntPtrBitAnd* ins) { |
| 12435 | Register lhs = ToRegister(ins->lhs()); |
| 12436 | const LAllocation* rhs = ins->rhs(); |
| 12437 | Register output = ToRegister(ins->output()); |
| 12438 | |
| 12439 | if (rhs->isConstant()) { |
| 12440 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 12441 | } else { |
| 12442 | masm.movePtr(ToRegister(rhs), output); |
| 12443 | } |
| 12444 | masm.andPtr(lhs, output); |
| 12445 | } |
| 12446 | |
| 12447 | void CodeGenerator::visitBigIntPtrBitOr(LBigIntPtrBitOr* ins) { |
| 12448 | Register lhs = ToRegister(ins->lhs()); |
| 12449 | const LAllocation* rhs = ins->rhs(); |
| 12450 | Register output = ToRegister(ins->output()); |
| 12451 | |
| 12452 | if (rhs->isConstant()) { |
| 12453 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 12454 | } else { |
| 12455 | masm.movePtr(ToRegister(rhs), output); |
| 12456 | } |
| 12457 | masm.orPtr(lhs, output); |
| 12458 | } |
| 12459 | |
| 12460 | void CodeGenerator::visitBigIntPtrBitXor(LBigIntPtrBitXor* ins) { |
| 12461 | Register lhs = ToRegister(ins->lhs()); |
| 12462 | const LAllocation* rhs = ins->rhs(); |
| 12463 | Register output = ToRegister(ins->output()); |
| 12464 | |
| 12465 | if (rhs->isConstant()) { |
| 12466 | masm.movePtr(ImmWord(ToIntPtr(rhs)), output); |
| 12467 | } else { |
| 12468 | masm.movePtr(ToRegister(rhs), output); |
| 12469 | } |
| 12470 | masm.xorPtr(lhs, output); |
| 12471 | } |
| 12472 | |
| 12473 | void CodeGenerator::visitBigIntPtrLsh(LBigIntPtrLsh* ins) { |
| 12474 | Register lhs = ToRegister(ins->lhs()); |
| 12475 | Register output = ToRegister(ins->output()); |
| 12476 | Register temp = ToTempRegisterOrInvalid(ins->temp0()); |
| 12477 | Register tempShift = ToTempRegisterOrInvalid(ins->temp1()); |
| 12478 | |
| 12479 | if (ins->rhs()->isConstant()) { |
| 12480 | intptr_t rhs = ToIntPtr(ins->rhs()); |
| 12481 | |
| 12482 | if (rhs >= intptr_t(BigInt::DigitBits)) { |
| 12483 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12483); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12483); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12484 | |
| 12485 | // x << DigitBits with x != 0n always exceeds pointer-sized storage. |
| 12486 | masm.movePtr(ImmWord(0), output); |
| 12487 | bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot()); |
| 12488 | } else if (rhs <= -intptr_t(BigInt::DigitBits)) { |
| 12489 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12489); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12489); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12490 | |
| 12491 | // x << -DigitBits == x >> DigitBits, which is either 0n or -1n. |
| 12492 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), lhs, output); |
| 12493 | } else if (rhs <= 0) { |
| 12494 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12494); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12494); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12495 | |
| 12496 | // |x << -y| is computed as |x >> y|. |
| 12497 | masm.rshiftPtrArithmetic(Imm32(-rhs), lhs, output); |
| 12498 | } else { |
| 12499 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12499); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12499); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12500 | |
| 12501 | masm.lshiftPtr(Imm32(rhs), lhs, output); |
| 12502 | |
| 12503 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
| 12504 | masm.rshiftPtrArithmetic(Imm32(rhs), output, temp); |
| 12505 | bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot()); |
| 12506 | } |
| 12507 | } else { |
| 12508 | Register rhs = ToRegister(ins->rhs()); |
| 12509 | |
| 12510 | Label done, bail; |
| 12511 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12511); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12511); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12512 | |
| 12513 | masm.movePtr(lhs, output); |
| 12514 | |
| 12515 | // 0n << x == 0n |
| 12516 | masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done); |
| 12517 | |
| 12518 | // x << DigitBits with x != 0n always exceeds pointer-sized storage. |
| 12519 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(BigInt::DigitBits), |
| 12520 | &bail); |
| 12521 | |
| 12522 | // x << -DigitBits == x >> DigitBits, which is either 0n or -1n. |
| 12523 | Label shift; |
| 12524 | masm.branchPtr(Assembler::GreaterThan, rhs, |
| 12525 | Imm32(-int32_t(BigInt::DigitBits)), &shift); |
| 12526 | { |
| 12527 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
| 12528 | masm.jump(&done); |
| 12529 | } |
| 12530 | masm.bind(&shift); |
| 12531 | |
| 12532 | // Move |rhs| into the designated shift register. |
| 12533 | masm.movePtr(rhs, tempShift); |
| 12534 | |
| 12535 | // |x << -y| is computed as |x >> y|. |
| 12536 | Label leftShift; |
| 12537 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &leftShift); |
| 12538 | { |
| 12539 | masm.negPtr(tempShift); |
| 12540 | masm.rshiftPtrArithmetic(tempShift, output); |
| 12541 | masm.jump(&done); |
| 12542 | } |
| 12543 | masm.bind(&leftShift); |
| 12544 | |
| 12545 | masm.lshiftPtr(tempShift, output); |
| 12546 | |
| 12547 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
| 12548 | masm.movePtr(output, temp); |
| 12549 | masm.rshiftPtrArithmetic(tempShift, temp); |
| 12550 | masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail); |
| 12551 | |
| 12552 | masm.bind(&done); |
| 12553 | bailoutFrom(&bail, ins->snapshot()); |
| 12554 | } |
| 12555 | } |
| 12556 | |
| 12557 | void CodeGenerator::visitBigIntPtrRsh(LBigIntPtrRsh* ins) { |
| 12558 | Register lhs = ToRegister(ins->lhs()); |
| 12559 | Register output = ToRegister(ins->output()); |
| 12560 | Register temp = ToTempRegisterOrInvalid(ins->temp0()); |
| 12561 | Register tempShift = ToTempRegisterOrInvalid(ins->temp1()); |
| 12562 | |
| 12563 | if (ins->rhs()->isConstant()) { |
| 12564 | intptr_t rhs = ToIntPtr(ins->rhs()); |
| 12565 | |
| 12566 | if (rhs <= -intptr_t(BigInt::DigitBits)) { |
| 12567 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12567); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12567); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12568 | |
| 12569 | // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage. |
| 12570 | masm.movePtr(ImmWord(0), output); |
| 12571 | bailoutCmpPtr(Assembler::NotEqual, lhs, Imm32(0), ins->snapshot()); |
| 12572 | } else if (rhs >= intptr_t(BigInt::DigitBits)) { |
| 12573 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12573); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12573); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12574 | |
| 12575 | // x >> DigitBits is either 0n or -1n. |
| 12576 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), lhs, output); |
| 12577 | } else if (rhs < 0) { |
| 12578 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12578); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12578); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12579 | |
| 12580 | // |x >> -y| is computed as |x << y|. |
| 12581 | masm.lshiftPtr(Imm32(-rhs), lhs, output); |
| 12582 | |
| 12583 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
| 12584 | masm.rshiftPtrArithmetic(Imm32(-rhs), output, temp); |
| 12585 | bailoutCmpPtr(Assembler::NotEqual, temp, lhs, ins->snapshot()); |
| 12586 | } else { |
| 12587 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12587); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12587); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12588 | |
| 12589 | masm.rshiftPtrArithmetic(Imm32(rhs), lhs, output); |
| 12590 | } |
| 12591 | } else { |
| 12592 | Register rhs = ToRegister(ins->rhs()); |
| 12593 | |
| 12594 | Label done, bail; |
| 12595 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 12595); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->fallible()" ")"); do { MOZ_CrashSequence(__null, 12595); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 12596 | |
| 12597 | masm.movePtr(lhs, output); |
| 12598 | |
| 12599 | // 0n >> x == 0n |
| 12600 | masm.branchPtr(Assembler::Equal, lhs, Imm32(0), &done); |
| 12601 | |
| 12602 | // x >> -DigitBits == x << DigitBits, which exceeds pointer-sized storage. |
| 12603 | masm.branchPtr(Assembler::LessThanOrEqual, rhs, |
| 12604 | Imm32(-int32_t(BigInt::DigitBits)), &bail); |
| 12605 | |
| 12606 | // x >> DigitBits is either 0n or -1n. |
| 12607 | Label shift; |
| 12608 | masm.branchPtr(Assembler::LessThan, rhs, Imm32(BigInt::DigitBits), &shift); |
| 12609 | { |
| 12610 | masm.rshiftPtrArithmetic(Imm32(BigInt::DigitBits - 1), output); |
| 12611 | masm.jump(&done); |
| 12612 | } |
| 12613 | masm.bind(&shift); |
| 12614 | |
| 12615 | // Move |rhs| into the designated shift register. |
| 12616 | masm.movePtr(rhs, tempShift); |
| 12617 | |
| 12618 | // |x >> -y| is computed as |x << y|. |
| 12619 | Label rightShift; |
| 12620 | masm.branchPtr(Assembler::GreaterThanOrEqual, rhs, Imm32(0), &rightShift); |
| 12621 | { |
| 12622 | masm.negPtr(tempShift); |
| 12623 | masm.lshiftPtr(tempShift, output); |
| 12624 | |
| 12625 | // Check for overflow: ((lhs << rhs) >> rhs) == lhs. |
| 12626 | masm.movePtr(output, temp); |
| 12627 | masm.rshiftPtrArithmetic(tempShift, temp); |
| 12628 | masm.branchPtr(Assembler::NotEqual, temp, lhs, &bail); |
| 12629 | |
| 12630 | masm.jump(&done); |
| 12631 | } |
| 12632 | masm.bind(&rightShift); |
| 12633 | |
| 12634 | masm.rshiftPtrArithmetic(tempShift, output); |
| 12635 | |
| 12636 | masm.bind(&done); |
| 12637 | bailoutFrom(&bail, ins->snapshot()); |
| 12638 | } |
| 12639 | } |
| 12640 | |
| 12641 | void CodeGenerator::visitBigIntPtrBitNot(LBigIntPtrBitNot* ins) { |
| 12642 | Register input = ToRegister(ins->input()); |
| 12643 | Register output = ToRegister(ins->output()); |
| 12644 | |
| 12645 | masm.movePtr(input, output); |
| 12646 | masm.notPtr(output); |
| 12647 | } |
| 12648 | |
| 12649 | void CodeGenerator::visitInt32ToStringWithBase(LInt32ToStringWithBase* lir) { |
| 12650 | Register input = ToRegister(lir->input()); |
| 12651 | RegisterOrInt32 base = ToRegisterOrInt32(lir->base()); |
| 12652 | Register output = ToRegister(lir->output()); |
| 12653 | Register temp0 = ToRegister(lir->temp0()); |
| 12654 | Register temp1 = ToRegister(lir->temp1()); |
| 12655 | |
| 12656 | bool lowerCase = lir->mir()->stringCase() == StringCase::Lower; |
| 12657 | |
| 12658 | using Fn = JSLinearString* (*)(JSContext*, int32_t, int32_t, bool); |
| 12659 | if (base.is<Register>()) { |
| 12660 | auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>( |
| 12661 | lir, ArgList(input, base.as<Register>(), Imm32(lowerCase)), |
| 12662 | StoreRegisterTo(output)); |
| 12663 | |
| 12664 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
| 12665 | masm.loadInt32ToStringWithBase(input, base.as<Register>(), output, temp0, |
| 12666 | temp1, gen->runtime->staticStrings(), |
| 12667 | liveRegs, lowerCase, ool->entry()); |
| 12668 | masm.bind(ool->rejoin()); |
| 12669 | } else { |
| 12670 | auto* ool = oolCallVM<Fn, js::Int32ToStringWithBase<CanGC>>( |
| 12671 | lir, ArgList(input, Imm32(base.as<int32_t>()), Imm32(lowerCase)), |
| 12672 | StoreRegisterTo(output)); |
| 12673 | |
| 12674 | masm.loadInt32ToStringWithBase(input, base.as<int32_t>(), output, temp0, |
| 12675 | temp1, gen->runtime->staticStrings(), |
| 12676 | lowerCase, ool->entry()); |
| 12677 | masm.bind(ool->rejoin()); |
| 12678 | } |
| 12679 | } |
| 12680 | |
| 12681 | void CodeGenerator::visitNumberParseInt(LNumberParseInt* lir) { |
| 12682 | Register string = ToRegister(lir->string()); |
| 12683 | Register radix = ToRegister(lir->radix()); |
| 12684 | ValueOperand output = ToOutValue(lir); |
| 12685 | Register temp = ToRegister(lir->temp0()); |
| 12686 | |
| 12687 | #ifdef DEBUG1 |
| 12688 | Label ok; |
| 12689 | masm.branch32(Assembler::Equal, radix, Imm32(0), &ok); |
| 12690 | masm.branch32(Assembler::Equal, radix, Imm32(10), &ok); |
| 12691 | masm.assumeUnreachable("radix must be 0 or 10 for indexed value fast path"); |
| 12692 | masm.bind(&ok); |
| 12693 | #endif |
| 12694 | |
| 12695 | // Use indexed value as fast path if possible. |
| 12696 | Label vmCall, done; |
| 12697 | masm.loadStringIndexValue(string, temp, &vmCall); |
| 12698 | masm.tagValue(JSVAL_TYPE_INT32, temp, output); |
| 12699 | masm.jump(&done); |
| 12700 | { |
| 12701 | masm.bind(&vmCall); |
| 12702 | |
| 12703 | pushArg(radix); |
| 12704 | pushArg(string); |
| 12705 | |
| 12706 | using Fn = bool (*)(JSContext*, HandleString, int32_t, MutableHandleValue); |
| 12707 | callVM<Fn, js::NumberParseInt>(lir); |
| 12708 | } |
| 12709 | masm.bind(&done); |
| 12710 | } |
| 12711 | |
| 12712 | void CodeGenerator::visitDoubleParseInt(LDoubleParseInt* lir) { |
| 12713 | FloatRegister number = ToFloatRegister(lir->number()); |
| 12714 | Register output = ToRegister(lir->output()); |
| 12715 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 12716 | |
| 12717 | Label bail; |
| 12718 | masm.branchDouble(Assembler::DoubleUnordered, number, number, &bail); |
| 12719 | masm.branchTruncateDoubleToInt32(number, output, &bail); |
| 12720 | |
| 12721 | Label ok; |
| 12722 | masm.branch32(Assembler::NotEqual, output, Imm32(0), &ok); |
| 12723 | { |
| 12724 | // Accept both +0 and -0 and return 0. |
| 12725 | masm.loadConstantDouble(0.0, temp); |
| 12726 | masm.branchDouble(Assembler::DoubleEqual, number, temp, &ok); |
| 12727 | |
| 12728 | // Fail if a non-zero input is in the exclusive range (-1, 1.0e-6). |
| 12729 | masm.loadConstantDouble(DOUBLE_DECIMAL_IN_SHORTEST_LOW, temp); |
| 12730 | masm.branchDouble(Assembler::DoubleLessThan, number, temp, &bail); |
| 12731 | } |
| 12732 | masm.bind(&ok); |
| 12733 | |
| 12734 | bailoutFrom(&bail, lir->snapshot()); |
| 12735 | } |
| 12736 | |
| 12737 | void CodeGenerator::visitFloor(LFloor* lir) { |
| 12738 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12739 | Register output = ToRegister(lir->output()); |
| 12740 | |
| 12741 | Label bail; |
| 12742 | masm.floorDoubleToInt32(input, output, &bail); |
| 12743 | bailoutFrom(&bail, lir->snapshot()); |
| 12744 | } |
| 12745 | |
| 12746 | void CodeGenerator::visitFloorF(LFloorF* lir) { |
| 12747 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12748 | Register output = ToRegister(lir->output()); |
| 12749 | |
| 12750 | Label bail; |
| 12751 | masm.floorFloat32ToInt32(input, output, &bail); |
| 12752 | bailoutFrom(&bail, lir->snapshot()); |
| 12753 | } |
| 12754 | |
| 12755 | void CodeGenerator::visitCeil(LCeil* lir) { |
| 12756 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12757 | Register output = ToRegister(lir->output()); |
| 12758 | |
| 12759 | Label bail; |
| 12760 | masm.ceilDoubleToInt32(input, output, &bail); |
| 12761 | bailoutFrom(&bail, lir->snapshot()); |
| 12762 | } |
| 12763 | |
| 12764 | void CodeGenerator::visitCeilF(LCeilF* lir) { |
| 12765 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12766 | Register output = ToRegister(lir->output()); |
| 12767 | |
| 12768 | Label bail; |
| 12769 | masm.ceilFloat32ToInt32(input, output, &bail); |
| 12770 | bailoutFrom(&bail, lir->snapshot()); |
| 12771 | } |
| 12772 | |
| 12773 | void CodeGenerator::visitRound(LRound* lir) { |
| 12774 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12775 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 12776 | Register output = ToRegister(lir->output()); |
| 12777 | |
| 12778 | Label bail; |
| 12779 | masm.roundDoubleToInt32(input, output, temp, &bail); |
| 12780 | bailoutFrom(&bail, lir->snapshot()); |
| 12781 | } |
| 12782 | |
| 12783 | void CodeGenerator::visitRoundF(LRoundF* lir) { |
| 12784 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12785 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 12786 | Register output = ToRegister(lir->output()); |
| 12787 | |
| 12788 | Label bail; |
| 12789 | masm.roundFloat32ToInt32(input, output, temp, &bail); |
| 12790 | bailoutFrom(&bail, lir->snapshot()); |
| 12791 | } |
| 12792 | |
| 12793 | void CodeGenerator::visitTrunc(LTrunc* lir) { |
| 12794 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12795 | Register output = ToRegister(lir->output()); |
| 12796 | |
| 12797 | Label bail; |
| 12798 | masm.truncDoubleToInt32(input, output, &bail); |
| 12799 | bailoutFrom(&bail, lir->snapshot()); |
| 12800 | } |
| 12801 | |
| 12802 | void CodeGenerator::visitTruncF(LTruncF* lir) { |
| 12803 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12804 | Register output = ToRegister(lir->output()); |
| 12805 | |
| 12806 | Label bail; |
| 12807 | masm.truncFloat32ToInt32(input, output, &bail); |
| 12808 | bailoutFrom(&bail, lir->snapshot()); |
| 12809 | } |
| 12810 | |
| 12811 | void CodeGenerator::visitNearbyInt(LNearbyInt* lir) { |
| 12812 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12813 | FloatRegister output = ToFloatRegister(lir->output()); |
| 12814 | |
| 12815 | RoundingMode roundingMode = lir->mir()->roundingMode(); |
| 12816 | masm.nearbyIntDouble(roundingMode, input, output); |
| 12817 | } |
| 12818 | |
| 12819 | void CodeGenerator::visitNearbyIntF(LNearbyIntF* lir) { |
| 12820 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12821 | FloatRegister output = ToFloatRegister(lir->output()); |
| 12822 | |
| 12823 | RoundingMode roundingMode = lir->mir()->roundingMode(); |
| 12824 | masm.nearbyIntFloat32(roundingMode, input, output); |
| 12825 | } |
| 12826 | |
| 12827 | void CodeGenerator::visitRoundToDouble(LRoundToDouble* lir) { |
| 12828 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12829 | FloatRegister output = ToFloatRegister(lir->output()); |
| 12830 | |
| 12831 | masm.roundDouble(input, output); |
| 12832 | } |
| 12833 | |
| 12834 | void CodeGenerator::visitRoundToFloat32(LRoundToFloat32* lir) { |
| 12835 | FloatRegister input = ToFloatRegister(lir->input()); |
| 12836 | FloatRegister output = ToFloatRegister(lir->output()); |
| 12837 | |
| 12838 | masm.roundFloat32(input, output); |
| 12839 | } |
| 12840 | |
| 12841 | void CodeGenerator::visitCopySignF(LCopySignF* lir) { |
| 12842 | FloatRegister lhs = ToFloatRegister(lir->lhs()); |
| 12843 | FloatRegister rhs = ToFloatRegister(lir->rhs()); |
| 12844 | FloatRegister out = ToFloatRegister(lir->output()); |
| 12845 | |
| 12846 | if (lhs == rhs) { |
| 12847 | if (lhs != out) { |
| 12848 | masm.moveFloat32(lhs, out); |
| 12849 | } |
| 12850 | return; |
| 12851 | } |
| 12852 | |
| 12853 | masm.copySignFloat32(lhs, rhs, out); |
| 12854 | } |
| 12855 | |
| 12856 | void CodeGenerator::visitCopySignD(LCopySignD* lir) { |
| 12857 | FloatRegister lhs = ToFloatRegister(lir->lhs()); |
| 12858 | FloatRegister rhs = ToFloatRegister(lir->rhs()); |
| 12859 | FloatRegister out = ToFloatRegister(lir->output()); |
| 12860 | |
| 12861 | if (lhs == rhs) { |
| 12862 | if (lhs != out) { |
| 12863 | masm.moveDouble(lhs, out); |
| 12864 | } |
| 12865 | return; |
| 12866 | } |
| 12867 | |
| 12868 | masm.copySignDouble(lhs, rhs, out); |
| 12869 | } |
| 12870 | |
| 12871 | void CodeGenerator::visitCompareS(LCompareS* lir) { |
| 12872 | JSOp op = lir->mir()->jsop(); |
| 12873 | Register left = ToRegister(lir->left()); |
| 12874 | Register right = ToRegister(lir->right()); |
| 12875 | Register output = ToRegister(lir->output()); |
| 12876 | |
| 12877 | OutOfLineCode* ool = nullptr; |
| 12878 | |
| 12879 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 12880 | if (op == JSOp::Eq || op == JSOp::StrictEq) { |
| 12881 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
| 12882 | lir, ArgList(left, right), StoreRegisterTo(output)); |
| 12883 | } else if (op == JSOp::Ne || op == JSOp::StrictNe) { |
| 12884 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
| 12885 | lir, ArgList(left, right), StoreRegisterTo(output)); |
| 12886 | } else if (op == JSOp::Lt) { |
| 12887 | ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>( |
| 12888 | lir, ArgList(left, right), StoreRegisterTo(output)); |
| 12889 | } else if (op == JSOp::Le) { |
| 12890 | // Push the operands in reverse order for JSOp::Le: |
| 12891 | // - |left <= right| is implemented as |right >= left|. |
| 12892 | ool = |
| 12893 | oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>( |
| 12894 | lir, ArgList(right, left), StoreRegisterTo(output)); |
| 12895 | } else if (op == JSOp::Gt) { |
| 12896 | // Push the operands in reverse order for JSOp::Gt: |
| 12897 | // - |left > right| is implemented as |right < left|. |
| 12898 | ool = oolCallVM<Fn, jit::StringsCompare<ComparisonKind::LessThan>>( |
| 12899 | lir, ArgList(right, left), StoreRegisterTo(output)); |
| 12900 | } else { |
| 12901 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 12901); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "op == JSOp::Ge" ")"); do { MOZ_CrashSequence (__null, 12901); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 12902 | ool = |
| 12903 | oolCallVM<Fn, jit::StringsCompare<ComparisonKind::GreaterThanOrEqual>>( |
| 12904 | lir, ArgList(left, right), StoreRegisterTo(output)); |
| 12905 | } |
| 12906 | |
| 12907 | masm.compareStrings(op, left, right, output, ool->entry()); |
| 12908 | |
| 12909 | masm.bind(ool->rejoin()); |
| 12910 | } |
| 12911 | |
| 12912 | void CodeGenerator::visitCompareSInline(LCompareSInline* lir) { |
| 12913 | JSOp op = lir->mir()->jsop(); |
| 12914 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 12914); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 12914); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 12915 | |
| 12916 | Register input = ToRegister(lir->input()); |
| 12917 | Register output = ToRegister(lir->output()); |
| 12918 | |
| 12919 | const JSOffThreadAtom* str = lir->constant(); |
| 12920 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 12920); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "str->length() > 0" ")"); do { MOZ_CrashSequence (__null, 12920); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 12921 | |
| 12922 | OutOfLineCode* ool = nullptr; |
| 12923 | |
| 12924 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 12925 | if (op == JSOp::Eq || op == JSOp::StrictEq) { |
| 12926 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
| 12927 | lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output)); |
| 12928 | } else { |
| 12929 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 12929); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "op == JSOp::Ne || op == JSOp::StrictNe" ")" ); do { MOZ_CrashSequence(__null, 12929); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 12930 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
| 12931 | lir, ArgList(ImmGCPtr(str), input), StoreRegisterTo(output)); |
| 12932 | } |
| 12933 | |
| 12934 | masm.equalStrings(op, input, str, output, ool->entry()); |
| 12935 | masm.bind(ool->rejoin()); |
| 12936 | } |
| 12937 | |
| 12938 | void CodeGenerator::visitCompareSSingle(LCompareSSingle* lir) { |
| 12939 | JSOp op = lir->jsop(); |
| 12940 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 12940); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsRelationalOp(op)" ")"); do { MOZ_CrashSequence (__null, 12940); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 12941 | |
| 12942 | Register input = ToRegister(lir->input()); |
| 12943 | Register output = ToRegister(lir->output()); |
| 12944 | Register temp = ToRegister(lir->temp0()); |
| 12945 | |
| 12946 | const JSOffThreadAtom* str = lir->constant(); |
| 12947 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 12947); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "str->length() == 1" ")"); do { MOZ_CrashSequence (__null, 12947); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 12948 | |
| 12949 | char16_t ch = str->latin1OrTwoByteChar(0); |
| 12950 | |
| 12951 | masm.movePtr(input, temp); |
| 12952 | |
| 12953 | // Check if the string is empty. |
| 12954 | Label compareLength; |
| 12955 | masm.branch32(Assembler::Equal, Address(temp, JSString::offsetOfLength()), |
| 12956 | Imm32(0), &compareLength); |
| 12957 | |
| 12958 | // The first character is in the left-most rope child. |
| 12959 | Label notRope; |
| 12960 | masm.branchIfNotRope(temp, ¬Rope); |
| 12961 | { |
| 12962 | // Unwind ropes at the start if possible. |
| 12963 | Label unwindRope; |
| 12964 | masm.bind(&unwindRope); |
| 12965 | masm.loadRopeLeftChild(temp, output); |
| 12966 | masm.movePtr(output, temp); |
| 12967 | |
| 12968 | #ifdef DEBUG1 |
| 12969 | Label notEmpty; |
| 12970 | masm.branch32(Assembler::NotEqual, |
| 12971 | Address(temp, JSString::offsetOfLength()), Imm32(0), |
| 12972 | ¬Empty); |
| 12973 | masm.assumeUnreachable("rope children are non-empty"); |
| 12974 | masm.bind(¬Empty); |
| 12975 | #endif |
| 12976 | |
| 12977 | // Otherwise keep unwinding ropes. |
| 12978 | masm.branchIfRope(temp, &unwindRope); |
| 12979 | } |
| 12980 | masm.bind(¬Rope); |
| 12981 | |
| 12982 | // Load the first character into |output|. |
| 12983 | auto loadFirstChar = [&](auto encoding) { |
| 12984 | masm.loadStringChars(temp, output, encoding); |
| 12985 | masm.loadChar(Address(output, 0), output, encoding); |
| 12986 | }; |
| 12987 | |
| 12988 | Label done; |
| 12989 | if (ch <= JSString::MAX_LATIN1_CHAR) { |
| 12990 | // Handle both encodings when the search character is Latin-1. |
| 12991 | Label twoByte, compare; |
| 12992 | masm.branchTwoByteString(temp, &twoByte); |
| 12993 | |
| 12994 | loadFirstChar(CharEncoding::Latin1); |
| 12995 | masm.jump(&compare); |
| 12996 | |
| 12997 | masm.bind(&twoByte); |
| 12998 | loadFirstChar(CharEncoding::TwoByte); |
| 12999 | |
| 13000 | masm.bind(&compare); |
| 13001 | } else { |
| 13002 | // The search character is a two-byte character, so it can't be equal to any |
| 13003 | // character of a Latin-1 string. |
| 13004 | masm.move32(Imm32(int32_t(op == JSOp::Lt || op == JSOp::Le)), output); |
| 13005 | masm.branchLatin1String(temp, &done); |
| 13006 | |
| 13007 | loadFirstChar(CharEncoding::TwoByte); |
| 13008 | } |
| 13009 | |
| 13010 | // Compare the string length when the search character is equal to the |
| 13011 | // input's first character. |
| 13012 | masm.branch32(Assembler::Equal, output, Imm32(ch), &compareLength); |
| 13013 | |
| 13014 | // Otherwise compute the result and jump to the end. |
| 13015 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), output, Imm32(ch), |
| 13016 | output); |
| 13017 | masm.jump(&done); |
| 13018 | |
| 13019 | // Compare the string length to compute the overall result. |
| 13020 | masm.bind(&compareLength); |
| 13021 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), |
| 13022 | Address(input, JSString::offsetOfLength()), Imm32(1), output); |
| 13023 | |
| 13024 | masm.bind(&done); |
| 13025 | } |
| 13026 | |
| 13027 | void CodeGenerator::visitStrictConstantCompareString( |
| 13028 | LStrictConstantCompareString* lir) { |
| 13029 | ValueOperand value = ToValue(lir->value()); |
| 13030 | Register output = ToRegister(lir->output()); |
| 13031 | Register temp = ToRegister(lir->temp0()); |
| 13032 | |
| 13033 | JSOffThreadAtom* str = lir->mir()->constant(); |
| 13034 | JSOp op = lir->mir()->jsop(); |
| 13035 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 13035); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 13035); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13036 | |
| 13037 | OutOfLineCode* ool = nullptr; |
| 13038 | |
| 13039 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 13040 | if (op == JSOp::StrictEq) { |
| 13041 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::Equal>>( |
| 13042 | lir, ArgList(temp, ImmGCPtr(str)), StoreRegisterTo(output)); |
| 13043 | } else { |
| 13044 | MOZ_ASSERT(op == JSOp::StrictNe)do { static_assert( mozilla::detail::AssertionConditionType< decltype(op == JSOp::StrictNe)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(op == JSOp::StrictNe))), 0)) ) { do { } while (false); MOZ_ReportAssertionFailure("op == JSOp::StrictNe" , "./../../../../js/src/jit/CodeGenerator.cpp", 13044); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "op == JSOp::StrictNe" ")"); do { MOZ_CrashSequence (__null, 13044); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13045 | ool = oolCallVM<Fn, jit::StringsEqual<EqualityKind::NotEqual>>( |
| 13046 | lir, ArgList(temp, ImmGCPtr(str)), StoreRegisterTo(output)); |
| 13047 | } |
| 13048 | |
| 13049 | masm.move32(Imm32(op == JSOp::StrictNe), output); |
| 13050 | masm.fallibleUnboxString(value, temp, ool->rejoin()); |
| 13051 | |
| 13052 | masm.equalStrings(op, temp, str, output, ool->entry()); |
| 13053 | masm.bind(ool->rejoin()); |
| 13054 | } |
| 13055 | |
| 13056 | void CodeGenerator::visitStrictConstantCompareObject( |
| 13057 | LStrictConstantCompareObject* lir) { |
| 13058 | ValueOperand value = ToValue(lir->value()); |
| 13059 | Register output = ToRegister(lir->output()); |
| 13060 | |
| 13061 | JSObject* obj = lir->mir()->constant(); |
| 13062 | JSOp op = lir->mir()->jsop(); |
| 13063 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 13063); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 13063); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13064 | |
| 13065 | masm.testValueSet(JSOpToCondition(MCompare::Compare_Object, op), value, |
| 13066 | JS::ObjectValue(*obj), output); |
| 13067 | } |
| 13068 | |
| 13069 | void CodeGenerator::visitCompareBigInt(LCompareBigInt* lir) { |
| 13070 | JSOp op = lir->mir()->jsop(); |
| 13071 | Register left = ToRegister(lir->left()); |
| 13072 | Register right = ToRegister(lir->right()); |
| 13073 | Register temp0 = ToRegister(lir->temp0()); |
| 13074 | Register temp1 = ToRegister(lir->temp1()); |
| 13075 | Register temp2 = ToRegister(lir->temp2()); |
| 13076 | Register output = ToRegister(lir->output()); |
| 13077 | |
| 13078 | Label notSame; |
| 13079 | Label compareSign; |
| 13080 | Label compareLength; |
| 13081 | Label compareDigit; |
| 13082 | |
| 13083 | Label* notSameSign; |
| 13084 | Label* notSameLength; |
| 13085 | Label* notSameDigit; |
| 13086 | if (IsEqualityOp(op)) { |
| 13087 | notSameSign = ¬Same; |
| 13088 | notSameLength = ¬Same; |
| 13089 | notSameDigit = ¬Same; |
| 13090 | } else { |
| 13091 | notSameSign = &compareSign; |
| 13092 | notSameLength = &compareLength; |
| 13093 | notSameDigit = &compareDigit; |
| 13094 | } |
| 13095 | |
| 13096 | masm.equalBigInts(left, right, temp0, temp1, temp2, output, notSameSign, |
| 13097 | notSameLength, notSameDigit); |
| 13098 | |
| 13099 | Label done; |
| 13100 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq || op == JSOp::Le || |
| 13101 | op == JSOp::Ge), |
| 13102 | output); |
| 13103 | masm.jump(&done); |
| 13104 | |
| 13105 | if (IsEqualityOp(op)) { |
| 13106 | masm.bind(¬Same); |
| 13107 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
| 13108 | } else { |
| 13109 | Label invertWhenNegative; |
| 13110 | |
| 13111 | // There are two cases when sign(left) != sign(right): |
| 13112 | // 1. sign(left) = positive and sign(right) = negative, |
| 13113 | // 2. or the dual case with reversed signs. |
| 13114 | // |
| 13115 | // For case 1, |left| <cmp> |right| is true for cmp=Gt or cmp=Ge and false |
| 13116 | // for cmp=Lt or cmp=Le. Initialize the result for case 1 and handle case 2 |
| 13117 | // with |invertWhenNegative|. |
| 13118 | masm.bind(&compareSign); |
| 13119 | masm.move32(Imm32(op == JSOp::Gt || op == JSOp::Ge), output); |
| 13120 | masm.jump(&invertWhenNegative); |
| 13121 | |
| 13122 | // For sign(left) = sign(right) and len(digits(left)) != len(digits(right)), |
| 13123 | // we have to consider the two cases: |
| 13124 | // 1. len(digits(left)) < len(digits(right)) |
| 13125 | // 2. len(digits(left)) > len(digits(right)) |
| 13126 | // |
| 13127 | // For |left| <cmp> |right| with cmp=Lt: |
| 13128 | // Assume both BigInts are positive, then |left < right| is true for case 1 |
| 13129 | // and false for case 2. When both are negative, the result is reversed. |
| 13130 | // |
| 13131 | // The other comparison operators can be handled similarly. |
| 13132 | // |
| 13133 | // |temp0| holds the digits length of the right-hand side operand. |
| 13134 | masm.bind(&compareLength); |
| 13135 | masm.cmp32Set(JSOpToCondition(op, /* isSigned = */ false), |
| 13136 | Address(left, BigInt::offsetOfLength()), temp0, output); |
| 13137 | masm.jump(&invertWhenNegative); |
| 13138 | |
| 13139 | // Similar to the case above, compare the current digit to determine the |
| 13140 | // overall comparison result. |
| 13141 | // |
| 13142 | // |temp1| points to the current digit of the left-hand side operand. |
| 13143 | // |output| holds the current digit of the right-hand side operand. |
| 13144 | masm.bind(&compareDigit); |
| 13145 | masm.cmpPtrSet(JSOpToCondition(op, /* isSigned = */ false), |
| 13146 | Address(temp1, 0), output, output); |
| 13147 | |
| 13148 | Label nonNegative; |
| 13149 | masm.bind(&invertWhenNegative); |
| 13150 | masm.branchIfBigIntIsNonNegative(left, &nonNegative); |
| 13151 | masm.xor32(Imm32(1), output); |
| 13152 | masm.bind(&nonNegative); |
| 13153 | } |
| 13154 | |
| 13155 | masm.bind(&done); |
| 13156 | } |
| 13157 | |
| 13158 | void CodeGenerator::visitCompareBigIntInt32(LCompareBigIntInt32* lir) { |
| 13159 | JSOp op = lir->mir()->jsop(); |
| 13160 | Register left = ToRegister(lir->left()); |
| 13161 | Register temp0 = ToRegister(lir->temp0()); |
| 13162 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
| 13163 | Register output = ToRegister(lir->output()); |
| 13164 | |
| 13165 | Label ifTrue, ifFalse; |
| 13166 | if (lir->right()->isConstant()) { |
| 13167 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13167); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp1 == InvalidReg" ")"); do { MOZ_CrashSequence (__null, 13167); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13168 | |
| 13169 | Imm32 right = Imm32(ToInt32(lir->right())); |
| 13170 | masm.compareBigIntAndInt32(op, left, right, temp0, &ifTrue, &ifFalse); |
| 13171 | } else { |
| 13172 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13172); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp1 != InvalidReg" ")"); do { MOZ_CrashSequence (__null, 13172); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13173 | |
| 13174 | Register right = ToRegister(lir->right()); |
| 13175 | masm.compareBigIntAndInt32(op, left, right, temp0, temp1, &ifTrue, |
| 13176 | &ifFalse); |
| 13177 | } |
| 13178 | |
| 13179 | Label done; |
| 13180 | masm.bind(&ifFalse); |
| 13181 | masm.move32(Imm32(0), output); |
| 13182 | masm.jump(&done); |
| 13183 | masm.bind(&ifTrue); |
| 13184 | masm.move32(Imm32(1), output); |
| 13185 | masm.bind(&done); |
| 13186 | } |
| 13187 | |
| 13188 | void CodeGenerator::visitCompareBigIntInt32AndBranch( |
| 13189 | LCompareBigIntInt32AndBranch* lir) { |
| 13190 | JSOp op = lir->cmpMir()->jsop(); |
| 13191 | Register left = ToRegister(lir->left()); |
| 13192 | Register temp1 = ToRegister(lir->temp0()); |
| 13193 | Register temp2 = ToTempRegisterOrInvalid(lir->temp1()); |
| 13194 | |
| 13195 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
| 13196 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
| 13197 | |
| 13198 | // compareBigIntAndInt32 falls through to the false case. If the next block |
| 13199 | // is the true case, negate the comparison so we can fall through. |
| 13200 | if (isNextBlock(lir->ifTrue()->lir())) { |
| 13201 | op = NegateCompareOp(op); |
| 13202 | std::swap(ifTrue, ifFalse); |
| 13203 | } |
| 13204 | |
| 13205 | if (lir->right()->isConstant()) { |
| 13206 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13206); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp2 == InvalidReg" ")"); do { MOZ_CrashSequence (__null, 13206); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13207 | |
| 13208 | Imm32 right = Imm32(ToInt32(lir->right())); |
| 13209 | masm.compareBigIntAndInt32(op, left, right, temp1, ifTrue, ifFalse); |
| 13210 | } else { |
| 13211 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13211); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp2 != InvalidReg" ")"); do { MOZ_CrashSequence (__null, 13211); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13212 | |
| 13213 | Register right = ToRegister(lir->right()); |
| 13214 | masm.compareBigIntAndInt32(op, left, right, temp1, temp2, ifTrue, ifFalse); |
| 13215 | } |
| 13216 | |
| 13217 | if (!isNextBlock(lir->ifTrue()->lir())) { |
| 13218 | jumpToBlock(lir->ifFalse()); |
| 13219 | } |
| 13220 | } |
| 13221 | |
| 13222 | void CodeGenerator::visitCompareBigIntDouble(LCompareBigIntDouble* lir) { |
| 13223 | JSOp op = lir->mir()->jsop(); |
| 13224 | Register left = ToRegister(lir->left()); |
| 13225 | FloatRegister right = ToFloatRegister(lir->right()); |
| 13226 | Register output = ToRegister(lir->output()); |
| 13227 | |
| 13228 | masm.setupAlignedABICall(); |
| 13229 | |
| 13230 | // Push the operands in reverse order for JSOp::Le and JSOp::Gt: |
| 13231 | // - |left <= right| is implemented as |right >= left|. |
| 13232 | // - |left > right| is implemented as |right < left|. |
| 13233 | if (op == JSOp::Le || op == JSOp::Gt) { |
| 13234 | masm.passABIArg(right, ABIType::Float64); |
| 13235 | masm.passABIArg(left); |
| 13236 | } else { |
| 13237 | masm.passABIArg(left); |
| 13238 | masm.passABIArg(right, ABIType::Float64); |
| 13239 | } |
| 13240 | |
| 13241 | using FnBigIntNumber = bool (*)(BigInt*, double); |
| 13242 | using FnNumberBigInt = bool (*)(double, BigInt*); |
| 13243 | switch (op) { |
| 13244 | case JSOp::Eq: { |
| 13245 | masm.callWithABI<FnBigIntNumber, |
| 13246 | jit::BigIntNumberEqual<EqualityKind::Equal>>(); |
| 13247 | break; |
| 13248 | } |
| 13249 | case JSOp::Ne: { |
| 13250 | masm.callWithABI<FnBigIntNumber, |
| 13251 | jit::BigIntNumberEqual<EqualityKind::NotEqual>>(); |
| 13252 | break; |
| 13253 | } |
| 13254 | case JSOp::Lt: { |
| 13255 | masm.callWithABI<FnBigIntNumber, |
| 13256 | jit::BigIntNumberCompare<ComparisonKind::LessThan>>(); |
| 13257 | break; |
| 13258 | } |
| 13259 | case JSOp::Gt: { |
| 13260 | masm.callWithABI<FnNumberBigInt, |
| 13261 | jit::NumberBigIntCompare<ComparisonKind::LessThan>>(); |
| 13262 | break; |
| 13263 | } |
| 13264 | case JSOp::Le: { |
| 13265 | masm.callWithABI< |
| 13266 | FnNumberBigInt, |
| 13267 | jit::NumberBigIntCompare<ComparisonKind::GreaterThanOrEqual>>(); |
| 13268 | break; |
| 13269 | } |
| 13270 | case JSOp::Ge: { |
| 13271 | masm.callWithABI< |
| 13272 | FnBigIntNumber, |
| 13273 | jit::BigIntNumberCompare<ComparisonKind::GreaterThanOrEqual>>(); |
| 13274 | break; |
| 13275 | } |
| 13276 | default: |
| 13277 | MOZ_CRASH("unhandled op")do { do { } while (false); MOZ_ReportCrash("" "unhandled op", "./../../../../js/src/jit/CodeGenerator.cpp", 13277); AnnotateMozCrashReason ("MOZ_CRASH(" "unhandled op" ")"); do { MOZ_CrashSequence(__null , 13277); __attribute__((nomerge)) ::abort(); } while (false) ; } while (false); |
| 13278 | } |
| 13279 | |
| 13280 | masm.storeCallBoolResult(output); |
| 13281 | } |
| 13282 | |
| 13283 | void CodeGenerator::visitCompareBigIntString(LCompareBigIntString* lir) { |
| 13284 | JSOp op = lir->mir()->jsop(); |
| 13285 | Register left = ToRegister(lir->left()); |
| 13286 | Register right = ToRegister(lir->right()); |
| 13287 | |
| 13288 | // Push the operands in reverse order for JSOp::Le and JSOp::Gt: |
| 13289 | // - |left <= right| is implemented as |right >= left|. |
| 13290 | // - |left > right| is implemented as |right < left|. |
| 13291 | if (op == JSOp::Le || op == JSOp::Gt) { |
| 13292 | pushArg(left); |
| 13293 | pushArg(right); |
| 13294 | } else { |
| 13295 | pushArg(right); |
| 13296 | pushArg(left); |
| 13297 | } |
| 13298 | |
| 13299 | using FnBigIntString = |
| 13300 | bool (*)(JSContext*, HandleBigInt, HandleString, bool*); |
| 13301 | using FnStringBigInt = |
| 13302 | bool (*)(JSContext*, HandleString, HandleBigInt, bool*); |
| 13303 | |
| 13304 | switch (op) { |
| 13305 | case JSOp::Eq: { |
| 13306 | constexpr auto Equal = EqualityKind::Equal; |
| 13307 | callVM<FnBigIntString, BigIntStringEqual<Equal>>(lir); |
| 13308 | break; |
| 13309 | } |
| 13310 | case JSOp::Ne: { |
| 13311 | constexpr auto NotEqual = EqualityKind::NotEqual; |
| 13312 | callVM<FnBigIntString, BigIntStringEqual<NotEqual>>(lir); |
| 13313 | break; |
| 13314 | } |
| 13315 | case JSOp::Lt: { |
| 13316 | constexpr auto LessThan = ComparisonKind::LessThan; |
| 13317 | callVM<FnBigIntString, BigIntStringCompare<LessThan>>(lir); |
| 13318 | break; |
| 13319 | } |
| 13320 | case JSOp::Gt: { |
| 13321 | constexpr auto LessThan = ComparisonKind::LessThan; |
| 13322 | callVM<FnStringBigInt, StringBigIntCompare<LessThan>>(lir); |
| 13323 | break; |
| 13324 | } |
| 13325 | case JSOp::Le: { |
| 13326 | constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual; |
| 13327 | callVM<FnStringBigInt, StringBigIntCompare<GreaterThanOrEqual>>(lir); |
| 13328 | break; |
| 13329 | } |
| 13330 | case JSOp::Ge: { |
| 13331 | constexpr auto GreaterThanOrEqual = ComparisonKind::GreaterThanOrEqual; |
| 13332 | callVM<FnBigIntString, BigIntStringCompare<GreaterThanOrEqual>>(lir); |
| 13333 | break; |
| 13334 | } |
| 13335 | default: |
| 13336 | MOZ_CRASH("Unexpected compare op")do { do { } while (false); MOZ_ReportCrash("" "Unexpected compare op" , "./../../../../js/src/jit/CodeGenerator.cpp", 13336); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected compare op" ")"); do { MOZ_CrashSequence (__null, 13336); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 13337 | } |
| 13338 | } |
| 13339 | |
| 13340 | void CodeGenerator::visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir) { |
| 13341 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13342); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { MOZ_CrashSequence(__null, 13342); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 13342 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13342); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { MOZ_CrashSequence(__null, 13342); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13343 | |
| 13344 | JSOp op = lir->mir()->jsop(); |
| 13345 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 13345); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 13345); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13346 | |
| 13347 | ValueOperand value = ToValue(lir->value()); |
| 13348 | Register output = ToRegister(lir->output()); |
| 13349 | |
| 13350 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 13351 | if (!intact) { |
| 13352 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 13353 | addOutOfLineCode(ool, lir->mir()); |
| 13354 | |
| 13355 | Label* nullOrLikeUndefined = ool->label1(); |
| 13356 | Label* notNullOrLikeUndefined = ool->label2(); |
| 13357 | |
| 13358 | { |
| 13359 | ScratchTagScope tag(masm, value); |
| 13360 | masm.splitTagForTest(value, tag); |
| 13361 | |
| 13362 | masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined); |
| 13363 | masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined); |
| 13364 | |
| 13365 | // Check whether it's a truthy object or a falsy object that emulates |
| 13366 | // undefined. |
| 13367 | masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined); |
| 13368 | } |
| 13369 | |
| 13370 | Register objreg = |
| 13371 | masm.extractObject(value, ToTempUnboxRegister(lir->temp0())); |
| 13372 | branchTestObjectEmulatesUndefined(objreg, nullOrLikeUndefined, |
| 13373 | notNullOrLikeUndefined, output, ool); |
| 13374 | // fall through |
| 13375 | |
| 13376 | Label done; |
| 13377 | |
| 13378 | // It's not null or undefined, and if it's an object it doesn't |
| 13379 | // emulate undefined, so it's not like undefined. |
| 13380 | masm.move32(Imm32(op == JSOp::Ne), output); |
| 13381 | masm.jump(&done); |
| 13382 | |
| 13383 | masm.bind(nullOrLikeUndefined); |
| 13384 | masm.move32(Imm32(op == JSOp::Eq), output); |
| 13385 | |
| 13386 | // Both branches meet here. |
| 13387 | masm.bind(&done); |
| 13388 | } else { |
| 13389 | Label nullOrUndefined, notNullOrLikeUndefined; |
| 13390 | #if defined(DEBUG1) || defined(FUZZING) |
| 13391 | Register objreg = Register::Invalid(); |
| 13392 | #endif |
| 13393 | { |
| 13394 | ScratchTagScope tag(masm, value); |
| 13395 | masm.splitTagForTest(value, tag); |
| 13396 | |
| 13397 | masm.branchTestNull(Assembler::Equal, tag, &nullOrUndefined); |
| 13398 | masm.branchTestUndefined(Assembler::Equal, tag, &nullOrUndefined); |
| 13399 | |
| 13400 | #if defined(DEBUG1) || defined(FUZZING) |
| 13401 | // Check whether it's a truthy object or a falsy object that emulates |
| 13402 | // undefined. |
| 13403 | masm.branchTestObject(Assembler::NotEqual, tag, ¬NullOrLikeUndefined); |
| 13404 | objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp0())); |
| 13405 | #endif |
| 13406 | } |
| 13407 | |
| 13408 | #if defined(DEBUG1) || defined(FUZZING) |
| 13409 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
| 13410 | masm.bind(¬NullOrLikeUndefined); |
| 13411 | #endif |
| 13412 | |
| 13413 | Label done; |
| 13414 | |
| 13415 | // It's not null or undefined, and if it's an object it doesn't |
| 13416 | // emulate undefined. |
| 13417 | masm.move32(Imm32(op == JSOp::Ne), output); |
| 13418 | masm.jump(&done); |
| 13419 | |
| 13420 | masm.bind(&nullOrUndefined); |
| 13421 | masm.move32(Imm32(op == JSOp::Eq), output); |
| 13422 | |
| 13423 | // Both branches meet here. |
| 13424 | masm.bind(&done); |
| 13425 | } |
| 13426 | } |
| 13427 | |
| 13428 | void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchV( |
| 13429 | LIsNullOrLikeUndefinedAndBranchV* lir) { |
| 13430 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13431); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { MOZ_CrashSequence(__null, 13431); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 13431 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13431); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { MOZ_CrashSequence(__null, 13431); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13432 | |
| 13433 | JSOp op = lir->cmpMir()->jsop(); |
| 13434 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 13434); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 13434); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13435 | |
| 13436 | ValueOperand value = ToValue(lir->value()); |
| 13437 | |
| 13438 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 13439 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 13440 | |
| 13441 | if (op == JSOp::Ne) { |
| 13442 | // Swap branches. |
| 13443 | std::swap(ifTrue, ifFalse); |
| 13444 | } |
| 13445 | |
| 13446 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 13447 | |
| 13448 | Label* ifTrueLabel = getJumpLabelForBranch(ifTrue); |
| 13449 | Label* ifFalseLabel = getJumpLabelForBranch(ifFalse); |
| 13450 | |
| 13451 | bool extractObject = !intact; |
Value stored to 'extractObject' during its initialization is never read | |
| 13452 | Register objreg = Register::Invalid(); |
| 13453 | #if defined(DEBUG1) || defined(FUZZING) |
| 13454 | // always extract objreg if we're in debug and |
| 13455 | // assertObjectDoesNotEmulateUndefined; |
| 13456 | extractObject = true; |
| 13457 | #endif |
| 13458 | |
| 13459 | { |
| 13460 | ScratchTagScope tag(masm, value); |
| 13461 | masm.splitTagForTest(value, tag); |
| 13462 | |
| 13463 | masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel); |
| 13464 | masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel); |
| 13465 | |
| 13466 | if (extractObject) { |
| 13467 | masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel); |
| 13468 | objreg = masm.extractObject(value, ToTempUnboxRegister(lir->temp1())); |
| 13469 | } |
| 13470 | } |
| 13471 | |
| 13472 | Register scratch = ToRegister(lir->temp0()); |
| 13473 | if (!intact) { |
| 13474 | // Objects that emulate undefined are loosely equal to null/undefined. |
| 13475 | OutOfLineTestObject* ool = new (alloc()) OutOfLineTestObject(); |
| 13476 | addOutOfLineCode(ool, lir->cmpMir()); |
| 13477 | testObjectEmulatesUndefined(objreg, ifTrueLabel, ifFalseLabel, scratch, |
| 13478 | ool); |
| 13479 | } else { |
| 13480 | assertObjectDoesNotEmulateUndefined(objreg, scratch, lir->cmpMir()); |
| 13481 | // Bug 1874905. This would be nice to optimize out at the MIR level. |
| 13482 | if (!isNextBlock(ifFalse->lir())) { |
| 13483 | masm.jump(ifFalseLabel); |
| 13484 | } |
| 13485 | } |
| 13486 | } |
| 13487 | |
| 13488 | void CodeGenerator::visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir) { |
| 13489 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13490); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { MOZ_CrashSequence(__null, 13490); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 13490 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13490); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->compareType() == MCompare::Compare_Undefined || lir->mir()->compareType() == MCompare::Compare_Null" ")"); do { MOZ_CrashSequence(__null, 13490); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13491 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 13491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->lhs()->type() == MIRType::Object" ")"); do { MOZ_CrashSequence(__null, 13491); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13492 | |
| 13493 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 13494 | JSOp op = lir->mir()->jsop(); |
| 13495 | Register output = ToRegister(lir->output()); |
| 13496 | Register objreg = ToRegister(lir->input()); |
| 13497 | if (!intact) { |
| 13498 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp" , 13499); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence (__null, 13499); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false) |
| 13499 | "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" ")", "./../../../../js/src/jit/CodeGenerator.cpp" , 13499); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence (__null, 13499); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13500 | |
| 13501 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 13502 | addOutOfLineCode(ool, lir->mir()); |
| 13503 | |
| 13504 | Label* emulatesUndefined = ool->label1(); |
| 13505 | Label* doesntEmulateUndefined = ool->label2(); |
| 13506 | |
| 13507 | branchTestObjectEmulatesUndefined(objreg, emulatesUndefined, |
| 13508 | doesntEmulateUndefined, output, ool); |
| 13509 | |
| 13510 | Label done; |
| 13511 | |
| 13512 | masm.move32(Imm32(op == JSOp::Ne), output); |
| 13513 | masm.jump(&done); |
| 13514 | |
| 13515 | masm.bind(emulatesUndefined); |
| 13516 | masm.move32(Imm32(op == JSOp::Eq), output); |
| 13517 | masm.bind(&done); |
| 13518 | } else { |
| 13519 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
| 13520 | masm.move32(Imm32(op == JSOp::Ne), output); |
| 13521 | } |
| 13522 | } |
| 13523 | |
| 13524 | void CodeGenerator::visitIsNullOrLikeUndefinedAndBranchT( |
| 13525 | LIsNullOrLikeUndefinedAndBranchT* lir) { |
| 13526 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13527); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { MOZ_CrashSequence(__null, 13527); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 13527 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13527); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->cmpMir()->compareType() == MCompare::Compare_Undefined || lir->cmpMir()->compareType() == MCompare::Compare_Null" ")"); do { MOZ_CrashSequence(__null, 13527); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13528 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 13528); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->cmpMir()->lhs()->type() == MIRType::Object" ")"); do { MOZ_CrashSequence(__null, 13528); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13529 | |
| 13530 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 13531 | |
| 13532 | JSOp op = lir->cmpMir()->jsop(); |
| 13533 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp" , 13533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsLooseEqualityOp(op)" ") (" "Strict equality should have been folded" ")"); do { MOZ_CrashSequence (__null, 13533); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13534 | |
| 13535 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 13536 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 13537 | |
| 13538 | if (op == JSOp::Ne) { |
| 13539 | // Swap branches. |
| 13540 | std::swap(ifTrue, ifFalse); |
| 13541 | } |
| 13542 | |
| 13543 | Register input = ToRegister(lir->value()); |
| 13544 | Register scratch = ToRegister(lir->temp0()); |
| 13545 | Label* ifTrueLabel = getJumpLabelForBranch(ifTrue); |
| 13546 | Label* ifFalseLabel = getJumpLabelForBranch(ifFalse); |
| 13547 | |
| 13548 | if (intact) { |
| 13549 | // Bug 1874905. Ideally branches like this would be optimized out. |
| 13550 | assertObjectDoesNotEmulateUndefined(input, scratch, lir->mir()); |
| 13551 | masm.jump(ifFalseLabel); |
| 13552 | } else { |
| 13553 | auto* ool = new (alloc()) OutOfLineTestObject(); |
| 13554 | addOutOfLineCode(ool, lir->cmpMir()); |
| 13555 | |
| 13556 | // Objects that emulate undefined are loosely equal to null/undefined. |
| 13557 | testObjectEmulatesUndefined(input, ifTrueLabel, ifFalseLabel, scratch, ool); |
| 13558 | } |
| 13559 | } |
| 13560 | |
| 13561 | void CodeGenerator::visitIsNull(LIsNull* lir) { |
| 13562 | MCompare::CompareType compareType = lir->mir()->compareType(); |
| 13563 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13563); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")" ); do { MOZ_CrashSequence(__null, 13563); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 13564 | |
| 13565 | JSOp op = lir->mir()->jsop(); |
| 13566 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 13566); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 13566); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13567 | |
| 13568 | ValueOperand value = ToValue(lir->value()); |
| 13569 | Register output = ToRegister(lir->output()); |
| 13570 | |
| 13571 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
| 13572 | masm.testNullSet(cond, value, output); |
| 13573 | } |
| 13574 | |
| 13575 | void CodeGenerator::visitIsUndefined(LIsUndefined* lir) { |
| 13576 | MCompare::CompareType compareType = lir->mir()->compareType(); |
| 13577 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13577); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined" ")"); do { MOZ_CrashSequence(__null, 13577); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13578 | |
| 13579 | JSOp op = lir->mir()->jsop(); |
| 13580 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 13580); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 13580); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13581 | |
| 13582 | ValueOperand value = ToValue(lir->value()); |
| 13583 | Register output = ToRegister(lir->output()); |
| 13584 | |
| 13585 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
| 13586 | masm.testUndefinedSet(cond, value, output); |
| 13587 | } |
| 13588 | |
| 13589 | void CodeGenerator::visitIsNullAndBranch(LIsNullAndBranch* lir) { |
| 13590 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
| 13591 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13591); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Null" ")" ); do { MOZ_CrashSequence(__null, 13591); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 13592 | |
| 13593 | JSOp op = lir->cmpMir()->jsop(); |
| 13594 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 13594); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 13594); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13595 | |
| 13596 | ValueOperand value = ToValue(lir->value()); |
| 13597 | |
| 13598 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
| 13599 | |
| 13600 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 13601 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 13602 | |
| 13603 | if (isNextBlock(ifFalse->lir())) { |
| 13604 | masm.branchTestNull(cond, value, getJumpLabelForBranch(ifTrue)); |
| 13605 | } else { |
| 13606 | masm.branchTestNull(Assembler::InvertCondition(cond), value, |
| 13607 | getJumpLabelForBranch(ifFalse)); |
| 13608 | jumpToBlock(ifTrue); |
| 13609 | } |
| 13610 | } |
| 13611 | |
| 13612 | void CodeGenerator::visitIsUndefinedAndBranch(LIsUndefinedAndBranch* lir) { |
| 13613 | MCompare::CompareType compareType = lir->cmpMir()->compareType(); |
| 13614 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13614); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "compareType == MCompare::Compare_Undefined" ")"); do { MOZ_CrashSequence(__null, 13614); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13615 | |
| 13616 | JSOp op = lir->cmpMir()->jsop(); |
| 13617 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 13617); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "IsStrictEqualityOp(op)" ")"); do { MOZ_CrashSequence (__null, 13617); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13618 | |
| 13619 | ValueOperand value = ToValue(lir->value()); |
| 13620 | |
| 13621 | Assembler::Condition cond = JSOpToCondition(compareType, op); |
| 13622 | |
| 13623 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 13624 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 13625 | |
| 13626 | if (isNextBlock(ifFalse->lir())) { |
| 13627 | masm.branchTestUndefined(cond, value, getJumpLabelForBranch(ifTrue)); |
| 13628 | } else { |
| 13629 | masm.branchTestUndefined(Assembler::InvertCondition(cond), value, |
| 13630 | getJumpLabelForBranch(ifFalse)); |
| 13631 | jumpToBlock(ifTrue); |
| 13632 | } |
| 13633 | } |
| 13634 | |
| 13635 | void CodeGenerator::visitSameValueDouble(LSameValueDouble* lir) { |
| 13636 | FloatRegister left = ToFloatRegister(lir->left()); |
| 13637 | FloatRegister right = ToFloatRegister(lir->right()); |
| 13638 | FloatRegister temp = ToFloatRegister(lir->temp0()); |
| 13639 | Register output = ToRegister(lir->output()); |
| 13640 | |
| 13641 | masm.sameValueDouble(left, right, temp, output); |
| 13642 | } |
| 13643 | |
| 13644 | void CodeGenerator::visitSameValue(LSameValue* lir) { |
| 13645 | ValueOperand lhs = ToValue(lir->left()); |
| 13646 | ValueOperand rhs = ToValue(lir->right()); |
| 13647 | Register output = ToRegister(lir->output()); |
| 13648 | |
| 13649 | using Fn = bool (*)(JSContext*, const Value&, const Value&, bool*); |
| 13650 | OutOfLineCode* ool = |
| 13651 | oolCallVM<Fn, SameValue>(lir, ArgList(lhs, rhs), StoreRegisterTo(output)); |
| 13652 | |
| 13653 | // First check to see if the values have identical bits. |
| 13654 | // This is correct for SameValue because SameValue(NaN,NaN) is true, |
| 13655 | // and SameValue(0,-0) is false. |
| 13656 | masm.branch64(Assembler::NotEqual, lhs.toRegister64(), rhs.toRegister64(), |
| 13657 | ool->entry()); |
| 13658 | masm.move32(Imm32(1), output); |
| 13659 | |
| 13660 | // If this fails, call SameValue. |
| 13661 | masm.bind(ool->rejoin()); |
| 13662 | } |
| 13663 | |
| 13664 | void CodeGenerator::emitConcat(LInstruction* lir, Register lhs, Register rhs, |
| 13665 | Register output) { |
| 13666 | using Fn = |
| 13667 | JSString* (*)(JSContext*, HandleString, HandleString, js::gc::Heap); |
| 13668 | OutOfLineCode* ool = oolCallVM<Fn, ConcatStrings<CanGC>>( |
| 13669 | lir, ArgList(lhs, rhs, static_cast<Imm32>(int32_t(gc::Heap::Default))), |
| 13670 | StoreRegisterTo(output)); |
| 13671 | |
| 13672 | JitCode* stringConcatStub = |
| 13673 | snapshot_->getZoneStub(JitZone::StubKind::StringConcat); |
| 13674 | masm.call(stringConcatStub); |
| 13675 | masm.branchTestPtr(Assembler::Zero, output, output, ool->entry()); |
| 13676 | |
| 13677 | masm.bind(ool->rejoin()); |
| 13678 | } |
| 13679 | |
| 13680 | void CodeGenerator::visitConcat(LConcat* lir) { |
| 13681 | Register lhs = ToRegister(lir->lhs()); |
| 13682 | Register rhs = ToRegister(lir->rhs()); |
| 13683 | |
| 13684 | Register output = ToRegister(lir->output()); |
| 13685 | |
| 13686 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13686); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lhs == CallTempReg0" ")"); do { MOZ_CrashSequence (__null, 13686); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13687 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13687); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "rhs == CallTempReg1" ")"); do { MOZ_CrashSequence (__null, 13687); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13688 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13688); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->temp0()) == CallTempReg0" ")"); do { MOZ_CrashSequence(__null, 13688); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13689 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13689); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->temp1()) == CallTempReg1" ")"); do { MOZ_CrashSequence(__null, 13689); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13690 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13690); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->temp2()) == CallTempReg2" ")"); do { MOZ_CrashSequence(__null, 13690); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13691 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13691); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->temp3()) == CallTempReg3" ")"); do { MOZ_CrashSequence(__null, 13691); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13692 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13692); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->temp4()) == CallTempReg4" ")"); do { MOZ_CrashSequence(__null, 13692); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13693 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13693); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output == CallTempReg5" ")"); do { MOZ_CrashSequence (__null, 13693); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13694 | |
| 13695 | emitConcat(lir, lhs, rhs, output); |
| 13696 | } |
| 13697 | |
| 13698 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
| 13699 | Register len, Register byteOpScratch, |
| 13700 | CharEncoding fromEncoding, CharEncoding toEncoding, |
| 13701 | size_t maximumLength = SIZE_MAX(18446744073709551615UL)) { |
| 13702 | // Copy |len| char16_t code units from |from| to |to|. Assumes len > 0 |
| 13703 | // (checked below in debug builds), and when done |to| must point to the |
| 13704 | // next available char. |
| 13705 | |
| 13706 | #ifdef DEBUG1 |
| 13707 | Label ok; |
| 13708 | masm.branch32(Assembler::GreaterThan, len, Imm32(0), &ok); |
| 13709 | masm.assumeUnreachable("Length should be greater than 0."); |
| 13710 | masm.bind(&ok); |
| 13711 | |
| 13712 | if (maximumLength != SIZE_MAX(18446744073709551615UL)) { |
| 13713 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp", 13713); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "maximumLength <= (2147483647)" ") (" "maximum length fits into int32" ")"); do { MOZ_CrashSequence(__null, 13713); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 13714 | |
| 13715 | Label ok; |
| 13716 | masm.branchPtr(Assembler::BelowOrEqual, len, Imm32(maximumLength), &ok); |
| 13717 | masm.assumeUnreachable("Length should not exceed maximum length."); |
| 13718 | masm.bind(&ok); |
| 13719 | } |
| 13720 | #endif |
| 13721 | |
| 13722 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 13723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1" ")"); do { MOZ_CrashSequence(__null, 13723); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 13723 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 13723); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fromEncoding == CharEncoding::Latin1" ")"); do { MOZ_CrashSequence(__null, 13723); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 13724 | |
| 13725 | size_t fromWidth = |
| 13726 | fromEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); |
| 13727 | size_t toWidth = |
| 13728 | toEncoding == CharEncoding::Latin1 ? sizeof(char) : sizeof(char16_t); |
| 13729 | |
| 13730 | // Try to copy multiple characters at once when both encoding are equal. |
| 13731 | if (fromEncoding == toEncoding) { |
| 13732 | constexpr size_t ptrWidth = sizeof(uintptr_t); |
| 13733 | |
| 13734 | // Copy |width| bytes and then adjust |from| and |to|. |
| 13735 | auto copyCharacters = [&](size_t width) { |
| 13736 | static_assert(ptrWidth <= 8, "switch handles only up to eight bytes"); |
| 13737 | |
| 13738 | switch (width) { |
| 13739 | case 1: |
| 13740 | masm.load8ZeroExtend(Address(from, 0), byteOpScratch); |
| 13741 | masm.store8(byteOpScratch, Address(to, 0)); |
| 13742 | break; |
| 13743 | case 2: |
| 13744 | masm.load16ZeroExtend(Address(from, 0), byteOpScratch); |
| 13745 | masm.store16(byteOpScratch, Address(to, 0)); |
| 13746 | break; |
| 13747 | case 4: |
| 13748 | masm.load32(Address(from, 0), byteOpScratch); |
| 13749 | masm.store32(byteOpScratch, Address(to, 0)); |
| 13750 | break; |
| 13751 | case 8: |
| 13752 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 13752); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "width == ptrWidth" ")"); do { MOZ_CrashSequence (__null, 13752); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 13753 | masm.loadPtr(Address(from, 0), byteOpScratch); |
| 13754 | masm.storePtr(byteOpScratch, Address(to, 0)); |
| 13755 | break; |
| 13756 | } |
| 13757 | |
| 13758 | masm.addPtr(Imm32(width), from); |
| 13759 | masm.addPtr(Imm32(width), to); |
| 13760 | }; |
| 13761 | |
| 13762 | // First align |len| to pointer width. |
| 13763 | Label done; |
| 13764 | for (size_t width = fromWidth; width < ptrWidth; width *= 2) { |
| 13765 | // Number of characters which fit into |width| bytes. |
| 13766 | size_t charsPerWidth = width / fromWidth; |
| 13767 | |
| 13768 | if (charsPerWidth < maximumLength) { |
| 13769 | Label next; |
| 13770 | masm.branchTest32(Assembler::Zero, len, Imm32(charsPerWidth), &next); |
| 13771 | |
| 13772 | copyCharacters(width); |
| 13773 | |
| 13774 | masm.branchSub32(Assembler::Zero, Imm32(charsPerWidth), len, &done); |
| 13775 | masm.bind(&next); |
| 13776 | } else if (charsPerWidth == maximumLength) { |
| 13777 | copyCharacters(width); |
| 13778 | masm.sub32(Imm32(charsPerWidth), len); |
| 13779 | } |
| 13780 | } |
| 13781 | |
| 13782 | size_t maxInlineLength; |
| 13783 | if (fromEncoding == CharEncoding::Latin1) { |
| 13784 | maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; |
| 13785 | } else { |
| 13786 | maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
| 13787 | } |
| 13788 | |
| 13789 | // Number of characters which fit into a single register. |
| 13790 | size_t charsPerPtr = ptrWidth / fromWidth; |
| 13791 | |
| 13792 | // Unroll small loops. |
| 13793 | constexpr size_t unrollLoopLimit = 3; |
| 13794 | size_t loopCount = std::min(maxInlineLength, maximumLength) / charsPerPtr; |
| 13795 | |
| 13796 | #ifdef JS_64BIT1 |
| 13797 | static constexpr size_t latin1MaxInlineByteLength = |
| 13798 | JSFatInlineString::MAX_LENGTH_LATIN1 * sizeof(char); |
| 13799 | static constexpr size_t twoByteMaxInlineByteLength = |
| 13800 | JSFatInlineString::MAX_LENGTH_TWO_BYTE * sizeof(char16_t); |
| 13801 | |
| 13802 | // |unrollLoopLimit| should be large enough to allow loop unrolling on |
| 13803 | // 64-bit targets. |
| 13804 | static_assert(latin1MaxInlineByteLength / ptrWidth == unrollLoopLimit, |
| 13805 | "Latin-1 loops are unrolled on 64-bit"); |
| 13806 | static_assert(twoByteMaxInlineByteLength / ptrWidth == unrollLoopLimit, |
| 13807 | "Two-byte loops are unrolled on 64-bit"); |
| 13808 | #endif |
| 13809 | |
| 13810 | if (loopCount <= unrollLoopLimit) { |
| 13811 | Label labels[unrollLoopLimit]; |
| 13812 | |
| 13813 | // Check up front how many characters can be copied. |
| 13814 | for (size_t i = 1; i < loopCount; i++) { |
| 13815 | masm.branch32(Assembler::Below, len, Imm32((i + 1) * charsPerPtr), |
| 13816 | &labels[i]); |
| 13817 | } |
| 13818 | |
| 13819 | // Generate the unrolled loop body. |
| 13820 | for (size_t i = loopCount; i > 0; i--) { |
| 13821 | copyCharacters(ptrWidth); |
| 13822 | masm.sub32(Imm32(charsPerPtr), len); |
| 13823 | |
| 13824 | // Jump target for the previous length check. |
| 13825 | if (i != 1) { |
| 13826 | masm.bind(&labels[i - 1]); |
| 13827 | } |
| 13828 | } |
| 13829 | } else { |
| 13830 | Label start; |
| 13831 | masm.bind(&start); |
| 13832 | copyCharacters(ptrWidth); |
| 13833 | masm.branchSub32(Assembler::NonZero, Imm32(charsPerPtr), len, &start); |
| 13834 | } |
| 13835 | |
| 13836 | masm.bind(&done); |
| 13837 | } else { |
| 13838 | Label start; |
| 13839 | masm.bind(&start); |
| 13840 | masm.loadChar(Address(from, 0), byteOpScratch, fromEncoding); |
| 13841 | masm.storeChar(byteOpScratch, Address(to, 0), toEncoding); |
| 13842 | masm.addPtr(Imm32(fromWidth), from); |
| 13843 | masm.addPtr(Imm32(toWidth), to); |
| 13844 | masm.branchSub32(Assembler::NonZero, Imm32(1), len, &start); |
| 13845 | } |
| 13846 | } |
| 13847 | |
| 13848 | static void CopyStringChars(MacroAssembler& masm, Register to, Register from, |
| 13849 | Register len, Register byteOpScratch, |
| 13850 | CharEncoding encoding, size_t maximumLength) { |
| 13851 | CopyStringChars(masm, to, from, len, byteOpScratch, encoding, encoding, |
| 13852 | maximumLength); |
| 13853 | } |
| 13854 | |
| 13855 | static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, |
| 13856 | Register destChars, Register temp1, |
| 13857 | Register temp2) { |
| 13858 | // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may |
| 13859 | // have to inflate. |
| 13860 | |
| 13861 | Label isLatin1, done; |
| 13862 | masm.loadStringLength(input, temp1); |
| 13863 | masm.branchLatin1String(input, &isLatin1); |
| 13864 | { |
| 13865 | masm.loadStringChars(input, temp2, CharEncoding::TwoByte); |
| 13866 | masm.movePtr(temp2, input); |
| 13867 | CopyStringChars(masm, destChars, input, temp1, temp2, |
| 13868 | CharEncoding::TwoByte); |
| 13869 | masm.jump(&done); |
| 13870 | } |
| 13871 | masm.bind(&isLatin1); |
| 13872 | { |
| 13873 | masm.loadStringChars(input, temp2, CharEncoding::Latin1); |
| 13874 | masm.movePtr(temp2, input); |
| 13875 | CopyStringChars(masm, destChars, input, temp1, temp2, CharEncoding::Latin1, |
| 13876 | CharEncoding::TwoByte); |
| 13877 | } |
| 13878 | masm.bind(&done); |
| 13879 | } |
| 13880 | |
| 13881 | static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output, |
| 13882 | Register length, Register temp, |
| 13883 | gc::Heap initialStringHeap, |
| 13884 | Label* failure, |
| 13885 | CharEncoding encoding) { |
| 13886 | #ifdef DEBUG1 |
| 13887 | size_t maxInlineLength; |
| 13888 | if (encoding == CharEncoding::Latin1) { |
| 13889 | maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; |
| 13890 | } else { |
| 13891 | maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
| 13892 | } |
| 13893 | |
| 13894 | Label ok; |
| 13895 | masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok); |
| 13896 | masm.assumeUnreachable("string length too large to be allocated as inline"); |
| 13897 | masm.bind(&ok); |
| 13898 | #endif |
| 13899 | |
| 13900 | size_t maxThinInlineLength; |
| 13901 | if (encoding == CharEncoding::Latin1) { |
| 13902 | maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1; |
| 13903 | } else { |
| 13904 | maxThinInlineLength = JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
| 13905 | } |
| 13906 | |
| 13907 | Label isFat, allocDone; |
| 13908 | masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat); |
| 13909 | { |
| 13910 | uint32_t flags = StringFlags::thinInlineStringFlags(encoding); |
| 13911 | masm.newGCString(output, temp, initialStringHeap, failure); |
| 13912 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
| 13913 | masm.jump(&allocDone); |
| 13914 | } |
| 13915 | masm.bind(&isFat); |
| 13916 | { |
| 13917 | uint32_t flags = StringFlags::fatInlineStringFlags(encoding); |
| 13918 | masm.newGCFatInlineString(output, temp, initialStringHeap, failure); |
| 13919 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
| 13920 | } |
| 13921 | masm.bind(&allocDone); |
| 13922 | |
| 13923 | // Store length. |
| 13924 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
| 13925 | } |
| 13926 | |
| 13927 | static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, |
| 13928 | Register output, Register andedFlags, |
| 13929 | Register temp2, Register temp3, |
| 13930 | gc::Heap initialStringHeap, Label* failure, |
| 13931 | CharEncoding encoding) { |
| 13932 | JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)", |
| 13933 | (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); |
| 13934 | |
| 13935 | // State: result length in temp2. |
| 13936 | |
| 13937 | #ifdef DEBUG1 |
| 13938 | Label skip, rope; |
| 13939 | |
| 13940 | // Ensure both strings are linear. |
| 13941 | masm.branchIfRope(lhs, &rope); |
| 13942 | masm.branchIfRope(rhs, &rope); |
| 13943 | |
| 13944 | masm.jump(&skip); |
| 13945 | masm.bind(&rope); |
| 13946 | masm.assertUnreachable("Ropes encountered in ConcatInlineString."); |
| 13947 | masm.bind(&skip); |
| 13948 | #endif |
| 13949 | |
| 13950 | // Allocate a JSThinInlineString or JSFatInlineString. |
| 13951 | AllocateThinOrFatInlineString(masm, output, temp2, temp3, initialStringHeap, |
| 13952 | failure, encoding); |
| 13953 | |
| 13954 | // Load chars pointer in temp2. |
| 13955 | masm.loadInlineStringCharsForStore(output, temp2); |
| 13956 | |
| 13957 | #if defined(JS_64BIT1) && defined(ENABLE_WASM_SIMD1) |
| 13958 | Label fastPath, done; |
| 13959 | masm.branchTest32(Assembler::NonZero, andedFlags, |
| 13960 | Imm32(StringFlags::INLINE_CHARS_BIT), &fastPath); |
| 13961 | #endif |
| 13962 | |
| 13963 | Register temp1 = andedFlags; |
| 13964 | auto copyChars = [&](Register src) { |
| 13965 | if (encoding == CharEncoding::TwoByte) { |
| 13966 | CopyStringCharsMaybeInflate(masm, src, temp2, temp3, temp1); |
| 13967 | } else { |
| 13968 | masm.loadStringLength(src, temp1); |
| 13969 | masm.loadStringChars(src, temp3, CharEncoding::Latin1); |
| 13970 | masm.movePtr(temp3, src); |
| 13971 | CopyStringChars(masm, temp2, src, temp1, temp3, CharEncoding::Latin1); |
| 13972 | } |
| 13973 | }; |
| 13974 | |
| 13975 | // Copy lhs chars. Note that this advances temp2 to point to the next |
| 13976 | // char. This also clobbers the lhs register. |
| 13977 | copyChars(lhs); |
| 13978 | |
| 13979 | // Copy rhs chars. Clobbers the rhs register. |
| 13980 | copyChars(rhs); |
| 13981 | |
| 13982 | // There's a lot of assumptions in here that inline strings are at least |
| 13983 | // 16 bytes, so while it's possible to write a faster version for 32-bit, |
| 13984 | // we elect to just leave 32-bit platforms behind with a little bit slower |
| 13985 | // string copying. |
| 13986 | #if defined(JS_64BIT1) && defined(ENABLE_WASM_SIMD1) |
| 13987 | masm.jump(&done); |
| 13988 | masm.bind(&fastPath); |
| 13989 | |
| 13990 | // Note: these assertions are here just to trip if this changes, because all |
| 13991 | // the code below is very much dependent on the specific sizes. |
| 13992 | static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 == 16); |
| 13993 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE == 8); |
| 13994 | static_assert(JSFatInlineString::MAX_LENGTH_LATIN1 == 24); |
| 13995 | static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE == 12); |
| 13996 | |
| 13997 | size_t thinInlineLength = encoding == CharEncoding::Latin1 |
| 13998 | ? JSThinInlineString::MAX_LENGTH_LATIN1 |
| 13999 | : JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
| 14000 | |
| 14001 | // Given that for the lhs we know we're copying into the start of an inline |
| 14002 | // string then on 64-bit we know we're safe to write 16 bytes, regardless |
| 14003 | // of the actual length. |
| 14004 | auto copyLhsFast = [&]() { |
| 14005 | masm.loadStringLength(lhs, temp1); |
| 14006 | masm.loadInlineStringCharsForStore(lhs, temp3); |
| 14007 | |
| 14008 | masm.loadUnalignedSimd128(Address(temp3, 0), ScratchSimd128Reg); |
| 14009 | masm.storeUnalignedSimd128(ScratchSimd128Reg, Address(temp2, 0)); |
| 14010 | |
| 14011 | Label lhsDone; |
| 14012 | masm.branch32(Assembler::BelowOrEqual, temp1, Imm32(thinInlineLength), |
| 14013 | &lhsDone); |
| 14014 | masm.loadPtr(Address(temp3, 16), temp3); |
| 14015 | masm.storePtr(temp3, Address(temp2, 16)); |
| 14016 | masm.bind(&lhsDone); |
| 14017 | |
| 14018 | if (encoding == CharEncoding::Latin1) { |
| 14019 | masm.addPtr(temp1, temp2); |
| 14020 | } else { |
| 14021 | masm.computeEffectiveAddress(BaseIndex(temp2, temp1, TimesTwo), temp2); |
| 14022 | } |
| 14023 | }; |
| 14024 | |
| 14025 | // For the RHS however we don't have any guarantees, but we know we |
| 14026 | // can handle everything >= 8 bytes with at most three overlapping |
| 14027 | // 8 byte copies. |
| 14028 | auto copyRhsFast = [&]() { |
| 14029 | masm.loadStringLength(rhs, temp1); |
| 14030 | masm.loadInlineStringCharsForStore(rhs, temp3); |
| 14031 | |
| 14032 | if (encoding == CharEncoding::TwoByte) { |
| 14033 | masm.lshift32(Imm32(1), temp1); |
| 14034 | } |
| 14035 | |
| 14036 | Label rhsBelow8, rhsBelow4, rhsDone; |
| 14037 | |
| 14038 | // byteLen >= 8: head + conditional middle + tail. |
| 14039 | masm.branch32(Assembler::Below, temp1, Imm32(8), &rhsBelow8); |
| 14040 | |
| 14041 | masm.loadPtr(Address(temp3, 0), lhs); |
| 14042 | masm.storePtr(lhs, Address(temp2, 0)); |
| 14043 | |
| 14044 | Label rhsTail; |
| 14045 | masm.branch32(Assembler::BelowOrEqual, temp1, |
| 14046 | Imm32(JSThinInlineString::InlineBytes), &rhsTail); |
| 14047 | masm.loadPtr(Address(temp3, 8), lhs); |
| 14048 | masm.storePtr(lhs, Address(temp2, 8)); |
| 14049 | |
| 14050 | masm.bind(&rhsTail); |
| 14051 | masm.loadPtr(BaseIndex(temp3, temp1, TimesOne, -8), lhs); |
| 14052 | masm.storePtr(lhs, BaseIndex(temp2, temp1, TimesOne, -8)); |
| 14053 | masm.jump(&rhsDone); |
| 14054 | |
| 14055 | // byteLen 4-7: two overlapping 4-byte copies. |
| 14056 | masm.bind(&rhsBelow8); |
| 14057 | masm.branch32(Assembler::Below, temp1, Imm32(4), &rhsBelow4); |
| 14058 | masm.load32(Address(temp3, 0), lhs); |
| 14059 | masm.store32(lhs, Address(temp2, 0)); |
| 14060 | masm.load32(BaseIndex(temp3, temp1, TimesOne, -4), lhs); |
| 14061 | masm.store32(lhs, BaseIndex(temp2, temp1, TimesOne, -4)); |
| 14062 | masm.jump(&rhsDone); |
| 14063 | |
| 14064 | // byteLen 1-3: first byte + overlapping 2-byte tail. |
| 14065 | masm.bind(&rhsBelow4); |
| 14066 | masm.load8ZeroExtend(Address(temp3, 0), lhs); |
| 14067 | masm.store8(lhs, Address(temp2, 0)); |
| 14068 | masm.branch32(Assembler::Below, temp1, Imm32(2), &rhsDone); |
| 14069 | masm.load16ZeroExtend(BaseIndex(temp3, temp1, TimesOne, -2), lhs); |
| 14070 | masm.store16(lhs, BaseIndex(temp2, temp1, TimesOne, -2)); |
| 14071 | |
| 14072 | masm.bind(&rhsDone); |
| 14073 | }; |
| 14074 | |
| 14075 | // If the output encoding is Latin1, both inputs are Latin1. However if the |
| 14076 | // output encoding is TwoByte, we only know that at least one of the inputs |
| 14077 | // is TwoByte. |
| 14078 | if (encoding == CharEncoding::Latin1) { |
| 14079 | copyLhsFast(); |
| 14080 | copyRhsFast(); |
| 14081 | } else { |
| 14082 | auto copyCharsInflate = [&](Register src) { |
| 14083 | masm.loadStringLength(src, temp3); |
| 14084 | masm.loadStringChars(src, temp1, CharEncoding::Latin1); |
| 14085 | masm.movePtr(temp1, src); |
| 14086 | CopyStringChars(masm, temp2, src, temp3, temp1, CharEncoding::Latin1, |
| 14087 | CharEncoding::TwoByte); |
| 14088 | }; |
| 14089 | |
| 14090 | Label lhsInflate, beginRhs, rhsInflate; |
| 14091 | masm.branchLatin1String(lhs, &lhsInflate); |
| 14092 | copyLhsFast(); |
| 14093 | masm.jump(&beginRhs); |
| 14094 | |
| 14095 | masm.bind(&lhsInflate); |
| 14096 | copyCharsInflate(lhs); |
| 14097 | |
| 14098 | // If lhs was latin1, we know rhs must be TwoByte, so we can skip |
| 14099 | // a branch here and just copy rhs directly |
| 14100 | copyRhsFast(); |
| 14101 | masm.jump(&done); |
| 14102 | |
| 14103 | masm.bind(&beginRhs); |
| 14104 | masm.branchLatin1String(rhs, &rhsInflate); |
| 14105 | copyRhsFast(); |
| 14106 | masm.jump(&done); |
| 14107 | |
| 14108 | masm.bind(&rhsInflate); |
| 14109 | copyCharsInflate(rhs); |
| 14110 | } |
| 14111 | masm.bind(&done); |
| 14112 | #endif |
| 14113 | } |
| 14114 | |
| 14115 | void CodeGenerator::visitSubstr(LSubstr* lir) { |
| 14116 | Register string = ToRegister(lir->string()); |
| 14117 | Register begin = ToRegister(lir->begin()); |
| 14118 | Register length = ToRegister(lir->length()); |
| 14119 | Register output = ToRegister(lir->output()); |
| 14120 | Register temp0 = ToRegister(lir->temp0()); |
| 14121 | Register temp2 = ToRegister(lir->temp2()); |
| 14122 | |
| 14123 | // On x86 there are not enough registers. In that case reuse the string |
| 14124 | // register as temporary. |
| 14125 | Register temp1 = |
| 14126 | lir->temp1()->isBogusTemp() ? string : ToRegister(lir->temp1()); |
| 14127 | |
| 14128 | size_t maximumLength = SIZE_MAX(18446744073709551615UL); |
| 14129 | |
| 14130 | Range* range = lir->mir()->length()->range(); |
| 14131 | if (range && range->hasInt32UpperBound()) { |
| 14132 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 14132); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "range->upper() >= 0" ")"); do { MOZ_CrashSequence (__null, 14132); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 14133 | maximumLength = size_t(range->upper()); |
| 14134 | } |
| 14135 | |
| 14136 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE <= |
| 14137 | JSThinInlineString::MAX_LENGTH_LATIN1); |
| 14138 | |
| 14139 | static_assert(JSFatInlineString::MAX_LENGTH_TWO_BYTE <= |
| 14140 | JSFatInlineString::MAX_LENGTH_LATIN1); |
| 14141 | |
| 14142 | bool tryFatInlineOrDependent = |
| 14143 | maximumLength > JSThinInlineString::MAX_LENGTH_TWO_BYTE; |
| 14144 | bool tryDependent = maximumLength > JSFatInlineString::MAX_LENGTH_TWO_BYTE; |
| 14145 | |
| 14146 | #ifdef DEBUG1 |
| 14147 | if (maximumLength != SIZE_MAX(18446744073709551615UL)) { |
| 14148 | Label ok; |
| 14149 | masm.branch32(Assembler::BelowOrEqual, length, Imm32(maximumLength), &ok); |
| 14150 | masm.assumeUnreachable("length should not exceed maximum length"); |
| 14151 | masm.bind(&ok); |
| 14152 | } |
| 14153 | #endif |
| 14154 | |
| 14155 | Label nonZero, nonInput; |
| 14156 | |
| 14157 | // For every edge case use the C++ variant. |
| 14158 | // Note: we also use this upon allocation failure in newGCString and |
| 14159 | // newGCFatInlineString. To squeeze out even more performance those failures |
| 14160 | // can be handled by allocate in ool code and returning to jit code to fill |
| 14161 | // in all data. |
| 14162 | using Fn = JSString* (*)(JSContext * cx, HandleString str, int32_t begin, |
| 14163 | int32_t len); |
| 14164 | OutOfLineCode* ool = oolCallVM<Fn, SubstringKernel>( |
| 14165 | lir, ArgList(string, begin, length), StoreRegisterTo(output)); |
| 14166 | Label* slowPath = ool->entry(); |
| 14167 | Label* done = ool->rejoin(); |
| 14168 | |
| 14169 | // Zero length, return emptystring. |
| 14170 | masm.branchTest32(Assembler::NonZero, length, length, &nonZero); |
| 14171 | const JSAtomState& names = gen->runtime->names(); |
| 14172 | masm.movePtr(ImmGCPtr(names.empty_), output); |
| 14173 | masm.jump(done); |
| 14174 | |
| 14175 | // Substring from 0..|str.length|, return str. |
| 14176 | masm.bind(&nonZero); |
| 14177 | masm.branch32(Assembler::NotEqual, |
| 14178 | Address(string, JSString::offsetOfLength()), length, &nonInput); |
| 14179 | #ifdef DEBUG1 |
| 14180 | { |
| 14181 | Label ok; |
| 14182 | masm.branchTest32(Assembler::Zero, begin, begin, &ok); |
| 14183 | masm.assumeUnreachable("length == str.length implies begin == 0"); |
| 14184 | masm.bind(&ok); |
| 14185 | } |
| 14186 | #endif |
| 14187 | masm.movePtr(string, output); |
| 14188 | masm.jump(done); |
| 14189 | |
| 14190 | // Use slow path for ropes. |
| 14191 | masm.bind(&nonInput); |
| 14192 | masm.branchIfRope(string, slowPath); |
| 14193 | |
| 14194 | // Optimize one and two character strings. |
| 14195 | Label nonStatic; |
| 14196 | masm.branch32(Assembler::Above, length, Imm32(2), &nonStatic); |
| 14197 | { |
| 14198 | Label loadLengthOne, loadLengthTwo; |
| 14199 | |
| 14200 | auto loadChars = [&](CharEncoding encoding, bool fallthru) { |
| 14201 | size_t size = encoding == CharEncoding::Latin1 ? sizeof(JS::Latin1Char) |
| 14202 | : sizeof(char16_t); |
| 14203 | |
| 14204 | masm.loadStringChars(string, temp0, encoding); |
| 14205 | masm.loadChar(temp0, begin, temp2, encoding); |
| 14206 | masm.branch32(Assembler::Equal, length, Imm32(1), &loadLengthOne); |
| 14207 | masm.loadChar(temp0, begin, temp0, encoding, int32_t(size)); |
| 14208 | if (!fallthru) { |
| 14209 | masm.jump(&loadLengthTwo); |
| 14210 | } |
| 14211 | }; |
| 14212 | |
| 14213 | Label isLatin1; |
| 14214 | masm.branchLatin1String(string, &isLatin1); |
| 14215 | loadChars(CharEncoding::TwoByte, /* fallthru = */ false); |
| 14216 | |
| 14217 | masm.bind(&isLatin1); |
| 14218 | loadChars(CharEncoding::Latin1, /* fallthru = */ true); |
| 14219 | |
| 14220 | // Try to load a length-two static string. |
| 14221 | masm.bind(&loadLengthTwo); |
| 14222 | masm.lookupStaticString(temp2, temp0, output, gen->runtime->staticStrings(), |
| 14223 | &nonStatic); |
| 14224 | masm.jump(done); |
| 14225 | |
| 14226 | // Try to load a length-one static string. |
| 14227 | masm.bind(&loadLengthOne); |
| 14228 | masm.lookupStaticString(temp2, output, gen->runtime->staticStrings(), |
| 14229 | &nonStatic); |
| 14230 | masm.jump(done); |
| 14231 | } |
| 14232 | masm.bind(&nonStatic); |
| 14233 | |
| 14234 | // Allocate either a JSThinInlineString or JSFatInlineString, or jump to |
| 14235 | // notInline if we need a dependent string. |
| 14236 | Label notInline; |
| 14237 | { |
| 14238 | static_assert(JSThinInlineString::MAX_LENGTH_LATIN1 < |
| 14239 | JSFatInlineString::MAX_LENGTH_LATIN1); |
| 14240 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE < |
| 14241 | JSFatInlineString::MAX_LENGTH_TWO_BYTE); |
| 14242 | |
| 14243 | // Use temp2 to store the JS(Thin|Fat)InlineString flags. This avoids having |
| 14244 | // duplicate newGCString/newGCFatInlineString codegen for Latin1 vs TwoByte |
| 14245 | // strings. |
| 14246 | |
| 14247 | Label allocFat, allocDone; |
| 14248 | if (tryFatInlineOrDependent) { |
| 14249 | Label isLatin1, allocThin; |
| 14250 | masm.branchLatin1String(string, &isLatin1); |
| 14251 | { |
| 14252 | if (tryDependent) { |
| 14253 | masm.branch32(Assembler::Above, length, |
| 14254 | Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), |
| 14255 | ¬Inline); |
| 14256 | } |
| 14257 | masm.move32(Imm32(0), temp2); |
| 14258 | masm.branch32(Assembler::Above, length, |
| 14259 | Imm32(JSThinInlineString::MAX_LENGTH_TWO_BYTE), |
| 14260 | &allocFat); |
| 14261 | masm.jump(&allocThin); |
| 14262 | } |
| 14263 | |
| 14264 | masm.bind(&isLatin1); |
| 14265 | { |
| 14266 | if (tryDependent) { |
| 14267 | masm.branch32(Assembler::Above, length, |
| 14268 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), |
| 14269 | ¬Inline); |
| 14270 | } |
| 14271 | masm.move32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp2); |
| 14272 | masm.branch32(Assembler::Above, length, |
| 14273 | Imm32(JSThinInlineString::MAX_LENGTH_LATIN1), &allocFat); |
| 14274 | } |
| 14275 | |
| 14276 | masm.bind(&allocThin); |
| 14277 | } else { |
| 14278 | masm.load32(Address(string, JSString::offsetOfFlags()), temp2); |
| 14279 | masm.and32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp2); |
| 14280 | } |
| 14281 | |
| 14282 | { |
| 14283 | masm.newGCString(output, temp0, initialStringHeap(), slowPath); |
| 14284 | masm.or32(Imm32(StringFlags::INIT_THIN_INLINE_FLAGS), temp2); |
| 14285 | } |
| 14286 | |
| 14287 | if (tryFatInlineOrDependent) { |
| 14288 | masm.jump(&allocDone); |
| 14289 | |
| 14290 | masm.bind(&allocFat); |
| 14291 | { |
| 14292 | masm.newGCFatInlineString(output, temp0, initialStringHeap(), slowPath); |
| 14293 | masm.or32(Imm32(StringFlags::INIT_FAT_INLINE_FLAGS), temp2); |
| 14294 | } |
| 14295 | |
| 14296 | masm.bind(&allocDone); |
| 14297 | } |
| 14298 | |
| 14299 | masm.store32(temp2, Address(output, JSString::offsetOfFlags())); |
| 14300 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
| 14301 | |
| 14302 | auto initializeInlineString = [&](CharEncoding encoding) { |
| 14303 | masm.loadStringChars(string, temp0, encoding); |
| 14304 | masm.addToCharPtr(temp0, begin, encoding); |
| 14305 | if (temp1 == string) { |
| 14306 | masm.push(string); |
| 14307 | } |
| 14308 | masm.loadInlineStringCharsForStore(output, temp1); |
| 14309 | CopyStringChars(masm, temp1, temp0, length, temp2, encoding, |
| 14310 | maximumLength); |
| 14311 | masm.loadStringLength(output, length); |
| 14312 | if (temp1 == string) { |
| 14313 | masm.pop(string); |
| 14314 | } |
| 14315 | }; |
| 14316 | |
| 14317 | Label isInlineLatin1; |
| 14318 | masm.branchTest32(Assembler::NonZero, temp2, |
| 14319 | Imm32(StringFlags::LATIN1_CHARS_BIT), &isInlineLatin1); |
| 14320 | initializeInlineString(CharEncoding::TwoByte); |
| 14321 | masm.jump(done); |
| 14322 | |
| 14323 | masm.bind(&isInlineLatin1); |
| 14324 | initializeInlineString(CharEncoding::Latin1); |
| 14325 | } |
| 14326 | |
| 14327 | // Handle other cases with a DependentString. |
| 14328 | if (tryDependent) { |
| 14329 | masm.jump(done); |
| 14330 | |
| 14331 | masm.bind(¬Inline); |
| 14332 | masm.newGCString(output, temp0, gen->initialStringHeap(), slowPath); |
| 14333 | masm.store32(length, Address(output, JSString::offsetOfLength())); |
| 14334 | |
| 14335 | // Note: no post barrier is needed because the dependent string is either |
| 14336 | // allocated in the nursery or both strings are tenured (if nursery strings |
| 14337 | // are disabled for this zone). |
| 14338 | EmitInitDependentStringBase(masm, output, string, temp0, temp2, |
| 14339 | /* needsPostBarrier = */ false); |
| 14340 | |
| 14341 | auto initializeDependentString = [&](CharEncoding encoding) { |
| 14342 | uint32_t flags = StringFlags::dependentStringFlags(encoding); |
| 14343 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
| 14344 | masm.loadNonInlineStringChars(string, temp0, encoding); |
| 14345 | masm.addToCharPtr(temp0, begin, encoding); |
| 14346 | masm.storeNonInlineStringChars(temp0, output); |
| 14347 | }; |
| 14348 | |
| 14349 | Label isLatin1; |
| 14350 | masm.branchLatin1String(string, &isLatin1); |
| 14351 | initializeDependentString(CharEncoding::TwoByte); |
| 14352 | masm.jump(done); |
| 14353 | |
| 14354 | masm.bind(&isLatin1); |
| 14355 | initializeDependentString(CharEncoding::Latin1); |
| 14356 | } |
| 14357 | |
| 14358 | masm.bind(done); |
| 14359 | } |
| 14360 | |
| 14361 | JitCode* JitZone::generateStringConcatStub(JSContext* cx) { |
| 14362 | JitSpew(JitSpew_Codegen, "# Emitting StringConcat stub"); |
| 14363 | |
| 14364 | TempAllocator temp(&cx->tempLifoAlloc()); |
| 14365 | JitContext jcx(cx); |
| 14366 | StackMacroAssembler masm(cx, temp); |
| 14367 | AutoCreatedBy acb(masm, "JitZone::generateStringConcatStub"); |
| 14368 | |
| 14369 | Register lhs = CallTempReg0; |
| 14370 | Register rhs = CallTempReg1; |
| 14371 | Register temp1 = CallTempReg2; |
| 14372 | Register temp2 = CallTempReg3; |
| 14373 | Register temp3 = CallTempReg4; |
| 14374 | Register output = CallTempReg5; |
| 14375 | |
| 14376 | Label failure; |
| 14377 | #ifdef JS_USE_LINK_REGISTER |
| 14378 | masm.pushReturnAddress(); |
| 14379 | #endif |
| 14380 | masm.Push(FramePointer); |
| 14381 | masm.moveStackPtrTo(FramePointer); |
| 14382 | |
| 14383 | // If lhs is empty, return rhs. |
| 14384 | Label leftEmpty; |
| 14385 | masm.loadStringLength(lhs, temp1); |
| 14386 | masm.branchTest32(Assembler::Zero, temp1, temp1, &leftEmpty); |
| 14387 | |
| 14388 | // If rhs is empty, return lhs. |
| 14389 | Label rightEmpty; |
| 14390 | masm.loadStringLength(rhs, temp2); |
| 14391 | masm.branchTest32(Assembler::Zero, temp2, temp2, &rightEmpty); |
| 14392 | |
| 14393 | masm.add32(temp1, temp2); |
| 14394 | |
| 14395 | // Check if we can use a JSInlineString. The result is a Latin1 string if |
| 14396 | // lhs and rhs are both Latin1, so we AND the flags. |
| 14397 | Label isInlineTwoByte, isInlineLatin1; |
| 14398 | masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1); |
| 14399 | masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1); |
| 14400 | |
| 14401 | Label isLatin1, notInline; |
| 14402 | masm.branchTest32(Assembler::NonZero, temp1, |
| 14403 | Imm32(StringFlags::LATIN1_CHARS_BIT), &isLatin1); |
| 14404 | { |
| 14405 | masm.branch32(Assembler::BelowOrEqual, temp2, |
| 14406 | Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), |
| 14407 | &isInlineTwoByte); |
| 14408 | masm.jump(¬Inline); |
| 14409 | } |
| 14410 | masm.bind(&isLatin1); |
| 14411 | { |
| 14412 | masm.branch32(Assembler::BelowOrEqual, temp2, |
| 14413 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1); |
| 14414 | } |
| 14415 | masm.bind(¬Inline); |
| 14416 | |
| 14417 | // Keep AND'ed flags in temp1. |
| 14418 | |
| 14419 | // Ensure result length <= JSString::MAX_LENGTH. |
| 14420 | masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure); |
| 14421 | |
| 14422 | // Allocate a new rope, guaranteed to be in the nursery if initialStringHeap |
| 14423 | // == gc::Heap::Default. (As a result, no post barriers are needed below.) |
| 14424 | masm.newGCString(output, temp3, initialStringHeap, &failure); |
| 14425 | |
| 14426 | // Store rope length and flags. temp1 still holds the result of AND'ing the |
| 14427 | // lhs and rhs flags, so we just have to clear the other flags to get our rope |
| 14428 | // flags (Latin1 if both lhs and rhs are Latin1). |
| 14429 | static_assert(StringFlags::INIT_ROPE_FLAGS == 0, |
| 14430 | "Rope type flags must have no bits set"); |
| 14431 | masm.and32(Imm32(StringFlags::LATIN1_CHARS_BIT), temp1); |
| 14432 | masm.store32(temp1, Address(output, JSString::offsetOfFlags())); |
| 14433 | masm.store32(temp2, Address(output, JSString::offsetOfLength())); |
| 14434 | |
| 14435 | // Store left and right nodes. |
| 14436 | masm.storeRopeChildren(lhs, rhs, output); |
| 14437 | masm.pop(FramePointer); |
| 14438 | masm.ret(); |
| 14439 | |
| 14440 | masm.bind(&leftEmpty); |
| 14441 | masm.mov(rhs, output); |
| 14442 | masm.pop(FramePointer); |
| 14443 | masm.ret(); |
| 14444 | |
| 14445 | masm.bind(&rightEmpty); |
| 14446 | masm.mov(lhs, output); |
| 14447 | masm.pop(FramePointer); |
| 14448 | masm.ret(); |
| 14449 | |
| 14450 | masm.bind(&isInlineTwoByte); |
| 14451 | ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, |
| 14452 | initialStringHeap, &failure, CharEncoding::TwoByte); |
| 14453 | masm.pop(FramePointer); |
| 14454 | masm.ret(); |
| 14455 | |
| 14456 | masm.bind(&isInlineLatin1); |
| 14457 | ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, |
| 14458 | initialStringHeap, &failure, CharEncoding::Latin1); |
| 14459 | masm.pop(FramePointer); |
| 14460 | masm.ret(); |
| 14461 | |
| 14462 | masm.bind(&failure); |
| 14463 | masm.movePtr(ImmPtr(nullptr), output); |
| 14464 | masm.pop(FramePointer); |
| 14465 | masm.ret(); |
| 14466 | |
| 14467 | Linker linker(masm); |
| 14468 | JitCode* code = linker.newCode(cx, CodeKind::Other); |
| 14469 | |
| 14470 | CollectPerfSpewerJitCodeProfile(code, "StringConcatStub"); |
| 14471 | #ifdef MOZ_VTUNE1 |
| 14472 | vtune::MarkStub(code, "StringConcatStub"); |
| 14473 | #endif |
| 14474 | |
| 14475 | return code; |
| 14476 | } |
| 14477 | |
| 14478 | void JitRuntime::generateLazyLinkStub(MacroAssembler& masm) { |
| 14479 | AutoCreatedBy acb(masm, "JitRuntime::generateLazyLinkStub"); |
| 14480 | |
| 14481 | lazyLinkStubOffset_ = startTrampolineCode(masm); |
| 14482 | |
| 14483 | #ifdef JS_USE_LINK_REGISTER |
| 14484 | masm.pushReturnAddress(); |
| 14485 | #endif |
| 14486 | masm.Push(FramePointer); |
| 14487 | masm.moveStackPtrTo(FramePointer); |
| 14488 | |
| 14489 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 14490 | Register temp0 = regs.takeAny(); |
| 14491 | Register temp1 = regs.takeAny(); |
| 14492 | Register temp2 = regs.takeAny(); |
| 14493 | |
| 14494 | masm.loadJSContext(temp0); |
| 14495 | masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::LazyLink); |
| 14496 | masm.moveStackPtrTo(temp1); |
| 14497 | |
| 14498 | using Fn = uint8_t* (*)(JSContext * cx, LazyLinkExitFrameLayout * frame); |
| 14499 | masm.setupUnalignedABICall(temp2); |
| 14500 | masm.passABIArg(temp0); |
| 14501 | masm.passABIArg(temp1); |
| 14502 | masm.callWithABI<Fn, LazyLinkTopActivation>( |
| 14503 | ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 14504 | |
| 14505 | // Discard exit frame and restore frame pointer. |
| 14506 | masm.leaveExitFrame(0); |
| 14507 | masm.pop(FramePointer); |
| 14508 | |
| 14509 | #ifdef JS_USE_LINK_REGISTER |
| 14510 | // Restore the return address such that the emitPrologue function of the |
| 14511 | // CodeGenerator can push it back on the stack with pushReturnAddress. |
| 14512 | masm.popReturnAddress(); |
| 14513 | #endif |
| 14514 | masm.jump(ReturnReg); |
| 14515 | } |
| 14516 | |
| 14517 | void JitRuntime::generateInterpreterStub(MacroAssembler& masm) { |
| 14518 | AutoCreatedBy acb(masm, "JitRuntime::generateInterpreterStub"); |
| 14519 | |
| 14520 | interpreterStubOffset_ = startTrampolineCode(masm); |
| 14521 | |
| 14522 | #ifdef JS_USE_LINK_REGISTER |
| 14523 | masm.pushReturnAddress(); |
| 14524 | #endif |
| 14525 | masm.Push(FramePointer); |
| 14526 | masm.moveStackPtrTo(FramePointer); |
| 14527 | |
| 14528 | AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile()); |
| 14529 | Register temp0 = regs.takeAny(); |
| 14530 | Register temp1 = regs.takeAny(); |
| 14531 | Register temp2 = regs.takeAny(); |
| 14532 | |
| 14533 | masm.loadJSContext(temp0); |
| 14534 | masm.enterFakeExitFrame(temp0, temp2, ExitFrameType::InterpreterStub); |
| 14535 | masm.moveStackPtrTo(temp1); |
| 14536 | |
| 14537 | using Fn = bool (*)(JSContext* cx, InterpreterStubExitFrameLayout* frame); |
| 14538 | masm.setupUnalignedABICall(temp2); |
| 14539 | masm.passABIArg(temp0); |
| 14540 | masm.passABIArg(temp1); |
| 14541 | masm.callWithABI<Fn, InvokeFromInterpreterStub>( |
| 14542 | ABIType::General, CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 14543 | |
| 14544 | masm.branchIfFalseBool(ReturnReg, masm.failureLabel()); |
| 14545 | |
| 14546 | // Discard exit frame and restore frame pointer. |
| 14547 | masm.leaveExitFrame(0); |
| 14548 | masm.pop(FramePointer); |
| 14549 | |
| 14550 | // InvokeFromInterpreterStub stores the return value in argv[0], where the |
| 14551 | // caller stored |this|. Subtract |sizeof(void*)| for the frame pointer we |
| 14552 | // just popped. |
| 14553 | masm.loadValue(Address(masm.getStackPointer(), |
| 14554 | JitFrameLayout::offsetOfThis() - sizeof(void*)), |
| 14555 | JSReturnOperand); |
| 14556 | masm.ret(); |
| 14557 | } |
| 14558 | |
| 14559 | void JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm) { |
| 14560 | AutoCreatedBy acb(masm, "JitRuntime::generateDoubleToInt32ValueStub"); |
| 14561 | doubleToInt32ValueStubOffset_ = startTrampolineCode(masm); |
| 14562 | |
| 14563 | Label done; |
| 14564 | masm.branchTestDouble(Assembler::NotEqual, R0, &done); |
| 14565 | |
| 14566 | masm.unboxDouble(R0, FloatReg0); |
| 14567 | masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done, |
| 14568 | /* negativeZeroCheck = */ false); |
| 14569 | masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0); |
| 14570 | |
| 14571 | masm.bind(&done); |
| 14572 | masm.abiret(); |
| 14573 | } |
| 14574 | |
| 14575 | void CodeGenerator::visitLinearizeString(LLinearizeString* lir) { |
| 14576 | Register str = ToRegister(lir->string()); |
| 14577 | Register output = ToRegister(lir->output()); |
| 14578 | |
| 14579 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 14580 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
| 14581 | lir, ArgList(str), StoreRegisterTo(output)); |
| 14582 | |
| 14583 | masm.branchIfRope(str, ool->entry()); |
| 14584 | |
| 14585 | if (str != output) { |
| 14586 | masm.movePtr(str, output); |
| 14587 | } |
| 14588 | masm.bind(ool->rejoin()); |
| 14589 | } |
| 14590 | |
| 14591 | void CodeGenerator::visitLinearizeForCharAccess(LLinearizeForCharAccess* lir) { |
| 14592 | Register str = ToRegister(lir->string()); |
| 14593 | Register index = ToRegister(lir->index()); |
| 14594 | Register output = ToRegister(lir->output()); |
| 14595 | |
| 14596 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 14597 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
| 14598 | lir, ArgList(str), StoreRegisterTo(output)); |
| 14599 | |
| 14600 | masm.branchIfNotCanLoadStringChar(str, index, output, ool->entry()); |
| 14601 | |
| 14602 | masm.movePtr(str, output); |
| 14603 | masm.bind(ool->rejoin()); |
| 14604 | } |
| 14605 | |
| 14606 | void CodeGenerator::visitLinearizeForCodePointAccess( |
| 14607 | LLinearizeForCodePointAccess* lir) { |
| 14608 | Register str = ToRegister(lir->string()); |
| 14609 | Register index = ToRegister(lir->index()); |
| 14610 | Register output = ToRegister(lir->output()); |
| 14611 | Register temp = ToRegister(lir->temp0()); |
| 14612 | |
| 14613 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 14614 | auto* ool = oolCallVM<Fn, jit::LinearizeForCharAccess>( |
| 14615 | lir, ArgList(str), StoreRegisterTo(output)); |
| 14616 | |
| 14617 | masm.branchIfNotCanLoadStringCodePoint(str, index, output, temp, |
| 14618 | ool->entry()); |
| 14619 | |
| 14620 | masm.movePtr(str, output); |
| 14621 | masm.bind(ool->rejoin()); |
| 14622 | } |
| 14623 | |
| 14624 | void CodeGenerator::visitToRelativeStringIndex(LToRelativeStringIndex* lir) { |
| 14625 | Register index = ToRegister(lir->index()); |
| 14626 | Register length = ToRegister(lir->length()); |
| 14627 | Register output = ToRegister(lir->output()); |
| 14628 | |
| 14629 | masm.move32(Imm32(0), output); |
| 14630 | masm.cmp32Move32(Assembler::LessThan, index, Imm32(0), length, output); |
| 14631 | masm.add32(index, output); |
| 14632 | } |
| 14633 | |
| 14634 | void CodeGenerator::visitCharCodeAt(LCharCodeAt* lir) { |
| 14635 | Register str = ToRegister(lir->string()); |
| 14636 | Register output = ToRegister(lir->output()); |
| 14637 | Register temp0 = ToRegister(lir->temp0()); |
| 14638 | Register temp1 = ToRegister(lir->temp1()); |
| 14639 | |
| 14640 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
| 14641 | |
| 14642 | if (lir->index()->isBogus()) { |
| 14643 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)), |
| 14644 | StoreRegisterTo(output)); |
| 14645 | masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry()); |
| 14646 | masm.bind(ool->rejoin()); |
| 14647 | } else { |
| 14648 | Register index = ToRegister(lir->index()); |
| 14649 | |
| 14650 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index), |
| 14651 | StoreRegisterTo(output)); |
| 14652 | masm.loadStringChar(str, index, output, temp0, temp1, ool->entry()); |
| 14653 | masm.bind(ool->rejoin()); |
| 14654 | } |
| 14655 | } |
| 14656 | |
| 14657 | void CodeGenerator::visitCharCodeAtOrNegative(LCharCodeAtOrNegative* lir) { |
| 14658 | Register str = ToRegister(lir->string()); |
| 14659 | Register output = ToRegister(lir->output()); |
| 14660 | Register temp0 = ToRegister(lir->temp0()); |
| 14661 | Register temp1 = ToRegister(lir->temp1()); |
| 14662 | |
| 14663 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
| 14664 | |
| 14665 | // Return -1 for out-of-bounds access. |
| 14666 | masm.move32(Imm32(-1), output); |
| 14667 | |
| 14668 | if (lir->index()->isBogus()) { |
| 14669 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, Imm32(0)), |
| 14670 | StoreRegisterTo(output)); |
| 14671 | |
| 14672 | masm.branch32(Assembler::Equal, Address(str, JSString::offsetOfLength()), |
| 14673 | Imm32(0), ool->rejoin()); |
| 14674 | masm.loadStringChar(str, 0, output, temp0, temp1, ool->entry()); |
| 14675 | masm.bind(ool->rejoin()); |
| 14676 | } else { |
| 14677 | Register index = ToRegister(lir->index()); |
| 14678 | |
| 14679 | auto* ool = oolCallVM<Fn, jit::CharCodeAt>(lir, ArgList(str, index), |
| 14680 | StoreRegisterTo(output)); |
| 14681 | |
| 14682 | masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), |
| 14683 | temp0, ool->rejoin()); |
| 14684 | masm.loadStringChar(str, index, output, temp0, temp1, ool->entry()); |
| 14685 | masm.bind(ool->rejoin()); |
| 14686 | } |
| 14687 | } |
| 14688 | |
| 14689 | void CodeGenerator::visitCodePointAt(LCodePointAt* lir) { |
| 14690 | Register str = ToRegister(lir->string()); |
| 14691 | Register index = ToRegister(lir->index()); |
| 14692 | Register output = ToRegister(lir->output()); |
| 14693 | Register temp0 = ToRegister(lir->temp0()); |
| 14694 | Register temp1 = ToRegister(lir->temp1()); |
| 14695 | |
| 14696 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
| 14697 | auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index), |
| 14698 | StoreRegisterTo(output)); |
| 14699 | |
| 14700 | masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry()); |
| 14701 | masm.bind(ool->rejoin()); |
| 14702 | } |
| 14703 | |
| 14704 | void CodeGenerator::visitCodePointAtOrNegative(LCodePointAtOrNegative* lir) { |
| 14705 | Register str = ToRegister(lir->string()); |
| 14706 | Register index = ToRegister(lir->index()); |
| 14707 | Register output = ToRegister(lir->output()); |
| 14708 | Register temp0 = ToRegister(lir->temp0()); |
| 14709 | Register temp1 = ToRegister(lir->temp1()); |
| 14710 | |
| 14711 | using Fn = bool (*)(JSContext*, HandleString, int32_t, uint32_t*); |
| 14712 | auto* ool = oolCallVM<Fn, jit::CodePointAt>(lir, ArgList(str, index), |
| 14713 | StoreRegisterTo(output)); |
| 14714 | |
| 14715 | // Return -1 for out-of-bounds access. |
| 14716 | masm.move32(Imm32(-1), output); |
| 14717 | |
| 14718 | masm.spectreBoundsCheck32(index, Address(str, JSString::offsetOfLength()), |
| 14719 | temp0, ool->rejoin()); |
| 14720 | masm.loadStringCodePoint(str, index, output, temp0, temp1, ool->entry()); |
| 14721 | masm.bind(ool->rejoin()); |
| 14722 | } |
| 14723 | |
| 14724 | void CodeGenerator::visitNegativeToNaN(LNegativeToNaN* lir) { |
| 14725 | Register input = ToRegister(lir->input()); |
| 14726 | ValueOperand output = ToOutValue(lir); |
| 14727 | |
| 14728 | masm.tagValue(JSVAL_TYPE_INT32, input, output); |
| 14729 | |
| 14730 | Label done; |
| 14731 | masm.branchTest32(Assembler::NotSigned, input, input, &done); |
| 14732 | masm.moveValue(JS::NaNValue(), output); |
| 14733 | masm.bind(&done); |
| 14734 | } |
| 14735 | |
| 14736 | void CodeGenerator::visitNegativeToUndefined(LNegativeToUndefined* lir) { |
| 14737 | Register input = ToRegister(lir->input()); |
| 14738 | ValueOperand output = ToOutValue(lir); |
| 14739 | |
| 14740 | masm.tagValue(JSVAL_TYPE_INT32, input, output); |
| 14741 | |
| 14742 | Label done; |
| 14743 | masm.branchTest32(Assembler::NotSigned, input, input, &done); |
| 14744 | masm.moveValue(JS::UndefinedValue(), output); |
| 14745 | masm.bind(&done); |
| 14746 | } |
| 14747 | |
| 14748 | void CodeGenerator::visitFromCharCode(LFromCharCode* lir) { |
| 14749 | Register code = ToRegister(lir->code()); |
| 14750 | Register output = ToRegister(lir->output()); |
| 14751 | |
| 14752 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
| 14753 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
| 14754 | StoreRegisterTo(output)); |
| 14755 | |
| 14756 | // OOL path if code >= UNIT_STATIC_LIMIT. |
| 14757 | masm.lookupStaticString(code, output, gen->runtime->staticStrings(), |
| 14758 | ool->entry()); |
| 14759 | |
| 14760 | masm.bind(ool->rejoin()); |
| 14761 | } |
| 14762 | |
| 14763 | void CodeGenerator::visitFromCharCodeEmptyIfNegative( |
| 14764 | LFromCharCodeEmptyIfNegative* lir) { |
| 14765 | Register code = ToRegister(lir->code()); |
| 14766 | Register output = ToRegister(lir->output()); |
| 14767 | |
| 14768 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
| 14769 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
| 14770 | StoreRegisterTo(output)); |
| 14771 | |
| 14772 | // Return the empty string for negative inputs. |
| 14773 | const JSAtomState& names = gen->runtime->names(); |
| 14774 | masm.movePtr(ImmGCPtr(names.empty_), output); |
| 14775 | masm.branchTest32(Assembler::Signed, code, code, ool->rejoin()); |
| 14776 | |
| 14777 | // OOL path if code >= UNIT_STATIC_LIMIT. |
| 14778 | masm.lookupStaticString(code, output, gen->runtime->staticStrings(), |
| 14779 | ool->entry()); |
| 14780 | |
| 14781 | masm.bind(ool->rejoin()); |
| 14782 | } |
| 14783 | |
| 14784 | void CodeGenerator::visitFromCharCodeUndefinedIfNegative( |
| 14785 | LFromCharCodeUndefinedIfNegative* lir) { |
| 14786 | Register code = ToRegister(lir->code()); |
| 14787 | ValueOperand output = ToOutValue(lir); |
| 14788 | Register temp = output.scratchReg(); |
| 14789 | |
| 14790 | using Fn = JSLinearString* (*)(JSContext*, int32_t); |
| 14791 | auto* ool = oolCallVM<Fn, js::StringFromCharCode>(lir, ArgList(code), |
| 14792 | StoreRegisterTo(temp)); |
| 14793 | |
| 14794 | // Return |undefined| for negative inputs. |
| 14795 | Label done; |
| 14796 | masm.moveValue(UndefinedValue(), output); |
| 14797 | masm.branchTest32(Assembler::Signed, code, code, &done); |
| 14798 | |
| 14799 | // OOL path if code >= UNIT_STATIC_LIMIT. |
| 14800 | masm.lookupStaticString(code, temp, gen->runtime->staticStrings(), |
| 14801 | ool->entry()); |
| 14802 | |
| 14803 | masm.bind(ool->rejoin()); |
| 14804 | masm.tagValue(JSVAL_TYPE_STRING, temp, output); |
| 14805 | |
| 14806 | masm.bind(&done); |
| 14807 | } |
| 14808 | |
| 14809 | void CodeGenerator::visitFromCodePoint(LFromCodePoint* lir) { |
| 14810 | Register codePoint = ToRegister(lir->codePoint()); |
| 14811 | Register output = ToRegister(lir->output()); |
| 14812 | Register temp0 = ToRegister(lir->temp0()); |
| 14813 | Register temp1 = ToRegister(lir->temp1()); |
| 14814 | LSnapshot* snapshot = lir->snapshot(); |
| 14815 | |
| 14816 | // The OOL path is only taken when we can't allocate the inline string. |
| 14817 | using Fn = JSLinearString* (*)(JSContext*, char32_t); |
| 14818 | auto* ool = oolCallVM<Fn, js::StringFromCodePoint>(lir, ArgList(codePoint), |
| 14819 | StoreRegisterTo(output)); |
| 14820 | |
| 14821 | Label isTwoByte; |
| 14822 | Label* done = ool->rejoin(); |
| 14823 | |
| 14824 | static_assert( |
| 14825 | StaticStrings::UNIT_STATIC_LIMIT - 1 == JSString::MAX_LATIN1_CHAR, |
| 14826 | "Latin-1 strings can be loaded from static strings"); |
| 14827 | |
| 14828 | { |
| 14829 | masm.lookupStaticString(codePoint, output, gen->runtime->staticStrings(), |
| 14830 | &isTwoByte); |
| 14831 | masm.jump(done); |
| 14832 | } |
| 14833 | masm.bind(&isTwoByte); |
| 14834 | { |
| 14835 | // Use a bailout if the input is not a valid code point, because |
| 14836 | // MFromCodePoint is movable and it'd be observable when a moved |
| 14837 | // fromCodePoint throws an exception before its actual call site. |
| 14838 | bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax), |
| 14839 | snapshot); |
| 14840 | |
| 14841 | // Allocate a JSThinInlineString. |
| 14842 | { |
| 14843 | static_assert(JSThinInlineString::MAX_LENGTH_TWO_BYTE >= 2, |
| 14844 | "JSThinInlineString can hold a supplementary code point"); |
| 14845 | |
| 14846 | uint32_t flags = |
| 14847 | StringFlags::thinInlineStringFlags(CharEncoding::TwoByte); |
| 14848 | masm.newGCString(output, temp0, gen->initialStringHeap(), ool->entry()); |
| 14849 | masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); |
| 14850 | } |
| 14851 | |
| 14852 | Label isSupplementary; |
| 14853 | masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(unicode::NonBMPMin), |
| 14854 | &isSupplementary); |
| 14855 | { |
| 14856 | // Store length. |
| 14857 | masm.store32(Imm32(1), Address(output, JSString::offsetOfLength())); |
| 14858 | |
| 14859 | // Load chars pointer in temp0. |
| 14860 | masm.loadInlineStringCharsForStore(output, temp0); |
| 14861 | |
| 14862 | masm.store16(codePoint, Address(temp0, 0)); |
| 14863 | |
| 14864 | masm.jump(done); |
| 14865 | } |
| 14866 | masm.bind(&isSupplementary); |
| 14867 | { |
| 14868 | // Store length. |
| 14869 | masm.store32(Imm32(2), Address(output, JSString::offsetOfLength())); |
| 14870 | |
| 14871 | // Load chars pointer in temp0. |
| 14872 | masm.loadInlineStringCharsForStore(output, temp0); |
| 14873 | |
| 14874 | // Inlined unicode::LeadSurrogate(uint32_t). |
| 14875 | masm.rshift32(Imm32(10), codePoint, temp1); |
| 14876 | masm.add32(Imm32(unicode::LeadSurrogateMin - (unicode::NonBMPMin >> 10)), |
| 14877 | temp1); |
| 14878 | |
| 14879 | masm.store16(temp1, Address(temp0, 0)); |
| 14880 | |
| 14881 | // Inlined unicode::TrailSurrogate(uint32_t). |
| 14882 | masm.and32(Imm32(0x3FF), codePoint, temp1); |
| 14883 | masm.or32(Imm32(unicode::TrailSurrogateMin), temp1); |
| 14884 | |
| 14885 | masm.store16(temp1, Address(temp0, sizeof(char16_t))); |
| 14886 | } |
| 14887 | } |
| 14888 | |
| 14889 | masm.bind(done); |
| 14890 | } |
| 14891 | |
| 14892 | void CodeGenerator::visitStringIncludes(LStringIncludes* lir) { |
| 14893 | pushArg(ToRegister(lir->searchString())); |
| 14894 | pushArg(ToRegister(lir->string())); |
| 14895 | |
| 14896 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 14897 | callVM<Fn, js::StringIncludes>(lir); |
| 14898 | } |
| 14899 | |
| 14900 | template <typename LIns> |
| 14901 | static void CallStringMatch(MacroAssembler& masm, LIns* lir, |
| 14902 | LiveRegisterSet volatileRegs) { |
| 14903 | Register string = ToRegister(lir->string()); |
| 14904 | Register output = ToRegister(lir->output()); |
| 14905 | Register tempLength = ToRegister(lir->temp0()); |
| 14906 | Register tempChars = ToRegister(lir->temp1()); |
| 14907 | Register maybeTempPat = ToTempRegisterOrInvalid(lir->temp2()); |
| 14908 | |
| 14909 | const JSOffThreadAtom* searchString = lir->searchString(); |
| 14910 | size_t length = searchString->length(); |
| 14911 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 14911); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "length == 1 || length == 2" ")"); do { MOZ_CrashSequence (__null, 14911); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 14912 | |
| 14913 | // The additional temp register is only needed when searching for two |
| 14914 | // pattern characters. |
| 14915 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 14915); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "maybeTempPat != InvalidReg" ")"); do { MOZ_CrashSequence (__null, 14915); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false); |
| 14916 | |
| 14917 | if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) { |
| 14918 | masm.move32(Imm32(0), output); |
| 14919 | } else { |
| 14920 | masm.move32(Imm32(-1), output); |
| 14921 | } |
| 14922 | |
| 14923 | masm.loadStringLength(string, tempLength); |
| 14924 | |
| 14925 | // Can't be a substring when the string is smaller than the search string. |
| 14926 | Label done; |
| 14927 | masm.branch32(Assembler::Below, tempLength, Imm32(length), &done); |
| 14928 | |
| 14929 | bool searchStringIsPureTwoByte = false; |
| 14930 | if (searchString->hasTwoByteChars()) { |
| 14931 | JS::AutoCheckCannotGC nogc; |
| 14932 | searchStringIsPureTwoByte = |
| 14933 | !mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc)); |
| 14934 | } |
| 14935 | |
| 14936 | // Pure two-byte strings can't occur in a Latin-1 string. |
| 14937 | if (searchStringIsPureTwoByte) { |
| 14938 | masm.branchLatin1String(string, &done); |
| 14939 | } |
| 14940 | |
| 14941 | #ifdef DEBUG1 |
| 14942 | // We don't expect to see ropes here. |
| 14943 | Label notRope; |
| 14944 | masm.branchIfNotRope(string, ¬Rope); |
| 14945 | masm.assumeUnreachable("input string must be linearized"); |
| 14946 | masm.bind(¬Rope); |
| 14947 | #endif |
| 14948 | |
| 14949 | Label restoreVolatile; |
| 14950 | |
| 14951 | auto callMatcher = [&](CharEncoding encoding) { |
| 14952 | masm.loadStringChars(string, tempChars, encoding); |
| 14953 | |
| 14954 | LiveGeneralRegisterSet liveRegs; |
| 14955 | if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) { |
| 14956 | // Save |tempChars| to compute the result index. |
| 14957 | liveRegs.add(tempChars); |
| 14958 | |
| 14959 | #ifdef DEBUG1 |
| 14960 | // Save |tempLength| in debug-mode for assertions. |
| 14961 | liveRegs.add(tempLength); |
| 14962 | #endif |
| 14963 | |
| 14964 | // Exclude non-volatile registers. |
| 14965 | liveRegs.set() = GeneralRegisterSet::Intersect( |
| 14966 | liveRegs.set(), GeneralRegisterSet::Volatile()); |
| 14967 | |
| 14968 | masm.PushRegsInMask(liveRegs); |
| 14969 | } |
| 14970 | |
| 14971 | if (length == 1) { |
| 14972 | char16_t pat = searchString->latin1OrTwoByteChar(0); |
| 14973 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 14974); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR" ")"); do { MOZ_CrashSequence(__null, 14974); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 14974 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 14974); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat <= JSString::MAX_LATIN1_CHAR" ")"); do { MOZ_CrashSequence(__null, 14974); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 14975 | |
| 14976 | masm.move32(Imm32(pat), output); |
| 14977 | |
| 14978 | masm.setupAlignedABICall(); |
| 14979 | masm.passABIArg(tempChars); |
| 14980 | masm.passABIArg(output); |
| 14981 | masm.passABIArg(tempLength); |
| 14982 | if (encoding == CharEncoding::Latin1) { |
| 14983 | using Fn = const char* (*)(const char*, char, size_t); |
| 14984 | masm.callWithABI<Fn, mozilla::SIMD::memchr8>( |
| 14985 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 14986 | } else { |
| 14987 | using Fn = const char16_t* (*)(const char16_t*, char16_t, size_t); |
| 14988 | masm.callWithABI<Fn, mozilla::SIMD::memchr16>( |
| 14989 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 14990 | } |
| 14991 | } else { |
| 14992 | char16_t pat0 = searchString->latin1OrTwoByteChar(0); |
| 14993 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 14994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR" ")"); do { MOZ_CrashSequence(__null, 14994); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 14994 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 14994); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat0 <= JSString::MAX_LATIN1_CHAR" ")"); do { MOZ_CrashSequence(__null, 14994); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 14995 | |
| 14996 | char16_t pat1 = searchString->latin1OrTwoByteChar(1); |
| 14997 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 14998); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR" ")"); do { MOZ_CrashSequence(__null, 14998); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false) |
| 14998 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 14998); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pat1 <= JSString::MAX_LATIN1_CHAR" ")"); do { MOZ_CrashSequence(__null, 14998); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 14999 | |
| 15000 | masm.move32(Imm32(pat0), output); |
| 15001 | masm.move32(Imm32(pat1), maybeTempPat); |
| 15002 | |
| 15003 | masm.setupAlignedABICall(); |
| 15004 | masm.passABIArg(tempChars); |
| 15005 | masm.passABIArg(output); |
| 15006 | masm.passABIArg(maybeTempPat); |
| 15007 | masm.passABIArg(tempLength); |
| 15008 | if (encoding == CharEncoding::Latin1) { |
| 15009 | using Fn = const char* (*)(const char*, char, char, size_t); |
| 15010 | masm.callWithABI<Fn, mozilla::SIMD::memchr2x8>( |
| 15011 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 15012 | } else { |
| 15013 | using Fn = |
| 15014 | const char16_t* (*)(const char16_t*, char16_t, char16_t, size_t); |
| 15015 | masm.callWithABI<Fn, mozilla::SIMD::memchr2x16>( |
| 15016 | ABIType::General, CheckUnsafeCallWithABI::DontCheckOther); |
| 15017 | } |
| 15018 | } |
| 15019 | |
| 15020 | masm.storeCallPointerResult(output); |
| 15021 | |
| 15022 | // Convert to string index for `indexOf`. |
| 15023 | if constexpr (std::is_same_v<LIns, LStringIndexOfSIMD>) { |
| 15024 | // Restore |tempChars|. (And in debug mode |tempLength|.) |
| 15025 | masm.PopRegsInMask(liveRegs); |
| 15026 | |
| 15027 | Label found; |
| 15028 | masm.branchPtr(Assembler::NotEqual, output, ImmPtr(nullptr), &found); |
| 15029 | { |
| 15030 | masm.move32(Imm32(-1), output); |
| 15031 | masm.jump(&restoreVolatile); |
| 15032 | } |
| 15033 | masm.bind(&found); |
| 15034 | |
| 15035 | #ifdef DEBUG1 |
| 15036 | // Check lower bound. |
| 15037 | Label lower; |
| 15038 | masm.branchPtr(Assembler::AboveOrEqual, output, tempChars, &lower); |
| 15039 | masm.assumeUnreachable("result pointer below string chars"); |
| 15040 | masm.bind(&lower); |
| 15041 | |
| 15042 | // Compute the end position of the characters. |
| 15043 | auto scale = encoding == CharEncoding::Latin1 ? TimesOne : TimesTwo; |
| 15044 | masm.computeEffectiveAddress(BaseIndex(tempChars, tempLength, scale), |
| 15045 | tempLength); |
| 15046 | |
| 15047 | // Check upper bound. |
| 15048 | Label upper; |
| 15049 | masm.branchPtr(Assembler::Below, output, tempLength, &upper); |
| 15050 | masm.assumeUnreachable("result pointer above string chars"); |
| 15051 | masm.bind(&upper); |
| 15052 | #endif |
| 15053 | |
| 15054 | masm.subPtr(tempChars, output); |
| 15055 | |
| 15056 | if (encoding == CharEncoding::TwoByte) { |
| 15057 | masm.rshiftPtr(Imm32(1), output); |
| 15058 | } |
| 15059 | } |
| 15060 | }; |
| 15061 | |
| 15062 | volatileRegs.takeUnchecked(output); |
| 15063 | volatileRegs.takeUnchecked(tempLength); |
| 15064 | volatileRegs.takeUnchecked(tempChars); |
| 15065 | if (maybeTempPat != InvalidReg) { |
| 15066 | volatileRegs.takeUnchecked(maybeTempPat); |
| 15067 | } |
| 15068 | masm.PushRegsInMask(volatileRegs); |
| 15069 | |
| 15070 | // Handle the case when the input is a Latin-1 string. |
| 15071 | if (!searchStringIsPureTwoByte) { |
| 15072 | Label twoByte; |
| 15073 | masm.branchTwoByteString(string, &twoByte); |
| 15074 | { |
| 15075 | callMatcher(CharEncoding::Latin1); |
| 15076 | masm.jump(&restoreVolatile); |
| 15077 | } |
| 15078 | masm.bind(&twoByte); |
| 15079 | } |
| 15080 | |
| 15081 | // Handle the case when the input is a two-byte string. |
| 15082 | callMatcher(CharEncoding::TwoByte); |
| 15083 | |
| 15084 | masm.bind(&restoreVolatile); |
| 15085 | masm.PopRegsInMask(volatileRegs); |
| 15086 | |
| 15087 | // Convert to bool for `includes`. |
| 15088 | if constexpr (std::is_same_v<LIns, LStringIncludesSIMD>) { |
| 15089 | masm.cmpPtrSet(Assembler::NotEqual, output, ImmPtr(nullptr), output); |
| 15090 | } |
| 15091 | |
| 15092 | masm.bind(&done); |
| 15093 | } |
| 15094 | |
| 15095 | void CodeGenerator::visitStringIncludesSIMD(LStringIncludesSIMD* lir) { |
| 15096 | CallStringMatch(masm, lir, liveVolatileRegs(lir)); |
| 15097 | } |
| 15098 | |
| 15099 | void CodeGenerator::visitStringIndexOf(LStringIndexOf* lir) { |
| 15100 | pushArg(ToRegister(lir->searchString())); |
| 15101 | pushArg(ToRegister(lir->string())); |
| 15102 | |
| 15103 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
| 15104 | callVM<Fn, js::StringIndexOf>(lir); |
| 15105 | } |
| 15106 | |
| 15107 | void CodeGenerator::visitStringIndexOfSIMD(LStringIndexOfSIMD* lir) { |
| 15108 | CallStringMatch(masm, lir, liveVolatileRegs(lir)); |
| 15109 | } |
| 15110 | |
| 15111 | void CodeGenerator::visitStringLastIndexOf(LStringLastIndexOf* lir) { |
| 15112 | pushArg(ToRegister(lir->searchString())); |
| 15113 | pushArg(ToRegister(lir->string())); |
| 15114 | |
| 15115 | using Fn = bool (*)(JSContext*, HandleString, HandleString, int32_t*); |
| 15116 | callVM<Fn, js::StringLastIndexOf>(lir); |
| 15117 | } |
| 15118 | |
| 15119 | void CodeGenerator::visitStringStartsWith(LStringStartsWith* lir) { |
| 15120 | pushArg(ToRegister(lir->searchString())); |
| 15121 | pushArg(ToRegister(lir->string())); |
| 15122 | |
| 15123 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 15124 | callVM<Fn, js::StringStartsWith>(lir); |
| 15125 | } |
| 15126 | |
| 15127 | void CodeGenerator::visitStringStartsWithInline(LStringStartsWithInline* lir) { |
| 15128 | Register string = ToRegister(lir->string()); |
| 15129 | Register output = ToRegister(lir->output()); |
| 15130 | Register temp = ToRegister(lir->temp0()); |
| 15131 | |
| 15132 | const JSOffThreadAtom* searchString = lir->searchString(); |
| 15133 | |
| 15134 | size_t length = searchString->length(); |
| 15135 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 15135); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "length > 0" ")"); do { MOZ_CrashSequence (__null, 15135); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 15136 | |
| 15137 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 15138 | auto* ool = oolCallVM<Fn, js::StringStartsWith>( |
| 15139 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
| 15140 | |
| 15141 | masm.move32(Imm32(0), output); |
| 15142 | |
| 15143 | // Can't be a prefix when the string is smaller than the search string. |
| 15144 | masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()), |
| 15145 | Imm32(length), ool->rejoin()); |
| 15146 | |
| 15147 | // Unwind ropes at the start if possible. |
| 15148 | Label compare; |
| 15149 | masm.movePtr(string, temp); |
| 15150 | masm.branchIfNotRope(temp, &compare); |
| 15151 | |
| 15152 | Label unwindRope; |
| 15153 | masm.bind(&unwindRope); |
| 15154 | masm.loadRopeLeftChild(temp, output); |
| 15155 | masm.movePtr(output, temp); |
| 15156 | |
| 15157 | // If the left child is smaller than the search string, jump into the VM to |
| 15158 | // linearize the string. |
| 15159 | masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()), |
| 15160 | Imm32(length), ool->entry()); |
| 15161 | |
| 15162 | // Otherwise keep unwinding ropes. |
| 15163 | masm.branchIfRope(temp, &unwindRope); |
| 15164 | |
| 15165 | masm.bind(&compare); |
| 15166 | |
| 15167 | // If operands point to the same instance, it's trivially a prefix. |
| 15168 | Label notPointerEqual; |
| 15169 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString), |
| 15170 | ¬PointerEqual); |
| 15171 | masm.move32(Imm32(1), output); |
| 15172 | masm.jump(ool->rejoin()); |
| 15173 | masm.bind(¬PointerEqual); |
| 15174 | |
| 15175 | if (searchString->hasTwoByteChars()) { |
| 15176 | // Pure two-byte strings can't be a prefix of Latin-1 strings. |
| 15177 | JS::AutoCheckCannotGC nogc; |
| 15178 | if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) { |
| 15179 | Label compareChars; |
| 15180 | masm.branchTwoByteString(temp, &compareChars); |
| 15181 | masm.move32(Imm32(0), output); |
| 15182 | masm.jump(ool->rejoin()); |
| 15183 | masm.bind(&compareChars); |
| 15184 | } |
| 15185 | } |
| 15186 | |
| 15187 | // Load the input string's characters. |
| 15188 | Register stringChars = output; |
| 15189 | masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry()); |
| 15190 | |
| 15191 | // Start comparing character by character. |
| 15192 | masm.compareStringChars(JSOp::Eq, stringChars, searchString, output); |
| 15193 | |
| 15194 | masm.bind(ool->rejoin()); |
| 15195 | } |
| 15196 | |
| 15197 | void CodeGenerator::visitStringEndsWith(LStringEndsWith* lir) { |
| 15198 | pushArg(ToRegister(lir->searchString())); |
| 15199 | pushArg(ToRegister(lir->string())); |
| 15200 | |
| 15201 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 15202 | callVM<Fn, js::StringEndsWith>(lir); |
| 15203 | } |
| 15204 | |
| 15205 | void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) { |
| 15206 | Register string = ToRegister(lir->string()); |
| 15207 | Register output = ToRegister(lir->output()); |
| 15208 | Register temp = ToRegister(lir->temp0()); |
| 15209 | |
| 15210 | const JSOffThreadAtom* searchString = lir->searchString(); |
| 15211 | |
| 15212 | size_t length = searchString->length(); |
| 15213 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 15213); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "length > 0" ")"); do { MOZ_CrashSequence (__null, 15213); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 15214 | |
| 15215 | using Fn = bool (*)(JSContext*, HandleString, HandleString, bool*); |
| 15216 | auto* ool = oolCallVM<Fn, js::StringEndsWith>( |
| 15217 | lir, ArgList(string, ImmGCPtr(searchString)), StoreRegisterTo(output)); |
| 15218 | |
| 15219 | masm.move32(Imm32(0), output); |
| 15220 | |
| 15221 | // Can't be a suffix when the string is smaller than the search string. |
| 15222 | masm.branch32(Assembler::Below, Address(string, JSString::offsetOfLength()), |
| 15223 | Imm32(length), ool->rejoin()); |
| 15224 | |
| 15225 | // Unwind ropes at the end if possible. |
| 15226 | Label compare; |
| 15227 | masm.movePtr(string, temp); |
| 15228 | masm.branchIfNotRope(temp, &compare); |
| 15229 | |
| 15230 | Label unwindRope; |
| 15231 | masm.bind(&unwindRope); |
| 15232 | masm.loadRopeRightChild(temp, output); |
| 15233 | masm.movePtr(output, temp); |
| 15234 | |
| 15235 | // If the right child is smaller than the search string, jump into the VM to |
| 15236 | // linearize the string. |
| 15237 | masm.branch32(Assembler::Below, Address(temp, JSString::offsetOfLength()), |
| 15238 | Imm32(length), ool->entry()); |
| 15239 | |
| 15240 | // Otherwise keep unwinding ropes. |
| 15241 | masm.branchIfRope(temp, &unwindRope); |
| 15242 | |
| 15243 | masm.bind(&compare); |
| 15244 | |
| 15245 | // If operands point to the same instance, it's trivially a suffix. |
| 15246 | Label notPointerEqual; |
| 15247 | masm.branchPtr(Assembler::NotEqual, temp, ImmGCPtr(searchString), |
| 15248 | ¬PointerEqual); |
| 15249 | masm.move32(Imm32(1), output); |
| 15250 | masm.jump(ool->rejoin()); |
| 15251 | masm.bind(¬PointerEqual); |
| 15252 | |
| 15253 | CharEncoding encoding = searchString->hasLatin1Chars() |
| 15254 | ? CharEncoding::Latin1 |
| 15255 | : CharEncoding::TwoByte; |
| 15256 | if (encoding == CharEncoding::TwoByte) { |
| 15257 | // Pure two-byte strings can't be a suffix of Latin-1 strings. |
| 15258 | JS::AutoCheckCannotGC nogc; |
| 15259 | if (!mozilla::IsUtf16Latin1(searchString->twoByteRange(nogc))) { |
| 15260 | Label compareChars; |
| 15261 | masm.branchTwoByteString(temp, &compareChars); |
| 15262 | masm.move32(Imm32(0), output); |
| 15263 | masm.jump(ool->rejoin()); |
| 15264 | masm.bind(&compareChars); |
| 15265 | } |
| 15266 | } |
| 15267 | |
| 15268 | // Load the input string's characters. |
| 15269 | Register stringChars = output; |
| 15270 | masm.loadStringCharsForCompare(temp, searchString, stringChars, ool->entry()); |
| 15271 | |
| 15272 | // Move string-char pointer to the suffix string. |
| 15273 | masm.loadStringLength(temp, temp); |
| 15274 | masm.sub32(Imm32(length), temp); |
| 15275 | masm.addToCharPtr(stringChars, temp, encoding); |
| 15276 | |
| 15277 | // Start comparing character by character. |
| 15278 | masm.compareStringChars(JSOp::Eq, stringChars, searchString, output); |
| 15279 | |
| 15280 | masm.bind(ool->rejoin()); |
| 15281 | } |
| 15282 | |
| 15283 | void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) { |
| 15284 | Register string = ToRegister(lir->string()); |
| 15285 | Register output = ToRegister(lir->output()); |
| 15286 | Register temp0 = ToRegister(lir->temp0()); |
| 15287 | Register temp1 = ToRegister(lir->temp1()); |
| 15288 | Register temp2 = ToRegister(lir->temp2()); |
| 15289 | |
| 15290 | // On x86 there are not enough registers. In that case reuse the string |
| 15291 | // register as a temporary. |
| 15292 | Register temp3 = |
| 15293 | lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3()); |
| 15294 | Register temp4 = ToRegister(lir->temp4()); |
| 15295 | |
| 15296 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 15297 | OutOfLineCode* ool = oolCallVM<Fn, js::StringToLowerCase>( |
| 15298 | lir, ArgList(string), StoreRegisterTo(output)); |
| 15299 | |
| 15300 | // Take the slow path if the string isn't a linear Latin-1 string. |
| 15301 | Imm32 linearLatin1Bits(StringFlags::LINEAR_BIT | |
| 15302 | StringFlags::LATIN1_CHARS_BIT); |
| 15303 | Register flags = temp0; |
| 15304 | masm.load32(Address(string, JSString::offsetOfFlags()), flags); |
| 15305 | masm.and32(linearLatin1Bits, flags); |
| 15306 | masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry()); |
| 15307 | |
| 15308 | Register length = temp0; |
| 15309 | masm.loadStringLength(string, length); |
| 15310 | |
| 15311 | // Return the input if it's the empty string. |
| 15312 | Label notEmptyString; |
| 15313 | masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬EmptyString); |
| 15314 | { |
| 15315 | masm.movePtr(string, output); |
| 15316 | masm.jump(ool->rejoin()); |
| 15317 | } |
| 15318 | masm.bind(¬EmptyString); |
| 15319 | |
| 15320 | Register inputChars = temp1; |
| 15321 | masm.loadStringChars(string, inputChars, CharEncoding::Latin1); |
| 15322 | |
| 15323 | Register toLowerCaseTable = temp2; |
| 15324 | masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable); |
| 15325 | |
| 15326 | // Single element strings can be directly retrieved from static strings cache. |
| 15327 | Label notSingleElementString; |
| 15328 | masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleElementString); |
| 15329 | { |
| 15330 | Register current = temp4; |
| 15331 | |
| 15332 | masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); |
| 15333 | masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), |
| 15334 | current); |
| 15335 | masm.lookupStaticString(current, output, gen->runtime->staticStrings()); |
| 15336 | |
| 15337 | masm.jump(ool->rejoin()); |
| 15338 | } |
| 15339 | masm.bind(¬SingleElementString); |
| 15340 | |
| 15341 | // Use the OOL-path when the string is too long. This prevents scanning long |
| 15342 | // strings which have upper case characters only near the end a second time in |
| 15343 | // the VM. |
| 15344 | constexpr int32_t MaxInlineLength = 64; |
| 15345 | masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry()); |
| 15346 | |
| 15347 | { |
| 15348 | // Check if there are any characters which need to be converted. |
| 15349 | // |
| 15350 | // This extra loop gives a small performance improvement for strings which |
| 15351 | // are already lower cased and lets us avoid calling into the runtime for |
| 15352 | // non-inline, all lower case strings. But more importantly it avoids |
| 15353 | // repeated inline allocation failures: |
| 15354 | // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the |
| 15355 | // |js::StringToLowerCase| runtime function when the result string can't be |
| 15356 | // allocated inline. And |js::StringToLowerCase| directly returns the input |
| 15357 | // string when no characters need to be converted. That means it won't |
| 15358 | // trigger GC to clear up the free nursery space, so the next toLowerCase() |
| 15359 | // call will again fail to inline allocate the result string. |
| 15360 | Label hasUpper; |
| 15361 | { |
| 15362 | Register checkInputChars = output; |
| 15363 | masm.movePtr(inputChars, checkInputChars); |
| 15364 | |
| 15365 | Register current = temp4; |
| 15366 | |
| 15367 | Label start; |
| 15368 | masm.bind(&start); |
| 15369 | masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1); |
| 15370 | masm.branch8(Assembler::NotEqual, |
| 15371 | BaseIndex(toLowerCaseTable, current, TimesOne), current, |
| 15372 | &hasUpper); |
| 15373 | masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars); |
| 15374 | masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); |
| 15375 | |
| 15376 | // Input is already in lower case. |
| 15377 | masm.movePtr(string, output); |
| 15378 | masm.jump(ool->rejoin()); |
| 15379 | } |
| 15380 | masm.bind(&hasUpper); |
| 15381 | |
| 15382 | // |length| was clobbered above, reload. |
| 15383 | masm.loadStringLength(string, length); |
| 15384 | |
| 15385 | // Call into the runtime when we can't create an inline string. |
| 15386 | masm.branch32(Assembler::Above, length, |
| 15387 | Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry()); |
| 15388 | |
| 15389 | AllocateThinOrFatInlineString(masm, output, length, temp4, |
| 15390 | initialStringHeap(), ool->entry(), |
| 15391 | CharEncoding::Latin1); |
| 15392 | |
| 15393 | if (temp3 == string) { |
| 15394 | masm.push(string); |
| 15395 | } |
| 15396 | |
| 15397 | Register outputChars = temp3; |
| 15398 | masm.loadInlineStringCharsForStore(output, outputChars); |
| 15399 | |
| 15400 | { |
| 15401 | Register current = temp4; |
| 15402 | |
| 15403 | Label start; |
| 15404 | masm.bind(&start); |
| 15405 | masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); |
| 15406 | masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), |
| 15407 | current); |
| 15408 | masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1); |
| 15409 | masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars); |
| 15410 | masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars); |
| 15411 | masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); |
| 15412 | } |
| 15413 | |
| 15414 | if (temp3 == string) { |
| 15415 | masm.pop(string); |
| 15416 | } |
| 15417 | } |
| 15418 | |
| 15419 | masm.bind(ool->rejoin()); |
| 15420 | } |
| 15421 | |
| 15422 | void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) { |
| 15423 | pushArg(ToRegister(lir->string())); |
| 15424 | |
| 15425 | using Fn = JSLinearString* (*)(JSContext*, JSString*); |
| 15426 | callVM<Fn, js::StringToUpperCase>(lir); |
| 15427 | } |
| 15428 | |
| 15429 | void CodeGenerator::visitCharCodeToLowerCase(LCharCodeToLowerCase* lir) { |
| 15430 | Register code = ToRegister(lir->code()); |
| 15431 | Register output = ToRegister(lir->output()); |
| 15432 | Register temp = ToRegister(lir->temp0()); |
| 15433 | |
| 15434 | using Fn = JSString* (*)(JSContext*, int32_t); |
| 15435 | auto* ool = oolCallVM<Fn, jit::CharCodeToLowerCase>(lir, ArgList(code), |
| 15436 | StoreRegisterTo(output)); |
| 15437 | |
| 15438 | constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1; |
| 15439 | |
| 15440 | // OOL path if code >= NonLatin1Min. |
| 15441 | masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry()); |
| 15442 | |
| 15443 | // Convert to lower case. |
| 15444 | masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), temp); |
| 15445 | masm.load8ZeroExtend(BaseIndex(temp, code, TimesOne), temp); |
| 15446 | |
| 15447 | // Load static string for lower case character. |
| 15448 | masm.lookupStaticString(temp, output, gen->runtime->staticStrings()); |
| 15449 | |
| 15450 | masm.bind(ool->rejoin()); |
| 15451 | } |
| 15452 | |
| 15453 | void CodeGenerator::visitCharCodeToUpperCase(LCharCodeToUpperCase* lir) { |
| 15454 | Register code = ToRegister(lir->code()); |
| 15455 | Register output = ToRegister(lir->output()); |
| 15456 | Register temp = ToRegister(lir->temp0()); |
| 15457 | |
| 15458 | using Fn = JSString* (*)(JSContext*, int32_t); |
| 15459 | auto* ool = oolCallVM<Fn, jit::CharCodeToUpperCase>(lir, ArgList(code), |
| 15460 | StoreRegisterTo(output)); |
| 15461 | |
| 15462 | constexpr char16_t NonLatin1Min = char16_t(JSString::MAX_LATIN1_CHAR) + 1; |
| 15463 | |
| 15464 | // OOL path if code >= NonLatin1Min. |
| 15465 | masm.boundsCheck32PowerOfTwo(code, NonLatin1Min, ool->entry()); |
| 15466 | |
| 15467 | // Most one element Latin-1 strings can be directly retrieved from the |
| 15468 | // static strings cache, except the following three characters: |
| 15469 | // |
| 15470 | // 1. ToUpper(U+00B5) = 0+039C |
| 15471 | // 2. ToUpper(U+00FF) = 0+0178 |
| 15472 | // 3. ToUpper(U+00DF) = 0+0053 0+0053 |
| 15473 | masm.branch32(Assembler::Equal, code, Imm32(unicode::MICRO_SIGN), |
| 15474 | ool->entry()); |
| 15475 | masm.branch32(Assembler::Equal, code, |
| 15476 | Imm32(unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS), |
| 15477 | ool->entry()); |
| 15478 | masm.branch32(Assembler::Equal, code, |
| 15479 | Imm32(unicode::LATIN_SMALL_LETTER_SHARP_S), ool->entry()); |
| 15480 | |
| 15481 | // Inline unicode::ToUpperCase (without the special case for ASCII characters) |
| 15482 | |
| 15483 | constexpr size_t shift = unicode::CharInfoShift; |
| 15484 | |
| 15485 | // code >> shift |
| 15486 | masm.rshift32(Imm32(shift), code, temp); |
| 15487 | |
| 15488 | // index = index1[code >> shift]; |
| 15489 | masm.movePtr(ImmPtr(unicode::index1), output); |
| 15490 | masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp); |
| 15491 | |
| 15492 | // (code & ((1 << shift) - 1) |
| 15493 | masm.and32(Imm32((1 << shift) - 1), code, output); |
| 15494 | |
| 15495 | // (index << shift) + (code & ((1 << shift) - 1)) |
| 15496 | masm.lshift32(Imm32(shift), temp); |
| 15497 | masm.add32(output, temp); |
| 15498 | |
| 15499 | // index = index2[(index << shift) + (code & ((1 << shift) - 1))] |
| 15500 | masm.movePtr(ImmPtr(unicode::index2), output); |
| 15501 | masm.load8ZeroExtend(BaseIndex(output, temp, TimesOne), temp); |
| 15502 | |
| 15503 | // Compute |index * 6| through |(index * 3) * TimesTwo|. |
| 15504 | static_assert(sizeof(unicode::CharacterInfo) == 6); |
| 15505 | masm.mulBy3(temp, temp); |
| 15506 | |
| 15507 | // upperCase = js_charinfo[index].upperCase |
| 15508 | masm.movePtr(ImmPtr(unicode::js_charinfo), output); |
| 15509 | masm.load16ZeroExtend(BaseIndex(output, temp, TimesTwo, |
| 15510 | offsetof(unicode::CharacterInfo, upperCase)__builtin_offsetof(unicode::CharacterInfo, upperCase)), |
| 15511 | temp); |
| 15512 | |
| 15513 | // uint16_t(ch) + upperCase |
| 15514 | masm.add32(code, temp); |
| 15515 | |
| 15516 | // Clear any high bits added when performing the unsigned 16-bit addition |
| 15517 | // through a signed 32-bit addition. |
| 15518 | masm.move8ZeroExtend(temp, temp); |
| 15519 | |
| 15520 | // Load static string for upper case character. |
| 15521 | masm.lookupStaticString(temp, output, gen->runtime->staticStrings()); |
| 15522 | |
| 15523 | masm.bind(ool->rejoin()); |
| 15524 | } |
| 15525 | |
| 15526 | void CodeGenerator::visitStringTrimStartIndex(LStringTrimStartIndex* lir) { |
| 15527 | Register string = ToRegister(lir->string()); |
| 15528 | Register output = ToRegister(lir->output()); |
| 15529 | |
| 15530 | using Fn = int32_t (*)(const JSString*); |
| 15531 | masm.setupAlignedABICall(); |
| 15532 | masm.passABIArg(string); |
| 15533 | masm.callWithABI<Fn, jit::StringTrimStartIndex>(); |
| 15534 | masm.storeCallInt32Result(output); |
| 15535 | } |
| 15536 | |
| 15537 | void CodeGenerator::visitStringTrimEndIndex(LStringTrimEndIndex* lir) { |
| 15538 | Register string = ToRegister(lir->string()); |
| 15539 | Register start = ToRegister(lir->start()); |
| 15540 | Register output = ToRegister(lir->output()); |
| 15541 | |
| 15542 | using Fn = int32_t (*)(const JSString*, int32_t); |
| 15543 | masm.setupAlignedABICall(); |
| 15544 | masm.passABIArg(string); |
| 15545 | masm.passABIArg(start); |
| 15546 | masm.callWithABI<Fn, jit::StringTrimEndIndex>(); |
| 15547 | masm.storeCallInt32Result(output); |
| 15548 | } |
| 15549 | |
| 15550 | void CodeGenerator::visitStringSplit(LStringSplit* lir) { |
| 15551 | pushArg(Imm32(INT32_MAX(2147483647))); |
| 15552 | pushArg(ToRegister(lir->separator())); |
| 15553 | pushArg(ToRegister(lir->string())); |
| 15554 | |
| 15555 | using Fn = ArrayObject* (*)(JSContext*, HandleString, HandleString, uint32_t); |
| 15556 | callVM<Fn, js::StringSplitString>(lir); |
| 15557 | } |
| 15558 | |
| 15559 | void CodeGenerator::visitInitializedLength(LInitializedLength* lir) { |
| 15560 | Address initLength(ToRegister(lir->elements()), |
| 15561 | ObjectElements::offsetOfInitializedLength()); |
| 15562 | masm.load32(initLength, ToRegister(lir->output())); |
| 15563 | } |
| 15564 | |
| 15565 | void CodeGenerator::visitSetInitializedLength(LSetInitializedLength* lir) { |
| 15566 | Register elements = ToRegister(lir->elements()); |
| 15567 | uint32_t newLength = lir->mir()->length(); |
| 15568 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
| 15569 | |
| 15570 | if (lir->mir()->needsPreBarrier()) { |
| 15571 | // The elements at or above the new initialized length are no longer part of |
| 15572 | // the object, so we need a pre-barrier. |
| 15573 | Register index = ToRegister(lir->temp0()); |
| 15574 | Label done; |
| 15575 | masm.branchTestNeedsMarkingBarrier(Assembler::Zero, &done); |
| 15576 | masm.load32(initLength, index); |
| 15577 | Label loop; |
| 15578 | masm.bind(&loop); |
| 15579 | masm.branch32(Assembler::BelowOrEqual, index, Imm32(newLength), &done); |
| 15580 | masm.sub32(Imm32(1), index); |
| 15581 | masm.unguardedCallPreBarrier(BaseValueIndex(elements, index), |
| 15582 | MIRType::Value); |
| 15583 | masm.jump(&loop); |
| 15584 | masm.bind(&done); |
| 15585 | } else { |
| 15586 | #ifdef DEBUG1 |
| 15587 | // Callers that don't need a barrier must not remove any elements. |
| 15588 | Label ok; |
| 15589 | masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(newLength), &ok); |
| 15590 | masm.assumeUnreachable("removing elements without a pre-barrier"); |
| 15591 | masm.bind(&ok); |
| 15592 | #endif |
| 15593 | } |
| 15594 | |
| 15595 | masm.store32(Imm32(newLength), initLength); |
| 15596 | } |
| 15597 | |
| 15598 | void CodeGenerator::visitNotI(LNotI* lir) { |
| 15599 | Register input = ToRegister(lir->input()); |
| 15600 | Register output = ToRegister(lir->output()); |
| 15601 | |
| 15602 | masm.cmp32Set(Assembler::Equal, input, Imm32(0), output); |
| 15603 | } |
| 15604 | |
| 15605 | void CodeGenerator::visitNotIPtr(LNotIPtr* lir) { |
| 15606 | Register input = ToRegister(lir->input()); |
| 15607 | Register output = ToRegister(lir->output()); |
| 15608 | |
| 15609 | masm.cmpPtrSet(Assembler::Equal, input, ImmWord(0), output); |
| 15610 | } |
| 15611 | |
| 15612 | void CodeGenerator::visitNotI64(LNotI64* lir) { |
| 15613 | Register64 input = ToRegister64(lir->inputI64()); |
| 15614 | Register output = ToRegister(lir->output()); |
| 15615 | |
| 15616 | masm.cmp64Set(Assembler::Equal, input, Imm64(0), output); |
| 15617 | } |
| 15618 | |
| 15619 | void CodeGenerator::visitNotBI(LNotBI* lir) { |
| 15620 | Register input = ToRegister(lir->input()); |
| 15621 | Register output = ToRegister(lir->output()); |
| 15622 | |
| 15623 | masm.cmp32Set(Assembler::Equal, Address(input, BigInt::offsetOfLength()), |
| 15624 | Imm32(0), output); |
| 15625 | } |
| 15626 | |
| 15627 | void CodeGenerator::visitNotO(LNotO* lir) { |
| 15628 | Register objreg = ToRegister(lir->input()); |
| 15629 | Register output = ToRegister(lir->output()); |
| 15630 | |
| 15631 | bool intact = hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted(); |
| 15632 | if (intact) { |
| 15633 | // Bug 1874905: It would be fantastic if this could be optimized out. |
| 15634 | assertObjectDoesNotEmulateUndefined(objreg, output, lir->mir()); |
| 15635 | masm.move32(Imm32(0), output); |
| 15636 | } else { |
| 15637 | auto* ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 15638 | addOutOfLineCode(ool, lir->mir()); |
| 15639 | |
| 15640 | Label* ifEmulatesUndefined = ool->label1(); |
| 15641 | Label* ifDoesntEmulateUndefined = ool->label2(); |
| 15642 | |
| 15643 | branchTestObjectEmulatesUndefined(objreg, ifEmulatesUndefined, |
| 15644 | ifDoesntEmulateUndefined, output, ool); |
| 15645 | // fall through |
| 15646 | |
| 15647 | Label join; |
| 15648 | |
| 15649 | masm.move32(Imm32(0), output); |
| 15650 | masm.jump(&join); |
| 15651 | |
| 15652 | masm.bind(ifEmulatesUndefined); |
| 15653 | masm.move32(Imm32(1), output); |
| 15654 | |
| 15655 | masm.bind(&join); |
| 15656 | } |
| 15657 | } |
| 15658 | |
| 15659 | void CodeGenerator::visitNotV(LNotV* lir) { |
| 15660 | Label defaultTruthy, defaultFalsy; |
| 15661 | Label* ifTruthy = &defaultTruthy; |
| 15662 | Label* ifFalsy = &defaultFalsy; |
| 15663 | OutOfLineTestObjectWithLabels* ool = nullptr; |
| 15664 | if (!hasSeenObjectEmulateUndefinedFuseIntactAndDependencyNoted()) { |
| 15665 | ool = new (alloc()) OutOfLineTestObjectWithLabels(); |
| 15666 | addOutOfLineCode(ool, lir->mir()); |
| 15667 | ifTruthy = ool->label1(); |
| 15668 | ifFalsy = ool->label2(); |
| 15669 | } |
| 15670 | |
| 15671 | ValueOperand input = ToValue(lir->input()); |
| 15672 | Register tempToUnbox = ToTempUnboxRegister(lir->temp1()); |
| 15673 | FloatRegister floatTemp = ToFloatRegister(lir->temp0()); |
| 15674 | Register output = ToRegister(lir->output()); |
| 15675 | const TypeDataList& observedTypes = lir->mir()->observedTypes(); |
| 15676 | |
| 15677 | testValueTruthy(input, tempToUnbox, output, floatTemp, observedTypes, |
| 15678 | ifTruthy, ifFalsy, ool); |
| 15679 | |
| 15680 | Label join; |
| 15681 | |
| 15682 | // Note that the testValueTruthy call above may choose to fall through |
| 15683 | // to ifTruthy instead of branching there. |
| 15684 | masm.bind(ifTruthy); |
| 15685 | masm.move32(Imm32(0), output); |
| 15686 | masm.jump(&join); |
| 15687 | |
| 15688 | masm.bind(ifFalsy); |
| 15689 | masm.move32(Imm32(1), output); |
| 15690 | |
| 15691 | // both branches meet here. |
| 15692 | masm.bind(&join); |
| 15693 | } |
| 15694 | |
| 15695 | void CodeGenerator::visitBoundsCheck(LBoundsCheck* lir) { |
| 15696 | const LAllocation* index = lir->index(); |
| 15697 | const LAllocation* length = lir->length(); |
| 15698 | LSnapshot* snapshot = lir->snapshot(); |
| 15699 | |
| 15700 | MIRType type = lir->mir()->type(); |
| 15701 | |
| 15702 | auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) { |
| 15703 | if (type == MIRType::Int32) { |
| 15704 | bailoutCmp32(cond, lhs, rhs, snapshot); |
| 15705 | } else { |
| 15706 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 15706); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence (__null, 15706); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 15707 | bailoutCmpPtr(cond, lhs, rhs, snapshot); |
| 15708 | } |
| 15709 | }; |
| 15710 | |
| 15711 | auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs, |
| 15712 | int32_t rhs) { |
| 15713 | if (type == MIRType::Int32) { |
| 15714 | bailoutCmp32(cond, lhs, Imm32(rhs), snapshot); |
| 15715 | } else { |
| 15716 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 15716); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence (__null, 15716); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 15717 | bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot); |
| 15718 | } |
| 15719 | }; |
| 15720 | |
| 15721 | if (index->isConstant()) { |
| 15722 | // Use uint32 so that the comparison is unsigned. |
| 15723 | uint32_t idx = ToInt32(index); |
| 15724 | if (length->isConstant()) { |
| 15725 | uint32_t len = ToInt32(lir->length()); |
| 15726 | if (idx < len) { |
| 15727 | return; |
| 15728 | } |
| 15729 | bailout(snapshot); |
| 15730 | return; |
| 15731 | } |
| 15732 | |
| 15733 | if (length->isGeneralReg()) { |
| 15734 | bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), idx); |
| 15735 | } else { |
| 15736 | bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), idx); |
| 15737 | } |
| 15738 | return; |
| 15739 | } |
| 15740 | |
| 15741 | Register indexReg = ToRegister(index); |
| 15742 | if (length->isConstant()) { |
| 15743 | bailoutCmpConstant(Assembler::AboveOrEqual, indexReg, ToInt32(length)); |
| 15744 | } else if (length->isGeneralReg()) { |
| 15745 | bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), indexReg); |
| 15746 | } else { |
| 15747 | bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), indexReg); |
| 15748 | } |
| 15749 | } |
| 15750 | |
| 15751 | void CodeGenerator::visitBoundsCheckRange(LBoundsCheckRange* lir) { |
| 15752 | int32_t min = lir->mir()->minimum(); |
| 15753 | int32_t max = lir->mir()->maximum(); |
| 15754 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 15754); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "max >= min" ")"); do { MOZ_CrashSequence (__null, 15754); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 15755 | |
| 15756 | LSnapshot* snapshot = lir->snapshot(); |
| 15757 | MIRType type = lir->mir()->type(); |
| 15758 | |
| 15759 | const LAllocation* length = lir->length(); |
| 15760 | Register temp = ToRegister(lir->temp0()); |
| 15761 | |
| 15762 | auto bailoutCmp = [&](Assembler::Condition cond, auto lhs, auto rhs) { |
| 15763 | if (type == MIRType::Int32) { |
| 15764 | bailoutCmp32(cond, lhs, rhs, snapshot); |
| 15765 | } else { |
| 15766 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 15766); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence (__null, 15766); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 15767 | bailoutCmpPtr(cond, lhs, rhs, snapshot); |
| 15768 | } |
| 15769 | }; |
| 15770 | |
| 15771 | auto bailoutCmpConstant = [&](Assembler::Condition cond, auto lhs, |
| 15772 | int32_t rhs) { |
| 15773 | if (type == MIRType::Int32) { |
| 15774 | bailoutCmp32(cond, lhs, Imm32(rhs), snapshot); |
| 15775 | } else { |
| 15776 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 15776); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence (__null, 15776); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 15777 | bailoutCmpPtr(cond, lhs, ImmWord(rhs), snapshot); |
| 15778 | } |
| 15779 | }; |
| 15780 | |
| 15781 | if (lir->index()->isConstant()) { |
| 15782 | int32_t nmin, nmax; |
| 15783 | int32_t index = ToInt32(lir->index()); |
| 15784 | if (mozilla::SafeAdd(index, min, &nmin) && |
| 15785 | mozilla::SafeAdd(index, max, &nmax) && nmin >= 0) { |
| 15786 | if (length->isGeneralReg()) { |
| 15787 | bailoutCmpConstant(Assembler::BelowOrEqual, ToRegister(length), nmax); |
| 15788 | } else { |
| 15789 | bailoutCmpConstant(Assembler::BelowOrEqual, ToAddress(length), nmax); |
| 15790 | } |
| 15791 | return; |
| 15792 | } |
| 15793 | masm.mov(ImmWord(index), temp); |
| 15794 | } else { |
| 15795 | masm.mov(ToRegister(lir->index()), temp); |
| 15796 | } |
| 15797 | |
| 15798 | // If the minimum and maximum differ then do an underflow check first. |
| 15799 | // If the two are the same then doing an unsigned comparison on the |
| 15800 | // length will also catch a negative index. |
| 15801 | if (min != max) { |
| 15802 | if (min != 0) { |
| 15803 | Label bail; |
| 15804 | if (type == MIRType::Int32) { |
| 15805 | masm.branchAdd32(Assembler::Overflow, Imm32(min), temp, &bail); |
| 15806 | } else { |
| 15807 | masm.branchAddPtr(Assembler::Overflow, Imm32(min), temp, &bail); |
| 15808 | } |
| 15809 | bailoutFrom(&bail, snapshot); |
| 15810 | } |
| 15811 | |
| 15812 | bailoutCmpConstant(Assembler::LessThan, temp, 0); |
| 15813 | |
| 15814 | if (min != 0) { |
| 15815 | int32_t diff; |
| 15816 | if (mozilla::SafeSub(max, min, &diff)) { |
| 15817 | max = diff; |
| 15818 | } else { |
| 15819 | if (type == MIRType::Int32) { |
| 15820 | masm.sub32(Imm32(min), temp); |
| 15821 | } else { |
| 15822 | masm.subPtr(Imm32(min), temp); |
| 15823 | } |
| 15824 | } |
| 15825 | } |
| 15826 | } |
| 15827 | |
| 15828 | // Compute the maximum possible index. No overflow check is needed when |
| 15829 | // max > 0. We can only wraparound to a negative number, which will test as |
| 15830 | // larger than all nonnegative numbers in the unsigned comparison, and the |
| 15831 | // length is required to be nonnegative (else testing a negative length |
| 15832 | // would succeed on any nonnegative index). |
| 15833 | if (max != 0) { |
| 15834 | if (max < 0) { |
| 15835 | Label bail; |
| 15836 | if (type == MIRType::Int32) { |
| 15837 | masm.branchAdd32(Assembler::Overflow, Imm32(max), temp, &bail); |
| 15838 | } else { |
| 15839 | masm.branchAddPtr(Assembler::Overflow, Imm32(max), temp, &bail); |
| 15840 | } |
| 15841 | bailoutFrom(&bail, snapshot); |
| 15842 | } else { |
| 15843 | if (type == MIRType::Int32) { |
| 15844 | masm.add32(Imm32(max), temp); |
| 15845 | } else { |
| 15846 | masm.addPtr(Imm32(max), temp); |
| 15847 | } |
| 15848 | } |
| 15849 | } |
| 15850 | |
| 15851 | if (length->isGeneralReg()) { |
| 15852 | bailoutCmp(Assembler::BelowOrEqual, ToRegister(length), temp); |
| 15853 | } else { |
| 15854 | bailoutCmp(Assembler::BelowOrEqual, ToAddress(length), temp); |
| 15855 | } |
| 15856 | } |
| 15857 | |
| 15858 | void CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir) { |
| 15859 | int32_t min = lir->mir()->minimum(); |
| 15860 | bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min), |
| 15861 | lir->snapshot()); |
| 15862 | } |
| 15863 | |
| 15864 | void CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir) { |
| 15865 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 15865); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JitOptions.spectreIndexMasking" ")"); do { MOZ_CrashSequence(__null, 15865); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15866 | |
| 15867 | const LAllocation* length = lir->length(); |
| 15868 | Register index = ToRegister(lir->index()); |
| 15869 | Register output = ToRegister(lir->output()); |
| 15870 | |
| 15871 | if (lir->mir()->type() == MIRType::Int32) { |
| 15872 | if (length->isGeneralReg()) { |
| 15873 | masm.spectreMaskIndex32(index, ToRegister(length), output); |
| 15874 | } else { |
| 15875 | masm.spectreMaskIndex32(index, ToAddress(length), output); |
| 15876 | } |
| 15877 | } else { |
| 15878 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 15878); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::IntPtr" ")"); do { MOZ_CrashSequence(__null, 15878); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15879 | if (length->isGeneralReg()) { |
| 15880 | masm.spectreMaskIndexPtr(index, ToRegister(length), output); |
| 15881 | } else { |
| 15882 | masm.spectreMaskIndexPtr(index, ToAddress(length), output); |
| 15883 | } |
| 15884 | } |
| 15885 | } |
| 15886 | |
| 15887 | CodeGenerator::AddressOrBaseObjectElementIndex |
| 15888 | CodeGenerator::ToAddressOrBaseObjectElementIndex(Register elements, |
| 15889 | const LAllocation* index) { |
| 15890 | if (index->isConstant()) { |
| 15891 | NativeObject::elementsSizeMustNotOverflow(); |
| 15892 | return AddressOrBaseObjectElementIndex( |
| 15893 | Address(elements, ToInt32(index) * sizeof(JS::Value))); |
| 15894 | } |
| 15895 | return AddressOrBaseObjectElementIndex( |
| 15896 | BaseObjectElementIndex(elements, ToRegister(index))); |
| 15897 | } |
| 15898 | |
| 15899 | void CodeGenerator::emitStoreHoleCheck(Address dest, LSnapshot* snapshot) { |
| 15900 | Label bail; |
| 15901 | masm.branchTestMagic(Assembler::Equal, dest, JS_ELEMENTS_HOLE, &bail); |
| 15902 | bailoutFrom(&bail, snapshot); |
| 15903 | } |
| 15904 | |
| 15905 | void CodeGenerator::emitStoreHoleCheck(BaseObjectElementIndex dest, |
| 15906 | LSnapshot* snapshot) { |
| 15907 | Label bail; |
| 15908 | masm.branchTestMagic(Assembler::Equal, dest, JS_ELEMENTS_HOLE, &bail); |
| 15909 | bailoutFrom(&bail, snapshot); |
| 15910 | } |
| 15911 | |
| 15912 | void CodeGenerator::visitStoreElementT(LStoreElementT* store) { |
| 15913 | Register elements = ToRegister(store->elements()); |
| 15914 | const LAllocation* index = store->index(); |
| 15915 | |
| 15916 | MIRType valueType = store->mir()->value()->type(); |
| 15917 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 15917); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole" ")"); do { MOZ_CrashSequence(__null, 15917); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15918 | |
| 15919 | ConstantOrRegister value = ToConstantOrRegister(store->value(), valueType); |
| 15920 | |
| 15921 | auto dest = ToAddressOrBaseObjectElementIndex(elements, index); |
| 15922 | |
| 15923 | dest.match([&](const auto& dest) { |
| 15924 | if (store->mir()->needsBarrier()) { |
| 15925 | emitPreBarrier(dest); |
| 15926 | } |
| 15927 | |
| 15928 | if (store->mir()->needsHoleCheck()) { |
| 15929 | emitStoreHoleCheck(dest, store->snapshot()); |
| 15930 | } |
| 15931 | |
| 15932 | masm.storeUnboxedValue(value, valueType, dest); |
| 15933 | }); |
| 15934 | } |
| 15935 | |
| 15936 | void CodeGenerator::visitStoreElementV(LStoreElementV* lir) { |
| 15937 | ValueOperand value = ToValue(lir->value()); |
| 15938 | Register elements = ToRegister(lir->elements()); |
| 15939 | const LAllocation* index = lir->index(); |
| 15940 | |
| 15941 | auto dest = ToAddressOrBaseObjectElementIndex(elements, index); |
| 15942 | |
| 15943 | dest.match([&](const auto& dest) { |
| 15944 | if (lir->mir()->needsBarrier()) { |
| 15945 | emitPreBarrier(dest); |
| 15946 | } |
| 15947 | |
| 15948 | if (lir->mir()->needsHoleCheck()) { |
| 15949 | emitStoreHoleCheck(dest, lir->snapshot()); |
| 15950 | } |
| 15951 | |
| 15952 | masm.storeValue(value, dest); |
| 15953 | }); |
| 15954 | } |
| 15955 | |
| 15956 | void CodeGenerator::visitStoreHoleValueElement(LStoreHoleValueElement* lir) { |
| 15957 | Register elements = ToRegister(lir->elements()); |
| 15958 | Register index = ToRegister(lir->index()); |
| 15959 | |
| 15960 | Address elementsFlags(elements, ObjectElements::offsetOfFlags()); |
| 15961 | masm.or32(Imm32(ObjectElements::NON_PACKED), elementsFlags); |
| 15962 | |
| 15963 | BaseObjectElementIndex element(elements, index); |
| 15964 | masm.storeValue(MagicValue(JS_ELEMENTS_HOLE), element); |
| 15965 | } |
| 15966 | |
| 15967 | void CodeGenerator::visitStoreElementHoleT(LStoreElementHoleT* lir) { |
| 15968 | Register obj = ToRegister(lir->object()); |
| 15969 | Register elements = ToRegister(lir->elements()); |
| 15970 | Register index = ToRegister(lir->index()); |
| 15971 | Register temp = ToRegister(lir->temp0()); |
| 15972 | |
| 15973 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 15974 | Label bail; |
| 15975 | masm.prepareOOBStoreElement(obj, index, elements, temp, &bail, |
| 15976 | liveVolatileRegs(lir)); |
| 15977 | bailoutFrom(&bail, lir->snapshot()); |
| 15978 | |
| 15979 | // Jump to the inline path where we will store the value. |
| 15980 | // We rejoin after the prebarrier, because the memory is uninitialized. |
| 15981 | masm.jump(ool.rejoin()); |
| 15982 | }); |
| 15983 | addOutOfLineCode(ool, lir->mir()); |
| 15984 | |
| 15985 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
| 15986 | masm.spectreBoundsCheck32(index, initLength, temp, ool->entry()); |
| 15987 | |
| 15988 | emitPreBarrier(BaseObjectElementIndex(elements, index)); |
| 15989 | |
| 15990 | masm.bind(ool->rejoin()); |
| 15991 | |
| 15992 | MIRType valueType = lir->mir()->value()->type(); |
| 15993 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 15993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "valueType != MIRType::MagicHole" ")"); do { MOZ_CrashSequence(__null, 15993); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 15994 | |
| 15995 | ConstantOrRegister val = ToConstantOrRegister(lir->value(), valueType); |
| 15996 | masm.storeUnboxedValue(val, valueType, |
| 15997 | BaseObjectElementIndex(elements, index)); |
| 15998 | |
| 15999 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
| 16000 | LiveRegisterSet regs = liveVolatileRegs(lir); |
| 16001 | emitElementPostWriteBarrier(lir->mir(), regs, obj, index, temp, val); |
| 16002 | } |
| 16003 | } |
| 16004 | |
| 16005 | void CodeGenerator::visitStoreElementHoleV(LStoreElementHoleV* lir) { |
| 16006 | Register obj = ToRegister(lir->object()); |
| 16007 | Register elements = ToRegister(lir->elements()); |
| 16008 | Register index = ToRegister(lir->index()); |
| 16009 | ValueOperand value = ToValue(lir->value()); |
| 16010 | Register temp = ToRegister(lir->temp0()); |
| 16011 | |
| 16012 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 16013 | Label bail; |
| 16014 | masm.prepareOOBStoreElement(obj, index, elements, temp, &bail, |
| 16015 | liveVolatileRegs(lir)); |
| 16016 | bailoutFrom(&bail, lir->snapshot()); |
| 16017 | |
| 16018 | // Jump to the inline path where we will store the value. |
| 16019 | // We rejoin after the prebarrier, because the memory is uninitialized. |
| 16020 | masm.jump(ool.rejoin()); |
| 16021 | }); |
| 16022 | addOutOfLineCode(ool, lir->mir()); |
| 16023 | |
| 16024 | Address initLength(elements, ObjectElements::offsetOfInitializedLength()); |
| 16025 | masm.spectreBoundsCheck32(index, initLength, temp, ool->entry()); |
| 16026 | |
| 16027 | emitPreBarrier(BaseObjectElementIndex(elements, index)); |
| 16028 | |
| 16029 | masm.bind(ool->rejoin()); |
| 16030 | masm.storeValue(value, BaseObjectElementIndex(elements, index)); |
| 16031 | |
| 16032 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
| 16033 | LiveRegisterSet regs = liveVolatileRegs(lir); |
| 16034 | emitElementPostWriteBarrier(lir->mir(), regs, obj, index, temp, |
| 16035 | ConstantOrRegister(value)); |
| 16036 | } |
| 16037 | } |
| 16038 | |
| 16039 | void CodeGenerator::visitArrayPopShift(LArrayPopShift* lir) { |
| 16040 | Register obj = ToRegister(lir->object()); |
| 16041 | Register temp1 = ToRegister(lir->temp0()); |
| 16042 | Register temp2 = ToRegister(lir->temp1()); |
| 16043 | ValueOperand out = ToOutValue(lir); |
| 16044 | |
| 16045 | Label bail; |
| 16046 | if (lir->mir()->mode() == MArrayPopShift::Pop) { |
| 16047 | masm.packedArrayPop(obj, out, temp1, temp2, &bail); |
| 16048 | } else { |
| 16049 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 16049); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->mode() == MArrayPopShift::Shift" ")"); do { MOZ_CrashSequence(__null, 16049); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16050 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 16051 | masm.packedArrayShift(obj, out, temp1, temp2, volatileRegs, &bail); |
| 16052 | } |
| 16053 | bailoutFrom(&bail, lir->snapshot()); |
| 16054 | } |
| 16055 | |
| 16056 | void CodeGenerator::visitArrayPush(LArrayPush* lir) { |
| 16057 | Register obj = ToRegister(lir->object()); |
| 16058 | Register elementsTemp = ToRegister(lir->temp0()); |
| 16059 | Register length = ToRegister(lir->output()); |
| 16060 | ValueOperand value = ToValue(lir->value()); |
| 16061 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
| 16062 | |
| 16063 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 16064 | Register temp = ToRegister(lir->temp0()); |
| 16065 | |
| 16066 | LiveRegisterSet liveRegs = liveVolatileRegs(lir); |
| 16067 | liveRegs.takeUnchecked(temp); |
| 16068 | liveRegs.addUnchecked(ToRegister(lir->output())); |
| 16069 | liveRegs.addUnchecked(ToValue(lir->value())); |
| 16070 | |
| 16071 | masm.PushRegsInMask(liveRegs); |
| 16072 | |
| 16073 | masm.setupAlignedABICall(); |
| 16074 | masm.loadJSContext(temp); |
| 16075 | masm.passABIArg(temp); |
| 16076 | masm.passABIArg(obj); |
| 16077 | |
| 16078 | using Fn = bool (*)(JSContext*, NativeObject* obj); |
| 16079 | masm.callWithABI<Fn, NativeObject::addDenseElementPure>(); |
| 16080 | masm.storeCallPointerResult(temp); |
| 16081 | |
| 16082 | masm.PopRegsInMask(liveRegs); |
| 16083 | bailoutIfFalseBool(temp, lir->snapshot()); |
| 16084 | |
| 16085 | // Load the reallocated elements pointer. |
| 16086 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), temp); |
| 16087 | |
| 16088 | masm.jump(ool.rejoin()); |
| 16089 | }); |
| 16090 | addOutOfLineCode(ool, lir->mir()); |
| 16091 | |
| 16092 | // Load obj->elements in elementsTemp. |
| 16093 | masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp); |
| 16094 | |
| 16095 | Address initLengthAddr(elementsTemp, |
| 16096 | ObjectElements::offsetOfInitializedLength()); |
| 16097 | Address lengthAddr(elementsTemp, ObjectElements::offsetOfLength()); |
| 16098 | Address capacityAddr(elementsTemp, ObjectElements::offsetOfCapacity()); |
| 16099 | |
| 16100 | // Bail out if length != initLength. |
| 16101 | masm.load32(lengthAddr, length); |
| 16102 | bailoutCmp32(Assembler::NotEqual, initLengthAddr, length, lir->snapshot()); |
| 16103 | |
| 16104 | // If length < capacity, we can add a dense element inline. If not, we |
| 16105 | // need to allocate more elements. |
| 16106 | masm.spectreBoundsCheck32(length, capacityAddr, spectreTemp, ool->entry()); |
| 16107 | masm.bind(ool->rejoin()); |
| 16108 | |
| 16109 | // Store the value. |
| 16110 | masm.storeValue(value, BaseObjectElementIndex(elementsTemp, length)); |
| 16111 | |
| 16112 | // Update length and initialized length. |
| 16113 | masm.add32(Imm32(1), length); |
| 16114 | masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength())); |
| 16115 | masm.store32(length, Address(elementsTemp, |
| 16116 | ObjectElements::offsetOfInitializedLength())); |
| 16117 | |
| 16118 | if (ValueNeedsPostBarrier(lir->mir()->value())) { |
| 16119 | LiveRegisterSet regs = liveVolatileRegs(lir); |
| 16120 | regs.addUnchecked(length); |
| 16121 | emitElementPostWriteBarrier(lir->mir(), regs, obj, length, elementsTemp, |
| 16122 | ConstantOrRegister(value), |
| 16123 | /* indexDiff = */ -1); |
| 16124 | } |
| 16125 | } |
| 16126 | |
| 16127 | void CodeGenerator::visitArraySlice(LArraySlice* lir) { |
| 16128 | Register object = ToRegister(lir->object()); |
| 16129 | Register begin = ToRegister(lir->begin()); |
| 16130 | Register end = ToRegister(lir->end()); |
| 16131 | Register temp0 = ToRegister(lir->temp0()); |
| 16132 | Register temp1 = ToRegister(lir->temp1()); |
| 16133 | |
| 16134 | Label call, fail; |
| 16135 | |
| 16136 | Label bail; |
| 16137 | masm.branchArrayIsNotPacked(object, temp0, temp1, &bail); |
| 16138 | bailoutFrom(&bail, lir->snapshot()); |
| 16139 | |
| 16140 | // Try to allocate an object. |
| 16141 | TemplateObject templateObject(lir->mir()->templateObj()); |
| 16142 | masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(), |
| 16143 | &fail); |
| 16144 | |
| 16145 | masm.jump(&call); |
| 16146 | { |
| 16147 | masm.bind(&fail); |
| 16148 | masm.movePtr(ImmPtr(nullptr), temp0); |
| 16149 | } |
| 16150 | masm.bind(&call); |
| 16151 | |
| 16152 | pushArg(temp0); |
| 16153 | pushArg(end); |
| 16154 | pushArg(begin); |
| 16155 | pushArg(object); |
| 16156 | |
| 16157 | using Fn = |
| 16158 | JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); |
| 16159 | callVM<Fn, ArraySliceDense>(lir); |
| 16160 | } |
| 16161 | |
| 16162 | void CodeGenerator::visitArgumentsSlice(LArgumentsSlice* lir) { |
| 16163 | Register object = ToRegister(lir->object()); |
| 16164 | Register begin = ToRegister(lir->begin()); |
| 16165 | Register end = ToRegister(lir->end()); |
| 16166 | Register temp0 = ToRegister(lir->temp0()); |
| 16167 | Register temp1 = ToRegister(lir->temp1()); |
| 16168 | |
| 16169 | Label call, fail; |
| 16170 | |
| 16171 | // Try to allocate an object. |
| 16172 | TemplateObject templateObject(lir->mir()->templateObj()); |
| 16173 | masm.createGCObject(temp0, temp1, templateObject, lir->mir()->initialHeap(), |
| 16174 | &fail); |
| 16175 | |
| 16176 | masm.jump(&call); |
| 16177 | { |
| 16178 | masm.bind(&fail); |
| 16179 | masm.movePtr(ImmPtr(nullptr), temp0); |
| 16180 | } |
| 16181 | masm.bind(&call); |
| 16182 | |
| 16183 | pushArg(temp0); |
| 16184 | pushArg(end); |
| 16185 | pushArg(begin); |
| 16186 | pushArg(object); |
| 16187 | |
| 16188 | using Fn = |
| 16189 | JSObject* (*)(JSContext*, HandleObject, int32_t, int32_t, HandleObject); |
| 16190 | callVM<Fn, ArgumentsSliceDense>(lir); |
| 16191 | } |
| 16192 | |
| 16193 | #ifdef DEBUG1 |
| 16194 | void CodeGenerator::emitAssertArgumentsSliceBounds(const RegisterOrInt32& begin, |
| 16195 | const RegisterOrInt32& count, |
| 16196 | Register numActualArgs) { |
| 16197 | // |begin| must be positive or zero. |
| 16198 | if (begin.is<Register>()) { |
| 16199 | Label beginOk; |
| 16200 | masm.branch32(Assembler::GreaterThanOrEqual, begin.as<Register>(), Imm32(0), |
| 16201 | &beginOk); |
| 16202 | masm.assumeUnreachable("begin < 0"); |
| 16203 | masm.bind(&beginOk); |
| 16204 | } else { |
| 16205 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 16205); AnnotateMozCrashReason("MOZ_ASSERT" "(" "begin.as<int32_t>() >= 0" ")"); do { MOZ_CrashSequence(__null, 16205); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16206 | } |
| 16207 | |
| 16208 | // |count| must be positive or zero. |
| 16209 | if (count.is<Register>()) { |
| 16210 | Label countOk; |
| 16211 | masm.branch32(Assembler::GreaterThanOrEqual, count.as<Register>(), Imm32(0), |
| 16212 | &countOk); |
| 16213 | masm.assumeUnreachable("count < 0"); |
| 16214 | masm.bind(&countOk); |
| 16215 | } else { |
| 16216 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 16216); AnnotateMozCrashReason("MOZ_ASSERT" "(" "count.as<int32_t>() >= 0" ")"); do { MOZ_CrashSequence(__null, 16216); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16217 | } |
| 16218 | |
| 16219 | // |begin| must be less-or-equal to |numActualArgs|. |
| 16220 | Label argsBeginOk; |
| 16221 | if (begin.is<Register>()) { |
| 16222 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(), |
| 16223 | &argsBeginOk); |
| 16224 | } else { |
| 16225 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
| 16226 | Imm32(begin.as<int32_t>()), &argsBeginOk); |
| 16227 | } |
| 16228 | masm.assumeUnreachable("begin <= numActualArgs"); |
| 16229 | masm.bind(&argsBeginOk); |
| 16230 | |
| 16231 | // |count| must be less-or-equal to |numActualArgs|. |
| 16232 | Label argsCountOk; |
| 16233 | if (count.is<Register>()) { |
| 16234 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, count.as<Register>(), |
| 16235 | &argsCountOk); |
| 16236 | } else { |
| 16237 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
| 16238 | Imm32(count.as<int32_t>()), &argsCountOk); |
| 16239 | } |
| 16240 | masm.assumeUnreachable("count <= numActualArgs"); |
| 16241 | masm.bind(&argsCountOk); |
| 16242 | |
| 16243 | // |begin| and |count| must be preserved, but |numActualArgs| can be changed. |
| 16244 | // |
| 16245 | // Pre-condition: |count| <= |numActualArgs| |
| 16246 | // Condition to test: |begin + count| <= |numActualArgs| |
| 16247 | // Transform to: |begin| <= |numActualArgs - count| |
| 16248 | if (count.is<Register>()) { |
| 16249 | masm.subPtr(count.as<Register>(), numActualArgs); |
| 16250 | } else { |
| 16251 | masm.subPtr(Imm32(count.as<int32_t>()), numActualArgs); |
| 16252 | } |
| 16253 | |
| 16254 | // |begin + count| must be less-or-equal to |numActualArgs|. |
| 16255 | Label argsBeginCountOk; |
| 16256 | if (begin.is<Register>()) { |
| 16257 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, begin.as<Register>(), |
| 16258 | &argsBeginCountOk); |
| 16259 | } else { |
| 16260 | masm.branchPtr(Assembler::AboveOrEqual, numActualArgs, |
| 16261 | Imm32(begin.as<int32_t>()), &argsBeginCountOk); |
| 16262 | } |
| 16263 | masm.assumeUnreachable("begin + count <= numActualArgs"); |
| 16264 | masm.bind(&argsBeginCountOk); |
| 16265 | } |
| 16266 | #endif |
| 16267 | |
| 16268 | template <class ArgumentsSlice> |
| 16269 | void CodeGenerator::emitNewArray(ArgumentsSlice* lir, |
| 16270 | const RegisterOrInt32& count, Register output, |
| 16271 | Register temp) { |
| 16272 | using Fn = ArrayObject* (*)(JSContext*, int32_t); |
| 16273 | auto* ool = count.match( |
| 16274 | [&](Register count) { |
| 16275 | return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>( |
| 16276 | lir, ArgList(count), StoreRegisterTo(output)); |
| 16277 | }, |
| 16278 | [&](int32_t count) { |
| 16279 | return oolCallVM<Fn, NewArrayObjectEnsureDenseInitLength>( |
| 16280 | lir, ArgList(Imm32(count)), StoreRegisterTo(output)); |
| 16281 | }); |
| 16282 | |
| 16283 | TemplateObject templateObject(lir->mir()->templateObj()); |
| 16284 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 16284); AnnotateMozCrashReason("MOZ_ASSERT" "(" "templateObject.isArrayObject()" ")"); do { MOZ_CrashSequence(__null, 16284); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16285 | |
| 16286 | auto templateNativeObj = templateObject.asTemplateNativeObject(); |
| 16287 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 16287); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "templateNativeObj.getArrayLength() == 0" ")" ); do { MOZ_CrashSequence(__null, 16287); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 16288 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 16288); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "templateNativeObj.getDenseInitializedLength() == 0" ")"); do { MOZ_CrashSequence(__null, 16288); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16289 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 16289); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!templateNativeObj.hasDynamicElements()" ")" ); do { MOZ_CrashSequence(__null, 16289); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 16290 | |
| 16291 | // Check array capacity. Call into the VM if the template object's capacity |
| 16292 | // is too small. |
| 16293 | bool tryAllocate = count.match( |
| 16294 | [&](Register count) { |
| 16295 | masm.branch32(Assembler::Above, count, |
| 16296 | Imm32(templateNativeObj.getDenseCapacity()), |
| 16297 | ool->entry()); |
| 16298 | return true; |
| 16299 | }, |
| 16300 | [&](int32_t count) { |
| 16301 | 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", "./../../../../js/src/jit/CodeGenerator.cpp", 16301); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "count >= 0" ")"); do { MOZ_CrashSequence (__null, 16301); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 16302 | if (uint32_t(count) > templateNativeObj.getDenseCapacity()) { |
| 16303 | masm.jump(ool->entry()); |
| 16304 | return false; |
| 16305 | } |
| 16306 | return true; |
| 16307 | }); |
| 16308 | |
| 16309 | if (tryAllocate) { |
| 16310 | // Try to allocate an object. |
| 16311 | masm.createGCObject(output, temp, templateObject, lir->mir()->initialHeap(), |
| 16312 | ool->entry()); |
| 16313 | |
| 16314 | auto setInitializedLengthAndLength = [&](auto count) { |
| 16315 | const int elementsOffset = NativeObject::offsetOfFixedElements(); |
| 16316 | |
| 16317 | // Update initialized length. |
| 16318 | Address initLength( |
| 16319 | output, elementsOffset + ObjectElements::offsetOfInitializedLength()); |
| 16320 | masm.store32(count, initLength); |
| 16321 | |
| 16322 | // Update length. |
| 16323 | Address length(output, elementsOffset + ObjectElements::offsetOfLength()); |
| 16324 | masm.store32(count, length); |
| 16325 | }; |
| 16326 | |
| 16327 | // The array object was successfully created. Set the length and initialized |
| 16328 | // length and then proceed to fill the elements. |
| 16329 | count.match([&](Register count) { setInitializedLengthAndLength(count); }, |
| 16330 | [&](int32_t count) { |
| 16331 | if (count > 0) { |
| 16332 | setInitializedLengthAndLength(Imm32(count)); |
| 16333 | } |
| 16334 | }); |
| 16335 | } |
| 16336 | |
| 16337 | masm.bind(ool->rejoin()); |
| 16338 | } |
| 16339 | |
| 16340 | void CodeGenerator::visitFrameArgumentsSlice(LFrameArgumentsSlice* lir) { |
| 16341 | Register begin = ToRegister(lir->begin()); |
| 16342 | Register count = ToRegister(lir->count()); |
| 16343 | Register temp = ToRegister(lir->temp0()); |
| 16344 | Register output = ToRegister(lir->output()); |
| 16345 | |
| 16346 | #ifdef DEBUG1 |
| 16347 | masm.loadNumActualArgs(FramePointer, temp); |
| 16348 | emitAssertArgumentsSliceBounds(RegisterOrInt32(begin), RegisterOrInt32(count), |
| 16349 | temp); |
| 16350 | #endif |
| 16351 | |
| 16352 | emitNewArray(lir, RegisterOrInt32(count), output, temp); |
| 16353 | |
| 16354 | Label done; |
| 16355 | masm.branch32(Assembler::Equal, count, Imm32(0), &done); |
| 16356 | { |
| 16357 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 16358 | allRegs.take(begin); |
| 16359 | allRegs.take(count); |
| 16360 | allRegs.take(temp); |
| 16361 | allRegs.take(output); |
| 16362 | |
| 16363 | ValueOperand value = allRegs.takeAnyValue(); |
| 16364 | |
| 16365 | LiveRegisterSet liveRegs; |
| 16366 | liveRegs.add(output); |
| 16367 | liveRegs.add(begin); |
| 16368 | liveRegs.add(value); |
| 16369 | |
| 16370 | masm.PushRegsInMask(liveRegs); |
| 16371 | |
| 16372 | // Initialize all elements. |
| 16373 | |
| 16374 | Register elements = output; |
| 16375 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
| 16376 | |
| 16377 | Register argIndex = begin; |
| 16378 | |
| 16379 | Register index = temp; |
| 16380 | masm.move32(Imm32(0), index); |
| 16381 | |
| 16382 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
| 16383 | BaseValueIndex argPtr(FramePointer, argIndex, argvOffset); |
| 16384 | |
| 16385 | Label loop; |
| 16386 | masm.bind(&loop); |
| 16387 | |
| 16388 | masm.loadValue(argPtr, value); |
| 16389 | |
| 16390 | // We don't need a pre-barrier, because the element at |index| is guaranteed |
| 16391 | // to be a non-GC thing (either uninitialized memory or the magic hole |
| 16392 | // value). |
| 16393 | masm.storeValue(value, BaseObjectElementIndex(elements, index)); |
| 16394 | |
| 16395 | masm.add32(Imm32(1), index); |
| 16396 | masm.add32(Imm32(1), argIndex); |
| 16397 | |
| 16398 | masm.branch32(Assembler::LessThan, index, count, &loop); |
| 16399 | |
| 16400 | masm.PopRegsInMask(liveRegs); |
| 16401 | |
| 16402 | // Emit a post-write barrier if |output| is tenured. |
| 16403 | // |
| 16404 | // We expect that |output| is nursery allocated, so it isn't worth the |
| 16405 | // trouble to check if no frame argument is a nursery thing, which would |
| 16406 | // allow to omit the post-write barrier. |
| 16407 | masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done); |
| 16408 | |
| 16409 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 16410 | volatileRegs.takeUnchecked(temp); |
| 16411 | if (output.volatile_()) { |
| 16412 | volatileRegs.addUnchecked(output); |
| 16413 | } |
| 16414 | |
| 16415 | masm.PushRegsInMask(volatileRegs); |
| 16416 | emitPostWriteBarrier(output); |
| 16417 | masm.PopRegsInMask(volatileRegs); |
| 16418 | } |
| 16419 | masm.bind(&done); |
| 16420 | } |
| 16421 | |
| 16422 | CodeGenerator::RegisterOrInt32 CodeGenerator::ToRegisterOrInt32( |
| 16423 | const LAllocation* allocation) { |
| 16424 | if (allocation->isConstant()) { |
| 16425 | return RegisterOrInt32(allocation->toConstant()->toInt32()); |
| 16426 | } |
| 16427 | return RegisterOrInt32(ToRegister(allocation)); |
| 16428 | } |
| 16429 | |
| 16430 | void CodeGenerator::visitInlineArgumentsSlice(LInlineArgumentsSlice* lir) { |
| 16431 | RegisterOrInt32 begin = ToRegisterOrInt32(lir->begin()); |
| 16432 | RegisterOrInt32 count = ToRegisterOrInt32(lir->count()); |
| 16433 | Register temp = ToRegister(lir->temp()); |
| 16434 | Register output = ToRegister(lir->output()); |
| 16435 | |
| 16436 | uint32_t numActuals = lir->mir()->numActuals(); |
| 16437 | |
| 16438 | #ifdef DEBUG1 |
| 16439 | masm.move32(Imm32(numActuals), temp); |
| 16440 | |
| 16441 | emitAssertArgumentsSliceBounds(begin, count, temp); |
| 16442 | #endif |
| 16443 | |
| 16444 | emitNewArray(lir, count, output, temp); |
| 16445 | |
| 16446 | // We're done if there are no actual arguments. |
| 16447 | if (numActuals == 0) { |
| 16448 | return; |
| 16449 | } |
| 16450 | |
| 16451 | // Check if any arguments have to be copied. |
| 16452 | Label done; |
| 16453 | if (count.is<Register>()) { |
| 16454 | masm.branch32(Assembler::Equal, count.as<Register>(), Imm32(0), &done); |
| 16455 | } else if (count.as<int32_t>() == 0) { |
| 16456 | return; |
| 16457 | } |
| 16458 | |
| 16459 | auto getArg = [&](uint32_t i) { |
| 16460 | return toConstantOrRegister(lir, LInlineArgumentsSlice::ArgIndex(i), |
| 16461 | lir->mir()->getArg(i)->type()); |
| 16462 | }; |
| 16463 | |
| 16464 | auto storeArg = [&](uint32_t i, auto dest) { |
| 16465 | // We don't need a pre-barrier because the element at |index| is guaranteed |
| 16466 | // to be a non-GC thing (either uninitialized memory or the magic hole |
| 16467 | // value). |
| 16468 | masm.storeConstantOrRegister(getArg(i), dest); |
| 16469 | }; |
| 16470 | |
| 16471 | // Initialize all elements. |
| 16472 | if (numActuals == 1) { |
| 16473 | // There's exactly one argument. We've checked that |count| is non-zero, |
| 16474 | // which implies that |begin| must be zero. |
| 16475 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 16475); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "begin.as<int32_t>() == 0" ")"); do { MOZ_CrashSequence(__null, 16475); __attribute__((nomerge)) :: abort(); } while (false); } } while (false); } } while (false ); |
| 16476 | |
| 16477 | Register elements = temp; |
| 16478 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
| 16479 | |
| 16480 | storeArg(0, Address(elements, 0)); |
| 16481 | } else if (begin.is<Register>()) { |
| 16482 | // There is more than one argument and |begin| isn't a compile-time |
| 16483 | // constant. Iterate through 0..numActuals to search for |begin| and then |
| 16484 | // start copying |count| arguments from that index. |
| 16485 | |
| 16486 | LiveGeneralRegisterSet liveRegs; |
| 16487 | liveRegs.add(output); |
| 16488 | liveRegs.add(begin.as<Register>()); |
| 16489 | |
| 16490 | masm.PushRegsInMask(liveRegs); |
| 16491 | |
| 16492 | Register elements = output; |
| 16493 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
| 16494 | |
| 16495 | Register argIndex = begin.as<Register>(); |
| 16496 | |
| 16497 | Register index = temp; |
| 16498 | masm.move32(Imm32(0), index); |
| 16499 | |
| 16500 | Label doneLoop; |
| 16501 | for (uint32_t i = 0; i < numActuals; ++i) { |
| 16502 | Label next; |
| 16503 | masm.branch32(Assembler::NotEqual, argIndex, Imm32(i), &next); |
| 16504 | |
| 16505 | storeArg(i, BaseObjectElementIndex(elements, index)); |
| 16506 | |
| 16507 | masm.add32(Imm32(1), index); |
| 16508 | masm.add32(Imm32(1), argIndex); |
| 16509 | |
| 16510 | if (count.is<Register>()) { |
| 16511 | masm.branch32(Assembler::GreaterThanOrEqual, index, |
| 16512 | count.as<Register>(), &doneLoop); |
| 16513 | } else { |
| 16514 | masm.branch32(Assembler::GreaterThanOrEqual, index, |
| 16515 | Imm32(count.as<int32_t>()), &doneLoop); |
| 16516 | } |
| 16517 | |
| 16518 | masm.bind(&next); |
| 16519 | } |
| 16520 | masm.bind(&doneLoop); |
| 16521 | |
| 16522 | masm.PopRegsInMask(liveRegs); |
| 16523 | } else { |
| 16524 | // There is more than one argument and |begin| is a compile-time constant. |
| 16525 | |
| 16526 | Register elements = temp; |
| 16527 | masm.loadPtr(Address(output, NativeObject::offsetOfElements()), elements); |
| 16528 | |
| 16529 | int32_t argIndex = begin.as<int32_t>(); |
| 16530 | |
| 16531 | int32_t index = 0; |
| 16532 | |
| 16533 | Label doneLoop; |
| 16534 | for (uint32_t i = argIndex; i < numActuals; ++i) { |
| 16535 | storeArg(i, Address(elements, index * sizeof(Value))); |
| 16536 | |
| 16537 | index += 1; |
| 16538 | |
| 16539 | if (count.is<Register>()) { |
| 16540 | masm.branch32(Assembler::LessThanOrEqual, count.as<Register>(), |
| 16541 | Imm32(index), &doneLoop); |
| 16542 | } else { |
| 16543 | if (index >= count.as<int32_t>()) { |
| 16544 | break; |
| 16545 | } |
| 16546 | } |
| 16547 | } |
| 16548 | masm.bind(&doneLoop); |
| 16549 | } |
| 16550 | |
| 16551 | // Determine if we have to emit post-write barrier. |
| 16552 | // |
| 16553 | // If either |begin| or |count| is a constant, use their value directly. |
| 16554 | // Otherwise assume we copy all inline arguments from 0..numActuals. |
| 16555 | bool postWriteBarrier = false; |
| 16556 | uint32_t actualBegin = begin.match([](Register) { return 0; }, |
| 16557 | [](int32_t value) { return value; }); |
| 16558 | uint32_t actualCount = |
| 16559 | count.match([=](Register) { return numActuals; }, |
| 16560 | [](int32_t value) -> uint32_t { return value; }); |
| 16561 | for (uint32_t i = 0; i < actualCount; ++i) { |
| 16562 | ConstantOrRegister arg = getArg(actualBegin + i); |
| 16563 | if (arg.constant()) { |
| 16564 | Value v = arg.value(); |
| 16565 | if (v.isGCThing() && IsInsideNursery(v.toGCThing())) { |
| 16566 | postWriteBarrier = true; |
| 16567 | } |
| 16568 | } else { |
| 16569 | MIRType type = arg.reg().type(); |
| 16570 | if (type == MIRType::Value || NeedsPostBarrier(type)) { |
| 16571 | postWriteBarrier = true; |
| 16572 | } |
| 16573 | } |
| 16574 | } |
| 16575 | |
| 16576 | // Emit a post-write barrier if |output| is tenured and we couldn't |
| 16577 | // determine at compile-time that no barrier is needed. |
| 16578 | if (postWriteBarrier) { |
| 16579 | masm.branchPtrInNurseryChunk(Assembler::Equal, output, temp, &done); |
| 16580 | |
| 16581 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 16582 | volatileRegs.takeUnchecked(temp); |
| 16583 | if (output.volatile_()) { |
| 16584 | volatileRegs.addUnchecked(output); |
| 16585 | } |
| 16586 | |
| 16587 | masm.PushRegsInMask(volatileRegs); |
| 16588 | emitPostWriteBarrier(output); |
| 16589 | masm.PopRegsInMask(volatileRegs); |
| 16590 | } |
| 16591 | |
| 16592 | masm.bind(&done); |
| 16593 | } |
| 16594 | |
| 16595 | void CodeGenerator::visitNormalizeSliceTerm(LNormalizeSliceTerm* lir) { |
| 16596 | Register value = ToRegister(lir->value()); |
| 16597 | Register length = ToRegister(lir->length()); |
| 16598 | Register output = ToRegister(lir->output()); |
| 16599 | |
| 16600 | masm.move32(value, output); |
| 16601 | |
| 16602 | Label positive; |
| 16603 | masm.branch32(Assembler::GreaterThanOrEqual, value, Imm32(0), &positive); |
| 16604 | |
| 16605 | Label done; |
| 16606 | masm.add32(length, output); |
| 16607 | masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(0), &done); |
| 16608 | masm.move32(Imm32(0), output); |
| 16609 | masm.jump(&done); |
| 16610 | |
| 16611 | masm.bind(&positive); |
| 16612 | masm.cmp32Move32(Assembler::LessThan, length, value, length, output); |
| 16613 | |
| 16614 | masm.bind(&done); |
| 16615 | } |
| 16616 | |
| 16617 | void CodeGenerator::visitArrayJoin(LArrayJoin* lir) { |
| 16618 | Label skipCall; |
| 16619 | |
| 16620 | Register output = ToRegister(lir->output()); |
| 16621 | Register sep = ToRegister(lir->separator()); |
| 16622 | Register array = ToRegister(lir->array()); |
| 16623 | Register temp = ToRegister(lir->temp0()); |
| 16624 | |
| 16625 | // Fast path for simple length <= 1 cases. |
| 16626 | { |
| 16627 | masm.loadPtr(Address(array, NativeObject::offsetOfElements()), temp); |
| 16628 | Address length(temp, ObjectElements::offsetOfLength()); |
| 16629 | Address initLength(temp, ObjectElements::offsetOfInitializedLength()); |
| 16630 | |
| 16631 | // Check for length == 0 |
| 16632 | Label notEmpty; |
| 16633 | masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬Empty); |
| 16634 | const JSAtomState& names = gen->runtime->names(); |
| 16635 | masm.movePtr(ImmGCPtr(names.empty_), output); |
| 16636 | masm.jump(&skipCall); |
| 16637 | |
| 16638 | masm.bind(¬Empty); |
| 16639 | Label notSingleString; |
| 16640 | // Check for length == 1, initializedLength >= 1, arr[0].isString() |
| 16641 | masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleString); |
| 16642 | masm.branch32(Assembler::LessThan, initLength, Imm32(1), ¬SingleString); |
| 16643 | |
| 16644 | Address elem0(temp, 0); |
| 16645 | masm.branchTestString(Assembler::NotEqual, elem0, ¬SingleString); |
| 16646 | |
| 16647 | // At this point, 'output' can be used as a scratch register, since we're |
| 16648 | // guaranteed to succeed. |
| 16649 | masm.unboxString(elem0, output); |
| 16650 | masm.jump(&skipCall); |
| 16651 | masm.bind(¬SingleString); |
| 16652 | } |
| 16653 | |
| 16654 | pushArg(sep); |
| 16655 | pushArg(array); |
| 16656 | |
| 16657 | using Fn = JSString* (*)(JSContext*, HandleObject, HandleString); |
| 16658 | callVM<Fn, jit::ArrayJoin>(lir); |
| 16659 | masm.bind(&skipCall); |
| 16660 | } |
| 16661 | |
| 16662 | void CodeGenerator::visitObjectKeys(LObjectKeys* lir) { |
| 16663 | Register object = ToRegister(lir->object()); |
| 16664 | |
| 16665 | pushArg(object); |
| 16666 | |
| 16667 | using Fn = JSObject* (*)(JSContext*, HandleObject); |
| 16668 | callVM<Fn, jit::ObjectKeys>(lir); |
| 16669 | } |
| 16670 | |
| 16671 | void CodeGenerator::visitGetIteratorCache(LGetIteratorCache* lir) { |
| 16672 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 16673 | TypedOrValueRegister val = |
| 16674 | toConstantOrRegister(lir, LGetIteratorCache::ValueIndex, |
| 16675 | lir->mir()->value()->type()) |
| 16676 | .reg(); |
| 16677 | Register output = ToRegister(lir->output()); |
| 16678 | Register temp0 = ToRegister(lir->temp0()); |
| 16679 | Register temp1 = ToRegister(lir->temp1()); |
| 16680 | |
| 16681 | IonGetIteratorIC ic(liveRegs, val, output, temp0, temp1); |
| 16682 | addIC(lir, allocateIC(ic)); |
| 16683 | } |
| 16684 | |
| 16685 | void CodeGenerator::visitOptimizeSpreadCallCache( |
| 16686 | LOptimizeSpreadCallCache* lir) { |
| 16687 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 16688 | ValueOperand val = ToValue(lir->value()); |
| 16689 | ValueOperand output = ToOutValue(lir); |
| 16690 | Register temp = ToRegister(lir->temp0()); |
| 16691 | |
| 16692 | IonOptimizeSpreadCallIC ic(liveRegs, val, output, temp); |
| 16693 | addIC(lir, allocateIC(ic)); |
| 16694 | } |
| 16695 | |
| 16696 | void CodeGenerator::visitCloseIterCache(LCloseIterCache* lir) { |
| 16697 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 16698 | Register iter = ToRegister(lir->iter()); |
| 16699 | Register temp = ToRegister(lir->temp0()); |
| 16700 | CompletionKind kind = CompletionKind(lir->mir()->completionKind()); |
| 16701 | |
| 16702 | IonCloseIterIC ic(liveRegs, iter, temp, kind); |
| 16703 | addIC(lir, allocateIC(ic)); |
| 16704 | } |
| 16705 | |
| 16706 | void CodeGenerator::visitOptimizeGetIteratorCache( |
| 16707 | LOptimizeGetIteratorCache* lir) { |
| 16708 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 16709 | ValueOperand val = ToValue(lir->value()); |
| 16710 | Register output = ToRegister(lir->output()); |
| 16711 | Register temp = ToRegister(lir->temp0()); |
| 16712 | |
| 16713 | IonOptimizeGetIteratorIC ic(liveRegs, val, output, temp); |
| 16714 | addIC(lir, allocateIC(ic)); |
| 16715 | } |
| 16716 | |
| 16717 | void CodeGenerator::visitIteratorMore(LIteratorMore* lir) { |
| 16718 | const Register obj = ToRegister(lir->iterator()); |
| 16719 | const ValueOperand output = ToOutValue(lir); |
| 16720 | const Register temp = ToRegister(lir->temp0()); |
| 16721 | |
| 16722 | masm.iteratorMore(obj, output, temp); |
| 16723 | } |
| 16724 | |
| 16725 | void CodeGenerator::visitIteratorLength(LIteratorLength* lir) { |
| 16726 | Register obj = ToRegister(lir->iter()); |
| 16727 | Register output = ToRegister(lir->output()); |
| 16728 | masm.iteratorLength(obj, output); |
| 16729 | } |
| 16730 | |
| 16731 | void CodeGenerator::visitLoadIteratorElement(LLoadIteratorElement* lir) { |
| 16732 | Register obj = ToRegister(lir->iter()); |
| 16733 | Register output = ToRegister(lir->output()); |
| 16734 | if (lir->index()->isConstant()) { |
| 16735 | int32_t index = ToInt32(lir->index()); |
| 16736 | masm.iteratorLoadElement(obj, index, output); |
| 16737 | } else { |
| 16738 | Register index = ToRegister(lir->index()); |
| 16739 | masm.iteratorLoadElement(obj, index, output); |
| 16740 | } |
| 16741 | } |
| 16742 | |
| 16743 | void CodeGenerator::visitIsNoIterAndBranch(LIsNoIterAndBranch* lir) { |
| 16744 | ValueOperand input = ToValue(lir->input()); |
| 16745 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
| 16746 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
| 16747 | |
| 16748 | masm.branchTestMagicValue(Assembler::Equal, input, JS_NO_ITER_VALUE, ifTrue); |
| 16749 | |
| 16750 | if (!isNextBlock(lir->ifFalse()->lir())) { |
| 16751 | masm.jump(ifFalse); |
| 16752 | } |
| 16753 | } |
| 16754 | |
| 16755 | void CodeGenerator::visitIteratorEnd(LIteratorEnd* lir) { |
| 16756 | const Register obj = ToRegister(lir->iterator()); |
| 16757 | const Register temp0 = ToRegister(lir->temp0()); |
| 16758 | const Register temp1 = ToRegister(lir->temp1()); |
| 16759 | const Register temp2 = ToRegister(lir->temp2()); |
| 16760 | |
| 16761 | masm.iteratorClose(obj, temp0, temp1, temp2); |
| 16762 | } |
| 16763 | |
| 16764 | void CodeGenerator::visitArgumentsLength(LArgumentsLength* lir) { |
| 16765 | // read number of actual arguments from the JS frame. |
| 16766 | Register argc = ToRegister(lir->output()); |
| 16767 | masm.loadNumActualArgs(FramePointer, argc); |
| 16768 | } |
| 16769 | |
| 16770 | void CodeGenerator::visitGetFrameArgument(LGetFrameArgument* lir) { |
| 16771 | ValueOperand result = ToOutValue(lir); |
| 16772 | const LAllocation* index = lir->index(); |
| 16773 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
| 16774 | |
| 16775 | // This instruction is used to access actual arguments and formal arguments. |
| 16776 | // The number of Values on the stack is |max(numFormals, numActuals)|, so we |
| 16777 | // assert |index < numFormals || index < numActuals| in debug builds. |
| 16778 | DebugOnly<size_t> numFormals = gen->outerInfo().script()->function()->nargs(); |
| 16779 | |
| 16780 | if (index->isConstant()) { |
| 16781 | int32_t i = index->toConstant()->toInt32(); |
| 16782 | #ifdef DEBUG1 |
| 16783 | if (uint32_t(i) >= numFormals) { |
| 16784 | Label ok; |
| 16785 | Register argc = result.scratchReg(); |
| 16786 | masm.loadNumActualArgs(FramePointer, argc); |
| 16787 | masm.branch32(Assembler::Above, argc, Imm32(i), &ok); |
| 16788 | masm.assumeUnreachable("Invalid argument index"); |
| 16789 | masm.bind(&ok); |
| 16790 | } |
| 16791 | #endif |
| 16792 | Address argPtr(FramePointer, sizeof(Value) * i + argvOffset); |
| 16793 | masm.loadValue(argPtr, result); |
| 16794 | } else { |
| 16795 | Register i = ToRegister(index); |
| 16796 | #ifdef DEBUG1 |
| 16797 | Label ok; |
| 16798 | Register argc = result.scratchReg(); |
| 16799 | masm.branch32(Assembler::Below, i, Imm32(numFormals), &ok); |
| 16800 | masm.loadNumActualArgs(FramePointer, argc); |
| 16801 | masm.branch32(Assembler::Above, argc, i, &ok); |
| 16802 | masm.assumeUnreachable("Invalid argument index"); |
| 16803 | masm.bind(&ok); |
| 16804 | #endif |
| 16805 | BaseValueIndex argPtr(FramePointer, i, argvOffset); |
| 16806 | masm.loadValue(argPtr, result); |
| 16807 | } |
| 16808 | } |
| 16809 | |
| 16810 | void CodeGenerator::visitGetFrameArgumentHole(LGetFrameArgumentHole* lir) { |
| 16811 | ValueOperand result = ToOutValue(lir); |
| 16812 | Register index = ToRegister(lir->index()); |
| 16813 | Register length = ToRegister(lir->length()); |
| 16814 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0()); |
| 16815 | size_t argvOffset = JitFrameLayout::offsetOfActualArgs(); |
| 16816 | |
| 16817 | Label outOfBounds, done; |
| 16818 | masm.spectreBoundsCheck32(index, length, spectreTemp, &outOfBounds); |
| 16819 | |
| 16820 | BaseValueIndex argPtr(FramePointer, index, argvOffset); |
| 16821 | masm.loadValue(argPtr, result); |
| 16822 | masm.jump(&done); |
| 16823 | |
| 16824 | masm.bind(&outOfBounds); |
| 16825 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 16826 | masm.moveValue(UndefinedValue(), result); |
| 16827 | |
| 16828 | masm.bind(&done); |
| 16829 | } |
| 16830 | |
| 16831 | void CodeGenerator::visitRest(LRest* lir) { |
| 16832 | Register numActuals = ToRegister(lir->numActuals()); |
| 16833 | Register temp0 = ToRegister(lir->temp0()); |
| 16834 | Register temp1 = ToRegister(lir->temp1()); |
| 16835 | Register temp2 = ToRegister(lir->temp2()); |
| 16836 | Register temp3 = ToRegister(lir->temp3()); |
| 16837 | unsigned numFormals = lir->mir()->numFormals(); |
| 16838 | |
| 16839 | // In baseline, DoRestFallback calls into NewArray to allocate the rest array. |
| 16840 | // If the length is 0, NewArray guesses a good capacity for it. We don't want |
| 16841 | // a smaller capacity in Ion, because that can lead to bailout loops. |
| 16842 | constexpr uint32_t arrayCapacity = 6; |
| 16843 | static_assert(GuessArrayGCKind(0) == GuessArrayGCKind(arrayCapacity)); |
| 16844 | |
| 16845 | if (Shape* shape = lir->mir()->shape()) { |
| 16846 | uint32_t arrayLength = 0; |
| 16847 | gc::AllocKind allocKind = GuessArrayGCKind(arrayCapacity); |
| 16848 | MOZ_ASSERT(gc::GetObjectFinalizeKind(&ArrayObject::class_) ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(& ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" , "./../../../../js/src/jit/CodeGenerator.cpp", 16849); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" ")"); do { MOZ_CrashSequence(__null, 16849); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 16849 | gc::FinalizeKind::None)do { static_assert( mozilla::detail::AssertionConditionType< decltype(gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(gc::GetObjectFinalizeKind(& ArrayObject::class_) == gc::FinalizeKind::None))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" , "./../../../../js/src/jit/CodeGenerator.cpp", 16849); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gc::GetObjectFinalizeKind(&ArrayObject::class_) == gc::FinalizeKind::None" ")"); do { MOZ_CrashSequence(__null, 16849); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16850 | MOZ_ASSERT(!IsFinalizedKind(allocKind))do { static_assert( mozilla::detail::AssertionConditionType< decltype(!IsFinalizedKind(allocKind))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!IsFinalizedKind(allocKind)) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!IsFinalizedKind(allocKind)" , "./../../../../js/src/jit/CodeGenerator.cpp", 16850); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!IsFinalizedKind(allocKind)" ")"); do { MOZ_CrashSequence (__null, 16850); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 16851 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 16852); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" ")"); do { MOZ_CrashSequence(__null, 16852); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 16852 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 16852); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GetGCKindSlots(allocKind) == arrayCapacity + ObjectElements::VALUES_PER_HEADER" ")"); do { MOZ_CrashSequence(__null, 16852); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 16853 | |
| 16854 | Label joinAlloc, failAlloc; |
| 16855 | masm.movePtr(ImmGCPtr(shape), temp0); |
| 16856 | masm.createArrayWithFixedElements(temp2, temp0, temp1, InvalidReg, |
| 16857 | arrayLength, arrayCapacity, 0, 0, |
| 16858 | allocKind, gc::Heap::Default, &failAlloc); |
| 16859 | masm.jump(&joinAlloc); |
| 16860 | { |
| 16861 | masm.bind(&failAlloc); |
| 16862 | masm.movePtr(ImmPtr(nullptr), temp2); |
| 16863 | } |
| 16864 | masm.bind(&joinAlloc); |
| 16865 | } else { |
| 16866 | masm.movePtr(ImmPtr(nullptr), temp2); |
| 16867 | } |
| 16868 | |
| 16869 | // Set temp1 to the address of the first actual argument. |
| 16870 | size_t actualsOffset = JitFrameLayout::offsetOfActualArgs(); |
| 16871 | masm.computeEffectiveAddress(Address(FramePointer, actualsOffset), temp1); |
| 16872 | |
| 16873 | // Compute array length: max(numActuals - numFormals, 0). |
| 16874 | Register lengthReg; |
| 16875 | if (numFormals) { |
| 16876 | lengthReg = temp0; |
| 16877 | Label emptyLength, joinLength; |
| 16878 | masm.branch32(Assembler::LessThanOrEqual, numActuals, Imm32(numFormals), |
| 16879 | &emptyLength); |
| 16880 | { |
| 16881 | masm.move32(numActuals, lengthReg); |
| 16882 | masm.sub32(Imm32(numFormals), lengthReg); |
| 16883 | |
| 16884 | // Skip formal arguments. |
| 16885 | masm.addPtr(Imm32(sizeof(Value) * numFormals), temp1); |
| 16886 | |
| 16887 | masm.jump(&joinLength); |
| 16888 | } |
| 16889 | masm.bind(&emptyLength); |
| 16890 | { |
| 16891 | masm.move32(Imm32(0), lengthReg); |
| 16892 | |
| 16893 | // Leave temp1 pointed to the start of actuals() when the rest-array |
| 16894 | // length is zero. We don't use |actuals() + numFormals| because |
| 16895 | // |numFormals| can be any non-negative int32 value when this MRest was |
| 16896 | // created from scalar replacement optimizations. And it seems |
| 16897 | // questionable to compute a Value* pointer which points to who knows |
| 16898 | // where. |
| 16899 | } |
| 16900 | masm.bind(&joinLength); |
| 16901 | } else { |
| 16902 | // Use numActuals directly when there are no formals. |
| 16903 | lengthReg = numActuals; |
| 16904 | } |
| 16905 | |
| 16906 | // Try to initialize the array elements. |
| 16907 | Label vmCall, done; |
| 16908 | if (lir->mir()->shape()) { |
| 16909 | // Call into C++ if we failed to allocate an array or there are more than |
| 16910 | // |arrayCapacity| elements. |
| 16911 | masm.branchTestPtr(Assembler::Zero, temp2, temp2, &vmCall); |
| 16912 | masm.branch32(Assembler::Above, lengthReg, Imm32(arrayCapacity), &vmCall); |
| 16913 | |
| 16914 | // The array must be nursery allocated so no post barrier is needed. |
| 16915 | #ifdef DEBUG1 |
| 16916 | Label ok; |
| 16917 | masm.branchPtrInNurseryChunk(Assembler::Equal, temp2, temp3, &ok); |
| 16918 | masm.assumeUnreachable("Unexpected tenured object for LRest"); |
| 16919 | masm.bind(&ok); |
| 16920 | #endif |
| 16921 | |
| 16922 | Label nonZeroLength; |
| 16923 | masm.branch32(Assembler::NotEqual, lengthReg, Imm32(0), &nonZeroLength); |
| 16924 | masm.movePtr(temp2, ReturnReg); |
| 16925 | masm.jump(&done); |
| 16926 | masm.bind(&nonZeroLength); |
| 16927 | |
| 16928 | // Store length and initializedLength. |
| 16929 | Register elements = temp3; |
| 16930 | masm.loadPtr(Address(temp2, NativeObject::offsetOfElements()), elements); |
| 16931 | Address lengthAddr(elements, ObjectElements::offsetOfLength()); |
| 16932 | Address initLengthAddr(elements, |
| 16933 | ObjectElements::offsetOfInitializedLength()); |
| 16934 | masm.store32(lengthReg, lengthAddr); |
| 16935 | masm.store32(lengthReg, initLengthAddr); |
| 16936 | |
| 16937 | masm.push(temp2); // Spill result to free up register. |
| 16938 | |
| 16939 | Register end = temp0; |
| 16940 | Register args = temp1; |
| 16941 | Register scratch = temp2; |
| 16942 | masm.computeEffectiveAddress(BaseObjectElementIndex(elements, lengthReg), |
| 16943 | end); |
| 16944 | |
| 16945 | Label loop; |
| 16946 | masm.bind(&loop); |
| 16947 | masm.storeValue(Address(args, 0), Address(elements, 0), scratch); |
| 16948 | masm.addPtr(Imm32(sizeof(Value)), args); |
| 16949 | masm.addPtr(Imm32(sizeof(Value)), elements); |
| 16950 | masm.branchPtr(Assembler::Below, elements, end, &loop); |
| 16951 | |
| 16952 | // Pop result |
| 16953 | masm.pop(ReturnReg); |
| 16954 | masm.jump(&done); |
| 16955 | } |
| 16956 | |
| 16957 | masm.bind(&vmCall); |
| 16958 | |
| 16959 | pushArg(temp2); |
| 16960 | pushArg(temp1); |
| 16961 | pushArg(lengthReg); |
| 16962 | |
| 16963 | using Fn = |
| 16964 | ArrayObject* (*)(JSContext*, uint32_t, Value*, Handle<ArrayObject*>); |
| 16965 | callVM<Fn, InitRestParameter>(lir); |
| 16966 | |
| 16967 | masm.bind(&done); |
| 16968 | } |
| 16969 | |
| 16970 | // Create a stackmap from the given safepoint, with the structure: |
| 16971 | // |
| 16972 | // <reg dump, if any> |
| 16973 | // | ++ <body (general spill)> |
| 16974 | // | | ++ <space for Frame> |
| 16975 | // | | ++ <inbound args> |
| 16976 | // | | | |
| 16977 | // Lowest Addr Highest Addr |
| 16978 | // | |
| 16979 | // framePushedAtStackMapBase |
| 16980 | // |
| 16981 | // The caller owns the resulting stackmap. This assumes a grow-down stack. |
| 16982 | // |
| 16983 | // For non-debug builds, if the stackmap would contain no pointers, no |
| 16984 | // stackmap is created, and nullptr is returned. For a debug build, a |
| 16985 | // stackmap is always created and returned. |
| 16986 | // |
| 16987 | // Depending on the type of safepoint, the stackmap may need to account for |
| 16988 | // spilled registers. WasmSafepointKind::LirCall corresponds to LIR nodes where |
| 16989 | // isCall() == true, for which the register allocator will spill/restore all |
| 16990 | // live registers at the LIR level - in this case, the LSafepoint sees only live |
| 16991 | // values on the stack, never in registers. WasmSafepointKind::CodegenCall, on |
| 16992 | // the other hand, is for LIR nodes which may manually spill/restore live |
| 16993 | // registers in codegen, in which case the stackmap must account for this. Traps |
| 16994 | // also require tracking of live registers, but spilling is handled by the trap |
| 16995 | // mechanism. |
| 16996 | static bool CreateStackMapFromLSafepoint(LSafepoint& safepoint, |
| 16997 | const RegisterOffsets& trapExitLayout, |
| 16998 | size_t trapExitLayoutNumWords, |
| 16999 | size_t nInboundStackArgBytes, |
| 17000 | wasm::StackMaps& stackMaps, |
| 17001 | wasm::StackMap** result) { |
| 17002 | // Ensure this is defined on all return paths. |
| 17003 | *result = nullptr; |
| 17004 | |
| 17005 | // The size of the wasm::Frame itself. |
| 17006 | const size_t nFrameBytes = sizeof(wasm::Frame); |
| 17007 | |
| 17008 | // This is the number of bytes spilled for live registers, outside of a trap. |
| 17009 | // For traps, trapExitLayout and trapExitLayoutNumWords will be used. |
| 17010 | const size_t nRegisterDumpBytes = |
| 17011 | MacroAssembler::PushRegsInMaskSizeInBytes(safepoint.liveRegs()); |
| 17012 | |
| 17013 | // As mentioned above, for WasmSafepointKind::LirCall, register spills and |
| 17014 | // restores are handled at the LIR level and there should therefore be no live |
| 17015 | // registers to handle here. |
| 17016 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17017); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { MOZ_CrashSequence (__null, 17017); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false) |
| 17017 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17017); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "nRegisterDumpBytes == 0" ")"); do { MOZ_CrashSequence (__null, 17017); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false); |
| 17018 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17018); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "nRegisterDumpBytes % sizeof(void*) == 0" ")" ); do { MOZ_CrashSequence(__null, 17018); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 17019 | |
| 17020 | // This is the number of bytes in the general spill area, below the Frame. |
| 17021 | const size_t nBodyBytes = safepoint.framePushedAtStackMapBase(); |
| 17022 | |
| 17023 | // The stack map owns any alignment padding around inbound stack args. |
| 17024 | const size_t nInboundStackArgBytesAligned = |
| 17025 | wasm::AlignStackArgAreaSize(nInboundStackArgBytes); |
| 17026 | |
| 17027 | // This is the number of bytes in the general spill area, the Frame, and the |
| 17028 | // incoming args, but not including any register dump area. |
| 17029 | const size_t nNonRegisterBytes = |
| 17030 | nBodyBytes + nFrameBytes + nInboundStackArgBytesAligned; |
| 17031 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17031); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "nNonRegisterBytes % sizeof(void*) == 0" ")" ); do { MOZ_CrashSequence(__null, 17031); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 17032 | |
| 17033 | // This is the number of bytes in the register dump area, if any, below the |
| 17034 | // general spill area. |
| 17035 | const size_t nRegisterBytes = |
| 17036 | (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) |
| 17037 | ? (trapExitLayoutNumWords * sizeof(void*)) |
| 17038 | : nRegisterDumpBytes; |
| 17039 | |
| 17040 | // This is the total number of bytes covered by the map. |
| 17041 | const size_t nTotalBytes = nNonRegisterBytes + nRegisterBytes; |
| 17042 | |
| 17043 | // This stackmap/safepoint is for a wasm frame, so there should be no |
| 17044 | // slotsOrElements-style roots. |
| 17045 | MOZ_RELEASE_ASSERT(safepoint.slotsOrElementsSlots().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.slotsOrElementsSlots().empty())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(safepoint.slotsOrElementsSlots().empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("safepoint.slotsOrElementsSlots().empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17045); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "safepoint.slotsOrElementsSlots().empty()" ")"); do { MOZ_CrashSequence(__null, 17045); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17046 | MOZ_RELEASE_ASSERT(safepoint.slotsOrElementsRegs().empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(safepoint.slotsOrElementsRegs().empty())>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(safepoint.slotsOrElementsRegs().empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("safepoint.slotsOrElementsRegs().empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17046); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "safepoint.slotsOrElementsRegs().empty()" ")"); do { MOZ_CrashSequence(__null, 17046); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17047 | |
| 17048 | #ifndef DEBUG1 |
| 17049 | bool needStackMap = !safepoint.wasmAnyRefRegs().empty() || |
| 17050 | !safepoint.wasmAnyRefSlots().empty() || |
| 17051 | !safepoint.wasmStructDataRegs().empty() || |
| 17052 | !safepoint.wasmStructDataSlots().empty() || |
| 17053 | !safepoint.wasmArrayDataRegs().empty() || |
| 17054 | !safepoint.wasmArrayDataSlots().empty(); |
| 17055 | // There are no references, and this is a non-debug build, so don't bother |
| 17056 | // building the stackmap. |
| 17057 | if (!needStackMap) { |
| 17058 | return true; |
| 17059 | } |
| 17060 | #endif |
| 17061 | |
| 17062 | wasm::StackMap* stackMap = stackMaps.create(nTotalBytes / sizeof(void*)); |
| 17063 | if (!stackMap) { |
| 17064 | return false; |
| 17065 | } |
| 17066 | if (safepoint.wasmSafepointKind() == WasmSafepointKind::Trap) { |
| 17067 | stackMap->setExitStubWords(trapExitLayoutNumWords); |
| 17068 | } |
| 17069 | |
| 17070 | // REG DUMP AREA, if any. |
| 17071 | size_t regDumpWords = 0; |
| 17072 | const LiveGeneralRegisterSet wasmAnyRefRegs = safepoint.wasmAnyRefRegs(); |
| 17073 | const LiveGeneralRegisterSet wasmStructDataRegs = |
| 17074 | safepoint.wasmStructDataRegs(); |
| 17075 | const LiveGeneralRegisterSet wasmArrayDataRegs = |
| 17076 | safepoint.wasmArrayDataRegs(); |
| 17077 | |
| 17078 | // These three sets should be disjoint. |
| 17079 | MOZ_ASSERT(GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs .set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17081); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17081); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17080 | wasmStructDataRegs.set())do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs .set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17081); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17081); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17081 | .empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs .set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17081); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmAnyRefRegs.set(), wasmStructDataRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17081); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17082 | MOZ_ASSERT(GeneralRegisterSet::Intersect(wasmStructDataRegs.set(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmStructDataRegs.set (), wasmArrayDataRegs.set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17084); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17084); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17083 | wasmArrayDataRegs.set())do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmStructDataRegs.set (), wasmArrayDataRegs.set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17084); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17084); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17084 | .empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmStructDataRegs.set (), wasmArrayDataRegs.set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()) )), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17084); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmStructDataRegs.set(), wasmArrayDataRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17084); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17085 | MOZ_ASSERT(GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(),do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmArrayDataRegs.set( ), wasmAnyRefRegs.set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17087); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17087); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17086 | wasmAnyRefRegs.set())do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmArrayDataRegs.set( ), wasmAnyRefRegs.set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17087); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17087); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17087 | .empty())do { static_assert( mozilla::detail::AssertionConditionType< decltype(GeneralRegisterSet::Intersect(wasmArrayDataRegs.set( ), wasmAnyRefRegs.set()) .empty())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(GeneralRegisterSet::Intersect (wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17087); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "GeneralRegisterSet::Intersect(wasmArrayDataRegs.set(), wasmAnyRefRegs.set()) .empty()" ")"); do { MOZ_CrashSequence(__null, 17087); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17088 | const LiveGeneralRegisterSet refRegs(GeneralRegisterSet::Union( |
| 17089 | wasmAnyRefRegs.set(), |
| 17090 | GeneralRegisterSet::Union(wasmStructDataRegs.set(), |
| 17091 | wasmArrayDataRegs.set()))); |
| 17092 | |
| 17093 | GeneralRegisterForwardIterator refRegsIter(refRegs); |
| 17094 | switch (safepoint.wasmSafepointKind()) { |
| 17095 | case WasmSafepointKind::LirCall: |
| 17096 | case WasmSafepointKind::StackSwitch: |
| 17097 | case WasmSafepointKind::CodegenCall: { |
| 17098 | size_t spilledNumWords = nRegisterDumpBytes / sizeof(void*); |
| 17099 | regDumpWords += spilledNumWords; |
| 17100 | |
| 17101 | for (; refRegsIter.more(); ++refRegsIter) { |
| 17102 | Register reg = *refRegsIter; |
| 17103 | size_t offsetFromSpillBase = |
| 17104 | safepoint.liveRegs().gprs().offsetOfPushedRegister(reg) / |
| 17105 | sizeof(void*); |
| 17106 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17107); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" ")"); do { MOZ_CrashSequence(__null, 17107); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17107 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17107); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "0 < offsetFromSpillBase && offsetFromSpillBase <= spilledNumWords" ")"); do { MOZ_CrashSequence(__null, 17107); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17108 | size_t index = spilledNumWords - offsetFromSpillBase; |
| 17109 | |
| 17110 | if (wasmAnyRefRegs.has(reg)) { |
| 17111 | stackMap->set(index, wasm::StackMap::AnyRef); |
| 17112 | } else if (wasmStructDataRegs.has(reg)) { |
| 17113 | stackMap->set(index, wasm::StackMap::StructDataPointer); |
| 17114 | } else { |
| 17115 | MOZ_ASSERT(wasmArrayDataRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasmArrayDataRegs.has(reg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wasmArrayDataRegs.has(reg))) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasmArrayDataRegs.has(reg)" , "./../../../../js/src/jit/CodeGenerator.cpp", 17115); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasmArrayDataRegs.has(reg)" ")"); do { MOZ_CrashSequence (__null, 17115); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17116 | stackMap->set(index, wasm::StackMap::ArrayDataPointer); |
| 17117 | } |
| 17118 | } |
| 17119 | // Float and vector registers do not have to be handled; they cannot |
| 17120 | // contain wasm anyrefs, and they are spilled after general-purpose |
| 17121 | // registers. Gprs are therefore closest to the spill base and thus their |
| 17122 | // offset calculation does not need to account for other spills. |
| 17123 | } break; |
| 17124 | case WasmSafepointKind::Trap: { |
| 17125 | regDumpWords += trapExitLayoutNumWords; |
| 17126 | |
| 17127 | for (; refRegsIter.more(); ++refRegsIter) { |
| 17128 | Register reg = *refRegsIter; |
| 17129 | size_t offsetFromTop = trapExitLayout.getOffset(reg); |
| 17130 | |
| 17131 | // If this doesn't hold, the associated register wasn't saved by |
| 17132 | // the trap exit stub. Better to crash now than much later, in |
| 17133 | // some obscure place, and possibly with security consequences. |
| 17134 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17134); AnnotateMozCrashReason ("MOZ_RELEASE_ASSERT" "(" "offsetFromTop < trapExitLayoutNumWords" ")"); do { MOZ_CrashSequence(__null, 17134); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17135 | |
| 17136 | // offsetFromTop is an offset in words down from the highest |
| 17137 | // address in the exit stub save area. Switch it around to be an |
| 17138 | // offset up from the bottom of the (integer register) save area. |
| 17139 | size_t offsetFromBottom = trapExitLayoutNumWords - 1 - offsetFromTop; |
| 17140 | |
| 17141 | if (wasmAnyRefRegs.has(reg)) { |
| 17142 | stackMap->set(offsetFromBottom, wasm::StackMap::AnyRef); |
| 17143 | } else if (wasmStructDataRegs.has(reg)) { |
| 17144 | stackMap->set(offsetFromBottom, wasm::StackMap::StructDataPointer); |
| 17145 | } else { |
| 17146 | MOZ_ASSERT(wasmArrayDataRegs.has(reg))do { static_assert( mozilla::detail::AssertionConditionType< decltype(wasmArrayDataRegs.has(reg))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(wasmArrayDataRegs.has(reg))) ), 0))) { do { } while (false); MOZ_ReportAssertionFailure("wasmArrayDataRegs.has(reg)" , "./../../../../js/src/jit/CodeGenerator.cpp", 17146); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasmArrayDataRegs.has(reg)" ")"); do { MOZ_CrashSequence (__null, 17146); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17147 | stackMap->set(offsetFromBottom, wasm::StackMap::ArrayDataPointer); |
| 17148 | } |
| 17149 | } |
| 17150 | } break; |
| 17151 | default: |
| 17152 | MOZ_CRASH("unreachable")do { do { } while (false); MOZ_ReportCrash("" "unreachable", "./../../../../js/src/jit/CodeGenerator.cpp" , 17152); AnnotateMozCrashReason("MOZ_CRASH(" "unreachable" ")" ); do { MOZ_CrashSequence(__null, 17152); __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 17153 | } |
| 17154 | |
| 17155 | // Ensure other reg/slot collections on LSafepoint are empty. |
| 17156 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17156); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "safepoint.gcRegs().empty() && safepoint.gcSlots().empty()" ")"); do { MOZ_CrashSequence(__null, 17156); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17157 | #ifdef JS_NUNBOX32 |
| 17158 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 17158); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepoint.nunboxParts().empty()" ")"); do { MOZ_CrashSequence(__null, 17158); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17159 | #elif JS_PUNBOX641 |
| 17160 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17160); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "safepoint.valueRegs().empty() && safepoint.valueSlots().empty()" ")"); do { MOZ_CrashSequence(__null, 17160); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17161 | #endif |
| 17162 | |
| 17163 | // BODY (GENERAL SPILL) AREA and FRAME and INCOMING ARGS |
| 17164 | // Deal with roots on the stack. |
| 17165 | const LSafepoint::SlotList& wasmAnyRefSlots = safepoint.wasmAnyRefSlots(); |
| 17166 | for (SafepointSlotEntry wasmAnyRefSlot : wasmAnyRefSlots) { |
| 17167 | // The following needs to correspond with JitFrameLayout::slotRef |
| 17168 | // wasmAnyRefSlot.stack == 0 means the slot is in the args area |
| 17169 | if (wasmAnyRefSlot.stack) { |
| 17170 | // It's a slot in the body allocation, so .slot is interpreted |
| 17171 | // as an index downwards from the Frame* |
| 17172 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17172); AnnotateMozCrashReason("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot <= nBodyBytes" ")"); do { MOZ_CrashSequence(__null, 17172); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17173 | uint32_t offsetInBytes = nBodyBytes - wasmAnyRefSlot.slot; |
| 17174 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { MOZ_CrashSequence(__null, 17174); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17175 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
| 17176 | wasm::StackMap::AnyRef); |
| 17177 | } else { |
| 17178 | // It's an argument slot |
| 17179 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17179); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasmAnyRefSlot.slot < nInboundStackArgBytes" ")"); do { MOZ_CrashSequence(__null, 17179); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17180 | uint32_t offsetInBytes = nBodyBytes + nFrameBytes + wasmAnyRefSlot.slot; |
| 17181 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { MOZ_CrashSequence(__null, 17181); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17182 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
| 17183 | wasm::StackMap::AnyRef); |
| 17184 | } |
| 17185 | } |
| 17186 | |
| 17187 | // Track struct data pointers on the stack |
| 17188 | for (SafepointSlotEntry slot : safepoint.wasmStructDataSlots()) { |
| 17189 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17189); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack" ")"); do { MOZ_CrashSequence(__null, 17189); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17190 | // It's a slot in the body allocation, so .slot is interpreted |
| 17191 | // as an index downwards from the Frame* |
| 17192 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17192); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { MOZ_CrashSequence (__null, 17192); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17193 | uint32_t offsetInBytes = nBodyBytes - slot.slot; |
| 17194 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17194); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { MOZ_CrashSequence(__null, 17194); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17195 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
| 17196 | wasm::StackMap::Kind::StructDataPointer); |
| 17197 | } |
| 17198 | |
| 17199 | // Track array data pointers on the stack |
| 17200 | for (SafepointSlotEntry slot : safepoint.wasmArrayDataSlots()) { |
| 17201 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17201); AnnotateMozCrashReason("MOZ_ASSERT" "(" "slot.stack" ")"); do { MOZ_CrashSequence(__null, 17201); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17202 | // It's a slot in the body allocation, so .slot is interpreted |
| 17203 | // as an index downwards from the Frame* |
| 17204 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17204); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "slot.slot <= nBodyBytes" ")"); do { MOZ_CrashSequence (__null, 17204); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17205 | uint32_t offsetInBytes = nBodyBytes - slot.slot; |
| 17206 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17206); AnnotateMozCrashReason("MOZ_ASSERT" "(" "offsetInBytes % sizeof(void*) == 0" ")"); do { MOZ_CrashSequence(__null, 17206); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17207 | stackMap->set(regDumpWords + offsetInBytes / sizeof(void*), |
| 17208 | wasm::StackMap::Kind::ArrayDataPointer); |
| 17209 | } |
| 17210 | |
| 17211 | // Record in the map, how far down from the highest address the Frame* is. |
| 17212 | // Take the opportunity to check that we haven't marked any part of the |
| 17213 | // Frame itself as a pointer. |
| 17214 | stackMap->setFrameOffsetFromTop((nInboundStackArgBytesAligned + nFrameBytes) / |
| 17215 | sizeof(void*)); |
| 17216 | #ifdef DEBUG1 |
| 17217 | for (uint32_t i = 0; i < nFrameBytes / sizeof(void*); i++) { |
| 17218 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17220); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { MOZ_CrashSequence(__null, 17220); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17219 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17220); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { MOZ_CrashSequence(__null, 17220); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17220 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17220); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "stackMap->get(stackMap->header.numMappedWords - stackMap->header.frameOffsetFromTop + i) == wasm::StackMap::Kind::POD" ")"); do { MOZ_CrashSequence(__null, 17220); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17221 | } |
| 17222 | #endif |
| 17223 | |
| 17224 | *result = stackMap; |
| 17225 | return true; |
| 17226 | } |
| 17227 | |
| 17228 | bool CodeGenerator::generateWasm(wasm::CallIndirectId callIndirectId, |
| 17229 | const wasm::TrapSiteDesc& entryTrapSiteDesc, |
| 17230 | const wasm::ArgTypeVector& argTypes, |
| 17231 | const RegisterOffsets& trapExitLayout, |
| 17232 | size_t trapExitLayoutNumWords, |
| 17233 | wasm::FuncOffsets* offsets, |
| 17234 | wasm::StackMaps* stackMaps, |
| 17235 | wasm::Decoder* decoder) { |
| 17236 | AutoCreatedBy acb(masm, "CodeGenerator::generateWasm"); |
| 17237 | |
| 17238 | JitSpew(JitSpew_Codegen, "# Emitting wasm code"); |
| 17239 | |
| 17240 | size_t nInboundStackArgBytes = |
| 17241 | StackArgAreaSizeUnaligned(argTypes, ABIKind::Wasm); |
| 17242 | inboundStackArgBytes_ = nInboundStackArgBytes; |
| 17243 | |
| 17244 | perfSpewer().markStartOffset(masm.currentOffset()); |
| 17245 | perfSpewer().recordOffset(masm, "Prologue"); |
| 17246 | wasm::GenerateFunctionPrologue(masm, callIndirectId, mozilla::Nothing(), |
| 17247 | offsets); |
| 17248 | |
| 17249 | #ifdef DEBUG1 |
| 17250 | // If we are doing full debug checks, always load the instance pointer into |
| 17251 | // the usual spot in the frame so that it can be loaded later regardless of |
| 17252 | // what is in InstanceReg. See CodeGenerator::emitDebugResultChecks. |
| 17253 | if (JitOptions.fullDebugChecks) { |
| 17254 | masm.storePtr(InstanceReg, |
| 17255 | Address(FramePointer, |
| 17256 | wasm::FrameWithInstances::calleeInstanceOffset())); |
| 17257 | } |
| 17258 | #endif |
| 17259 | |
| 17260 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17260); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "masm.framePushed() == 0" ")"); do { MOZ_CrashSequence (__null, 17260); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17261 | |
| 17262 | // Very large frames are implausible, probably an attack. |
| 17263 | if (frameSize() > wasm::MaxFrameSize) { |
| 17264 | return decoder->fail(decoder->beginOffset(), "stack frame is too large"); |
| 17265 | } |
| 17266 | |
| 17267 | if (omitOverRecursedStackCheck()) { |
| 17268 | masm.reserveStack(frameSize()); |
| 17269 | |
| 17270 | // If we don't need to check the stack for recursion, we definitely don't |
| 17271 | // need to check for interrupts. |
| 17272 | MOZ_ASSERT(omitOverRecursedInterruptCheck())do { static_assert( mozilla::detail::AssertionConditionType< decltype(omitOverRecursedInterruptCheck())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(omitOverRecursedInterruptCheck ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("omitOverRecursedInterruptCheck()", "./../../../../js/src/jit/CodeGenerator.cpp" , 17272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "omitOverRecursedInterruptCheck()" ")"); do { MOZ_CrashSequence(__null, 17272); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17273 | } else { |
| 17274 | auto* ool = new (alloc()) |
| 17275 | LambdaOutOfLineCode([this, entryTrapSiteDesc](OutOfLineCode& ool) { |
| 17276 | masm.wasmTrap(wasm::Trap::StackOverflow, entryTrapSiteDesc); |
| 17277 | }); |
| 17278 | addOutOfLineCode(ool, (const BytecodeSite*)nullptr); |
| 17279 | masm.wasmReserveStackChecked(frameSize(), ool->entry()); |
| 17280 | |
| 17281 | if (!omitOverRecursedInterruptCheck()) { |
| 17282 | wasm::StackMap* functionEntryStackMap = nullptr; |
| 17283 | if (!CreateStackMapForFunctionEntryTrap( |
| 17284 | argTypes, trapExitLayout, trapExitLayoutNumWords, frameSize(), |
| 17285 | nInboundStackArgBytes, *stackMaps, &functionEntryStackMap)) { |
| 17286 | return false; |
| 17287 | } |
| 17288 | |
| 17289 | // In debug builds, we'll always have a stack map, even if there are no |
| 17290 | // refs to track. |
| 17291 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17291); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "functionEntryStackMap" ")"); do { MOZ_CrashSequence (__null, 17291); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17292 | |
| 17293 | auto* ool = new (alloc()) LambdaOutOfLineCode( |
| 17294 | [this, stackMaps, functionEntryStackMap](OutOfLineCode& ool) { |
| 17295 | masm.wasmTrap(wasm::Trap::CheckInterrupt, wasm::TrapSiteDesc()); |
| 17296 | CodeOffset trapInsnOffset = CodeOffset(masm.currentOffset()); |
| 17297 | |
| 17298 | if (functionEntryStackMap && |
| 17299 | !stackMaps->add(trapInsnOffset.offset(), |
| 17300 | functionEntryStackMap)) { |
| 17301 | masm.setOOM(); |
| 17302 | } |
| 17303 | masm.jump(ool.rejoin()); |
| 17304 | }); |
| 17305 | |
| 17306 | addOutOfLineCode(ool, (const BytecodeSite*)nullptr); |
| 17307 | masm.branch32(Assembler::NotEqual, |
| 17308 | Address(InstanceReg, wasm::Instance::offsetOfInterrupt()), |
| 17309 | Imm32(0), ool->entry()); |
| 17310 | masm.bind(ool->rejoin()); |
| 17311 | } |
| 17312 | } |
| 17313 | |
| 17314 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 17314); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == frameSize()" ")"); do { MOZ_CrashSequence(__null, 17314); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17315 | |
| 17316 | if (!generateBody()) { |
| 17317 | return false; |
| 17318 | } |
| 17319 | |
| 17320 | perfSpewer().recordOffset(masm, "Epilogue"); |
| 17321 | masm.bind(&returnLabel_); |
| 17322 | wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets); |
| 17323 | |
| 17324 | perfSpewer().recordOffset(masm, "OOLBlocks"); |
| 17325 | // This must come before we generate OOL code, as OOL blocks may |
| 17326 | // generate OOL code. |
| 17327 | if (!generateOutOfLineBlocks()) { |
| 17328 | return false; |
| 17329 | } |
| 17330 | |
| 17331 | perfSpewer().recordOffset(masm, "OOLCode"); |
| 17332 | if (!generateOutOfLineCode()) { |
| 17333 | return false; |
| 17334 | } |
| 17335 | |
| 17336 | masm.flush(); |
| 17337 | if (masm.oom()) { |
| 17338 | return false; |
| 17339 | } |
| 17340 | |
| 17341 | offsets->end = masm.currentOffset(); |
| 17342 | |
| 17343 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 17343); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!masm.failureLabel()->used()" ")"); do { MOZ_CrashSequence(__null, 17343); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17344 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17344); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "snapshots_.listSize() == 0" ")"); do { MOZ_CrashSequence (__null, 17344); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17345 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17345); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshots_.RVATableSize() == 0" ")"); do { MOZ_CrashSequence(__null, 17345); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17346 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17346); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "recovers_.size() == 0" ")"); do { MOZ_CrashSequence (__null, 17346); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17347 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17347); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "graph.numConstants() == 0" ")"); do { MOZ_CrashSequence (__null, 17347); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17348 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17348); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "osiIndices_.empty()" ")"); do { MOZ_CrashSequence (__null, 17348); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17349 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17349); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "icList_.empty()" ")"); do { MOZ_CrashSequence (__null, 17349); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17350 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 17350); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "safepoints_.size() == 0" ")"); do { MOZ_CrashSequence (__null, 17350); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17351 | 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_" , "./../../../../js/src/jit/CodeGenerator.cpp", 17351); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!scriptCounts_" ")"); do { MOZ_CrashSequence (__null, 17351); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17352 | |
| 17353 | // Convert the safepoints to stackmaps and add them to our running |
| 17354 | // collection thereof. |
| 17355 | for (CodegenSafepointIndex& index : safepointIndices_) { |
| 17356 | wasm::StackMap* stackMap = nullptr; |
| 17357 | if (!CreateStackMapFromLSafepoint( |
| 17358 | *index.safepoint(), trapExitLayout, trapExitLayoutNumWords, |
| 17359 | nInboundStackArgBytes, *stackMaps, &stackMap)) { |
| 17360 | return false; |
| 17361 | } |
| 17362 | |
| 17363 | // In debug builds, we'll always have a stack map. |
| 17364 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17364); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stackMap" ")" ); do { MOZ_CrashSequence(__null, 17364); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 17365 | if (!stackMap) { |
| 17366 | continue; |
| 17367 | } |
| 17368 | |
| 17369 | if (!stackMaps->finalize(index.displacement(), stackMap)) { |
| 17370 | return false; |
| 17371 | } |
| 17372 | } |
| 17373 | |
| 17374 | return true; |
| 17375 | } |
| 17376 | |
| 17377 | bool CodeGenerator::generate(const WarpSnapshot* snapshot) { |
| 17378 | AutoCreatedBy acb(masm, "CodeGenerator::generate"); |
| 17379 | |
| 17380 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17380); AnnotateMozCrashReason("MOZ_ASSERT" "(" "snapshot" ")" ); do { MOZ_CrashSequence(__null, 17380); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 17381 | snapshot_ = snapshot; |
| 17382 | |
| 17383 | JitSpew(JitSpew_Codegen, "# Emitting code for script %s:%u:%u", |
| 17384 | gen->outerInfo().script()->filename(), |
| 17385 | gen->outerInfo().script()->lineno(), |
| 17386 | gen->outerInfo().script()->column().oneOriginValue()); |
| 17387 | |
| 17388 | // Initialize native code table with an entry to the start of |
| 17389 | // top-level script. |
| 17390 | InlineScriptTree* tree = gen->outerInfo().inlineScriptTree(); |
| 17391 | jsbytecode* startPC = tree->script()->code(); |
| 17392 | BytecodeSite* startSite = new (gen->alloc()) BytecodeSite(tree, startPC); |
| 17393 | if (!addNativeToBytecodeEntry(startSite)) { |
| 17394 | return false; |
| 17395 | } |
| 17396 | |
| 17397 | if (!safepoints_.init(gen->alloc())) { |
| 17398 | return false; |
| 17399 | } |
| 17400 | |
| 17401 | size_t maxSafepointIndices = |
| 17402 | graph.numSafepoints() + graph.extraSafepointUses(); |
| 17403 | if (!safepointIndices_.reserve(maxSafepointIndices)) { |
| 17404 | return false; |
| 17405 | } |
| 17406 | if (!osiIndices_.reserve(graph.numSafepoints())) { |
| 17407 | return false; |
| 17408 | } |
| 17409 | |
| 17410 | perfSpewer().recordOffset(masm, "Prologue"); |
| 17411 | if (!generatePrologue()) { |
| 17412 | return false; |
| 17413 | } |
| 17414 | |
| 17415 | // Reset native => bytecode map table with top-level script and startPc. |
| 17416 | if (!addNativeToBytecodeEntry(startSite)) { |
| 17417 | return false; |
| 17418 | } |
| 17419 | |
| 17420 | if (!generateBody()) { |
| 17421 | return false; |
| 17422 | } |
| 17423 | |
| 17424 | // Reset native => bytecode map table with top-level script and startPc. |
| 17425 | if (!addNativeToBytecodeEntry(startSite)) { |
| 17426 | return false; |
| 17427 | } |
| 17428 | |
| 17429 | perfSpewer().recordOffset(masm, "Epilogue"); |
| 17430 | if (!generateEpilogue()) { |
| 17431 | return false; |
| 17432 | } |
| 17433 | |
| 17434 | // Reset native => bytecode map table with top-level script and startPc. |
| 17435 | if (!addNativeToBytecodeEntry(startSite)) { |
| 17436 | return false; |
| 17437 | } |
| 17438 | |
| 17439 | perfSpewer().recordOffset(masm, "InvalidateEpilogue"); |
| 17440 | generateInvalidateEpilogue(); |
| 17441 | |
| 17442 | perfSpewer().recordOffset(masm, "OOLBlocks"); |
| 17443 | // This must come before we generate OOL code, as OOL blocks may |
| 17444 | // generate OOL code. |
| 17445 | if (!generateOutOfLineBlocks()) { |
| 17446 | return false; |
| 17447 | } |
| 17448 | |
| 17449 | // native => bytecode entries for OOL code will be added |
| 17450 | // by CodeGeneratorShared::generateOutOfLineCode |
| 17451 | perfSpewer().recordOffset(masm, "OOLCode"); |
| 17452 | if (!generateOutOfLineCode()) { |
| 17453 | return false; |
| 17454 | } |
| 17455 | |
| 17456 | // Add terminal entry. |
| 17457 | if (!addNativeToBytecodeEntry(startSite)) { |
| 17458 | return false; |
| 17459 | } |
| 17460 | |
| 17461 | // Dump Native to bytecode entries to spew. |
| 17462 | dumpNativeToBytecodeEntries(); |
| 17463 | |
| 17464 | // We encode safepoints after the OSI-point offsets have been determined. |
| 17465 | if (!encodeSafepoints()) { |
| 17466 | return false; |
| 17467 | } |
| 17468 | |
| 17469 | // If this assertion trips, then you have multiple things to do: |
| 17470 | // |
| 17471 | // This assertion will report if a safepoint is used multiple times for the |
| 17472 | // same instruction. To fix this assertion make sure to call |
| 17473 | // `lirGraph_.addExtraSafepointUses(..);` in the Lowering phase. |
| 17474 | // |
| 17475 | // However, this non-worrying issue might hide a more dramatic security issue, |
| 17476 | // which is that having multiple encoding of a safepoint in a single LIR |
| 17477 | // instruction is not safe, unless: |
| 17478 | // |
| 17479 | // - The multiple uses of the safepoints are in different code path. i-e |
| 17480 | // there should be not single execution trace making use of multiple |
| 17481 | // calls within a single instruction. |
| 17482 | // |
| 17483 | // - There is enough space to encode data in-place of the call instruction. |
| 17484 | // Such that a patched-call site does not corrupt the code path on another |
| 17485 | // execution trace. |
| 17486 | // |
| 17487 | // This issue is caused by the way invalidation works, to keep the code alive |
| 17488 | // when invalidated code is only referenced by the stack. This works by |
| 17489 | // storing data in-place of the calling code, which thus becomes unsafe to |
| 17490 | // execute. |
| 17491 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17491); AnnotateMozCrashReason("MOZ_ASSERT" "(" "safepointIndices_.length() <= maxSafepointIndices" ")"); do { MOZ_CrashSequence(__null, 17491); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17492 | |
| 17493 | // For each instruction with a safepoint, we have an OSI point inserted after |
| 17494 | // which handles bailouts in case of invalidation of the code. |
| 17495 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17495); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "osiIndices_.length() == graph.numSafepoints()" ")"); do { MOZ_CrashSequence(__null, 17495); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17496 | |
| 17497 | return !masm.oom(); |
| 17498 | } |
| 17499 | |
| 17500 | static bool AddInlinedCompilations(JSContext* cx, HandleScript script, |
| 17501 | IonCompilationId compilationId, |
| 17502 | const WarpSnapshot* snapshot, |
| 17503 | bool* isValid) { |
| 17504 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 17504); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!*isValid" ")"); do { MOZ_CrashSequence(__null, 17504); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17505 | IonScriptKey ionScriptKey(script, compilationId); |
| 17506 | |
| 17507 | JitZone* jitZone = cx->zone()->jitZone(); |
| 17508 | |
| 17509 | for (const auto* scriptSnapshot : snapshot->scripts()) { |
| 17510 | JSScript* inlinedScript = scriptSnapshot->script(); |
| 17511 | if (inlinedScript == script) { |
| 17512 | continue; |
| 17513 | } |
| 17514 | |
| 17515 | // TODO(post-Warp): This matches FinishCompilation and is necessary to |
| 17516 | // ensure in-progress compilations are canceled when an inlined functon |
| 17517 | // becomes a debuggee. See the breakpoint-14.js jit-test. |
| 17518 | // When TI is gone, try to clean this up by moving AddInlinedCompilations to |
| 17519 | // WarpOracle so that we can handle this as part of addPendingRecompile |
| 17520 | // instead of requiring this separate check. |
| 17521 | if (inlinedScript->isDebuggee()) { |
| 17522 | *isValid = false; |
| 17523 | return true; |
| 17524 | } |
| 17525 | |
| 17526 | if (!jitZone->addInlinedCompilation(ionScriptKey, inlinedScript)) { |
| 17527 | return false; |
| 17528 | } |
| 17529 | } |
| 17530 | |
| 17531 | *isValid = true; |
| 17532 | return true; |
| 17533 | } |
| 17534 | |
| 17535 | template <auto FuseMember, CompilationDependency::Type DepType> |
| 17536 | struct RuntimeFuseDependency final : public CompilationDependency { |
| 17537 | explicit RuntimeFuseDependency() : CompilationDependency(DepType) {} |
| 17538 | |
| 17539 | bool registerDependency(JSContext* cx, |
| 17540 | const IonScriptKey& ionScript) override { |
| 17541 | 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)" , "./../../../../js/src/jit/CodeGenerator.cpp", 17541); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "checkDependency(cx)" ")"); do { MOZ_CrashSequence (__null, 17541); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17542 | return (cx->runtime()->runtimeFuses.ref().*FuseMember) |
| 17543 | .addFuseDependency(cx, ionScript); |
| 17544 | } |
| 17545 | |
| 17546 | CompilationDependency* clone(TempAllocator& alloc) const override { |
| 17547 | return new (alloc.fallible()) RuntimeFuseDependency<FuseMember, DepType>(); |
| 17548 | } |
| 17549 | |
| 17550 | bool checkDependency(JSContext* cx) const override { |
| 17551 | return (cx->runtime()->runtimeFuses.ref().*FuseMember).intact(); |
| 17552 | } |
| 17553 | |
| 17554 | HashNumber hash() const override { return mozilla::HashGeneric(type); } |
| 17555 | |
| 17556 | bool operator==(const CompilationDependency& dep) const override { |
| 17557 | // Since this dependency is runtime wide, they are all equal. |
| 17558 | return dep.type == type; |
| 17559 | } |
| 17560 | }; |
| 17561 | |
| 17562 | bool CodeGenerator::addHasSeenObjectEmulateUndefinedFuseDependency() { |
| 17563 | using Dependency = |
| 17564 | RuntimeFuseDependency<&RuntimeFuses::hasSeenObjectEmulateUndefinedFuse, |
| 17565 | CompilationDependency::Type::EmulatesUndefined>; |
| 17566 | return mirGen().tracker.addDependency(alloc(), Dependency()); |
| 17567 | } |
| 17568 | |
| 17569 | bool CodeGenerator::addHasSeenArrayExceedsInt32LengthFuseDependency() { |
| 17570 | using Dependency = RuntimeFuseDependency< |
| 17571 | &RuntimeFuses::hasSeenArrayExceedsInt32LengthFuse, |
| 17572 | CompilationDependency::Type::ArrayExceedsInt32Length>; |
| 17573 | return mirGen().tracker.addDependency(alloc(), Dependency()); |
| 17574 | } |
| 17575 | |
| 17576 | bool CodeGenerator::link(JSContext* cx) { |
| 17577 | AutoCreatedBy acb(masm, "CodeGenerator::link"); |
| 17578 | |
| 17579 | // We cancel off-thread Ion compilations in a few places during GC, but if |
| 17580 | // this compilation was performed off-thread it will already have been |
| 17581 | // removed from the relevant lists by this point. Don't allow GC here. |
| 17582 | JS::AutoAssertNoGC nogc(cx); |
| 17583 | |
| 17584 | RootedScript script(cx, gen->outerInfo().script()); |
| 17585 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17585); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "!script->hasIonScript()" ")"); do { MOZ_CrashSequence (__null, 17585); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17586 | |
| 17587 | if (scriptCounts_ && !script->hasScriptCounts() && |
| 17588 | !script->initScriptCounts(cx)) { |
| 17589 | return false; |
| 17590 | } |
| 17591 | |
| 17592 | // Add all used nursery-values to the Value constant pool that's copied to the |
| 17593 | // IonScript. |
| 17594 | for (NurseryValueLabel& label : nurseryValueLabels_) { |
| 17595 | Value v = snapshot_->nurseryValues()[label.nurseryIndex]; |
| 17596 | MOZ_ASSERT(v.isGCThing())do { static_assert( mozilla::detail::AssertionConditionType< decltype(v.isGCThing())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(v.isGCThing()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("v.isGCThing()", "./../../../../js/src/jit/CodeGenerator.cpp", 17596); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "v.isGCThing()" ")"); do { MOZ_CrashSequence (__null, 17596); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17597 | if (!graph.addConstantToPool(v, &label.constantPoolIndex)) { |
| 17598 | return false; |
| 17599 | } |
| 17600 | } |
| 17601 | |
| 17602 | JitZone* jitZone = cx->zone()->jitZone(); |
| 17603 | |
| 17604 | IonCompilationId compilationId = |
| 17605 | cx->runtime()->jitRuntime()->nextCompilationId(); |
| 17606 | jitZone->currentCompilationIdRef().emplace(compilationId); |
| 17607 | auto resetCurrentId = mozilla::MakeScopeExit( |
| 17608 | [jitZone] { jitZone->currentCompilationIdRef().reset(); }); |
| 17609 | |
| 17610 | // Record constraints. If an error occured, returns false and potentially |
| 17611 | // prevent future compilations. Otherwise, if an invalidation occured, then |
| 17612 | // skip the current compilation. |
| 17613 | bool isValid = false; |
| 17614 | |
| 17615 | // If an inlined script is invalidated (for example, by attaching |
| 17616 | // a debugger), we must also invalidate the parent IonScript. |
| 17617 | if (!AddInlinedCompilations(cx, script, compilationId, snapshot_, &isValid)) { |
| 17618 | return false; |
| 17619 | } |
| 17620 | |
| 17621 | // This compilation is no longer valid; don't proceed, but return true as this |
| 17622 | // isn't an error case either. |
| 17623 | if (!isValid) { |
| 17624 | return true; |
| 17625 | } |
| 17626 | |
| 17627 | CompilationDependencyTracker& tracker = mirGen().tracker; |
| 17628 | // Make sure we're using the same realm as this context. |
| 17629 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17629); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "mirGen().realm->realmPtr() == cx->realm()" ")"); do { MOZ_CrashSequence(__null, 17629); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17630 | if (!tracker.checkDependencies(cx)) { |
| 17631 | return true; |
| 17632 | } |
| 17633 | |
| 17634 | IonScriptKey ionScriptKey(script, compilationId); |
| 17635 | for (auto iter = tracker.dependencies.iter(); !iter.done(); iter.next()) { |
| 17636 | CompilationDependency* dep = iter.get(); |
| 17637 | if (!dep->registerDependency(cx, ionScriptKey)) { |
| 17638 | return false; |
| 17639 | } |
| 17640 | } |
| 17641 | |
| 17642 | uint32_t argumentSlots = (gen->outerInfo().nargs() + 1) * sizeof(Value); |
| 17643 | |
| 17644 | size_t numNurseryObjects = snapshot_->nurseryObjects().length(); |
| 17645 | |
| 17646 | IonScript* ionScript = IonScript::New( |
| 17647 | cx, compilationId, graph.localSlotsSize(), argumentSlots, frameDepth_, |
| 17648 | snapshots_.listSize(), snapshots_.RVATableSize(), recovers_.size(), |
| 17649 | graph.numConstants(), numNurseryObjects, safepointIndices_.length(), |
| 17650 | osiIndices_.length(), icList_.length(), runtimeData_.length(), |
| 17651 | safepoints_.size()); |
| 17652 | if (!ionScript) { |
| 17653 | return false; |
| 17654 | } |
| 17655 | #ifdef DEBUG1 |
| 17656 | ionScript->setICHash(snapshot_->icHash()); |
| 17657 | #endif |
| 17658 | |
| 17659 | auto freeIonScript = mozilla::MakeScopeExit([&ionScript] { |
| 17660 | // Use js_free instead of IonScript::Destroy: the cache list is still |
| 17661 | // uninitialized. |
| 17662 | js_free(ionScript); |
| 17663 | }); |
| 17664 | |
| 17665 | Linker linker(masm); |
| 17666 | JitCode* code = linker.newCode(cx, CodeKind::Ion); |
| 17667 | if (!code) { |
| 17668 | return false; |
| 17669 | } |
| 17670 | |
| 17671 | // Encode native to bytecode map if profiling is enabled. |
| 17672 | if (isProfilerInstrumentationEnabled()) { |
| 17673 | // Generate native-to-bytecode main table. |
| 17674 | IonEntry::ScriptList scriptList; |
| 17675 | if (!generateCompactNativeToBytecodeMap(cx, code, scriptList)) { |
| 17676 | return false; |
| 17677 | } |
| 17678 | |
| 17679 | // Find the realmId. We do not do cross-realm inlining, so it should be the |
| 17680 | // same for every inlined script. |
| 17681 | uint64_t realmId = script->realm()->creationOptions().profilerRealmID(); |
| 17682 | #ifdef DEBUG1 |
| 17683 | for (const auto* scriptSnapshot : snapshot_->scripts()) { |
| 17684 | JSScript* inlinedScript = scriptSnapshot->script(); |
| 17685 | MOZ_ASSERT(inlinedScript->realm()->creationOptions().profilerRealmID() ==do { static_assert( mozilla::detail::AssertionConditionType< decltype(inlinedScript->realm()->creationOptions().profilerRealmID () == realmId)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(inlinedScript->realm()->creationOptions ().profilerRealmID() == realmId))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("inlinedScript->realm()->creationOptions().profilerRealmID() == realmId" , "./../../../../js/src/jit/CodeGenerator.cpp", 17686); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "inlinedScript->realm()->creationOptions().profilerRealmID() == realmId" ")"); do { MOZ_CrashSequence(__null, 17686); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 17686 | realmId)do { static_assert( mozilla::detail::AssertionConditionType< decltype(inlinedScript->realm()->creationOptions().profilerRealmID () == realmId)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(inlinedScript->realm()->creationOptions ().profilerRealmID() == realmId))), 0))) { do { } while (false ); MOZ_ReportAssertionFailure("inlinedScript->realm()->creationOptions().profilerRealmID() == realmId" , "./../../../../js/src/jit/CodeGenerator.cpp", 17686); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "inlinedScript->realm()->creationOptions().profilerRealmID() == realmId" ")"); do { MOZ_CrashSequence(__null, 17686); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 17687 | } |
| 17688 | #endif |
| 17689 | |
| 17690 | uint8_t* ionTableAddr = |
| 17691 | ((uint8_t*)nativeToBytecodeMap_.get()) + nativeToBytecodeTableOffset_; |
| 17692 | JitcodeIonTable* ionTable = (JitcodeIonTable*)ionTableAddr; |
| 17693 | |
| 17694 | // Construct the IonEntry that will go into the global table. |
| 17695 | auto entry = MakeJitcodeGlobalEntry<IonEntry>( |
| 17696 | cx, code, code->raw(), code->rawEnd(), std::move(scriptList), ionTable, |
| 17697 | realmId); |
| 17698 | if (!entry) { |
| 17699 | return false; |
| 17700 | } |
| 17701 | (void)nativeToBytecodeMap_.release(); // Table is now owned by |entry|. |
| 17702 | |
| 17703 | // Add entry to the global table. |
| 17704 | JitcodeGlobalTable* globalTable = |
| 17705 | cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); |
| 17706 | if (!globalTable->addEntry(std::move(entry))) { |
| 17707 | return false; |
| 17708 | } |
| 17709 | |
| 17710 | // Mark the jitcode as having a bytecode map. |
| 17711 | code->setHasBytecodeMap(); |
| 17712 | } else { |
| 17713 | // Add a dumy jitcodeGlobalTable entry. |
| 17714 | auto entry = MakeJitcodeGlobalEntry<DummyEntry>(cx, code, code->raw(), |
| 17715 | code->rawEnd()); |
| 17716 | if (!entry) { |
| 17717 | return false; |
| 17718 | } |
| 17719 | |
| 17720 | // Add entry to the global table. |
| 17721 | JitcodeGlobalTable* globalTable = |
| 17722 | cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); |
| 17723 | if (!globalTable->addEntry(std::move(entry))) { |
| 17724 | return false; |
| 17725 | } |
| 17726 | |
| 17727 | // Mark the jitcode as having a bytecode map. |
| 17728 | code->setHasBytecodeMap(); |
| 17729 | } |
| 17730 | |
| 17731 | ionScript->setMethod(code); |
| 17732 | |
| 17733 | // If the Gecko Profiler is enabled, mark IonScript as having been |
| 17734 | // instrumented accordingly. |
| 17735 | if (isProfilerInstrumentationEnabled()) { |
| 17736 | ionScript->setHasProfilingInstrumentation(); |
| 17737 | } |
| 17738 | |
| 17739 | Assembler::PatchDataWithValueCheck( |
| 17740 | CodeLocationLabel(code, invalidateEpilogueData_), ImmPtr(ionScript), |
| 17741 | ImmPtr((void*)-1)); |
| 17742 | |
| 17743 | for (CodeOffset offset : ionScriptLabels_) { |
| 17744 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, offset), |
| 17745 | ImmPtr(ionScript), ImmPtr((void*)-1)); |
| 17746 | } |
| 17747 | |
| 17748 | for (NurseryObjectLabel label : nurseryObjectLabels_) { |
| 17749 | void* entry = ionScript->addressOfNurseryObject(label.nurseryIndex); |
| 17750 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset), |
| 17751 | ImmPtr(entry), ImmPtr((void*)-1)); |
| 17752 | } |
| 17753 | for (NurseryValueLabel label : nurseryValueLabels_) { |
| 17754 | void* entry = &ionScript->getConstant(label.constantPoolIndex); |
| 17755 | Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label.offset), |
| 17756 | ImmPtr(entry), ImmPtr((void*)-1)); |
| 17757 | } |
| 17758 | |
| 17759 | // for generating inline caches during the execution. |
| 17760 | if (runtimeData_.length()) { |
| 17761 | ionScript->copyRuntimeData(&runtimeData_[0]); |
| 17762 | } |
| 17763 | if (icList_.length()) { |
| 17764 | ionScript->copyICEntries(&icList_[0]); |
| 17765 | } |
| 17766 | |
| 17767 | for (size_t i = 0; i < icInfo_.length(); i++) { |
| 17768 | IonIC& ic = ionScript->getICFromIndex(i); |
| 17769 | Assembler::PatchDataWithValueCheck( |
| 17770 | CodeLocationLabel(code, icInfo_[i].icOffsetForJump), |
| 17771 | ImmPtr(ic.codeRawPtr()), ImmPtr((void*)-1)); |
| 17772 | Assembler::PatchDataWithValueCheck( |
| 17773 | CodeLocationLabel(code, icInfo_[i].icOffsetForPush), ImmPtr(&ic), |
| 17774 | ImmPtr((void*)-1)); |
| 17775 | } |
| 17776 | |
| 17777 | JitSpew(JitSpew_Codegen, "Created IonScript %p (raw %p)", (void*)ionScript, |
| 17778 | (void*)code->raw()); |
| 17779 | |
| 17780 | ionScript->setInvalidationEpilogueDataOffset( |
| 17781 | invalidateEpilogueData_.offset()); |
| 17782 | if (jsbytecode* osrPc = gen->outerInfo().osrPc()) { |
| 17783 | ionScript->setOsrPc(osrPc); |
| 17784 | ionScript->setOsrEntryOffset(getOsrEntryOffset()); |
| 17785 | } |
| 17786 | ionScript->setInvalidationEpilogueOffset(invalidate_.offset()); |
| 17787 | |
| 17788 | perfSpewer().saveJSProfile(cx, script, code); |
| 17789 | |
| 17790 | #ifdef MOZ_VTUNE1 |
| 17791 | vtune::MarkScript(code, script, "ion"); |
| 17792 | #endif |
| 17793 | |
| 17794 | // Set a Ion counter hint for this script. |
| 17795 | if (cx->runtime()->jitRuntime()->hasJitHintsMap()) { |
| 17796 | JitHintsMap* jitHints = cx->runtime()->jitRuntime()->getJitHintsMap(); |
| 17797 | jitHints->recordIonCompilation(script); |
| 17798 | } |
| 17799 | |
| 17800 | // for marking during GC. |
| 17801 | if (safepointIndices_.length()) { |
| 17802 | ionScript->copySafepointIndices(&safepointIndices_[0]); |
| 17803 | } |
| 17804 | if (safepoints_.size()) { |
| 17805 | ionScript->copySafepoints(&safepoints_); |
| 17806 | } |
| 17807 | |
| 17808 | // for recovering from an Ion Frame. |
| 17809 | if (osiIndices_.length()) { |
| 17810 | ionScript->copyOsiIndices(&osiIndices_[0]); |
| 17811 | } |
| 17812 | if (snapshots_.listSize()) { |
| 17813 | ionScript->copySnapshots(&snapshots_); |
| 17814 | } |
| 17815 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 17815); AnnotateMozCrashReason("MOZ_ASSERT" "(" "recovers_.size()" ")"); do { MOZ_CrashSequence(__null, 17815); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); } } while (false); |
| 17816 | if (recovers_.size()) { |
| 17817 | ionScript->copyRecovers(&recovers_); |
| 17818 | } |
| 17819 | if (graph.numConstants()) { |
| 17820 | const Value* vp = graph.constantPool(); |
| 17821 | ionScript->copyConstants(vp); |
| 17822 | } |
| 17823 | |
| 17824 | // Attach any generated script counts to the script. |
| 17825 | if (IonScriptCounts* counts = extractScriptCounts()) { |
| 17826 | script->addIonCounts(counts); |
| 17827 | } |
| 17828 | // WARNING: Code after this point must be infallible! |
| 17829 | |
| 17830 | // Copy the list of nursery objects. Note that the store buffer can add |
| 17831 | // HeapPtr edges that must be cleared in IonScript::Destroy. See the |
| 17832 | // infallibility warning above. |
| 17833 | const auto& nurseryObjects = snapshot_->nurseryObjects(); |
| 17834 | for (size_t i = 0; i < nurseryObjects.length(); i++) { |
| 17835 | ionScript->nurseryObjects()[i].init(nurseryObjects[i]); |
| 17836 | } |
| 17837 | |
| 17838 | // Initialization fence for the IonScript. |
| 17839 | MemoryReleaseFence(script.get()); |
| 17840 | |
| 17841 | // Transfer ownership of the IonScript to the JitScript. At this point enough |
| 17842 | // of the IonScript must be initialized for IonScript::Destroy to work. |
| 17843 | freeIonScript.release(); |
| 17844 | script->jitScript()->setIonScript(script, ionScript); |
| 17845 | |
| 17846 | return true; |
| 17847 | } |
| 17848 | |
| 17849 | void CodeGenerator::visitUnboxFloatingPoint(LUnboxFloatingPoint* lir) { |
| 17850 | ValueOperand box = ToValue(lir->input()); |
| 17851 | const LDefinition* result = lir->output(); |
| 17852 | |
| 17853 | // Out-of-line path to convert int32 to double or bailout |
| 17854 | // if this instruction is fallible. |
| 17855 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 17856 | ValueOperand value = ToValue(lir->input()); |
| 17857 | |
| 17858 | if (lir->mir()->fallible()) { |
| 17859 | Label bail; |
| 17860 | masm.branchTestInt32(Assembler::NotEqual, value, &bail); |
| 17861 | bailoutFrom(&bail, lir->snapshot()); |
| 17862 | } |
| 17863 | masm.convertInt32ToDouble(value.payloadOrValueReg(), |
| 17864 | ToFloatRegister(lir->output())); |
| 17865 | masm.jump(ool.rejoin()); |
| 17866 | }); |
| 17867 | addOutOfLineCode(ool, lir->mir()); |
| 17868 | |
| 17869 | FloatRegister resultReg = ToFloatRegister(result); |
| 17870 | masm.branchTestDouble(Assembler::NotEqual, box, ool->entry()); |
| 17871 | masm.unboxDouble(box, resultReg); |
| 17872 | masm.bind(ool->rejoin()); |
| 17873 | } |
| 17874 | |
| 17875 | void CodeGenerator::visitMegamorphicSetElement(LMegamorphicSetElement* lir) { |
| 17876 | Register obj = ToRegister(lir->object()); |
| 17877 | ValueOperand idVal = ToValue(lir->index()); |
| 17878 | ValueOperand value = ToValue(lir->value()); |
| 17879 | |
| 17880 | Register temp0 = ToRegister(lir->temp0()); |
| 17881 | // See comment in LIROps.yaml (x86 is short on registers) |
| 17882 | #ifndef JS_CODEGEN_X86 |
| 17883 | Register temp1 = ToRegister(lir->temp1()); |
| 17884 | Register temp2 = ToRegister(lir->temp2()); |
| 17885 | #endif |
| 17886 | |
| 17887 | // The instruction is marked as call-instruction so only these registers are |
| 17888 | // live. |
| 17889 | LiveRegisterSet liveRegs; |
| 17890 | liveRegs.addUnchecked(obj); |
| 17891 | liveRegs.addUnchecked(idVal); |
| 17892 | liveRegs.addUnchecked(value); |
| 17893 | liveRegs.addUnchecked(temp0); |
| 17894 | #ifndef JS_CODEGEN_X86 |
| 17895 | liveRegs.addUnchecked(temp1); |
| 17896 | liveRegs.addUnchecked(temp2); |
| 17897 | #endif |
| 17898 | |
| 17899 | Label cacheHit, done; |
| 17900 | #ifdef JS_CODEGEN_X86 |
| 17901 | masm.emitMegamorphicCachedSetSlot( |
| 17902 | idVal, obj, temp0, value, liveRegs, &cacheHit, |
| 17903 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
| 17904 | EmitPreBarrier(masm, addr, mirType); |
| 17905 | }); |
| 17906 | #else |
| 17907 | masm.emitMegamorphicCachedSetSlot( |
| 17908 | idVal, obj, temp0, temp1, temp2, value, liveRegs, &cacheHit, |
| 17909 | [](MacroAssembler& masm, const Address& addr, MIRType mirType) { |
| 17910 | EmitPreBarrier(masm, addr, mirType); |
| 17911 | }); |
| 17912 | #endif |
| 17913 | |
| 17914 | pushArg(Imm32(lir->mir()->strict())); |
| 17915 | pushArg(ToValue(lir->value())); |
| 17916 | pushArg(ToValue(lir->index())); |
| 17917 | pushArg(obj); |
| 17918 | |
| 17919 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, bool); |
| 17920 | callVM<Fn, js::jit::SetElementMegamorphic<true>>(lir); |
| 17921 | |
| 17922 | masm.jump(&done); |
| 17923 | masm.bind(&cacheHit); |
| 17924 | |
| 17925 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, temp0, &done); |
| 17926 | masm.branchPtrInNurseryChunk(Assembler::Equal, obj, temp0, &done); |
| 17927 | |
| 17928 | // Note: because this is a call-instruction, no registers need to be saved. |
| 17929 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 17929); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->isCall()" ")"); do { MOZ_CrashSequence (__null, 17929); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 17930 | emitPostWriteBarrier(obj); |
| 17931 | |
| 17932 | masm.bind(&done); |
| 17933 | } |
| 17934 | |
| 17935 | void CodeGenerator::visitLoadScriptedProxyHandler( |
| 17936 | LLoadScriptedProxyHandler* ins) { |
| 17937 | Register obj = ToRegister(ins->object()); |
| 17938 | Register output = ToRegister(ins->output()); |
| 17939 | |
| 17940 | Label bail; |
| 17941 | Address handlerAddr(obj, ProxyObject::offsetOfReservedSlot( |
| 17942 | ScriptedProxyHandler::HANDLER_EXTRA)); |
| 17943 | masm.fallibleUnboxObject(handlerAddr, output, &bail); |
| 17944 | bailoutFrom(&bail, ins->snapshot()); |
| 17945 | } |
| 17946 | |
| 17947 | #ifdef JS_PUNBOX641 |
| 17948 | void CodeGenerator::visitCheckScriptedProxyGetResult( |
| 17949 | LCheckScriptedProxyGetResult* ins) { |
| 17950 | ValueOperand target = ToValue(ins->target()); |
| 17951 | ValueOperand value = ToValue(ins->value()); |
| 17952 | ValueOperand id = ToValue(ins->id()); |
| 17953 | Register scratch = ToRegister(ins->temp0()); |
| 17954 | Register scratch2 = ToRegister(ins->temp1()); |
| 17955 | |
| 17956 | using Fn = bool (*)(JSContext*, HandleObject, HandleValue, HandleValue, |
| 17957 | MutableHandleValue); |
| 17958 | OutOfLineCode* ool = oolCallVM<Fn, CheckProxyGetByValueResult>( |
| 17959 | ins, ArgList(scratch, id, value), StoreValueTo(value)); |
| 17960 | |
| 17961 | masm.unboxObject(target, scratch); |
| 17962 | masm.branchTestObjectNeedsProxyResultValidation(Assembler::NonZero, scratch, |
| 17963 | scratch2, ool->entry()); |
| 17964 | masm.bind(ool->rejoin()); |
| 17965 | } |
| 17966 | #endif |
| 17967 | |
| 17968 | void CodeGenerator::visitIdToStringOrSymbol(LIdToStringOrSymbol* ins) { |
| 17969 | ValueOperand id = ToValue(ins->idVal()); |
| 17970 | ValueOperand output = ToOutValue(ins); |
| 17971 | Register scratch = ToRegister(ins->temp0()); |
| 17972 | |
| 17973 | masm.moveValue(id, output); |
| 17974 | |
| 17975 | Label done, callVM; |
| 17976 | Label bail; |
| 17977 | { |
| 17978 | ScratchTagScope tag(masm, output); |
| 17979 | masm.splitTagForTest(output, tag); |
| 17980 | masm.branchTestString(Assembler::Equal, tag, &done); |
| 17981 | masm.branchTestSymbol(Assembler::Equal, tag, &done); |
| 17982 | masm.branchTestInt32(Assembler::NotEqual, tag, &bail); |
| 17983 | } |
| 17984 | |
| 17985 | masm.unboxInt32(output, scratch); |
| 17986 | |
| 17987 | using Fn = JSLinearString* (*)(JSContext*, int); |
| 17988 | OutOfLineCode* ool = oolCallVM<Fn, Int32ToString<CanGC>>( |
| 17989 | ins, ArgList(scratch), StoreRegisterTo(output.scratchReg())); |
| 17990 | |
| 17991 | masm.lookupStaticIntString(scratch, output.scratchReg(), |
| 17992 | gen->runtime->staticStrings(), ool->entry()); |
| 17993 | |
| 17994 | masm.bind(ool->rejoin()); |
| 17995 | masm.tagValue(JSVAL_TYPE_STRING, output.scratchReg(), output); |
| 17996 | masm.bind(&done); |
| 17997 | |
| 17998 | bailoutFrom(&bail, ins->snapshot()); |
| 17999 | } |
| 18000 | |
| 18001 | void CodeGenerator::visitLoadFixedSlotV(LLoadFixedSlotV* ins) { |
| 18002 | const Register obj = ToRegister(ins->object()); |
| 18003 | size_t slot = ins->mir()->slot(); |
| 18004 | ValueOperand result = ToOutValue(ins); |
| 18005 | |
| 18006 | masm.loadValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), result); |
| 18007 | } |
| 18008 | |
| 18009 | void CodeGenerator::visitLoadFixedSlotT(LLoadFixedSlotT* ins) { |
| 18010 | const Register obj = ToRegister(ins->object()); |
| 18011 | size_t slot = ins->mir()->slot(); |
| 18012 | AnyRegister result = ToAnyRegister(ins->output()); |
| 18013 | MIRType type = ins->mir()->type(); |
| 18014 | |
| 18015 | masm.loadUnboxedValue(Address(obj, NativeObject::getFixedSlotOffset(slot)), |
| 18016 | type, result); |
| 18017 | } |
| 18018 | |
| 18019 | void CodeGenerator::visitLoadFixedSlotFromOffset( |
| 18020 | LLoadFixedSlotFromOffset* lir) { |
| 18021 | Register obj = ToRegister(lir->object()); |
| 18022 | Register offset = ToRegister(lir->offset()); |
| 18023 | ValueOperand out = ToOutValue(lir); |
| 18024 | |
| 18025 | // obj[offset] |
| 18026 | masm.loadValue(BaseIndex(obj, offset, TimesOne), out); |
| 18027 | } |
| 18028 | |
| 18029 | void CodeGenerator::visitStoreFixedSlotFromOffsetV( |
| 18030 | LStoreFixedSlotFromOffsetV* lir) { |
| 18031 | Register obj = ToRegister(lir->object()); |
| 18032 | Register offset = ToRegister(lir->offset()); |
| 18033 | ValueOperand value = ToValue(lir->value()); |
| 18034 | Register temp = ToRegister(lir->temp0()); |
| 18035 | |
| 18036 | BaseIndex baseIndex(obj, offset, TimesOne); |
| 18037 | masm.computeEffectiveAddress(baseIndex, temp); |
| 18038 | |
| 18039 | Address slot(temp, 0); |
| 18040 | if (lir->mir()->needsBarrier()) { |
| 18041 | emitPreBarrier(slot); |
| 18042 | } |
| 18043 | |
| 18044 | // obj[offset] |
| 18045 | masm.storeValue(value, slot); |
| 18046 | } |
| 18047 | |
| 18048 | void CodeGenerator::visitStoreFixedSlotFromOffsetT( |
| 18049 | LStoreFixedSlotFromOffsetT* lir) { |
| 18050 | Register obj = ToRegister(lir->object()); |
| 18051 | Register offset = ToRegister(lir->offset()); |
| 18052 | const LAllocation* value = lir->value(); |
| 18053 | MIRType valueType = lir->mir()->value()->type(); |
| 18054 | Register temp = ToRegister(lir->temp0()); |
| 18055 | |
| 18056 | BaseIndex baseIndex(obj, offset, TimesOne); |
| 18057 | masm.computeEffectiveAddress(baseIndex, temp); |
| 18058 | |
| 18059 | Address slot(temp, 0); |
| 18060 | if (lir->mir()->needsBarrier()) { |
| 18061 | emitPreBarrier(slot); |
| 18062 | } |
| 18063 | |
| 18064 | // obj[offset] |
| 18065 | ConstantOrRegister nvalue = |
| 18066 | value->isConstant() |
| 18067 | ? ConstantOrRegister(value->toConstant()->toJSValue()) |
| 18068 | : TypedOrValueRegister(valueType, ToAnyRegister(value)); |
| 18069 | masm.storeConstantOrRegister(nvalue, slot); |
| 18070 | } |
| 18071 | |
| 18072 | template <typename T> |
| 18073 | static void EmitLoadAndUnbox(MacroAssembler& masm, const T& src, MIRType type, |
| 18074 | bool fallible, AnyRegister dest, Register64 temp, |
| 18075 | Label* fail) { |
| 18076 | MOZ_ASSERT_IF(type == MIRType::Double, temp != Register64::Invalid())do { if (type == MIRType::Double) { do { static_assert( mozilla ::detail::AssertionConditionType<decltype(temp != Register64 ::Invalid())>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(temp != Register64::Invalid()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("temp != Register64::Invalid()" , "./../../../../js/src/jit/CodeGenerator.cpp", 18076); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "temp != Register64::Invalid()" ")"); do { MOZ_CrashSequence (__null, 18076); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); } } while (false); |
| 18077 | if (type == MIRType::Double) { |
| 18078 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 18078); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "dest.isFloat()" ")"); do { MOZ_CrashSequence (__null, 18078); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 18079 | #if defined(JS_NUNBOX32) |
| 18080 | auto tempVal = ValueOperand(temp.high, temp.low); |
| 18081 | #else |
| 18082 | auto tempVal = ValueOperand(temp.reg); |
| 18083 | #endif |
| 18084 | masm.loadValue(src, tempVal); |
| 18085 | masm.ensureDouble(tempVal, dest.fpu(), fail); |
| 18086 | return; |
| 18087 | } |
| 18088 | if (fallible) { |
| 18089 | switch (type) { |
| 18090 | case MIRType::Int32: |
| 18091 | masm.fallibleUnboxInt32(src, dest.gpr(), fail); |
| 18092 | break; |
| 18093 | case MIRType::Boolean: |
| 18094 | masm.fallibleUnboxBoolean(src, dest.gpr(), fail); |
| 18095 | break; |
| 18096 | case MIRType::Object: |
| 18097 | masm.fallibleUnboxObject(src, dest.gpr(), fail); |
| 18098 | break; |
| 18099 | case MIRType::String: |
| 18100 | masm.fallibleUnboxString(src, dest.gpr(), fail); |
| 18101 | break; |
| 18102 | case MIRType::Symbol: |
| 18103 | masm.fallibleUnboxSymbol(src, dest.gpr(), fail); |
| 18104 | break; |
| 18105 | case MIRType::BigInt: |
| 18106 | masm.fallibleUnboxBigInt(src, dest.gpr(), fail); |
| 18107 | break; |
| 18108 | default: |
| 18109 | MOZ_CRASH("Unexpected MIRType")do { do { } while (false); MOZ_ReportCrash("" "Unexpected MIRType" , "./../../../../js/src/jit/CodeGenerator.cpp", 18109); AnnotateMozCrashReason ("MOZ_CRASH(" "Unexpected MIRType" ")"); do { MOZ_CrashSequence (__null, 18109); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 18110 | } |
| 18111 | return; |
| 18112 | } |
| 18113 | masm.loadUnboxedValue(src, type, dest); |
| 18114 | } |
| 18115 | |
| 18116 | void CodeGenerator::visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* ins) { |
| 18117 | const MLoadFixedSlotAndUnbox* mir = ins->mir(); |
| 18118 | MIRType type = mir->type(); |
| 18119 | Register input = ToRegister(ins->object()); |
| 18120 | AnyRegister result = ToAnyRegister(ins->output()); |
| 18121 | Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0()); |
| 18122 | size_t slot = mir->slot(); |
| 18123 | |
| 18124 | Address address(input, NativeObject::getFixedSlotOffset(slot)); |
| 18125 | |
| 18126 | Label bail; |
| 18127 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, maybeTemp, |
| 18128 | &bail); |
| 18129 | if (mir->fallible()) { |
| 18130 | bailoutFrom(&bail, ins->snapshot()); |
| 18131 | } |
| 18132 | } |
| 18133 | |
| 18134 | void CodeGenerator::visitLoadDynamicSlotAndUnbox( |
| 18135 | LLoadDynamicSlotAndUnbox* ins) { |
| 18136 | const MLoadDynamicSlotAndUnbox* mir = ins->mir(); |
| 18137 | MIRType type = mir->type(); |
| 18138 | Register input = ToRegister(ins->slots()); |
| 18139 | AnyRegister result = ToAnyRegister(ins->output()); |
| 18140 | Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0()); |
| 18141 | size_t slot = mir->slot(); |
| 18142 | |
| 18143 | Address address(input, slot * sizeof(JS::Value)); |
| 18144 | |
| 18145 | Label bail; |
| 18146 | EmitLoadAndUnbox(masm, address, type, mir->fallible(), result, maybeTemp, |
| 18147 | &bail); |
| 18148 | if (mir->fallible()) { |
| 18149 | bailoutFrom(&bail, ins->snapshot()); |
| 18150 | } |
| 18151 | } |
| 18152 | |
| 18153 | void CodeGenerator::visitLoadElementAndUnbox(LLoadElementAndUnbox* ins) { |
| 18154 | const MLoadElementAndUnbox* mir = ins->mir(); |
| 18155 | MIRType type = mir->type(); |
| 18156 | Register elements = ToRegister(ins->elements()); |
| 18157 | AnyRegister result = ToAnyRegister(ins->output()); |
| 18158 | Register64 maybeTemp = ToTempRegister64OrInvalid(ins->temp0()); |
| 18159 | |
| 18160 | auto source = ToAddressOrBaseObjectElementIndex(elements, ins->index()); |
| 18161 | |
| 18162 | Label bail; |
| 18163 | source.match([&](const auto& source) { |
| 18164 | EmitLoadAndUnbox(masm, source, type, mir->fallible(), result, maybeTemp, |
| 18165 | &bail); |
| 18166 | }); |
| 18167 | |
| 18168 | if (mir->fallible()) { |
| 18169 | bailoutFrom(&bail, ins->snapshot()); |
| 18170 | } |
| 18171 | } |
| 18172 | |
| 18173 | void CodeGenerator::emitMaybeAtomizeSlot(LInstruction* ins, Register stringReg, |
| 18174 | Address slotAddr, |
| 18175 | TypedOrValueRegister dest) { |
| 18176 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 18177 | // This code is called with a non-atomic string in |stringReg|. |
| 18178 | // When it returns, |stringReg| contains an unboxed pointer to an |
| 18179 | // atomized version of that string, and |slotAddr| contains a |
| 18180 | // StringValue pointing to that atom. If |dest| is a ValueOperand, |
| 18181 | // it contains the same StringValue; otherwise we assert that |dest| |
| 18182 | // is |stringReg|. |
| 18183 | |
| 18184 | saveLive(ins); |
| 18185 | pushArg(stringReg); |
| 18186 | |
| 18187 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
| 18188 | callVM<Fn, js::AtomizeString>(ins); |
| 18189 | StoreRegisterTo(stringReg).generate(this); |
| 18190 | restoreLiveIgnore(ins, StoreRegisterTo(stringReg).clobbered()); |
| 18191 | |
| 18192 | if (dest.hasValue()) { |
| 18193 | masm.moveValue( |
| 18194 | TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)), |
| 18195 | dest.valueReg()); |
| 18196 | } else { |
| 18197 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 18197); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg" ")"); do { MOZ_CrashSequence(__null, 18197); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18198 | } |
| 18199 | |
| 18200 | emitPreBarrier(slotAddr); |
| 18201 | masm.storeTypedOrValue(dest, slotAddr); |
| 18202 | |
| 18203 | // We don't need a post-barrier because atoms aren't nursery-allocated. |
| 18204 | #ifdef DEBUG1 |
| 18205 | // We need a temp register for the nursery check. Spill something. |
| 18206 | AllocatableGeneralRegisterSet allRegs(GeneralRegisterSet::All()); |
| 18207 | allRegs.take(stringReg); |
| 18208 | Register temp = allRegs.takeAny(); |
| 18209 | masm.push(temp); |
| 18210 | |
| 18211 | Label tenured; |
| 18212 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, stringReg, temp, |
| 18213 | &tenured); |
| 18214 | masm.assumeUnreachable("AtomizeString returned a nursery pointer"); |
| 18215 | masm.bind(&tenured); |
| 18216 | |
| 18217 | masm.pop(temp); |
| 18218 | #endif |
| 18219 | |
| 18220 | masm.jump(ool.rejoin()); |
| 18221 | }); |
| 18222 | addOutOfLineCode(ool, ins->mirRaw()->toInstruction()); |
| 18223 | masm.branchTest32(Assembler::NonZero, |
| 18224 | Address(stringReg, JSString::offsetOfFlags()), |
| 18225 | Imm32(StringFlags::ATOM_BIT), ool->rejoin()); |
| 18226 | |
| 18227 | masm.branchTest32(Assembler::Zero, |
| 18228 | Address(stringReg, JSString::offsetOfFlags()), |
| 18229 | Imm32(StringFlags::ATOM_REF_BIT), ool->entry()); |
| 18230 | masm.loadPtr(Address(stringReg, JSAtomRefString::offsetOfAtom()), stringReg); |
| 18231 | |
| 18232 | if (dest.hasValue()) { |
| 18233 | masm.moveValue( |
| 18234 | TypedOrValueRegister(MIRType::String, AnyRegister(stringReg)), |
| 18235 | dest.valueReg()); |
| 18236 | } else { |
| 18237 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 18237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dest.typedReg().gpr() == stringReg" ")"); do { MOZ_CrashSequence(__null, 18237); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18238 | } |
| 18239 | |
| 18240 | emitPreBarrier(slotAddr); |
| 18241 | masm.storeTypedOrValue(dest, slotAddr); |
| 18242 | |
| 18243 | masm.bind(ool->rejoin()); |
| 18244 | } |
| 18245 | |
| 18246 | void CodeGenerator::visitLoadFixedSlotAndAtomize( |
| 18247 | LLoadFixedSlotAndAtomize* ins) { |
| 18248 | Register obj = ToRegister(ins->object()); |
| 18249 | Register temp = ToRegister(ins->temp0()); |
| 18250 | size_t slot = ins->mir()->slot(); |
| 18251 | ValueOperand result = ToOutValue(ins); |
| 18252 | |
| 18253 | Address slotAddr(obj, NativeObject::getFixedSlotOffset(slot)); |
| 18254 | masm.loadValue(slotAddr, result); |
| 18255 | |
| 18256 | Label notString; |
| 18257 | masm.branchTestString(Assembler::NotEqual, result, ¬String); |
| 18258 | masm.unboxString(result, temp); |
| 18259 | emitMaybeAtomizeSlot(ins, temp, slotAddr, result); |
| 18260 | masm.bind(¬String); |
| 18261 | } |
| 18262 | |
| 18263 | void CodeGenerator::visitLoadDynamicSlotAndAtomize( |
| 18264 | LLoadDynamicSlotAndAtomize* ins) { |
| 18265 | ValueOperand result = ToOutValue(ins); |
| 18266 | Register temp = ToRegister(ins->temp0()); |
| 18267 | Register base = ToRegister(ins->input()); |
| 18268 | int32_t offset = ins->mir()->slot() * sizeof(js::Value); |
| 18269 | |
| 18270 | Address slotAddr(base, offset); |
| 18271 | masm.loadValue(slotAddr, result); |
| 18272 | |
| 18273 | Label notString; |
| 18274 | masm.branchTestString(Assembler::NotEqual, result, ¬String); |
| 18275 | masm.unboxString(result, temp); |
| 18276 | emitMaybeAtomizeSlot(ins, temp, slotAddr, result); |
| 18277 | masm.bind(¬String); |
| 18278 | } |
| 18279 | |
| 18280 | void CodeGenerator::visitLoadFixedSlotUnboxAndAtomize( |
| 18281 | LLoadFixedSlotUnboxAndAtomize* ins) { |
| 18282 | const MLoadFixedSlotAndUnbox* mir = ins->mir(); |
| 18283 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 18283); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String" ")"); do { MOZ_CrashSequence(__null, 18283); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18284 | Register input = ToRegister(ins->object()); |
| 18285 | AnyRegister result = ToAnyRegister(ins->output()); |
| 18286 | size_t slot = mir->slot(); |
| 18287 | |
| 18288 | Address slotAddr(input, NativeObject::getFixedSlotOffset(slot)); |
| 18289 | |
| 18290 | Label bail; |
| 18291 | EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result, |
| 18292 | Register64::Invalid(), &bail); |
| 18293 | emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr, |
| 18294 | TypedOrValueRegister(MIRType::String, result)); |
| 18295 | |
| 18296 | if (mir->fallible()) { |
| 18297 | bailoutFrom(&bail, ins->snapshot()); |
| 18298 | } |
| 18299 | } |
| 18300 | |
| 18301 | void CodeGenerator::visitLoadDynamicSlotUnboxAndAtomize( |
| 18302 | LLoadDynamicSlotUnboxAndAtomize* ins) { |
| 18303 | const MLoadDynamicSlotAndUnbox* mir = ins->mir(); |
| 18304 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 18304); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->type() == MIRType::String" ")"); do { MOZ_CrashSequence(__null, 18304); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18305 | Register input = ToRegister(ins->slots()); |
| 18306 | AnyRegister result = ToAnyRegister(ins->output()); |
| 18307 | size_t slot = mir->slot(); |
| 18308 | |
| 18309 | Address slotAddr(input, slot * sizeof(JS::Value)); |
| 18310 | |
| 18311 | Label bail; |
| 18312 | EmitLoadAndUnbox(masm, slotAddr, MIRType::String, mir->fallible(), result, |
| 18313 | Register64::Invalid(), &bail); |
| 18314 | emitMaybeAtomizeSlot(ins, result.gpr(), slotAddr, |
| 18315 | TypedOrValueRegister(MIRType::String, result)); |
| 18316 | |
| 18317 | if (mir->fallible()) { |
| 18318 | bailoutFrom(&bail, ins->snapshot()); |
| 18319 | } |
| 18320 | } |
| 18321 | |
| 18322 | void CodeGenerator::visitAddAndStoreSlot(LAddAndStoreSlot* ins) { |
| 18323 | MOZ_ASSERT(!ins->mir()->preserveWrapper())do { static_assert( mozilla::detail::AssertionConditionType< decltype(!ins->mir()->preserveWrapper())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(!ins->mir()->preserveWrapper ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("!ins->mir()->preserveWrapper()", "./../../../../js/src/jit/CodeGenerator.cpp" , 18323); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!ins->mir()->preserveWrapper()" ")"); do { MOZ_CrashSequence(__null, 18323); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18324 | |
| 18325 | Register obj = ToRegister(ins->object()); |
| 18326 | ValueOperand value = ToValue(ins->value()); |
| 18327 | Register maybeTemp = ToTempRegisterOrInvalid(ins->temp0()); |
| 18328 | |
| 18329 | Shape* shape = ins->mir()->shape(); |
| 18330 | masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) { |
| 18331 | EmitPreBarrier(masm, addr, MIRType::Shape); |
| 18332 | }); |
| 18333 | |
| 18334 | // Perform the store. No pre-barrier required since this is a new |
| 18335 | // initialization. |
| 18336 | |
| 18337 | uint32_t offset = ins->mir()->slotOffset(); |
| 18338 | if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) { |
| 18339 | Address slot(obj, offset); |
| 18340 | masm.storeValue(value, slot); |
| 18341 | } else { |
| 18342 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), maybeTemp); |
| 18343 | Address slot(maybeTemp, offset); |
| 18344 | masm.storeValue(value, slot); |
| 18345 | } |
| 18346 | } |
| 18347 | |
| 18348 | void CodeGenerator::visitAddAndStoreSlotPreserveWrapper( |
| 18349 | LAddAndStoreSlotPreserveWrapper* ins) { |
| 18350 | MOZ_ASSERT(ins->mir()->preserveWrapper())do { static_assert( mozilla::detail::AssertionConditionType< decltype(ins->mir()->preserveWrapper())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ins->mir()->preserveWrapper ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ins->mir()->preserveWrapper()", "./../../../../js/src/jit/CodeGenerator.cpp" , 18350); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ins->mir()->preserveWrapper()" ")"); do { MOZ_CrashSequence(__null, 18350); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18351 | |
| 18352 | Register obj = ToRegister(ins->object()); |
| 18353 | ValueOperand value = ToValue(ins->value()); |
| 18354 | Register temp0 = ToTempRegisterOrInvalid(ins->temp0()); |
| 18355 | Register temp1 = ToTempRegisterOrInvalid(ins->temp1()); |
| 18356 | |
| 18357 | LiveRegisterSet liveRegs = liveVolatileRegs(ins); |
| 18358 | liveRegs.takeUnchecked(temp0); |
| 18359 | liveRegs.takeUnchecked(temp1); |
| 18360 | masm.preserveWrapper(obj, temp0, temp1, liveRegs); |
| 18361 | bailoutIfFalseBool(temp0, ins->snapshot()); |
| 18362 | |
| 18363 | Shape* shape = ins->mir()->shape(); |
| 18364 | masm.storeObjShape(shape, obj, [](MacroAssembler& masm, const Address& addr) { |
| 18365 | EmitPreBarrier(masm, addr, MIRType::Shape); |
| 18366 | }); |
| 18367 | |
| 18368 | // Perform the store. No pre-barrier required since this is a new |
| 18369 | // initialization. |
| 18370 | |
| 18371 | uint32_t offset = ins->mir()->slotOffset(); |
| 18372 | if (ins->mir()->kind() == MAddAndStoreSlot::Kind::FixedSlot) { |
| 18373 | Address slot(obj, offset); |
| 18374 | masm.storeValue(value, slot); |
| 18375 | } else { |
| 18376 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0); |
| 18377 | Address slot(temp0, offset); |
| 18378 | masm.storeValue(value, slot); |
| 18379 | } |
| 18380 | } |
| 18381 | |
| 18382 | void CodeGenerator::visitAllocateAndStoreSlot(LAllocateAndStoreSlot* ins) { |
| 18383 | Register obj = ToRegister(ins->object()); |
| 18384 | ValueOperand value = ToValue(ins->value()); |
| 18385 | Register temp0 = ToRegister(ins->temp0()); |
| 18386 | Register temp1 = ToRegister(ins->temp1()); |
| 18387 | |
| 18388 | if (ins->mir()->preserveWrapper()) { |
| 18389 | LiveRegisterSet liveRegs; |
| 18390 | liveRegs.addUnchecked(obj); |
| 18391 | liveRegs.addUnchecked(value); |
| 18392 | masm.preserveWrapper(obj, temp0, temp1, liveRegs); |
| 18393 | bailoutIfFalseBool(temp0, ins->snapshot()); |
| 18394 | } |
| 18395 | |
| 18396 | masm.Push(obj); |
| 18397 | masm.Push(value); |
| 18398 | |
| 18399 | using Fn = bool (*)(JSContext* cx, NativeObject* obj, uint32_t newCount); |
| 18400 | masm.setupAlignedABICall(); |
| 18401 | masm.loadJSContext(temp0); |
| 18402 | masm.passABIArg(temp0); |
| 18403 | masm.passABIArg(obj); |
| 18404 | masm.move32(Imm32(ins->mir()->numNewSlots()), temp1); |
| 18405 | masm.passABIArg(temp1); |
| 18406 | masm.callWithABI<Fn, NativeObject::growSlotsPure>(); |
| 18407 | masm.storeCallPointerResult(temp0); |
| 18408 | |
| 18409 | masm.Pop(value); |
| 18410 | masm.Pop(obj); |
| 18411 | |
| 18412 | bailoutIfFalseBool(temp0, ins->snapshot()); |
| 18413 | |
| 18414 | masm.storeObjShape(ins->mir()->shape(), obj, |
| 18415 | [](MacroAssembler& masm, const Address& addr) { |
| 18416 | EmitPreBarrier(masm, addr, MIRType::Shape); |
| 18417 | }); |
| 18418 | |
| 18419 | // Perform the store. No pre-barrier required since this is a new |
| 18420 | // initialization. |
| 18421 | masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), temp0); |
| 18422 | Address slot(temp0, ins->mir()->slotOffset()); |
| 18423 | masm.storeValue(value, slot); |
| 18424 | } |
| 18425 | |
| 18426 | void CodeGenerator::visitStoreFixedSlotV(LStoreFixedSlotV* ins) { |
| 18427 | Register obj = ToRegister(ins->obj()); |
| 18428 | size_t slot = ins->mir()->slot(); |
| 18429 | |
| 18430 | ValueOperand value = ToValue(ins->value()); |
| 18431 | |
| 18432 | Address address(obj, NativeObject::getFixedSlotOffset(slot)); |
| 18433 | if (ins->mir()->needsBarrier()) { |
| 18434 | emitPreBarrier(address); |
| 18435 | } |
| 18436 | |
| 18437 | masm.storeValue(value, address); |
| 18438 | } |
| 18439 | |
| 18440 | void CodeGenerator::visitStoreFixedSlotT(LStoreFixedSlotT* ins) { |
| 18441 | const Register obj = ToRegister(ins->obj()); |
| 18442 | size_t slot = ins->mir()->slot(); |
| 18443 | |
| 18444 | const LAllocation* value = ins->value(); |
| 18445 | MIRType valueType = ins->mir()->value()->type(); |
| 18446 | |
| 18447 | Address address(obj, NativeObject::getFixedSlotOffset(slot)); |
| 18448 | if (ins->mir()->needsBarrier()) { |
| 18449 | emitPreBarrier(address); |
| 18450 | } |
| 18451 | |
| 18452 | ConstantOrRegister nvalue = |
| 18453 | value->isConstant() |
| 18454 | ? ConstantOrRegister(value->toConstant()->toJSValue()) |
| 18455 | : TypedOrValueRegister(valueType, ToAnyRegister(value)); |
| 18456 | masm.storeConstantOrRegister(nvalue, address); |
| 18457 | } |
| 18458 | |
| 18459 | void CodeGenerator::visitGetNameCache(LGetNameCache* ins) { |
| 18460 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 18461 | Register envChain = ToRegister(ins->envObj()); |
| 18462 | ValueOperand output = ToOutValue(ins); |
| 18463 | Register temp = ToRegister(ins->temp0()); |
| 18464 | |
| 18465 | IonGetNameIC ic(liveRegs, envChain, output, temp); |
| 18466 | addIC(ins, allocateIC(ic)); |
| 18467 | } |
| 18468 | |
| 18469 | static bool IsConstantNonIndexString(const ConstantOrRegister& id) { |
| 18470 | if (!id.constant() || !id.value().isString()) { |
| 18471 | return false; |
| 18472 | } |
| 18473 | return !id.value().toString()->asOffThreadAtom().isIndex(); |
| 18474 | } |
| 18475 | |
| 18476 | void CodeGenerator::addGetPropertyCache(LInstruction* ins, |
| 18477 | LiveRegisterSet liveRegs, |
| 18478 | TypedOrValueRegister value, |
| 18479 | const ConstantOrRegister& id, |
| 18480 | ValueOperand output) { |
| 18481 | CacheKind kind = CacheKind::GetElem; |
| 18482 | if (IsConstantNonIndexString(id)) { |
| 18483 | kind = CacheKind::GetProp; |
| 18484 | } |
| 18485 | IonGetPropertyIC cache(kind, liveRegs, value, id, output); |
| 18486 | addIC(ins, allocateIC(cache)); |
| 18487 | } |
| 18488 | |
| 18489 | void CodeGenerator::addSetPropertyCache(LInstruction* ins, |
| 18490 | LiveRegisterSet liveRegs, |
| 18491 | Register objReg, Register temp, |
| 18492 | const ConstantOrRegister& id, |
| 18493 | const ConstantOrRegister& value, |
| 18494 | bool strict) { |
| 18495 | CacheKind kind = CacheKind::SetElem; |
| 18496 | if (IsConstantNonIndexString(id)) { |
| 18497 | kind = CacheKind::SetProp; |
| 18498 | } |
| 18499 | IonSetPropertyIC cache(kind, liveRegs, objReg, temp, id, value, strict); |
| 18500 | addIC(ins, allocateIC(cache)); |
| 18501 | } |
| 18502 | |
| 18503 | ConstantOrRegister CodeGenerator::toConstantOrRegister(LInstruction* lir, |
| 18504 | size_t n, MIRType type) { |
| 18505 | if (type == MIRType::Value) { |
| 18506 | return TypedOrValueRegister(ToValue(lir->getBoxOperand(n))); |
| 18507 | } |
| 18508 | |
| 18509 | const LAllocation* value = lir->getOperand(n); |
| 18510 | if (value->isConstant()) { |
| 18511 | return ConstantOrRegister(value->toConstant()->toJSValue()); |
| 18512 | } |
| 18513 | |
| 18514 | return TypedOrValueRegister(type, ToAnyRegister(value)); |
| 18515 | } |
| 18516 | |
| 18517 | void CodeGenerator::visitGetPropertyCache(LGetPropertyCache* ins) { |
| 18518 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 18519 | TypedOrValueRegister value = |
| 18520 | toConstantOrRegister(ins, LGetPropertyCache::ValueIndex, |
| 18521 | ins->mir()->value()->type()) |
| 18522 | .reg(); |
| 18523 | ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCache::IdIndex, |
| 18524 | ins->mir()->idval()->type()); |
| 18525 | ValueOperand output = ToOutValue(ins); |
| 18526 | addGetPropertyCache(ins, liveRegs, value, id, output); |
| 18527 | } |
| 18528 | |
| 18529 | void CodeGenerator::visitGetPropSuperCache(LGetPropSuperCache* ins) { |
| 18530 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 18531 | Register obj = ToRegister(ins->obj()); |
| 18532 | TypedOrValueRegister receiver = |
| 18533 | toConstantOrRegister(ins, LGetPropSuperCache::ReceiverIndex, |
| 18534 | ins->mir()->receiver()->type()) |
| 18535 | .reg(); |
| 18536 | ConstantOrRegister id = toConstantOrRegister(ins, LGetPropSuperCache::IdIndex, |
| 18537 | ins->mir()->idval()->type()); |
| 18538 | ValueOperand output = ToOutValue(ins); |
| 18539 | |
| 18540 | CacheKind kind = CacheKind::GetElemSuper; |
| 18541 | if (IsConstantNonIndexString(id)) { |
| 18542 | kind = CacheKind::GetPropSuper; |
| 18543 | } |
| 18544 | |
| 18545 | IonGetPropSuperIC cache(kind, liveRegs, obj, receiver, id, output); |
| 18546 | addIC(ins, allocateIC(cache)); |
| 18547 | } |
| 18548 | |
| 18549 | void CodeGenerator::visitBindNameCache(LBindNameCache* ins) { |
| 18550 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 18551 | Register envChain = ToRegister(ins->environmentChain()); |
| 18552 | Register output = ToRegister(ins->output()); |
| 18553 | Register temp = ToRegister(ins->temp0()); |
| 18554 | |
| 18555 | IonBindNameIC ic(liveRegs, envChain, output, temp); |
| 18556 | addIC(ins, allocateIC(ic)); |
| 18557 | } |
| 18558 | |
| 18559 | void CodeGenerator::visitHasOwnCache(LHasOwnCache* ins) { |
| 18560 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 18561 | TypedOrValueRegister value = |
| 18562 | toConstantOrRegister(ins, LHasOwnCache::ValueIndex, |
| 18563 | ins->mir()->value()->type()) |
| 18564 | .reg(); |
| 18565 | TypedOrValueRegister id = toConstantOrRegister(ins, LHasOwnCache::IdIndex, |
| 18566 | ins->mir()->idval()->type()) |
| 18567 | .reg(); |
| 18568 | Register output = ToRegister(ins->output()); |
| 18569 | |
| 18570 | IonHasOwnIC cache(liveRegs, value, id, output); |
| 18571 | addIC(ins, allocateIC(cache)); |
| 18572 | } |
| 18573 | |
| 18574 | void CodeGenerator::visitCheckPrivateFieldCache(LCheckPrivateFieldCache* ins) { |
| 18575 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 18576 | TypedOrValueRegister value = |
| 18577 | toConstantOrRegister(ins, LCheckPrivateFieldCache::ValueIndex, |
| 18578 | ins->mir()->value()->type()) |
| 18579 | .reg(); |
| 18580 | TypedOrValueRegister id = |
| 18581 | toConstantOrRegister(ins, LCheckPrivateFieldCache::IdIndex, |
| 18582 | ins->mir()->idval()->type()) |
| 18583 | .reg(); |
| 18584 | Register output = ToRegister(ins->output()); |
| 18585 | |
| 18586 | IonCheckPrivateFieldIC cache(liveRegs, value, id, output); |
| 18587 | addIC(ins, allocateIC(cache)); |
| 18588 | } |
| 18589 | |
| 18590 | void CodeGenerator::visitNewPrivateName(LNewPrivateName* ins) { |
| 18591 | pushArg(ImmGCPtr(ins->mir()->name())); |
| 18592 | |
| 18593 | using Fn = JS::Symbol* (*)(JSContext*, Handle<JSAtom*>); |
| 18594 | callVM<Fn, NewPrivateName>(ins); |
| 18595 | } |
| 18596 | |
| 18597 | void CodeGenerator::visitDeleteProperty(LDeleteProperty* lir) { |
| 18598 | pushArg(ImmGCPtr(lir->mir()->name())); |
| 18599 | pushArg(ToValue(lir->value())); |
| 18600 | |
| 18601 | using Fn = bool (*)(JSContext*, HandleValue, Handle<PropertyName*>, bool*); |
| 18602 | if (lir->mir()->strict()) { |
| 18603 | callVM<Fn, DelPropOperation<true>>(lir); |
| 18604 | } else { |
| 18605 | callVM<Fn, DelPropOperation<false>>(lir); |
| 18606 | } |
| 18607 | } |
| 18608 | |
| 18609 | void CodeGenerator::visitDeleteElement(LDeleteElement* lir) { |
| 18610 | pushArg(ToValue(lir->index())); |
| 18611 | pushArg(ToValue(lir->value())); |
| 18612 | |
| 18613 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue, bool*); |
| 18614 | if (lir->mir()->strict()) { |
| 18615 | callVM<Fn, DelElemOperation<true>>(lir); |
| 18616 | } else { |
| 18617 | callVM<Fn, DelElemOperation<false>>(lir); |
| 18618 | } |
| 18619 | } |
| 18620 | |
| 18621 | void CodeGenerator::visitObjectToIterator(LObjectToIterator* lir) { |
| 18622 | Register obj = ToRegister(lir->object()); |
| 18623 | Register iterObj = ToRegister(lir->output()); |
| 18624 | Register temp = ToRegister(lir->temp0()); |
| 18625 | Register temp2 = ToRegister(lir->temp1()); |
| 18626 | Register temp3 = ToRegister(lir->temp2()); |
| 18627 | |
| 18628 | using Fn = PropertyIteratorObject* (*)(JSContext*, HandleObject); |
| 18629 | OutOfLineCode* ool = nullptr; |
| 18630 | |
| 18631 | if (lir->mir()->skipRegistration()) { |
| 18632 | if (lir->mir()->wantsIndices()) { |
| 18633 | ool = oolCallVM<Fn, GetIteratorWithIndicesForObjectKeys>( |
| 18634 | lir, ArgList(obj), StoreRegisterTo(iterObj)); |
| 18635 | } else { |
| 18636 | ool = oolCallVM<Fn, GetIteratorForObjectKeys>(lir, ArgList(obj), |
| 18637 | StoreRegisterTo(iterObj)); |
| 18638 | } |
| 18639 | } else { |
| 18640 | if (lir->mir()->wantsIndices()) { |
| 18641 | ool = oolCallVM<Fn, GetIteratorWithIndices>(lir, ArgList(obj), |
| 18642 | StoreRegisterTo(iterObj)); |
| 18643 | } else { |
| 18644 | ool = oolCallVM<Fn, GetIterator>(lir, ArgList(obj), |
| 18645 | StoreRegisterTo(iterObj)); |
| 18646 | } |
| 18647 | } |
| 18648 | |
| 18649 | #ifdef DEBUG1 |
| 18650 | if (!lir->mir()->getAliasSet().isStore()) { |
| 18651 | MOZ_ASSERT(lir->mir()->skipRegistration())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->mir()->skipRegistration())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->mir()->skipRegistration ()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->mir()->skipRegistration()", "./../../../../js/src/jit/CodeGenerator.cpp" , 18651); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->skipRegistration()" ")"); do { MOZ_CrashSequence(__null, 18651); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 18652 | Label done; |
| 18653 | masm.branchTestObjectIsProxy(false, obj, temp, &done); |
| 18654 | masm.assumeUnreachable("ObjectToIterator on a proxy must be a store."); |
| 18655 | masm.bind(&done); |
| 18656 | } |
| 18657 | #endif |
| 18658 | |
| 18659 | masm.maybeLoadIteratorFromShape(obj, iterObj, temp, temp2, temp3, |
| 18660 | ool->entry(), |
| 18661 | !lir->mir()->skipRegistration()); |
| 18662 | |
| 18663 | Register nativeIter = temp; |
| 18664 | masm.loadPrivate( |
| 18665 | Address(iterObj, PropertyIteratorObject::offsetOfIteratorSlot()), |
| 18666 | nativeIter); |
| 18667 | |
| 18668 | Address iterFlagsAddr(nativeIter, NativeIterator::offsetOfFlags()); |
| 18669 | if (lir->mir()->wantsIndices()) { |
| 18670 | // At least one consumer of the output of this iterator has been optimized |
| 18671 | // to use iterator indices. If the cached iterator doesn't include indices, |
| 18672 | // but it was marked to indicate that we can create them if needed, then we |
| 18673 | // do a VM call to replace the cached iterator with a fresh iterator |
| 18674 | // including indices. |
| 18675 | masm.branchTest32(Assembler::NonZero, iterFlagsAddr, |
| 18676 | Imm32(NativeIterator::Flags::IndicesSupported), |
| 18677 | ool->entry()); |
| 18678 | } |
| 18679 | |
| 18680 | if (!lir->mir()->skipRegistration()) { |
| 18681 | masm.storePtr(obj, Address(nativeIter, |
| 18682 | NativeIterator::offsetOfObjectBeingIterated())); |
| 18683 | masm.or32(Imm32(NativeIterator::Flags::Active), iterFlagsAddr); |
| 18684 | |
| 18685 | Register enumeratorsAddr = temp2; |
| 18686 | masm.movePtr(ImmPtr(lir->mir()->enumeratorsAddr()), enumeratorsAddr); |
| 18687 | masm.registerIterator(enumeratorsAddr, nativeIter, temp3); |
| 18688 | |
| 18689 | // Generate post-write barrier for storing to |
| 18690 | // |iterObj->objectBeingIterated_|. We already know that |iterObj| is |
| 18691 | // tenured, so we only have to check |obj|. |
| 18692 | Label skipBarrier; |
| 18693 | masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp2, &skipBarrier); |
| 18694 | { |
| 18695 | LiveRegisterSet save = liveVolatileRegs(lir); |
| 18696 | save.takeUnchecked(temp); |
| 18697 | save.takeUnchecked(temp2); |
| 18698 | save.takeUnchecked(temp3); |
| 18699 | if (iterObj.volatile_()) { |
| 18700 | save.addUnchecked(iterObj); |
| 18701 | } |
| 18702 | |
| 18703 | masm.PushRegsInMask(save); |
| 18704 | emitPostWriteBarrier(iterObj); |
| 18705 | masm.PopRegsInMask(save); |
| 18706 | } |
| 18707 | masm.bind(&skipBarrier); |
| 18708 | } |
| 18709 | |
| 18710 | masm.bind(ool->rejoin()); |
| 18711 | } |
| 18712 | |
| 18713 | void CodeGenerator::visitValueToIterator(LValueToIterator* lir) { |
| 18714 | pushArg(ToValue(lir->value())); |
| 18715 | |
| 18716 | using Fn = PropertyIteratorObject* (*)(JSContext*, HandleValue); |
| 18717 | callVM<Fn, ValueToIterator>(lir); |
| 18718 | } |
| 18719 | |
| 18720 | void CodeGenerator::emitIteratorHasIndicesAndBranch(Register iterator, |
| 18721 | Register object, |
| 18722 | Register temp, |
| 18723 | Register temp2, |
| 18724 | Label* ifFalse) { |
| 18725 | // Check that the iterator has indices available. |
| 18726 | Address nativeIterAddr(iterator, |
| 18727 | PropertyIteratorObject::offsetOfIteratorSlot()); |
| 18728 | masm.loadPrivate(nativeIterAddr, temp); |
| 18729 | masm.branchTest32(Assembler::Zero, |
| 18730 | Address(temp, NativeIterator::offsetOfFlags()), |
| 18731 | Imm32(NativeIterator::Flags::IndicesAvailable), ifFalse); |
| 18732 | |
| 18733 | // Guard that the first shape stored in the iterator matches the current |
| 18734 | // shape of the iterated object. |
| 18735 | Address objShapeAddr(temp, NativeIterator::offsetOfObjectShape()); |
| 18736 | masm.loadPtr(objShapeAddr, temp); |
| 18737 | masm.branchTestObjShape(Assembler::NotEqual, object, temp, temp2, object, |
| 18738 | ifFalse); |
| 18739 | } |
| 18740 | |
| 18741 | void CodeGenerator::visitIteratorHasIndicesAndBranch( |
| 18742 | LIteratorHasIndicesAndBranch* lir) { |
| 18743 | Register iterator = ToRegister(lir->iterator()); |
| 18744 | Register object = ToRegister(lir->object()); |
| 18745 | Register temp = ToRegister(lir->temp0()); |
| 18746 | Register temp2 = ToRegister(lir->temp1()); |
| 18747 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
| 18748 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
| 18749 | |
| 18750 | emitIteratorHasIndicesAndBranch(iterator, object, temp, temp2, ifFalse); |
| 18751 | |
| 18752 | if (!isNextBlock(lir->ifTrue()->lir())) { |
| 18753 | masm.jump(ifTrue); |
| 18754 | } |
| 18755 | } |
| 18756 | |
| 18757 | void CodeGenerator::visitIteratorsMatchAndHaveIndicesAndBranch( |
| 18758 | LIteratorsMatchAndHaveIndicesAndBranch* lir) { |
| 18759 | Register iterator = ToRegister(lir->iterator()); |
| 18760 | Register otherIterator = ToRegister(lir->otherIterator()); |
| 18761 | Register object = ToRegister(lir->object()); |
| 18762 | Register temp = ToRegister(lir->temp0()); |
| 18763 | Register temp2 = ToRegister(lir->temp1()); |
| 18764 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
| 18765 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
| 18766 | |
| 18767 | // Check that the iterators match, and then we can use either iterator |
| 18768 | // as a basis as if this were visitIteratorHasIndicesAndBranch |
| 18769 | masm.branchPtr(Assembler::NotEqual, iterator, otherIterator, ifFalse); |
| 18770 | |
| 18771 | emitIteratorHasIndicesAndBranch(iterator, object, temp, temp2, ifFalse); |
| 18772 | |
| 18773 | if (!isNextBlock(lir->ifTrue()->lir())) { |
| 18774 | masm.jump(ifTrue); |
| 18775 | } |
| 18776 | } |
| 18777 | |
| 18778 | void CodeGenerator::visitLoadSlotByIteratorIndexCommon(Register object, |
| 18779 | Register indexScratch, |
| 18780 | Register kindScratch, |
| 18781 | ValueOperand result) { |
| 18782 | Label notDynamicSlot, notFixedSlot, done; |
| 18783 | masm.branch32(Assembler::NotEqual, kindScratch, |
| 18784 | Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), |
| 18785 | ¬DynamicSlot); |
| 18786 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), kindScratch); |
| 18787 | masm.loadValue(BaseValueIndex(kindScratch, indexScratch), result); |
| 18788 | masm.jump(&done); |
| 18789 | |
| 18790 | masm.bind(¬DynamicSlot); |
| 18791 | masm.branch32(Assembler::NotEqual, kindScratch, |
| 18792 | Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), ¬FixedSlot); |
| 18793 | // Fixed slot |
| 18794 | masm.loadValue(BaseValueIndex(object, indexScratch, sizeof(NativeObject)), |
| 18795 | result); |
| 18796 | masm.jump(&done); |
| 18797 | masm.bind(¬FixedSlot); |
| 18798 | |
| 18799 | #ifdef DEBUG1 |
| 18800 | Label kindOkay; |
| 18801 | masm.branch32(Assembler::Equal, kindScratch, |
| 18802 | Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); |
| 18803 | masm.assumeUnreachable("Invalid PropertyIndex::Kind"); |
| 18804 | masm.bind(&kindOkay); |
| 18805 | #endif |
| 18806 | |
| 18807 | // Dense element |
| 18808 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), kindScratch); |
| 18809 | Label indexOkay; |
| 18810 | Address initLength(kindScratch, ObjectElements::offsetOfInitializedLength()); |
| 18811 | masm.branch32(Assembler::Above, initLength, indexScratch, &indexOkay); |
| 18812 | masm.assumeUnreachable("Dense element out of bounds"); |
| 18813 | masm.bind(&indexOkay); |
| 18814 | |
| 18815 | masm.loadValue(BaseObjectElementIndex(kindScratch, indexScratch), result); |
| 18816 | masm.branchTestMagicValue(Assembler::NotEqual, result, JS_ELEMENTS_HOLE, |
| 18817 | &done); |
| 18818 | masm.assumeUnreachable("Dense element is a hole"); |
| 18819 | masm.bind(&done); |
| 18820 | } |
| 18821 | |
| 18822 | void CodeGenerator::visitLoadSlotByIteratorIndex( |
| 18823 | LLoadSlotByIteratorIndex* lir) { |
| 18824 | Register object = ToRegister(lir->object()); |
| 18825 | Register iterator = ToRegister(lir->iterator()); |
| 18826 | Register indexScratch = ToRegister(lir->temp0()); |
| 18827 | Register kindScratch = ToRegister(lir->temp1()); |
| 18828 | ValueOperand result = ToOutValue(lir); |
| 18829 | |
| 18830 | masm.extractCurrentIndexAndKindFromIterator(iterator, indexScratch, |
| 18831 | kindScratch); |
| 18832 | |
| 18833 | visitLoadSlotByIteratorIndexCommon(object, indexScratch, kindScratch, result); |
| 18834 | } |
| 18835 | |
| 18836 | void CodeGenerator::visitLoadSlotByIteratorIndexIndexed( |
| 18837 | LLoadSlotByIteratorIndexIndexed* lir) { |
| 18838 | Register object = ToRegister(lir->object()); |
| 18839 | Register iterator = ToRegister(lir->iterator()); |
| 18840 | Register index = ToRegister(lir->index()); |
| 18841 | Register indexScratch = ToRegister(lir->temp0()); |
| 18842 | Register kindScratch = ToRegister(lir->temp1()); |
| 18843 | ValueOperand result = ToOutValue(lir); |
| 18844 | |
| 18845 | masm.extractIndexAndKindFromIteratorByIterIndex(iterator, index, kindScratch, |
| 18846 | indexScratch); |
| 18847 | |
| 18848 | visitLoadSlotByIteratorIndexCommon(object, indexScratch, kindScratch, result); |
| 18849 | } |
| 18850 | |
| 18851 | void CodeGenerator::visitStoreSlotByIteratorIndexCommon(Register object, |
| 18852 | Register indexScratch, |
| 18853 | Register kindScratch, |
| 18854 | ValueOperand value) { |
| 18855 | Label notDynamicSlot, notFixedSlot, done, doStore; |
| 18856 | masm.branch32(Assembler::NotEqual, kindScratch, |
| 18857 | Imm32(uint32_t(PropertyIndex::Kind::DynamicSlot)), |
| 18858 | ¬DynamicSlot); |
| 18859 | masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), kindScratch); |
| 18860 | masm.computeEffectiveAddress(BaseValueIndex(kindScratch, indexScratch), |
| 18861 | indexScratch); |
| 18862 | masm.jump(&doStore); |
| 18863 | |
| 18864 | masm.bind(¬DynamicSlot); |
| 18865 | masm.branch32(Assembler::NotEqual, kindScratch, |
| 18866 | Imm32(uint32_t(PropertyIndex::Kind::FixedSlot)), ¬FixedSlot); |
| 18867 | // Fixed slot |
| 18868 | masm.computeEffectiveAddress( |
| 18869 | BaseValueIndex(object, indexScratch, sizeof(NativeObject)), indexScratch); |
| 18870 | masm.jump(&doStore); |
| 18871 | masm.bind(¬FixedSlot); |
| 18872 | |
| 18873 | #ifdef DEBUG1 |
| 18874 | Label kindOkay; |
| 18875 | masm.branch32(Assembler::Equal, kindScratch, |
| 18876 | Imm32(uint32_t(PropertyIndex::Kind::Element)), &kindOkay); |
| 18877 | masm.assumeUnreachable("Invalid PropertyIndex::Kind"); |
| 18878 | masm.bind(&kindOkay); |
| 18879 | #endif |
| 18880 | |
| 18881 | // Dense element |
| 18882 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), kindScratch); |
| 18883 | Label indexOkay; |
| 18884 | Address initLength(kindScratch, ObjectElements::offsetOfInitializedLength()); |
| 18885 | masm.branch32(Assembler::Above, initLength, indexScratch, &indexOkay); |
| 18886 | masm.assumeUnreachable("Dense element out of bounds"); |
| 18887 | masm.bind(&indexOkay); |
| 18888 | |
| 18889 | BaseObjectElementIndex elementAddress(kindScratch, indexScratch); |
| 18890 | masm.computeEffectiveAddress(elementAddress, indexScratch); |
| 18891 | |
| 18892 | masm.bind(&doStore); |
| 18893 | Address storeAddress(indexScratch, 0); |
| 18894 | emitPreBarrier(storeAddress); |
| 18895 | masm.storeValue(value, storeAddress); |
| 18896 | |
| 18897 | masm.branchValueIsNurseryCell(Assembler::NotEqual, value, kindScratch, &done); |
| 18898 | masm.branchPtrInNurseryChunk(Assembler::Equal, object, kindScratch, &done); |
| 18899 | |
| 18900 | saveVolatile(kindScratch); |
| 18901 | emitPostWriteBarrier(object); |
| 18902 | restoreVolatile(kindScratch); |
| 18903 | |
| 18904 | masm.bind(&done); |
| 18905 | } |
| 18906 | |
| 18907 | void CodeGenerator::visitStoreSlotByIteratorIndex( |
| 18908 | LStoreSlotByIteratorIndex* lir) { |
| 18909 | Register object = ToRegister(lir->object()); |
| 18910 | Register iterator = ToRegister(lir->iterator()); |
| 18911 | ValueOperand value = ToValue(lir->value()); |
| 18912 | Register indexScratch = ToRegister(lir->temp0()); |
| 18913 | Register kindScratch = ToRegister(lir->temp1()); |
| 18914 | |
| 18915 | masm.extractCurrentIndexAndKindFromIterator(iterator, indexScratch, |
| 18916 | kindScratch); |
| 18917 | |
| 18918 | visitStoreSlotByIteratorIndexCommon(object, indexScratch, kindScratch, value); |
| 18919 | } |
| 18920 | |
| 18921 | void CodeGenerator::visitStoreSlotByIteratorIndexIndexed( |
| 18922 | LStoreSlotByIteratorIndexIndexed* lir) { |
| 18923 | Register object = ToRegister(lir->object()); |
| 18924 | Register iterator = ToRegister(lir->iterator()); |
| 18925 | Register index = ToRegister(lir->index()); |
| 18926 | ValueOperand value = ToValue(lir->value()); |
| 18927 | Register indexScratch = ToRegister(lir->temp0()); |
| 18928 | Register kindScratch = ToRegister(lir->temp1()); |
| 18929 | |
| 18930 | masm.extractIndexAndKindFromIteratorByIterIndex(iterator, index, kindScratch, |
| 18931 | indexScratch); |
| 18932 | |
| 18933 | visitStoreSlotByIteratorIndexCommon(object, indexScratch, kindScratch, value); |
| 18934 | } |
| 18935 | |
| 18936 | void CodeGenerator::visitSetPropertyCache(LSetPropertyCache* ins) { |
| 18937 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 18938 | Register objReg = ToRegister(ins->object()); |
| 18939 | Register temp = ToRegister(ins->temp0()); |
| 18940 | |
| 18941 | ConstantOrRegister id = toConstantOrRegister(ins, LSetPropertyCache::IdIndex, |
| 18942 | ins->mir()->idval()->type()); |
| 18943 | ConstantOrRegister value = toConstantOrRegister( |
| 18944 | ins, LSetPropertyCache::ValueIndex, ins->mir()->value()->type()); |
| 18945 | |
| 18946 | addSetPropertyCache(ins, liveRegs, objReg, temp, id, value, |
| 18947 | ins->mir()->strict()); |
| 18948 | } |
| 18949 | |
| 18950 | void CodeGenerator::visitThrow(LThrow* lir) { |
| 18951 | pushArg(ToValue(lir->value())); |
| 18952 | |
| 18953 | using Fn = bool (*)(JSContext*, HandleValue); |
| 18954 | callVM<Fn, js::ThrowOperation>(lir); |
| 18955 | } |
| 18956 | |
| 18957 | void CodeGenerator::visitThrowWithStack(LThrowWithStack* lir) { |
| 18958 | pushArg(ToValue(lir->stack())); |
| 18959 | pushArg(ToValue(lir->value())); |
| 18960 | |
| 18961 | using Fn = bool (*)(JSContext*, HandleValue, HandleValue); |
| 18962 | callVM<Fn, js::ThrowWithStackOperation>(lir); |
| 18963 | } |
| 18964 | |
| 18965 | void CodeGenerator::emitTypeOfJSType(JSValueType type, Register output) { |
| 18966 | switch (type) { |
| 18967 | case JSVAL_TYPE_OBJECT: |
| 18968 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
| 18969 | break; |
| 18970 | case JSVAL_TYPE_DOUBLE: |
| 18971 | case JSVAL_TYPE_INT32: |
| 18972 | masm.move32(Imm32(JSTYPE_NUMBER), output); |
| 18973 | break; |
| 18974 | case JSVAL_TYPE_BOOLEAN: |
| 18975 | masm.move32(Imm32(JSTYPE_BOOLEAN), output); |
| 18976 | break; |
| 18977 | case JSVAL_TYPE_UNDEFINED: |
| 18978 | masm.move32(Imm32(JSTYPE_UNDEFINED), output); |
| 18979 | break; |
| 18980 | case JSVAL_TYPE_NULL: |
| 18981 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
| 18982 | break; |
| 18983 | case JSVAL_TYPE_STRING: |
| 18984 | masm.move32(Imm32(JSTYPE_STRING), output); |
| 18985 | break; |
| 18986 | case JSVAL_TYPE_SYMBOL: |
| 18987 | masm.move32(Imm32(JSTYPE_SYMBOL), output); |
| 18988 | break; |
| 18989 | case JSVAL_TYPE_BIGINT: |
| 18990 | masm.move32(Imm32(JSTYPE_BIGINT), output); |
| 18991 | break; |
| 18992 | default: |
| 18993 | MOZ_CRASH("Unsupported JSValueType")do { do { } while (false); MOZ_ReportCrash("" "Unsupported JSValueType" , "./../../../../js/src/jit/CodeGenerator.cpp", 18993); AnnotateMozCrashReason ("MOZ_CRASH(" "Unsupported JSValueType" ")"); do { MOZ_CrashSequence (__null, 18993); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 18994 | } |
| 18995 | } |
| 18996 | |
| 18997 | void CodeGenerator::emitTypeOfCheck(JSValueType type, Register tag, |
| 18998 | Register output, Label* done, |
| 18999 | Label* oolObject) { |
| 19000 | Label notMatch; |
| 19001 | switch (type) { |
| 19002 | case JSVAL_TYPE_OBJECT: |
| 19003 | // The input may be a callable object (result is "function") or |
| 19004 | // may emulate undefined (result is "undefined"). Use an OOL path. |
| 19005 | masm.branchTestObject(Assembler::Equal, tag, oolObject); |
| 19006 | return; |
| 19007 | case JSVAL_TYPE_DOUBLE: |
| 19008 | case JSVAL_TYPE_INT32: |
| 19009 | masm.branchTestNumber(Assembler::NotEqual, tag, ¬Match); |
| 19010 | break; |
| 19011 | default: |
| 19012 | masm.branchTestType(Assembler::NotEqual, tag, type, ¬Match); |
| 19013 | break; |
| 19014 | } |
| 19015 | |
| 19016 | emitTypeOfJSType(type, output); |
| 19017 | masm.jump(done); |
| 19018 | masm.bind(¬Match); |
| 19019 | } |
| 19020 | |
| 19021 | void CodeGenerator::visitTypeOfV(LTypeOfV* lir) { |
| 19022 | ValueOperand value = ToValue(lir->input()); |
| 19023 | Register output = ToRegister(lir->output()); |
| 19024 | Register tag = masm.extractTag(value, output); |
| 19025 | |
| 19026 | Label done; |
| 19027 | |
| 19028 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 19029 | ValueOperand input = ToValue(lir->input()); |
| 19030 | Register temp = ToTempUnboxRegister(lir->temp0()); |
| 19031 | Register output = ToRegister(lir->output()); |
| 19032 | |
| 19033 | Register obj = masm.extractObject(input, temp); |
| 19034 | emitTypeOfObject(obj, output, ool.rejoin()); |
| 19035 | masm.jump(ool.rejoin()); |
| 19036 | }); |
| 19037 | addOutOfLineCode(ool, lir->mir()); |
| 19038 | |
| 19039 | const std::initializer_list<JSValueType> defaultOrder = { |
| 19040 | JSVAL_TYPE_OBJECT, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_UNDEFINED, |
| 19041 | JSVAL_TYPE_NULL, JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_STRING, |
| 19042 | JSVAL_TYPE_SYMBOL, JSVAL_TYPE_BIGINT}; |
| 19043 | |
| 19044 | mozilla::EnumSet<JSValueType, uint32_t> remaining(defaultOrder); |
| 19045 | |
| 19046 | // Generate checks for previously observed types first. |
| 19047 | // The TypeDataList is sorted by descending frequency. |
| 19048 | for (auto& observed : lir->mir()->observedTypes()) { |
| 19049 | JSValueType type = observed.type(); |
| 19050 | |
| 19051 | // Unify number types. |
| 19052 | if (type == JSVAL_TYPE_INT32) { |
| 19053 | type = JSVAL_TYPE_DOUBLE; |
| 19054 | } |
| 19055 | |
| 19056 | remaining -= type; |
| 19057 | |
| 19058 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
| 19059 | } |
| 19060 | |
| 19061 | // Generate checks for remaining types. |
| 19062 | for (auto type : defaultOrder) { |
| 19063 | if (!remaining.contains(type)) { |
| 19064 | continue; |
| 19065 | } |
| 19066 | remaining -= type; |
| 19067 | |
| 19068 | if (remaining.isEmpty() && type != JSVAL_TYPE_OBJECT) { |
| 19069 | // We can skip the check for the last remaining type, unless the type is |
| 19070 | // JSVAL_TYPE_OBJECT, which may have to go through the OOL path. |
| 19071 | #ifdef DEBUG1 |
| 19072 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
| 19073 | masm.assumeUnreachable("Unexpected Value type in visitTypeOfV"); |
| 19074 | #else |
| 19075 | emitTypeOfJSType(type, output); |
| 19076 | #endif |
| 19077 | } else { |
| 19078 | emitTypeOfCheck(type, tag, output, &done, ool->entry()); |
| 19079 | } |
| 19080 | } |
| 19081 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 19081); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "remaining.isEmpty()" ")"); do { MOZ_CrashSequence (__null, 19081); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 19082 | |
| 19083 | masm.bind(&done); |
| 19084 | masm.bind(ool->rejoin()); |
| 19085 | } |
| 19086 | |
| 19087 | void CodeGenerator::emitTypeOfObject(Register obj, Register output, |
| 19088 | Label* done) { |
| 19089 | Label slowCheck, isObject, isCallable, isUndefined; |
| 19090 | masm.typeOfObject(obj, output, &slowCheck, &isObject, &isCallable, |
| 19091 | &isUndefined); |
| 19092 | |
| 19093 | masm.bind(&isCallable); |
| 19094 | masm.move32(Imm32(JSTYPE_FUNCTION), output); |
| 19095 | masm.jump(done); |
| 19096 | |
| 19097 | masm.bind(&isUndefined); |
| 19098 | masm.move32(Imm32(JSTYPE_UNDEFINED), output); |
| 19099 | masm.jump(done); |
| 19100 | |
| 19101 | masm.bind(&isObject); |
| 19102 | masm.move32(Imm32(JSTYPE_OBJECT), output); |
| 19103 | masm.jump(done); |
| 19104 | |
| 19105 | masm.bind(&slowCheck); |
| 19106 | |
| 19107 | saveVolatile(output); |
| 19108 | using Fn = JSType (*)(JSObject*); |
| 19109 | masm.setupAlignedABICall(); |
| 19110 | masm.passABIArg(obj); |
| 19111 | masm.callWithABI<Fn, js::TypeOfObject>(); |
| 19112 | masm.storeCallInt32Result(output); |
| 19113 | restoreVolatile(output); |
| 19114 | } |
| 19115 | |
| 19116 | void CodeGenerator::visitTypeOfO(LTypeOfO* lir) { |
| 19117 | Register obj = ToRegister(lir->object()); |
| 19118 | Register output = ToRegister(lir->output()); |
| 19119 | |
| 19120 | Label done; |
| 19121 | emitTypeOfObject(obj, output, &done); |
| 19122 | masm.bind(&done); |
| 19123 | } |
| 19124 | |
| 19125 | void CodeGenerator::visitTypeOfName(LTypeOfName* lir) { |
| 19126 | Register input = ToRegister(lir->input()); |
| 19127 | Register output = ToRegister(lir->output()); |
| 19128 | |
| 19129 | #ifdef DEBUG1 |
| 19130 | Label ok; |
| 19131 | masm.branch32(Assembler::Below, input, Imm32(JSTYPE_LIMIT), &ok); |
| 19132 | masm.assumeUnreachable("bad JSType"); |
| 19133 | masm.bind(&ok); |
| 19134 | #endif |
| 19135 | |
| 19136 | static_assert(JSTYPE_UNDEFINED == 0); |
| 19137 | |
| 19138 | masm.movePtr(ImmPtr(&gen->runtime->names().undefined), output); |
| 19139 | masm.loadPtr(BaseIndex(output, input, ScalePointer), output); |
| 19140 | } |
| 19141 | |
| 19142 | void CodeGenerator::emitTypeOfIsObjectOOL(MTypeOfIs* mir, Register obj, |
| 19143 | Register output) { |
| 19144 | saveVolatile(output); |
| 19145 | using Fn = JSType (*)(JSObject*); |
| 19146 | masm.setupAlignedABICall(); |
| 19147 | masm.passABIArg(obj); |
| 19148 | masm.callWithABI<Fn, js::TypeOfObject>(); |
| 19149 | masm.storeCallInt32Result(output); |
| 19150 | restoreVolatile(output); |
| 19151 | |
| 19152 | auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false); |
| 19153 | masm.cmp32Set(cond, output, Imm32(mir->jstype()), output); |
| 19154 | } |
| 19155 | |
| 19156 | void CodeGenerator::emitTypeOfIsObject(MTypeOfIs* mir, Register obj, |
| 19157 | Register output, Label* success, |
| 19158 | Label* fail, Label* slowCheck) { |
| 19159 | Label* isObject = fail; |
| 19160 | Label* isFunction = fail; |
| 19161 | Label* isUndefined = fail; |
| 19162 | |
| 19163 | switch (mir->jstype()) { |
| 19164 | case JSTYPE_UNDEFINED: |
| 19165 | isUndefined = success; |
| 19166 | break; |
| 19167 | |
| 19168 | case JSTYPE_OBJECT: |
| 19169 | isObject = success; |
| 19170 | break; |
| 19171 | |
| 19172 | case JSTYPE_FUNCTION: |
| 19173 | isFunction = success; |
| 19174 | break; |
| 19175 | |
| 19176 | case JSTYPE_STRING: |
| 19177 | case JSTYPE_NUMBER: |
| 19178 | case JSTYPE_BOOLEAN: |
| 19179 | case JSTYPE_SYMBOL: |
| 19180 | case JSTYPE_BIGINT: |
| 19181 | case JSTYPE_LIMIT: |
| 19182 | MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19182); AnnotateMozCrashReason ("MOZ_CRASH(" "Primitive type" ")"); do { MOZ_CrashSequence(__null , 19182); __attribute__((nomerge)) ::abort(); } while (false) ; } while (false); |
| 19183 | } |
| 19184 | |
| 19185 | masm.typeOfObject(obj, output, slowCheck, isObject, isFunction, isUndefined); |
| 19186 | |
| 19187 | auto op = mir->jsop(); |
| 19188 | |
| 19189 | Label done; |
| 19190 | masm.bind(fail); |
| 19191 | masm.move32(Imm32(op == JSOp::Ne || op == JSOp::StrictNe), output); |
| 19192 | masm.jump(&done); |
| 19193 | masm.bind(success); |
| 19194 | masm.move32(Imm32(op == JSOp::Eq || op == JSOp::StrictEq), output); |
| 19195 | masm.bind(&done); |
| 19196 | } |
| 19197 | |
| 19198 | void CodeGenerator::visitTypeOfIsNonPrimitiveV(LTypeOfIsNonPrimitiveV* lir) { |
| 19199 | ValueOperand input = ToValue(lir->input()); |
| 19200 | Register output = ToRegister(lir->output()); |
| 19201 | Register temp = ToTempUnboxRegister(lir->temp0()); |
| 19202 | |
| 19203 | auto* mir = lir->mir(); |
| 19204 | |
| 19205 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 19206 | ValueOperand input = ToValue(lir->input()); |
| 19207 | Register output = ToRegister(lir->output()); |
| 19208 | Register temp = ToTempUnboxRegister(lir->temp0()); |
| 19209 | |
| 19210 | Register obj = masm.extractObject(input, temp); |
| 19211 | |
| 19212 | emitTypeOfIsObjectOOL(lir->mir(), obj, output); |
| 19213 | |
| 19214 | masm.jump(ool.rejoin()); |
| 19215 | }); |
| 19216 | addOutOfLineCode(ool, mir); |
| 19217 | |
| 19218 | Label success, fail; |
| 19219 | |
| 19220 | switch (mir->jstype()) { |
| 19221 | case JSTYPE_UNDEFINED: { |
| 19222 | ScratchTagScope tag(masm, input); |
| 19223 | masm.splitTagForTest(input, tag); |
| 19224 | |
| 19225 | masm.branchTestUndefined(Assembler::Equal, tag, &success); |
| 19226 | masm.branchTestObject(Assembler::NotEqual, tag, &fail); |
| 19227 | break; |
| 19228 | } |
| 19229 | |
| 19230 | case JSTYPE_OBJECT: { |
| 19231 | ScratchTagScope tag(masm, input); |
| 19232 | masm.splitTagForTest(input, tag); |
| 19233 | |
| 19234 | masm.branchTestNull(Assembler::Equal, tag, &success); |
| 19235 | masm.branchTestObject(Assembler::NotEqual, tag, &fail); |
| 19236 | break; |
| 19237 | } |
| 19238 | |
| 19239 | case JSTYPE_FUNCTION: { |
| 19240 | masm.branchTestObject(Assembler::NotEqual, input, &fail); |
| 19241 | break; |
| 19242 | } |
| 19243 | |
| 19244 | case JSTYPE_STRING: |
| 19245 | case JSTYPE_NUMBER: |
| 19246 | case JSTYPE_BOOLEAN: |
| 19247 | case JSTYPE_SYMBOL: |
| 19248 | case JSTYPE_BIGINT: |
| 19249 | case JSTYPE_LIMIT: |
| 19250 | MOZ_CRASH("Primitive type")do { do { } while (false); MOZ_ReportCrash("" "Primitive type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19250); AnnotateMozCrashReason ("MOZ_CRASH(" "Primitive type" ")"); do { MOZ_CrashSequence(__null , 19250); __attribute__((nomerge)) ::abort(); } while (false) ; } while (false); |
| 19251 | } |
| 19252 | |
| 19253 | Register obj = masm.extractObject(input, temp); |
| 19254 | |
| 19255 | emitTypeOfIsObject(mir, obj, output, &success, &fail, ool->entry()); |
| 19256 | |
| 19257 | masm.bind(ool->rejoin()); |
| 19258 | } |
| 19259 | |
| 19260 | void CodeGenerator::visitTypeOfIsNonPrimitiveO(LTypeOfIsNonPrimitiveO* lir) { |
| 19261 | Register input = ToRegister(lir->input()); |
| 19262 | Register output = ToRegister(lir->output()); |
| 19263 | |
| 19264 | auto* mir = lir->mir(); |
| 19265 | |
| 19266 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 19267 | Register input = ToRegister(lir->input()); |
| 19268 | Register output = ToRegister(lir->output()); |
| 19269 | |
| 19270 | emitTypeOfIsObjectOOL(lir->mir(), input, output); |
| 19271 | |
| 19272 | masm.jump(ool.rejoin()); |
| 19273 | }); |
| 19274 | addOutOfLineCode(ool, mir); |
| 19275 | |
| 19276 | Label success, fail; |
| 19277 | emitTypeOfIsObject(mir, input, output, &success, &fail, ool->entry()); |
| 19278 | |
| 19279 | masm.bind(ool->rejoin()); |
| 19280 | } |
| 19281 | |
| 19282 | void CodeGenerator::visitTypeOfIsPrimitive(LTypeOfIsPrimitive* lir) { |
| 19283 | ValueOperand input = ToValue(lir->input()); |
| 19284 | Register output = ToRegister(lir->output()); |
| 19285 | |
| 19286 | auto* mir = lir->mir(); |
| 19287 | auto cond = JSOpToCondition(mir->jsop(), /* isSigned = */ false); |
| 19288 | |
| 19289 | switch (mir->jstype()) { |
| 19290 | case JSTYPE_STRING: |
| 19291 | masm.testStringSet(cond, input, output); |
| 19292 | break; |
| 19293 | case JSTYPE_NUMBER: |
| 19294 | masm.testNumberSet(cond, input, output); |
| 19295 | break; |
| 19296 | case JSTYPE_BOOLEAN: |
| 19297 | masm.testBooleanSet(cond, input, output); |
| 19298 | break; |
| 19299 | case JSTYPE_SYMBOL: |
| 19300 | masm.testSymbolSet(cond, input, output); |
| 19301 | break; |
| 19302 | case JSTYPE_BIGINT: |
| 19303 | masm.testBigIntSet(cond, input, output); |
| 19304 | break; |
| 19305 | |
| 19306 | case JSTYPE_UNDEFINED: |
| 19307 | case JSTYPE_OBJECT: |
| 19308 | case JSTYPE_FUNCTION: |
| 19309 | case JSTYPE_LIMIT: |
| 19310 | MOZ_CRASH("Non-primitive type")do { do { } while (false); MOZ_ReportCrash("" "Non-primitive type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19310); AnnotateMozCrashReason ("MOZ_CRASH(" "Non-primitive type" ")"); do { MOZ_CrashSequence (__null, 19310); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 19311 | } |
| 19312 | } |
| 19313 | |
| 19314 | void CodeGenerator::visitToAsyncIter(LToAsyncIter* lir) { |
| 19315 | pushArg(ToValue(lir->nextMethod())); |
| 19316 | pushArg(ToRegister(lir->iterator())); |
| 19317 | |
| 19318 | using Fn = JSObject* (*)(JSContext*, HandleObject, HandleValue); |
| 19319 | callVM<Fn, js::CreateAsyncFromSyncIterator>(lir); |
| 19320 | } |
| 19321 | |
| 19322 | void CodeGenerator::visitToPropertyKeyCache(LToPropertyKeyCache* lir) { |
| 19323 | LiveRegisterSet liveRegs = lir->safepoint()->liveRegs(); |
| 19324 | ValueOperand input = ToValue(lir->input()); |
| 19325 | ValueOperand output = ToOutValue(lir); |
| 19326 | |
| 19327 | IonToPropertyKeyIC ic(liveRegs, input, output); |
| 19328 | addIC(lir, allocateIC(ic)); |
| 19329 | } |
| 19330 | |
| 19331 | void CodeGenerator::visitLoadElementV(LLoadElementV* load) { |
| 19332 | Register elements = ToRegister(load->elements()); |
| 19333 | const ValueOperand out = ToOutValue(load); |
| 19334 | |
| 19335 | auto source = ToAddressOrBaseObjectElementIndex(elements, load->index()); |
| 19336 | |
| 19337 | source.match([&](auto const& source) { masm.loadValue(source, out); }); |
| 19338 | |
| 19339 | if (load->mir()->needsHoleCheck()) { |
| 19340 | Label testMagic; |
| 19341 | masm.branchTestMagicValue(Assembler::Equal, out, JS_ELEMENTS_HOLE, |
| 19342 | &testMagic); |
| 19343 | bailoutFrom(&testMagic, load->snapshot()); |
| 19344 | } else { |
| 19345 | #ifdef DEBUG1 |
| 19346 | Label ok; |
| 19347 | masm.branchTestMagicValue(Assembler::NotEqual, out, JS_ELEMENTS_HOLE, &ok); |
| 19348 | masm.assumeUnreachable("LoadElementV had incorrect needsHoleCheck"); |
| 19349 | masm.bind(&ok); |
| 19350 | #endif |
| 19351 | } |
| 19352 | } |
| 19353 | |
| 19354 | void CodeGenerator::visitLoadElementHole(LLoadElementHole* lir) { |
| 19355 | Register elements = ToRegister(lir->elements()); |
| 19356 | Register index = ToRegister(lir->index()); |
| 19357 | Register initLength = ToRegister(lir->initLength()); |
| 19358 | const ValueOperand out = ToOutValue(lir); |
| 19359 | |
| 19360 | const MLoadElementHole* mir = lir->mir(); |
| 19361 | |
| 19362 | // If the index is out of bounds, load |undefined|. Otherwise, load the |
| 19363 | // value. |
| 19364 | Label outOfBounds, done; |
| 19365 | masm.spectreBoundsCheck32(index, initLength, out.scratchReg(), &outOfBounds); |
| 19366 | |
| 19367 | masm.loadValue(BaseObjectElementIndex(elements, index), out); |
| 19368 | |
| 19369 | // If the value wasn't a hole, we're done. Otherwise, we'll load undefined. |
| 19370 | masm.branchTestMagicValue(Assembler::NotEqual, out, JS_ELEMENTS_HOLE, &done); |
| 19371 | |
| 19372 | if (mir->needsNegativeIntCheck()) { |
| 19373 | Label loadUndefined; |
| 19374 | masm.jump(&loadUndefined); |
| 19375 | |
| 19376 | masm.bind(&outOfBounds); |
| 19377 | |
| 19378 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 19379 | |
| 19380 | masm.bind(&loadUndefined); |
| 19381 | } else { |
| 19382 | masm.bind(&outOfBounds); |
| 19383 | } |
| 19384 | masm.moveValue(UndefinedValue(), out); |
| 19385 | |
| 19386 | masm.bind(&done); |
| 19387 | } |
| 19388 | |
| 19389 | CodeGenerator::AddressOrBaseIndex CodeGenerator::ToAddressOrBaseIndex( |
| 19390 | Register elements, const LAllocation* index, Scalar::Type type) { |
| 19391 | if (index->isConstant()) { |
| 19392 | return AddressOrBaseIndex(ToAddress(elements, index, type)); |
| 19393 | } |
| 19394 | return AddressOrBaseIndex( |
| 19395 | BaseIndex(elements, ToRegister(index), ScaleFromScalarType(type))); |
| 19396 | } |
| 19397 | |
| 19398 | void CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir) { |
| 19399 | Register elements = ToRegister(lir->elements()); |
| 19400 | Register temp0 = ToTempRegisterOrInvalid(lir->temp0()); |
| 19401 | Register temp1 = ToTempRegisterOrInvalid(lir->temp1()); |
| 19402 | AnyRegister out = ToAnyRegister(lir->output()); |
| 19403 | |
| 19404 | Scalar::Type storageType = lir->mir()->storageType(); |
| 19405 | |
| 19406 | LiveRegisterSet volatileRegs; |
| 19407 | if (MacroAssembler::LoadRequiresCall(storageType)) { |
| 19408 | volatileRegs = liveVolatileRegs(lir); |
| 19409 | } |
| 19410 | |
| 19411 | auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType); |
| 19412 | |
| 19413 | Label fail; |
| 19414 | source.match([&](const auto& source) { |
| 19415 | masm.loadFromTypedArray(storageType, source, out, temp0, temp1, &fail, |
| 19416 | volatileRegs); |
| 19417 | }); |
| 19418 | |
| 19419 | if (fail.used()) { |
| 19420 | bailoutFrom(&fail, lir->snapshot()); |
| 19421 | } |
| 19422 | } |
| 19423 | |
| 19424 | void CodeGenerator::visitLoadUnboxedInt64(LLoadUnboxedInt64* lir) { |
| 19425 | Register elements = ToRegister(lir->elements()); |
| 19426 | Register64 out = ToOutRegister64(lir); |
| 19427 | |
| 19428 | Scalar::Type storageType = lir->mir()->storageType(); |
| 19429 | |
| 19430 | auto source = ToAddressOrBaseIndex(elements, lir->index(), storageType); |
| 19431 | |
| 19432 | source.match([&](const auto& source) { masm.load64(source, out); }); |
| 19433 | } |
| 19434 | |
| 19435 | static bool IsNativeEndian(const LAllocation* littleEndian) { |
| 19436 | constexpr bool isLittleEndian = std::endian::native == std::endian::little; |
| 19437 | return littleEndian->isConstant() && |
| 19438 | ToBoolean(littleEndian) == isLittleEndian; |
| 19439 | } |
| 19440 | |
| 19441 | static void BranchIfNativeEndian(MacroAssembler& masm, |
| 19442 | const LAllocation* littleEndian, |
| 19443 | Label* label) { |
| 19444 | if (!littleEndian->isConstant()) { |
| 19445 | if constexpr (std::endian::native == std::endian::little) { |
| 19446 | masm.branch32(Assembler::NotEqual, ToRegister(littleEndian), Imm32(0), |
| 19447 | label); |
| 19448 | } else { |
| 19449 | masm.branch32(Assembler::Equal, ToRegister(littleEndian), Imm32(0), |
| 19450 | label); |
| 19451 | } |
| 19452 | } |
| 19453 | } |
| 19454 | |
| 19455 | void CodeGenerator::visitLoadDataViewElement(LLoadDataViewElement* lir) { |
| 19456 | Register elements = ToRegister(lir->elements()); |
| 19457 | const LAllocation* littleEndian = lir->littleEndian(); |
| 19458 | Register temp1 = ToTempRegisterOrInvalid(lir->temp0()); |
| 19459 | Register temp2 = ToTempRegisterOrInvalid(lir->temp1()); |
| 19460 | Register64 temp64 = ToTempRegister64OrInvalid(lir->temp2()); |
| 19461 | AnyRegister out = ToAnyRegister(lir->output()); |
| 19462 | |
| 19463 | Scalar::Type storageType = lir->mir()->storageType(); |
| 19464 | |
| 19465 | LiveRegisterSet volatileRegs; |
| 19466 | if (MacroAssembler::LoadRequiresCall(storageType)) { |
| 19467 | volatileRegs = liveVolatileRegs(lir); |
| 19468 | } |
| 19469 | |
| 19470 | auto source = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8); |
| 19471 | |
| 19472 | bool noSwap = IsNativeEndian(littleEndian); |
| 19473 | |
| 19474 | // Directly load if no byte swap is needed and the platform supports unaligned |
| 19475 | // accesses for the access. (Such support is assumed for integer types.) |
| 19476 | if (noSwap && (!Scalar::isFloatingType(storageType) || |
| 19477 | MacroAssembler::SupportsFastUnalignedFPAccesses())) { |
| 19478 | Label fail; |
| 19479 | source.match([&](const auto& source) { |
| 19480 | masm.loadFromTypedArray(storageType, source, out, temp1, temp2, &fail, |
| 19481 | volatileRegs); |
| 19482 | }); |
| 19483 | |
| 19484 | if (fail.used()) { |
| 19485 | bailoutFrom(&fail, lir->snapshot()); |
| 19486 | } |
| 19487 | return; |
| 19488 | } |
| 19489 | |
| 19490 | // Load the value into a gpr register. |
| 19491 | source.match([&](const auto& source) { |
| 19492 | switch (storageType) { |
| 19493 | case Scalar::Int16: |
| 19494 | masm.load16UnalignedSignExtend(source, out.gpr()); |
| 19495 | break; |
| 19496 | case Scalar::Uint16: |
| 19497 | masm.load16UnalignedZeroExtend(source, out.gpr()); |
| 19498 | break; |
| 19499 | case Scalar::Int32: |
| 19500 | masm.load32Unaligned(source, out.gpr()); |
| 19501 | break; |
| 19502 | case Scalar::Uint32: |
| 19503 | masm.load32Unaligned(source, out.isFloat() ? temp1 : out.gpr()); |
| 19504 | break; |
| 19505 | case Scalar::Float16: |
| 19506 | masm.load16UnalignedZeroExtend(source, temp1); |
| 19507 | break; |
| 19508 | case Scalar::Float32: |
| 19509 | masm.load32Unaligned(source, temp1); |
| 19510 | break; |
| 19511 | case Scalar::Float64: |
| 19512 | masm.load64Unaligned(source, temp64); |
| 19513 | break; |
| 19514 | case Scalar::Int8: |
| 19515 | case Scalar::Uint8: |
| 19516 | case Scalar::Uint8Clamped: |
| 19517 | case Scalar::BigInt64: |
| 19518 | case Scalar::BigUint64: |
| 19519 | default: |
| 19520 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19520); AnnotateMozCrashReason ("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence (__null, 19520); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 19521 | } |
| 19522 | }); |
| 19523 | |
| 19524 | if (!noSwap) { |
| 19525 | // Swap the bytes in the loaded value. |
| 19526 | Label skip; |
| 19527 | BranchIfNativeEndian(masm, littleEndian, &skip); |
| 19528 | |
| 19529 | switch (storageType) { |
| 19530 | case Scalar::Int16: |
| 19531 | masm.byteSwap16SignExtend(out.gpr()); |
| 19532 | break; |
| 19533 | case Scalar::Uint16: |
| 19534 | masm.byteSwap16ZeroExtend(out.gpr()); |
| 19535 | break; |
| 19536 | case Scalar::Int32: |
| 19537 | masm.byteSwap32(out.gpr()); |
| 19538 | break; |
| 19539 | case Scalar::Uint32: |
| 19540 | masm.byteSwap32(out.isFloat() ? temp1 : out.gpr()); |
| 19541 | break; |
| 19542 | case Scalar::Float16: |
| 19543 | masm.byteSwap16ZeroExtend(temp1); |
| 19544 | break; |
| 19545 | case Scalar::Float32: |
| 19546 | masm.byteSwap32(temp1); |
| 19547 | break; |
| 19548 | case Scalar::Float64: |
| 19549 | masm.byteSwap64(temp64); |
| 19550 | break; |
| 19551 | case Scalar::Int8: |
| 19552 | case Scalar::Uint8: |
| 19553 | case Scalar::Uint8Clamped: |
| 19554 | case Scalar::BigInt64: |
| 19555 | case Scalar::BigUint64: |
| 19556 | default: |
| 19557 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19557); AnnotateMozCrashReason ("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence (__null, 19557); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 19558 | } |
| 19559 | |
| 19560 | if (skip.used()) { |
| 19561 | masm.bind(&skip); |
| 19562 | } |
| 19563 | } |
| 19564 | |
| 19565 | // Move the value into the output register. |
| 19566 | switch (storageType) { |
| 19567 | case Scalar::Int16: |
| 19568 | case Scalar::Uint16: |
| 19569 | case Scalar::Int32: |
| 19570 | break; |
| 19571 | case Scalar::Uint32: |
| 19572 | if (out.isFloat()) { |
| 19573 | masm.convertUInt32ToDouble(temp1, out.fpu()); |
| 19574 | } else { |
| 19575 | // Bail out if the value doesn't fit into a signed int32 value. This |
| 19576 | // is what allows MLoadDataViewElement to have a type() of |
| 19577 | // MIRType::Int32 for UInt32 array loads. |
| 19578 | bailoutTest32(Assembler::Signed, out.gpr(), out.gpr(), lir->snapshot()); |
| 19579 | } |
| 19580 | break; |
| 19581 | case Scalar::Float16: |
| 19582 | masm.moveGPRToFloat16(temp1, out.fpu(), temp2, volatileRegs); |
| 19583 | break; |
| 19584 | case Scalar::Float32: |
| 19585 | masm.moveGPRToFloat32(temp1, out.fpu()); |
| 19586 | break; |
| 19587 | case Scalar::Float64: |
| 19588 | masm.moveGPR64ToDouble(temp64, out.fpu()); |
| 19589 | break; |
| 19590 | case Scalar::Int8: |
| 19591 | case Scalar::Uint8: |
| 19592 | case Scalar::Uint8Clamped: |
| 19593 | case Scalar::BigInt64: |
| 19594 | case Scalar::BigUint64: |
| 19595 | default: |
| 19596 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19596); AnnotateMozCrashReason ("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence (__null, 19596); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 19597 | } |
| 19598 | } |
| 19599 | |
| 19600 | void CodeGenerator::visitLoadDataViewElement64(LLoadDataViewElement64* lir) { |
| 19601 | Register elements = ToRegister(lir->elements()); |
| 19602 | const LAllocation* littleEndian = lir->littleEndian(); |
| 19603 | Register64 out = ToOutRegister64(lir); |
| 19604 | |
| 19605 | 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())", "./../../../../js/src/jit/CodeGenerator.cpp" , 19605); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->storageType())" ")"); do { MOZ_CrashSequence(__null, 19605); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19606 | |
| 19607 | auto source = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8); |
| 19608 | |
| 19609 | bool noSwap = IsNativeEndian(littleEndian); |
| 19610 | |
| 19611 | // Load the value into a register. |
| 19612 | source.match([&](const auto& source) { masm.load64Unaligned(source, out); }); |
| 19613 | |
| 19614 | if (!noSwap) { |
| 19615 | // Swap the bytes in the loaded value. |
| 19616 | Label skip; |
| 19617 | BranchIfNativeEndian(masm, littleEndian, &skip); |
| 19618 | |
| 19619 | masm.byteSwap64(out); |
| 19620 | |
| 19621 | if (skip.used()) { |
| 19622 | masm.bind(&skip); |
| 19623 | } |
| 19624 | } |
| 19625 | } |
| 19626 | |
| 19627 | void CodeGenerator::visitLoadTypedArrayElementHole( |
| 19628 | LLoadTypedArrayElementHole* lir) { |
| 19629 | Register elements = ToRegister(lir->elements()); |
| 19630 | Register index = ToRegister(lir->index()); |
| 19631 | Register length = ToRegister(lir->length()); |
| 19632 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 19633 | const ValueOperand out = ToOutValue(lir); |
| 19634 | |
| 19635 | Register scratch = out.scratchReg(); |
| 19636 | |
| 19637 | // Load undefined if index >= length. |
| 19638 | Label outOfBounds, done; |
| 19639 | masm.spectreBoundsCheckPtr(index, length, scratch, &outOfBounds); |
| 19640 | |
| 19641 | Scalar::Type arrayType = lir->mir()->arrayType(); |
| 19642 | |
| 19643 | LiveRegisterSet volatileRegs; |
| 19644 | if (MacroAssembler::LoadRequiresCall(arrayType)) { |
| 19645 | volatileRegs = liveVolatileRegs(lir); |
| 19646 | } |
| 19647 | |
| 19648 | Label fail; |
| 19649 | BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); |
| 19650 | MacroAssembler::Uint32Mode uint32Mode = |
| 19651 | lir->mir()->forceDouble() ? MacroAssembler::Uint32Mode::ForceDouble |
| 19652 | : MacroAssembler::Uint32Mode::FailOnDouble; |
| 19653 | masm.loadFromTypedArray(arrayType, source, out, uint32Mode, temp, &fail, |
| 19654 | volatileRegs); |
| 19655 | masm.jump(&done); |
| 19656 | |
| 19657 | masm.bind(&outOfBounds); |
| 19658 | masm.moveValue(UndefinedValue(), out); |
| 19659 | |
| 19660 | if (fail.used()) { |
| 19661 | bailoutFrom(&fail, lir->snapshot()); |
| 19662 | } |
| 19663 | |
| 19664 | masm.bind(&done); |
| 19665 | } |
| 19666 | |
| 19667 | void CodeGenerator::visitLoadTypedArrayElementHoleBigInt( |
| 19668 | LLoadTypedArrayElementHoleBigInt* lir) { |
| 19669 | Register elements = ToRegister(lir->elements()); |
| 19670 | Register index = ToRegister(lir->index()); |
| 19671 | Register length = ToRegister(lir->length()); |
| 19672 | const ValueOperand out = ToOutValue(lir); |
| 19673 | |
| 19674 | Register temp = ToRegister(lir->temp0()); |
| 19675 | |
| 19676 | // On x86 there are not enough registers. In that case reuse the output |
| 19677 | // registers as temporaries. |
| 19678 | #ifdef JS_CODEGEN_X86 |
| 19679 | MOZ_ASSERT(lir->temp1().isBogusTemp())do { static_assert( mozilla::detail::AssertionConditionType< decltype(lir->temp1().isBogusTemp())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(lir->temp1().isBogusTemp( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("lir->temp1().isBogusTemp()", "./../../../../js/src/jit/CodeGenerator.cpp" , 19679); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->temp1().isBogusTemp()" ")"); do { MOZ_CrashSequence(__null, 19679); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19680 | Register64 temp64 = out.toRegister64(); |
| 19681 | #else |
| 19682 | Register64 temp64 = ToRegister64(lir->temp1()); |
| 19683 | #endif |
| 19684 | |
| 19685 | // Load undefined if index >= length. |
| 19686 | Label outOfBounds, done; |
| 19687 | masm.spectreBoundsCheckPtr(index, length, temp, &outOfBounds); |
| 19688 | |
| 19689 | Scalar::Type arrayType = lir->mir()->arrayType(); |
| 19690 | BaseIndex source(elements, index, ScaleFromScalarType(arrayType)); |
| 19691 | masm.load64(source, temp64); |
| 19692 | |
| 19693 | #ifdef JS_CODEGEN_X86 |
| 19694 | Register bigInt = temp; |
| 19695 | Register maybeTemp = InvalidReg; |
| 19696 | #else |
| 19697 | Register bigInt = out.scratchReg(); |
| 19698 | Register maybeTemp = temp; |
| 19699 | #endif |
| 19700 | emitCreateBigInt(lir, arrayType, temp64, bigInt, maybeTemp); |
| 19701 | |
| 19702 | masm.tagValue(JSVAL_TYPE_BIGINT, bigInt, out); |
| 19703 | masm.jump(&done); |
| 19704 | |
| 19705 | masm.bind(&outOfBounds); |
| 19706 | masm.moveValue(UndefinedValue(), out); |
| 19707 | |
| 19708 | masm.bind(&done); |
| 19709 | } |
| 19710 | |
| 19711 | template <typename T> |
| 19712 | static inline void StoreToTypedArray(MacroAssembler& masm, |
| 19713 | Scalar::Type writeType, |
| 19714 | const LAllocation* value, const T& dest, |
| 19715 | Register temp, |
| 19716 | LiveRegisterSet volatileRegs) { |
| 19717 | if (Scalar::isFloatingType(writeType)) { |
| 19718 | masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, temp, |
| 19719 | volatileRegs); |
| 19720 | } else { |
| 19721 | if (value->isConstant()) { |
| 19722 | masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest); |
| 19723 | } else { |
| 19724 | masm.storeToTypedIntArray(writeType, ToRegister(value), dest); |
| 19725 | } |
| 19726 | } |
| 19727 | } |
| 19728 | |
| 19729 | void CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir) { |
| 19730 | Register elements = ToRegister(lir->elements()); |
| 19731 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 19732 | const LAllocation* value = lir->value(); |
| 19733 | |
| 19734 | Scalar::Type writeType = lir->mir()->writeType(); |
| 19735 | |
| 19736 | LiveRegisterSet volatileRegs; |
| 19737 | if (MacroAssembler::StoreRequiresCall(writeType)) { |
| 19738 | volatileRegs = liveVolatileRegs(lir); |
| 19739 | } |
| 19740 | |
| 19741 | auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType); |
| 19742 | |
| 19743 | dest.match([&](const auto& dest) { |
| 19744 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
| 19745 | }); |
| 19746 | } |
| 19747 | |
| 19748 | template <typename T> |
| 19749 | static inline void StoreToTypedBigIntArray(MacroAssembler& masm, |
| 19750 | const LInt64Allocation& value, |
| 19751 | const T& dest) { |
| 19752 | if (IsConstant(value)) { |
| 19753 | masm.storeToTypedBigIntArray(Imm64(ToInt64(value)), dest); |
| 19754 | } else { |
| 19755 | masm.storeToTypedBigIntArray(ToRegister64(value), dest); |
| 19756 | } |
| 19757 | } |
| 19758 | |
| 19759 | void CodeGenerator::visitStoreUnboxedInt64(LStoreUnboxedInt64* lir) { |
| 19760 | Register elements = ToRegister(lir->elements()); |
| 19761 | LInt64Allocation value = lir->value(); |
| 19762 | |
| 19763 | Scalar::Type writeType = lir->mir()->writeType(); |
| 19764 | MOZ_ASSERT(Scalar::isBigIntType(writeType))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Scalar::isBigIntType(writeType))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(Scalar::isBigIntType(writeType )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("Scalar::isBigIntType(writeType)", "./../../../../js/src/jit/CodeGenerator.cpp" , 19764); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(writeType)" ")"); do { MOZ_CrashSequence(__null, 19764); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19765 | |
| 19766 | auto dest = ToAddressOrBaseIndex(elements, lir->index(), writeType); |
| 19767 | |
| 19768 | dest.match( |
| 19769 | [&](const auto& dest) { StoreToTypedBigIntArray(masm, value, dest); }); |
| 19770 | } |
| 19771 | |
| 19772 | void CodeGenerator::visitStoreDataViewElement(LStoreDataViewElement* lir) { |
| 19773 | Register elements = ToRegister(lir->elements()); |
| 19774 | const LAllocation* value = lir->value(); |
| 19775 | const LAllocation* littleEndian = lir->littleEndian(); |
| 19776 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 19777 | Register64 temp64 = ToTempRegister64OrInvalid(lir->temp1()); |
| 19778 | |
| 19779 | Scalar::Type writeType = lir->mir()->writeType(); |
| 19780 | |
| 19781 | LiveRegisterSet volatileRegs; |
| 19782 | if (MacroAssembler::StoreRequiresCall(writeType)) { |
| 19783 | volatileRegs = liveVolatileRegs(lir); |
| 19784 | } |
| 19785 | |
| 19786 | auto dest = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8); |
| 19787 | |
| 19788 | bool noSwap = IsNativeEndian(littleEndian); |
| 19789 | |
| 19790 | // Directly store if no byte swap is needed and the platform supports |
| 19791 | // unaligned accesses for the access. (Such support is assumed for integer |
| 19792 | // types.) |
| 19793 | if (noSwap && (!Scalar::isFloatingType(writeType) || |
| 19794 | MacroAssembler::SupportsFastUnalignedFPAccesses())) { |
| 19795 | dest.match([&](const auto& dest) { |
| 19796 | StoreToTypedArray(masm, writeType, value, dest, temp, volatileRegs); |
| 19797 | }); |
| 19798 | return; |
| 19799 | } |
| 19800 | |
| 19801 | // Load the value into a gpr register. |
| 19802 | switch (writeType) { |
| 19803 | case Scalar::Int16: |
| 19804 | case Scalar::Uint16: |
| 19805 | case Scalar::Int32: |
| 19806 | case Scalar::Uint32: |
| 19807 | if (value->isConstant()) { |
| 19808 | masm.move32(Imm32(ToInt32(value)), temp); |
| 19809 | } else { |
| 19810 | masm.move32(ToRegister(value), temp); |
| 19811 | } |
| 19812 | break; |
| 19813 | case Scalar::Float16: { |
| 19814 | FloatRegister fvalue = ToFloatRegister(value); |
| 19815 | masm.moveFloat16ToGPR(fvalue, temp, volatileRegs); |
| 19816 | break; |
| 19817 | } |
| 19818 | case Scalar::Float32: { |
| 19819 | FloatRegister fvalue = ToFloatRegister(value); |
| 19820 | masm.moveFloat32ToGPR(fvalue, temp); |
| 19821 | break; |
| 19822 | } |
| 19823 | case Scalar::Float64: { |
| 19824 | FloatRegister fvalue = ToFloatRegister(value); |
| 19825 | masm.moveDoubleToGPR64(fvalue, temp64); |
| 19826 | break; |
| 19827 | } |
| 19828 | case Scalar::Int8: |
| 19829 | case Scalar::Uint8: |
| 19830 | case Scalar::Uint8Clamped: |
| 19831 | case Scalar::BigInt64: |
| 19832 | case Scalar::BigUint64: |
| 19833 | default: |
| 19834 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19834); AnnotateMozCrashReason ("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence (__null, 19834); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 19835 | } |
| 19836 | |
| 19837 | if (!noSwap) { |
| 19838 | // Swap the bytes in the loaded value. |
| 19839 | Label skip; |
| 19840 | BranchIfNativeEndian(masm, littleEndian, &skip); |
| 19841 | |
| 19842 | switch (writeType) { |
| 19843 | case Scalar::Int16: |
| 19844 | masm.byteSwap16SignExtend(temp); |
| 19845 | break; |
| 19846 | case Scalar::Uint16: |
| 19847 | case Scalar::Float16: |
| 19848 | masm.byteSwap16ZeroExtend(temp); |
| 19849 | break; |
| 19850 | case Scalar::Int32: |
| 19851 | case Scalar::Uint32: |
| 19852 | case Scalar::Float32: |
| 19853 | masm.byteSwap32(temp); |
| 19854 | break; |
| 19855 | case Scalar::Float64: |
| 19856 | masm.byteSwap64(temp64); |
| 19857 | break; |
| 19858 | case Scalar::Int8: |
| 19859 | case Scalar::Uint8: |
| 19860 | case Scalar::Uint8Clamped: |
| 19861 | case Scalar::BigInt64: |
| 19862 | case Scalar::BigUint64: |
| 19863 | default: |
| 19864 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19864); AnnotateMozCrashReason ("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence (__null, 19864); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 19865 | } |
| 19866 | |
| 19867 | if (skip.used()) { |
| 19868 | masm.bind(&skip); |
| 19869 | } |
| 19870 | } |
| 19871 | |
| 19872 | // Store the value into the destination. |
| 19873 | dest.match([&](const auto& dest) { |
| 19874 | switch (writeType) { |
| 19875 | case Scalar::Int16: |
| 19876 | case Scalar::Uint16: |
| 19877 | case Scalar::Float16: |
| 19878 | masm.store16Unaligned(temp, dest); |
| 19879 | break; |
| 19880 | case Scalar::Int32: |
| 19881 | case Scalar::Uint32: |
| 19882 | case Scalar::Float32: |
| 19883 | masm.store32Unaligned(temp, dest); |
| 19884 | break; |
| 19885 | case Scalar::Float64: |
| 19886 | masm.store64Unaligned(temp64, dest); |
| 19887 | break; |
| 19888 | case Scalar::Int8: |
| 19889 | case Scalar::Uint8: |
| 19890 | case Scalar::Uint8Clamped: |
| 19891 | case Scalar::BigInt64: |
| 19892 | case Scalar::BigUint64: |
| 19893 | default: |
| 19894 | MOZ_CRASH("Invalid typed array type")do { do { } while (false); MOZ_ReportCrash("" "Invalid typed array type" , "./../../../../js/src/jit/CodeGenerator.cpp", 19894); AnnotateMozCrashReason ("MOZ_CRASH(" "Invalid typed array type" ")"); do { MOZ_CrashSequence (__null, 19894); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 19895 | } |
| 19896 | }); |
| 19897 | } |
| 19898 | |
| 19899 | void CodeGenerator::visitStoreDataViewElement64(LStoreDataViewElement64* lir) { |
| 19900 | Register elements = ToRegister(lir->elements()); |
| 19901 | LInt64Allocation value = lir->value(); |
| 19902 | const LAllocation* littleEndian = lir->littleEndian(); |
| 19903 | Register64 temp = ToTempRegister64OrInvalid(lir->temp0()); |
| 19904 | |
| 19905 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 19905); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "Scalar::isBigIntType(lir->mir()->writeType())" ")"); do { MOZ_CrashSequence(__null, 19905); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19906 | |
| 19907 | auto dest = ToAddressOrBaseIndex(elements, lir->index(), Scalar::Uint8); |
| 19908 | |
| 19909 | bool noSwap = IsNativeEndian(littleEndian); |
| 19910 | |
| 19911 | // Directly store if no byte swap is needed and the platform supports |
| 19912 | // unaligned accesses for the access. (Such support is assumed for integer |
| 19913 | // types.) |
| 19914 | if (noSwap) { |
| 19915 | dest.match( |
| 19916 | [&](const auto& dest) { StoreToTypedBigIntArray(masm, value, dest); }); |
| 19917 | return; |
| 19918 | } |
| 19919 | |
| 19920 | Register64 valueReg = Register64::Invalid(); |
| 19921 | if (IsConstant(value)) { |
| 19922 | MOZ_ASSERT(temp != Register64::Invalid())do { static_assert( mozilla::detail::AssertionConditionType< decltype(temp != Register64::Invalid())>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(temp != Register64::Invalid( )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("temp != Register64::Invalid()", "./../../../../js/src/jit/CodeGenerator.cpp" , 19922); AnnotateMozCrashReason("MOZ_ASSERT" "(" "temp != Register64::Invalid()" ")"); do { MOZ_CrashSequence(__null, 19922); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19923 | masm.move64(Imm64(ToInt64(value)), temp); |
| 19924 | } else { |
| 19925 | valueReg = ToRegister64(value); |
| 19926 | |
| 19927 | // Preserve the input value. |
| 19928 | if (temp != Register64::Invalid()) { |
| 19929 | masm.move64(valueReg, temp); |
| 19930 | } else { |
| 19931 | masm.Push(valueReg); |
| 19932 | temp = valueReg; |
| 19933 | } |
| 19934 | } |
| 19935 | |
| 19936 | // Swap the bytes in the loaded value. |
| 19937 | Label skip; |
| 19938 | BranchIfNativeEndian(masm, littleEndian, &skip); |
| 19939 | |
| 19940 | masm.byteSwap64(temp); |
| 19941 | |
| 19942 | if (skip.used()) { |
| 19943 | masm.bind(&skip); |
| 19944 | } |
| 19945 | |
| 19946 | // Store the value into the destination. |
| 19947 | dest.match([&](const auto& dest) { masm.store64Unaligned(temp, dest); }); |
| 19948 | |
| 19949 | // Restore |value| if it was modified. |
| 19950 | if (valueReg == temp) { |
| 19951 | masm.Pop(valueReg); |
| 19952 | } |
| 19953 | } |
| 19954 | |
| 19955 | void CodeGenerator::visitStoreTypedArrayElementHole( |
| 19956 | LStoreTypedArrayElementHole* lir) { |
| 19957 | Register elements = ToRegister(lir->elements()); |
| 19958 | const LAllocation* value = lir->value(); |
| 19959 | |
| 19960 | Scalar::Type arrayType = lir->mir()->arrayType(); |
| 19961 | |
| 19962 | Register index = ToRegister(lir->index()); |
| 19963 | const LAllocation* length = lir->length(); |
| 19964 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 19965 | |
| 19966 | LiveRegisterSet volatileRegs; |
| 19967 | if (MacroAssembler::StoreRequiresCall(arrayType)) { |
| 19968 | volatileRegs = liveVolatileRegs(lir); |
| 19969 | } |
| 19970 | |
| 19971 | Label skip; |
| 19972 | if (length->isGeneralReg()) { |
| 19973 | masm.spectreBoundsCheckPtr(index, ToRegister(length), temp, &skip); |
| 19974 | } else { |
| 19975 | masm.spectreBoundsCheckPtr(index, ToAddress(length), temp, &skip); |
| 19976 | } |
| 19977 | |
| 19978 | BaseIndex dest(elements, index, ScaleFromScalarType(arrayType)); |
| 19979 | StoreToTypedArray(masm, arrayType, value, dest, temp, volatileRegs); |
| 19980 | |
| 19981 | masm.bind(&skip); |
| 19982 | } |
| 19983 | |
| 19984 | void CodeGenerator::visitStoreTypedArrayElementHoleInt64( |
| 19985 | LStoreTypedArrayElementHoleInt64* lir) { |
| 19986 | Register elements = ToRegister(lir->elements()); |
| 19987 | LInt64Allocation value = lir->value(); |
| 19988 | |
| 19989 | Scalar::Type arrayType = lir->mir()->arrayType(); |
| 19990 | MOZ_ASSERT(Scalar::isBigIntType(arrayType))do { static_assert( mozilla::detail::AssertionConditionType< decltype(Scalar::isBigIntType(arrayType))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(Scalar::isBigIntType(arrayType )))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("Scalar::isBigIntType(arrayType)", "./../../../../js/src/jit/CodeGenerator.cpp" , 19990); AnnotateMozCrashReason("MOZ_ASSERT" "(" "Scalar::isBigIntType(arrayType)" ")"); do { MOZ_CrashSequence(__null, 19990); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 19991 | |
| 19992 | Register index = ToRegister(lir->index()); |
| 19993 | const LAllocation* length = lir->length(); |
| 19994 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp0()); |
| 19995 | |
| 19996 | Label skip; |
| 19997 | if (length->isGeneralReg()) { |
| 19998 | masm.spectreBoundsCheckPtr(index, ToRegister(length), spectreTemp, &skip); |
| 19999 | } else { |
| 20000 | masm.spectreBoundsCheckPtr(index, ToAddress(length), spectreTemp, &skip); |
| 20001 | } |
| 20002 | |
| 20003 | BaseIndex dest(elements, index, ScaleFromScalarType(arrayType)); |
| 20004 | StoreToTypedBigIntArray(masm, value, dest); |
| 20005 | |
| 20006 | masm.bind(&skip); |
| 20007 | } |
| 20008 | |
| 20009 | void CodeGenerator::visitMemoryBarrier(LMemoryBarrier* ins) { |
| 20010 | masm.memoryBarrier(ins->barrier()); |
| 20011 | } |
| 20012 | |
| 20013 | void CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) { |
| 20014 | Register value = ToRegister(lir->value()); |
| 20015 | Register output = ToRegister(lir->output()); |
| 20016 | |
| 20017 | masm.atomicIsLockFreeJS(value, output); |
| 20018 | } |
| 20019 | |
| 20020 | void CodeGenerator::visitAtomicPause(LAtomicPause* lir) { masm.atomicPause(); } |
| 20021 | |
| 20022 | void CodeGenerator::visitClampIToUint8(LClampIToUint8* lir) { |
| 20023 | Register output = ToRegister(lir->output()); |
| 20024 | 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())" , "./../../../../js/src/jit/CodeGenerator.cpp", 20024); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output == ToRegister(lir->input())" ")" ); do { MOZ_CrashSequence(__null, 20024); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 20025 | masm.clampIntToUint8(output); |
| 20026 | } |
| 20027 | |
| 20028 | void CodeGenerator::visitClampDToUint8(LClampDToUint8* lir) { |
| 20029 | FloatRegister input = ToFloatRegister(lir->input()); |
| 20030 | Register output = ToRegister(lir->output()); |
| 20031 | masm.clampDoubleToUint8(input, output); |
| 20032 | } |
| 20033 | |
| 20034 | void CodeGenerator::visitClampVToUint8(LClampVToUint8* lir) { |
| 20035 | ValueOperand operand = ToValue(lir->input()); |
| 20036 | FloatRegister tempFloat = ToFloatRegister(lir->temp0()); |
| 20037 | Register output = ToRegister(lir->output()); |
| 20038 | |
| 20039 | using Fn = bool (*)(JSContext*, JSString*, double*); |
| 20040 | OutOfLineCode* oolString = oolCallVM<Fn, StringToNumber>( |
| 20041 | lir, ArgList(output), StoreFloatRegisterTo(tempFloat)); |
| 20042 | Label* stringEntry = oolString->entry(); |
| 20043 | Label* stringRejoin = oolString->rejoin(); |
| 20044 | |
| 20045 | Label fails; |
| 20046 | masm.clampValueToUint8(operand, stringEntry, stringRejoin, output, tempFloat, |
| 20047 | output, &fails); |
| 20048 | |
| 20049 | bailoutFrom(&fails, lir->snapshot()); |
| 20050 | } |
| 20051 | |
| 20052 | void CodeGenerator::visitInCache(LInCache* ins) { |
| 20053 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 20054 | |
| 20055 | ConstantOrRegister key = |
| 20056 | toConstantOrRegister(ins, LInCache::LhsIndex, ins->mir()->key()->type()); |
| 20057 | Register object = ToRegister(ins->rhs()); |
| 20058 | Register output = ToRegister(ins->output()); |
| 20059 | Register temp = ToRegister(ins->temp0()); |
| 20060 | |
| 20061 | IonInIC cache(liveRegs, key, object, output, temp); |
| 20062 | addIC(ins, allocateIC(cache)); |
| 20063 | } |
| 20064 | |
| 20065 | void CodeGenerator::visitInArray(LInArray* lir) { |
| 20066 | const MInArray* mir = lir->mir(); |
| 20067 | Register elements = ToRegister(lir->elements()); |
| 20068 | Register initLength = ToRegister(lir->initLength()); |
| 20069 | Register output = ToRegister(lir->output()); |
| 20070 | |
| 20071 | Label falseBranch, done, trueBranch; |
| 20072 | |
| 20073 | if (lir->index()->isConstant()) { |
| 20074 | int32_t index = ToInt32(lir->index()); |
| 20075 | |
| 20076 | if (index < 0) { |
| 20077 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 20077); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->needsNegativeIntCheck()" ")"); do { MOZ_CrashSequence(__null, 20077); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20078 | bailout(lir->snapshot()); |
| 20079 | return; |
| 20080 | } |
| 20081 | |
| 20082 | masm.branch32(Assembler::BelowOrEqual, initLength, Imm32(index), |
| 20083 | &falseBranch); |
| 20084 | |
| 20085 | NativeObject::elementsSizeMustNotOverflow(); |
| 20086 | Address address = Address(elements, index * sizeof(Value)); |
| 20087 | masm.branchTestMagic(Assembler::Equal, address, JS_ELEMENTS_HOLE, |
| 20088 | &falseBranch); |
| 20089 | } else { |
| 20090 | Register index = ToRegister(lir->index()); |
| 20091 | |
| 20092 | Label negativeIntCheck; |
| 20093 | Label* failedInitLength = &falseBranch; |
| 20094 | if (mir->needsNegativeIntCheck()) { |
| 20095 | failedInitLength = &negativeIntCheck; |
| 20096 | } |
| 20097 | |
| 20098 | masm.branch32(Assembler::BelowOrEqual, initLength, index, failedInitLength); |
| 20099 | |
| 20100 | BaseObjectElementIndex address(elements, index); |
| 20101 | masm.branchTestMagic(Assembler::Equal, address, JS_ELEMENTS_HOLE, |
| 20102 | &falseBranch); |
| 20103 | |
| 20104 | if (mir->needsNegativeIntCheck()) { |
| 20105 | masm.jump(&trueBranch); |
| 20106 | masm.bind(&negativeIntCheck); |
| 20107 | |
| 20108 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 20109 | |
| 20110 | masm.jump(&falseBranch); |
| 20111 | } |
| 20112 | } |
| 20113 | |
| 20114 | masm.bind(&trueBranch); |
| 20115 | masm.move32(Imm32(1), output); |
| 20116 | masm.jump(&done); |
| 20117 | |
| 20118 | masm.bind(&falseBranch); |
| 20119 | masm.move32(Imm32(0), output); |
| 20120 | masm.bind(&done); |
| 20121 | } |
| 20122 | |
| 20123 | void CodeGenerator::visitGuardElementNotHole(LGuardElementNotHole* lir) { |
| 20124 | Register elements = ToRegister(lir->elements()); |
| 20125 | const LAllocation* index = lir->index(); |
| 20126 | |
| 20127 | auto source = ToAddressOrBaseObjectElementIndex(elements, index); |
| 20128 | |
| 20129 | Label testMagic; |
| 20130 | source.match([&](const auto& source) { |
| 20131 | masm.branchTestMagic(Assembler::Equal, source, JS_ELEMENTS_HOLE, |
| 20132 | &testMagic); |
| 20133 | }); |
| 20134 | bailoutFrom(&testMagic, lir->snapshot()); |
| 20135 | } |
| 20136 | |
| 20137 | void CodeGenerator::visitInstanceOfO(LInstanceOfO* ins) { |
| 20138 | Register protoReg = ToRegister(ins->rhs()); |
| 20139 | emitInstanceOf(ins, protoReg); |
| 20140 | } |
| 20141 | |
| 20142 | void CodeGenerator::visitInstanceOfV(LInstanceOfV* ins) { |
| 20143 | Register protoReg = ToRegister(ins->rhs()); |
| 20144 | emitInstanceOf(ins, protoReg); |
| 20145 | } |
| 20146 | |
| 20147 | void CodeGenerator::emitInstanceOf(LInstruction* ins, Register protoReg) { |
| 20148 | // This path implements fun_hasInstance when the function's prototype is |
| 20149 | // known to be the object in protoReg |
| 20150 | |
| 20151 | Label done; |
| 20152 | Register output = ToRegister(ins->getDef(0)); |
| 20153 | |
| 20154 | // If the lhs is a primitive, the result is false. |
| 20155 | Register objReg; |
| 20156 | if (ins->isInstanceOfV()) { |
| 20157 | Label isObject; |
| 20158 | ValueOperand lhsValue = ToValue(ins->toInstanceOfV()->lhs()); |
| 20159 | masm.branchTestObject(Assembler::Equal, lhsValue, &isObject); |
| 20160 | masm.mov(ImmWord(0), output); |
| 20161 | masm.jump(&done); |
| 20162 | masm.bind(&isObject); |
| 20163 | objReg = masm.extractObject(lhsValue, output); |
| 20164 | } else { |
| 20165 | objReg = ToRegister(ins->toInstanceOfO()->lhs()); |
| 20166 | } |
| 20167 | |
| 20168 | // Crawl the lhs's prototype chain in a loop to search for prototypeObject. |
| 20169 | // This follows the main loop of js::IsPrototypeOf, though additionally breaks |
| 20170 | // out of the loop on Proxy::LazyProto. |
| 20171 | |
| 20172 | // Load the lhs's prototype. |
| 20173 | masm.loadObjProto(objReg, output); |
| 20174 | |
| 20175 | Label testLazy; |
| 20176 | { |
| 20177 | Label loopPrototypeChain; |
| 20178 | masm.bind(&loopPrototypeChain); |
| 20179 | |
| 20180 | // Test for the target prototype object. |
| 20181 | Label notPrototypeObject; |
| 20182 | masm.branchPtr(Assembler::NotEqual, output, protoReg, ¬PrototypeObject); |
| 20183 | masm.mov(ImmWord(1), output); |
| 20184 | masm.jump(&done); |
| 20185 | masm.bind(¬PrototypeObject); |
| 20186 | |
| 20187 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 20187); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")" ); do { MOZ_CrashSequence(__null, 20187); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 20188 | |
| 20189 | // Test for nullptr or Proxy::LazyProto |
| 20190 | masm.branchPtr(Assembler::BelowOrEqual, output, ImmWord(1), &testLazy); |
| 20191 | |
| 20192 | // Load the current object's prototype. |
| 20193 | masm.loadObjProto(output, output); |
| 20194 | |
| 20195 | masm.jump(&loopPrototypeChain); |
| 20196 | } |
| 20197 | |
| 20198 | // Make a VM call if an object with a lazy proto was found on the prototype |
| 20199 | // chain. This currently occurs only for cross compartment wrappers, which |
| 20200 | // we do not expect to be compared with non-wrapper functions from this |
| 20201 | // compartment. Otherwise, we stopped on a nullptr prototype and the output |
| 20202 | // register is already correct. |
| 20203 | |
| 20204 | using Fn = bool (*)(JSContext*, HandleObject, JSObject*, bool*); |
| 20205 | auto* ool = oolCallVM<Fn, IsPrototypeOf>(ins, ArgList(protoReg, objReg), |
| 20206 | StoreRegisterTo(output)); |
| 20207 | |
| 20208 | // Regenerate the original lhs object for the VM call. |
| 20209 | Label regenerate, *lazyEntry; |
| 20210 | if (objReg != output) { |
| 20211 | lazyEntry = ool->entry(); |
| 20212 | } else { |
| 20213 | masm.bind(®enerate); |
| 20214 | lazyEntry = ®enerate; |
| 20215 | if (ins->isInstanceOfV()) { |
| 20216 | ValueOperand lhsValue = ToValue(ins->toInstanceOfV()->lhs()); |
| 20217 | objReg = masm.extractObject(lhsValue, output); |
| 20218 | } else { |
| 20219 | objReg = ToRegister(ins->toInstanceOfO()->lhs()); |
| 20220 | } |
| 20221 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 20221); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "objReg == output" ")"); do { MOZ_CrashSequence (__null, 20221); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 20222 | masm.jump(ool->entry()); |
| 20223 | } |
| 20224 | |
| 20225 | masm.bind(&testLazy); |
| 20226 | masm.branchPtr(Assembler::Equal, output, ImmWord(1), lazyEntry); |
| 20227 | |
| 20228 | masm.bind(&done); |
| 20229 | masm.bind(ool->rejoin()); |
| 20230 | } |
| 20231 | |
| 20232 | void CodeGenerator::visitInstanceOfCache(LInstanceOfCache* ins) { |
| 20233 | // The Lowering ensures that RHS is an object, and that LHS is a value. |
| 20234 | LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); |
| 20235 | TypedOrValueRegister lhs = TypedOrValueRegister(ToValue(ins->obj())); |
| 20236 | Register rhs = ToRegister(ins->proto()); |
| 20237 | Register output = ToRegister(ins->output()); |
| 20238 | |
| 20239 | IonInstanceOfIC ic(liveRegs, lhs, rhs, output); |
| 20240 | addIC(ins, allocateIC(ic)); |
| 20241 | } |
| 20242 | |
| 20243 | void CodeGenerator::visitGetDOMProperty(LGetDOMProperty* ins) { |
| 20244 | const Register JSContextReg = ToRegister(ins->temp0()); |
| 20245 | const Register ObjectReg = ToRegister(ins->object()); |
| 20246 | const Register PrivateReg = ToRegister(ins->temp1()); |
| 20247 | const Register ValueReg = ToRegister(ins->temp2()); |
| 20248 | |
| 20249 | Label haveValue; |
| 20250 | if (ins->mir()->valueMayBeInSlot()) { |
| 20251 | size_t slot = ins->mir()->domMemberSlotIndex(); |
| 20252 | // It's a bit annoying to redo these slot calculations, which duplcate |
| 20253 | // LSlots and a few other things like that, but I'm not sure there's a |
| 20254 | // way to reuse those here. |
| 20255 | // |
| 20256 | // If this ever gets fixed to work with proxies (by not assuming that |
| 20257 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
| 20258 | // match fixed slot indices), we can reenable MGetDOMProperty for |
| 20259 | // proxies in IonBuilder. |
| 20260 | if (slot < NativeObject::MAX_FIXED_SLOTS) { |
| 20261 | masm.loadValue(Address(ObjectReg, NativeObject::getFixedSlotOffset(slot)), |
| 20262 | JSReturnOperand); |
| 20263 | } else { |
| 20264 | // It's a dynamic slot. |
| 20265 | slot -= NativeObject::MAX_FIXED_SLOTS; |
| 20266 | // Use PrivateReg as a scratch register for the slots pointer. |
| 20267 | masm.loadPtr(Address(ObjectReg, NativeObject::offsetOfSlots()), |
| 20268 | PrivateReg); |
| 20269 | masm.loadValue(Address(PrivateReg, slot * sizeof(js::Value)), |
| 20270 | JSReturnOperand); |
| 20271 | } |
| 20272 | masm.branchTestUndefined(Assembler::NotEqual, JSReturnOperand, &haveValue); |
| 20273 | } |
| 20274 | |
| 20275 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
| 20276 | |
| 20277 | masm.checkStackAlignment(); |
| 20278 | |
| 20279 | // Make space for the outparam. Pre-initialize it to UndefinedValue so we |
| 20280 | // can trace it at GC time. |
| 20281 | masm.Push(UndefinedValue()); |
| 20282 | // We pass the pointer to our out param as an instance of |
| 20283 | // JSJitGetterCallArgs, since on the binary level it's the same thing. |
| 20284 | static_assert(sizeof(JSJitGetterCallArgs) == sizeof(Value*)); |
| 20285 | masm.moveStackPtrTo(ValueReg); |
| 20286 | |
| 20287 | masm.Push(ObjectReg); |
| 20288 | |
| 20289 | LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind()); |
| 20290 | |
| 20291 | // Rooting will happen at GC time. |
| 20292 | masm.moveStackPtrTo(ObjectReg); |
| 20293 | |
| 20294 | Realm* getterRealm = ins->mir()->getterRealm(); |
| 20295 | if (gen->realm->realmPtr() != getterRealm) { |
| 20296 | // We use JSContextReg as scratch register here. |
| 20297 | masm.switchToRealm(getterRealm, JSContextReg); |
| 20298 | } |
| 20299 | |
| 20300 | uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); |
| 20301 | masm.loadJSContext(JSContextReg); |
| 20302 | masm.enterFakeExitFrame(JSContextReg, JSContextReg, |
| 20303 | ExitFrameType::IonDOMGetter); |
| 20304 | |
| 20305 | markSafepointAt(safepointOffset, ins); |
| 20306 | |
| 20307 | masm.setupAlignedABICall(); |
| 20308 | masm.loadJSContext(JSContextReg); |
| 20309 | masm.passABIArg(JSContextReg); |
| 20310 | masm.passABIArg(ObjectReg); |
| 20311 | masm.passABIArg(PrivateReg); |
| 20312 | masm.passABIArg(ValueReg); |
| 20313 | ensureOsiSpace(); |
| 20314 | masm.callWithABI(DynamicFunction<JSJitGetterOp>(ins->mir()->fun()), |
| 20315 | ABIType::General, |
| 20316 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 20317 | |
| 20318 | if (ins->mir()->isInfallible()) { |
| 20319 | masm.loadValue(Address(masm.getStackPointer(), |
| 20320 | IonDOMExitFrameLayout::offsetOfResult()), |
| 20321 | JSReturnOperand); |
| 20322 | } else { |
| 20323 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
| 20324 | |
| 20325 | masm.loadValue(Address(masm.getStackPointer(), |
| 20326 | IonDOMExitFrameLayout::offsetOfResult()), |
| 20327 | JSReturnOperand); |
| 20328 | } |
| 20329 | |
| 20330 | // Switch back to the current realm if needed. Note: if the getter threw an |
| 20331 | // exception, the exception handler will do this. |
| 20332 | if (gen->realm->realmPtr() != getterRealm) { |
| 20333 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 20334 | "Clobbering ReturnReg should not affect the return value"); |
| 20335 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 20336 | } |
| 20337 | |
| 20338 | // Until C++ code is instrumented against Spectre, prevent speculative |
| 20339 | // execution from returning any private data. |
| 20340 | if (JitOptions.spectreJitToCxxCalls && ins->mir()->hasLiveDefUses()) { |
| 20341 | masm.speculationBarrier(); |
| 20342 | } |
| 20343 | |
| 20344 | masm.adjustStack(IonDOMExitFrameLayout::Size()); |
| 20345 | |
| 20346 | masm.bind(&haveValue); |
| 20347 | |
| 20348 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 20348); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { MOZ_CrashSequence(__null, 20348); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20349 | } |
| 20350 | |
| 20351 | void CodeGenerator::visitGetDOMMemberV(LGetDOMMemberV* ins) { |
| 20352 | // It's simpler to duplicate visitLoadFixedSlotV here than it is to try to |
| 20353 | // use an LLoadFixedSlotV or some subclass of it for this case: that would |
| 20354 | // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then |
| 20355 | // we'd have to duplicate a bunch of stuff we now get for free from |
| 20356 | // MGetDOMProperty. |
| 20357 | // |
| 20358 | // If this ever gets fixed to work with proxies (by not assuming that |
| 20359 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
| 20360 | // match fixed slot indices), we can reenable MGetDOMMember for |
| 20361 | // proxies in IonBuilder. |
| 20362 | Register object = ToRegister(ins->object()); |
| 20363 | size_t slot = ins->mir()->domMemberSlotIndex(); |
| 20364 | ValueOperand result = ToOutValue(ins); |
| 20365 | |
| 20366 | masm.loadValue(Address(object, NativeObject::getFixedSlotOffset(slot)), |
| 20367 | result); |
| 20368 | } |
| 20369 | |
| 20370 | void CodeGenerator::visitGetDOMMemberT(LGetDOMMemberT* ins) { |
| 20371 | // It's simpler to duplicate visitLoadFixedSlotT here than it is to try to |
| 20372 | // use an LLoadFixedSlotT or some subclass of it for this case: that would |
| 20373 | // require us to have MGetDOMMember inherit from MLoadFixedSlot, and then |
| 20374 | // we'd have to duplicate a bunch of stuff we now get for free from |
| 20375 | // MGetDOMProperty. |
| 20376 | // |
| 20377 | // If this ever gets fixed to work with proxies (by not assuming that |
| 20378 | // reserved slot indices, which is what domMemberSlotIndex() returns, |
| 20379 | // match fixed slot indices), we can reenable MGetDOMMember for |
| 20380 | // proxies in IonBuilder. |
| 20381 | Register object = ToRegister(ins->object()); |
| 20382 | size_t slot = ins->mir()->domMemberSlotIndex(); |
| 20383 | AnyRegister result = ToAnyRegister(ins->output()); |
| 20384 | MIRType type = ins->mir()->type(); |
| 20385 | |
| 20386 | masm.loadUnboxedValue(Address(object, NativeObject::getFixedSlotOffset(slot)), |
| 20387 | type, result); |
| 20388 | } |
| 20389 | |
| 20390 | void CodeGenerator::visitSetDOMProperty(LSetDOMProperty* ins) { |
| 20391 | const Register JSContextReg = ToRegister(ins->temp0()); |
| 20392 | const Register ObjectReg = ToRegister(ins->object()); |
| 20393 | const Register PrivateReg = ToRegister(ins->temp1()); |
| 20394 | const Register ValueReg = ToRegister(ins->temp2()); |
| 20395 | |
| 20396 | DebugOnly<uint32_t> initialStack = masm.framePushed(); |
| 20397 | |
| 20398 | masm.checkStackAlignment(); |
| 20399 | |
| 20400 | // Push the argument. Rooting will happen at GC time. |
| 20401 | ValueOperand argVal = ToValue(ins->value()); |
| 20402 | masm.Push(argVal); |
| 20403 | // We pass the pointer to our out param as an instance of |
| 20404 | // JSJitGetterCallArgs, since on the binary level it's the same thing. |
| 20405 | static_assert(sizeof(JSJitSetterCallArgs) == sizeof(Value*)); |
| 20406 | masm.moveStackPtrTo(ValueReg); |
| 20407 | |
| 20408 | masm.Push(ObjectReg); |
| 20409 | |
| 20410 | LoadDOMPrivate(masm, ObjectReg, PrivateReg, ins->mir()->objectKind()); |
| 20411 | |
| 20412 | // Rooting will happen at GC time. |
| 20413 | masm.moveStackPtrTo(ObjectReg); |
| 20414 | |
| 20415 | Realm* setterRealm = ins->mir()->setterRealm(); |
| 20416 | if (gen->realm->realmPtr() != setterRealm) { |
| 20417 | // We use JSContextReg as scratch register here. |
| 20418 | masm.switchToRealm(setterRealm, JSContextReg); |
| 20419 | } |
| 20420 | |
| 20421 | uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg); |
| 20422 | masm.loadJSContext(JSContextReg); |
| 20423 | masm.enterFakeExitFrame(JSContextReg, JSContextReg, |
| 20424 | ExitFrameType::IonDOMSetter); |
| 20425 | |
| 20426 | markSafepointAt(safepointOffset, ins); |
| 20427 | |
| 20428 | masm.setupAlignedABICall(); |
| 20429 | masm.loadJSContext(JSContextReg); |
| 20430 | masm.passABIArg(JSContextReg); |
| 20431 | masm.passABIArg(ObjectReg); |
| 20432 | masm.passABIArg(PrivateReg); |
| 20433 | masm.passABIArg(ValueReg); |
| 20434 | ensureOsiSpace(); |
| 20435 | masm.callWithABI(DynamicFunction<JSJitSetterOp>(ins->mir()->fun()), |
| 20436 | ABIType::General, |
| 20437 | CheckUnsafeCallWithABI::DontCheckHasExitFrame); |
| 20438 | |
| 20439 | masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel()); |
| 20440 | |
| 20441 | // Switch back to the current realm if needed. Note: if the setter threw an |
| 20442 | // exception, the exception handler will do this. |
| 20443 | if (gen->realm->realmPtr() != setterRealm) { |
| 20444 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 20445 | } |
| 20446 | |
| 20447 | masm.adjustStack(IonDOMExitFrameLayout::Size()); |
| 20448 | |
| 20449 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 20449); AnnotateMozCrashReason("MOZ_ASSERT" "(" "masm.framePushed() == initialStack" ")"); do { MOZ_CrashSequence(__null, 20449); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 20450 | } |
| 20451 | |
| 20452 | void CodeGenerator::visitLoadDOMExpandoValue(LLoadDOMExpandoValue* ins) { |
| 20453 | Register proxy = ToRegister(ins->proxy()); |
| 20454 | ValueOperand out = ToOutValue(ins); |
| 20455 | |
| 20456 | masm.loadValue(Address(proxy, ProxyObject::offsetOfPrivateSlot()), out); |
| 20457 | } |
| 20458 | |
| 20459 | void CodeGenerator::visitLoadDOMExpandoValueGuardGeneration( |
| 20460 | LLoadDOMExpandoValueGuardGeneration* ins) { |
| 20461 | Register proxy = ToRegister(ins->proxy()); |
| 20462 | ValueOperand out = ToOutValue(ins); |
| 20463 | |
| 20464 | Label bail; |
| 20465 | masm.loadDOMExpandoValueGuardGeneration(proxy, out, |
| 20466 | ins->mir()->expandoAndGeneration(), |
| 20467 | ins->mir()->generation(), &bail); |
| 20468 | bailoutFrom(&bail, ins->snapshot()); |
| 20469 | } |
| 20470 | |
| 20471 | void CodeGenerator::visitLoadDOMExpandoValueIgnoreGeneration( |
| 20472 | LLoadDOMExpandoValueIgnoreGeneration* ins) { |
| 20473 | Register proxy = ToRegister(ins->proxy()); |
| 20474 | ValueOperand out = ToOutValue(ins); |
| 20475 | |
| 20476 | // Load the ExpandoAndGeneration* from the PrivateValue. |
| 20477 | masm.loadPrivate(Address(proxy, ProxyObject::offsetOfPrivateSlot()), |
| 20478 | out.scratchReg()); |
| 20479 | |
| 20480 | // Load expandoAndGeneration->expando into the output Value register. |
| 20481 | masm.loadValue( |
| 20482 | Address(out.scratchReg(), ExpandoAndGeneration::offsetOfExpando()), out); |
| 20483 | } |
| 20484 | |
| 20485 | void CodeGenerator::visitGuardDOMExpandoMissingOrGuardShape( |
| 20486 | LGuardDOMExpandoMissingOrGuardShape* ins) { |
| 20487 | Register temp = ToRegister(ins->temp0()); |
| 20488 | ValueOperand input = ToValue(ins->expando()); |
| 20489 | |
| 20490 | Label done; |
| 20491 | masm.branchTestUndefined(Assembler::Equal, input, &done); |
| 20492 | |
| 20493 | masm.debugAssertIsObject(input); |
| 20494 | masm.unboxObject(input, temp); |
| 20495 | // The expando object is not used in this case, so we don't need Spectre |
| 20496 | // mitigations. |
| 20497 | Label bail; |
| 20498 | masm.branchTestObjShapeNoSpectreMitigations(Assembler::NotEqual, temp, |
| 20499 | ins->mir()->shape(), &bail); |
| 20500 | bailoutFrom(&bail, ins->snapshot()); |
| 20501 | |
| 20502 | masm.bind(&done); |
| 20503 | } |
| 20504 | |
| 20505 | void CodeGenerator::emitIsCallableOOL(Register object, Register output) { |
| 20506 | saveVolatile(output); |
| 20507 | using Fn = bool (*)(JSObject* obj); |
| 20508 | masm.setupAlignedABICall(); |
| 20509 | masm.passABIArg(object); |
| 20510 | masm.callWithABI<Fn, ObjectIsCallable>(); |
| 20511 | masm.storeCallBoolResult(output); |
| 20512 | restoreVolatile(output); |
| 20513 | } |
| 20514 | |
| 20515 | void CodeGenerator::visitIsCallableO(LIsCallableO* ins) { |
| 20516 | Register object = ToRegister(ins->object()); |
| 20517 | Register output = ToRegister(ins->output()); |
| 20518 | |
| 20519 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 20520 | emitIsCallableOOL(object, output); |
| 20521 | masm.jump(ool.rejoin()); |
| 20522 | }); |
| 20523 | addOutOfLineCode(ool, ins->mir()); |
| 20524 | |
| 20525 | masm.isCallable(object, output, ool->entry()); |
| 20526 | |
| 20527 | masm.bind(ool->rejoin()); |
| 20528 | } |
| 20529 | |
| 20530 | void CodeGenerator::visitIsCallableV(LIsCallableV* ins) { |
| 20531 | ValueOperand val = ToValue(ins->object()); |
| 20532 | Register output = ToRegister(ins->output()); |
| 20533 | Register temp = ToRegister(ins->temp0()); |
| 20534 | |
| 20535 | Label notObject; |
| 20536 | masm.fallibleUnboxObject(val, temp, ¬Object); |
| 20537 | |
| 20538 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 20539 | emitIsCallableOOL(temp, output); |
| 20540 | masm.jump(ool.rejoin()); |
| 20541 | }); |
| 20542 | addOutOfLineCode(ool, ins->mir()); |
| 20543 | |
| 20544 | masm.isCallable(temp, output, ool->entry()); |
| 20545 | masm.jump(ool->rejoin()); |
| 20546 | |
| 20547 | masm.bind(¬Object); |
| 20548 | masm.move32(Imm32(0), output); |
| 20549 | |
| 20550 | masm.bind(ool->rejoin()); |
| 20551 | } |
| 20552 | |
| 20553 | void CodeGenerator::visitIsConstructor(LIsConstructor* ins) { |
| 20554 | Register object = ToRegister(ins->object()); |
| 20555 | Register output = ToRegister(ins->output()); |
| 20556 | |
| 20557 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 20558 | saveVolatile(output); |
| 20559 | using Fn = bool (*)(JSObject* obj); |
| 20560 | masm.setupAlignedABICall(); |
| 20561 | masm.passABIArg(object); |
| 20562 | masm.callWithABI<Fn, ObjectIsConstructor>(); |
| 20563 | masm.storeCallBoolResult(output); |
| 20564 | restoreVolatile(output); |
| 20565 | masm.jump(ool.rejoin()); |
| 20566 | }); |
| 20567 | addOutOfLineCode(ool, ins->mir()); |
| 20568 | |
| 20569 | masm.isConstructor(object, output, ool->entry()); |
| 20570 | |
| 20571 | masm.bind(ool->rejoin()); |
| 20572 | } |
| 20573 | |
| 20574 | void CodeGenerator::visitIsCrossRealmArrayConstructor( |
| 20575 | LIsCrossRealmArrayConstructor* ins) { |
| 20576 | Register object = ToRegister(ins->object()); |
| 20577 | Register output = ToRegister(ins->output()); |
| 20578 | |
| 20579 | masm.setIsCrossRealmArrayConstructor(object, output); |
| 20580 | } |
| 20581 | |
| 20582 | static void EmitObjectIsArray(MacroAssembler& masm, OutOfLineCode* ool, |
| 20583 | Register obj, Register output, |
| 20584 | Label* notArray = nullptr) { |
| 20585 | masm.loadObjClassUnsafe(obj, output); |
| 20586 | |
| 20587 | Label isArray; |
| 20588 | masm.branchPtr(Assembler::Equal, output, ImmPtr(&ArrayObject::class_), |
| 20589 | &isArray); |
| 20590 | |
| 20591 | // Branch to OOL path if it's a proxy. |
| 20592 | masm.branchTestClassIsProxy(true, output, ool->entry()); |
| 20593 | |
| 20594 | if (notArray) { |
| 20595 | masm.bind(notArray); |
| 20596 | } |
| 20597 | masm.move32(Imm32(0), output); |
| 20598 | masm.jump(ool->rejoin()); |
| 20599 | |
| 20600 | masm.bind(&isArray); |
| 20601 | masm.move32(Imm32(1), output); |
| 20602 | |
| 20603 | masm.bind(ool->rejoin()); |
| 20604 | } |
| 20605 | |
| 20606 | void CodeGenerator::visitIsArrayO(LIsArrayO* lir) { |
| 20607 | Register object = ToRegister(lir->object()); |
| 20608 | Register output = ToRegister(lir->output()); |
| 20609 | |
| 20610 | using Fn = bool (*)(JSContext*, HandleObject, bool*); |
| 20611 | OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>( |
| 20612 | lir, ArgList(object), StoreRegisterTo(output)); |
| 20613 | EmitObjectIsArray(masm, ool, object, output); |
| 20614 | } |
| 20615 | |
| 20616 | void CodeGenerator::visitIsArrayV(LIsArrayV* lir) { |
| 20617 | ValueOperand val = ToValue(lir->value()); |
| 20618 | Register output = ToRegister(lir->output()); |
| 20619 | Register temp = ToRegister(lir->temp0()); |
| 20620 | |
| 20621 | Label notArray; |
| 20622 | masm.fallibleUnboxObject(val, temp, ¬Array); |
| 20623 | |
| 20624 | using Fn = bool (*)(JSContext*, HandleObject, bool*); |
| 20625 | OutOfLineCode* ool = oolCallVM<Fn, js::IsArrayFromJit>( |
| 20626 | lir, ArgList(temp), StoreRegisterTo(output)); |
| 20627 | EmitObjectIsArray(masm, ool, temp, output, ¬Array); |
| 20628 | } |
| 20629 | |
| 20630 | void CodeGenerator::visitIsTypedArray(LIsTypedArray* lir) { |
| 20631 | Register object = ToRegister(lir->object()); |
| 20632 | Register output = ToRegister(lir->output()); |
| 20633 | |
| 20634 | OutOfLineCode* ool = nullptr; |
| 20635 | if (lir->mir()->isPossiblyWrapped()) { |
| 20636 | using Fn = bool (*)(JSContext*, JSObject*, bool*); |
| 20637 | ool = oolCallVM<Fn, jit::IsPossiblyWrappedTypedArray>( |
| 20638 | lir, ArgList(object), StoreRegisterTo(output)); |
| 20639 | } |
| 20640 | |
| 20641 | Label notTypedArray; |
| 20642 | Label done; |
| 20643 | |
| 20644 | masm.loadObjClassUnsafe(object, output); |
| 20645 | masm.branchIfClassIsNotTypedArray(output, ¬TypedArray); |
| 20646 | |
| 20647 | masm.move32(Imm32(1), output); |
| 20648 | masm.jump(&done); |
| 20649 | masm.bind(¬TypedArray); |
| 20650 | if (ool) { |
| 20651 | Label notProxy; |
| 20652 | masm.branchTestClassIsProxy(false, output, ¬Proxy); |
| 20653 | masm.branchTestProxyHandlerFamily(Assembler::Equal, object, output, |
| 20654 | &Wrapper::family, ool->entry()); |
| 20655 | masm.bind(¬Proxy); |
| 20656 | } |
| 20657 | masm.move32(Imm32(0), output); |
| 20658 | masm.bind(&done); |
| 20659 | if (ool) { |
| 20660 | masm.bind(ool->rejoin()); |
| 20661 | } |
| 20662 | } |
| 20663 | |
| 20664 | void CodeGenerator::visitIsObject(LIsObject* ins) { |
| 20665 | Register output = ToRegister(ins->output()); |
| 20666 | ValueOperand value = ToValue(ins->object()); |
| 20667 | masm.testObjectSet(Assembler::Equal, value, output); |
| 20668 | } |
| 20669 | |
| 20670 | void CodeGenerator::visitIsSuspendedGenerator(LIsSuspendedGenerator* lir) { |
| 20671 | Register obj = ToRegister(lir->object()); |
| 20672 | Register output = ToRegister(lir->output()); |
| 20673 | Register scratch = ToRegister(lir->temp0()); |
| 20674 | |
| 20675 | Label returnFalse, done; |
| 20676 | masm.branchIfNotSuspendedGenerator(obj, scratch, obj, &returnFalse); |
| 20677 | |
| 20678 | masm.move32(Imm32(1), output); |
| 20679 | masm.jump(&done); |
| 20680 | |
| 20681 | masm.bind(&returnFalse); |
| 20682 | masm.move32(Imm32(0), output); |
| 20683 | |
| 20684 | masm.bind(&done); |
| 20685 | } |
| 20686 | |
| 20687 | void CodeGenerator::visitIsSuspendedGeneratorAndBranch( |
| 20688 | LIsSuspendedGeneratorAndBranch* lir) { |
| 20689 | Register obj = ToRegister(lir->object()); |
| 20690 | Register scratch = ToRegister(lir->temp0()); |
| 20691 | |
| 20692 | MBasicBlock* ifTrue = lir->ifTrue(); |
| 20693 | MBasicBlock* ifFalse = lir->ifFalse(); |
| 20694 | |
| 20695 | masm.branchIfNotSuspendedGenerator(obj, scratch, obj, |
| 20696 | getJumpLabelForBranch(ifFalse)); |
| 20697 | jumpToBlock(ifTrue); |
| 20698 | } |
| 20699 | |
| 20700 | void CodeGenerator::visitIsObjectAndBranch(LIsObjectAndBranch* ins) { |
| 20701 | ValueOperand value = ToValue(ins->input()); |
| 20702 | |
| 20703 | MBasicBlock* ifTrue = ins->ifTrue(); |
| 20704 | MBasicBlock* ifFalse = ins->ifFalse(); |
| 20705 | |
| 20706 | if (isNextBlock(ifFalse->lir())) { |
| 20707 | masm.branchTestObject(Assembler::Equal, value, |
| 20708 | getJumpLabelForBranch(ifTrue)); |
| 20709 | } else { |
| 20710 | masm.branchTestObject(Assembler::NotEqual, value, |
| 20711 | getJumpLabelForBranch(ifFalse)); |
| 20712 | jumpToBlock(ifTrue); |
| 20713 | } |
| 20714 | } |
| 20715 | |
| 20716 | void CodeGenerator::visitIsNullOrUndefined(LIsNullOrUndefined* ins) { |
| 20717 | Register output = ToRegister(ins->output()); |
| 20718 | ValueOperand value = ToValue(ins->value()); |
| 20719 | |
| 20720 | Label isNotNull, done; |
| 20721 | masm.branchTestNull(Assembler::NotEqual, value, &isNotNull); |
| 20722 | |
| 20723 | masm.move32(Imm32(1), output); |
| 20724 | masm.jump(&done); |
| 20725 | |
| 20726 | masm.bind(&isNotNull); |
| 20727 | masm.testUndefinedSet(Assembler::Equal, value, output); |
| 20728 | |
| 20729 | masm.bind(&done); |
| 20730 | } |
| 20731 | |
| 20732 | void CodeGenerator::visitIsNullOrUndefinedAndBranch( |
| 20733 | LIsNullOrUndefinedAndBranch* ins) { |
| 20734 | Label* ifTrue = getJumpLabelForBranch(ins->ifTrue()); |
| 20735 | Label* ifFalse = getJumpLabelForBranch(ins->ifFalse()); |
| 20736 | ValueOperand value = ToValue(ins->input()); |
| 20737 | |
| 20738 | ScratchTagScope tag(masm, value); |
| 20739 | masm.splitTagForTest(value, tag); |
| 20740 | |
| 20741 | masm.branchTestNull(Assembler::Equal, tag, ifTrue); |
| 20742 | masm.branchTestUndefined(Assembler::Equal, tag, ifTrue); |
| 20743 | |
| 20744 | if (!isNextBlock(ins->ifFalse()->lir())) { |
| 20745 | masm.jump(ifFalse); |
| 20746 | } |
| 20747 | } |
| 20748 | |
| 20749 | void CodeGenerator::visitHasClass(LHasClass* ins) { |
| 20750 | Register lhs = ToRegister(ins->lhs()); |
| 20751 | Register output = ToRegister(ins->output()); |
| 20752 | |
| 20753 | masm.loadObjClassUnsafe(lhs, output); |
| 20754 | masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), |
| 20755 | output); |
| 20756 | } |
| 20757 | |
| 20758 | void CodeGenerator::visitHasShape(LHasShape* ins) { |
| 20759 | Register obj = ToRegister(ins->object()); |
| 20760 | Register output = ToRegister(ins->output()); |
| 20761 | |
| 20762 | // Note: no Spectre mitigations are needed here because this shape check only |
| 20763 | // affects correctness. |
| 20764 | masm.loadObjShapeUnsafe(obj, output); |
| 20765 | masm.cmpPtrSet(Assembler::Equal, output, ImmGCPtr(ins->mir()->shape()), |
| 20766 | output); |
| 20767 | } |
| 20768 | |
| 20769 | void CodeGenerator::visitGuardToClass(LGuardToClass* ins) { |
| 20770 | Register lhs = ToRegister(ins->lhs()); |
| 20771 | Register temp = ToRegister(ins->temp0()); |
| 20772 | |
| 20773 | // branchTestObjClass may zero the object register on speculative paths |
| 20774 | // (we should have a defineReuseInput allocation in this case). |
| 20775 | Register spectreRegToZero = lhs; |
| 20776 | |
| 20777 | Label notEqual; |
| 20778 | |
| 20779 | masm.branchTestObjClass(Assembler::NotEqual, lhs, ins->mir()->getClass(), |
| 20780 | temp, spectreRegToZero, ¬Equal); |
| 20781 | |
| 20782 | // Can't return null-return here, so bail. |
| 20783 | bailoutFrom(¬Equal, ins->snapshot()); |
| 20784 | } |
| 20785 | |
| 20786 | void CodeGenerator::visitGuardToFunction(LGuardToFunction* ins) { |
| 20787 | Register lhs = ToRegister(ins->lhs()); |
| 20788 | Register temp = ToRegister(ins->temp0()); |
| 20789 | |
| 20790 | // branchTestObjClass may zero the object register on speculative paths |
| 20791 | // (we should have a defineReuseInput allocation in this case). |
| 20792 | Register spectreRegToZero = lhs; |
| 20793 | |
| 20794 | Label notEqual; |
| 20795 | |
| 20796 | masm.branchTestObjIsFunction(Assembler::NotEqual, lhs, temp, spectreRegToZero, |
| 20797 | ¬Equal); |
| 20798 | |
| 20799 | // Can't return null-return here, so bail. |
| 20800 | bailoutFrom(¬Equal, ins->snapshot()); |
| 20801 | } |
| 20802 | |
| 20803 | void CodeGenerator::visitObjectClassToString(LObjectClassToString* lir) { |
| 20804 | Register obj = ToRegister(lir->object()); |
| 20805 | Register temp = ToRegister(lir->temp0()); |
| 20806 | |
| 20807 | using Fn = JSString* (*)(JSContext*, JSObject*); |
| 20808 | masm.setupAlignedABICall(); |
| 20809 | masm.loadJSContext(temp); |
| 20810 | masm.passABIArg(temp); |
| 20811 | masm.passABIArg(obj); |
| 20812 | masm.callWithABI<Fn, js::ObjectClassToString>(); |
| 20813 | |
| 20814 | bailoutCmpPtr(Assembler::Equal, ReturnReg, ImmWord(0), lir->snapshot()); |
| 20815 | } |
| 20816 | |
| 20817 | void CodeGenerator::visitWasmParameter(LWasmParameter* lir) {} |
| 20818 | |
| 20819 | void CodeGenerator::visitWasmParameterI64(LWasmParameterI64* lir) {} |
| 20820 | |
| 20821 | void CodeGenerator::visitWasmReturn(LWasmReturn* lir) { |
| 20822 | // Don't emit a jump to the return label if this is the last block. |
| 20823 | if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) { |
| 20824 | masm.jump(&returnLabel_); |
| 20825 | } |
| 20826 | } |
| 20827 | |
| 20828 | void CodeGenerator::visitWasmReturnI64(LWasmReturnI64* lir) { |
| 20829 | // Don't emit a jump to the return label if this is the last block. |
| 20830 | if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) { |
| 20831 | masm.jump(&returnLabel_); |
| 20832 | } |
| 20833 | } |
| 20834 | |
| 20835 | void CodeGenerator::visitWasmReturnVoid(LWasmReturnVoid* lir) { |
| 20836 | // Don't emit a jump to the return label if this is the last block. |
| 20837 | if (current->mir() != *gen->graph().poBegin() || current->isOutOfLine()) { |
| 20838 | masm.jump(&returnLabel_); |
| 20839 | } |
| 20840 | } |
| 20841 | |
| 20842 | void CodeGenerator::emitAssertRangeI(MIRType type, const Range* r, |
| 20843 | Register input) { |
| 20844 | // Check the lower bound. |
| 20845 | if (r->hasInt32LowerBound() && r->lower() > INT32_MIN(-2147483647-1)) { |
| 20846 | Label success; |
| 20847 | if (type == MIRType::Int32 || type == MIRType::Boolean) { |
| 20848 | masm.branch32(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), |
| 20849 | &success); |
| 20850 | } else { |
| 20851 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 20851); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence (__null, 20851); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 20852 | masm.branchPtr(Assembler::GreaterThanOrEqual, input, Imm32(r->lower()), |
| 20853 | &success); |
| 20854 | } |
| 20855 | masm.assumeUnreachable( |
| 20856 | "Integer input should be equal or higher than Lowerbound."); |
| 20857 | masm.bind(&success); |
| 20858 | } |
| 20859 | |
| 20860 | // Check the upper bound. |
| 20861 | if (r->hasInt32UpperBound() && r->upper() < INT32_MAX(2147483647)) { |
| 20862 | Label success; |
| 20863 | if (type == MIRType::Int32 || type == MIRType::Boolean) { |
| 20864 | masm.branch32(Assembler::LessThanOrEqual, input, Imm32(r->upper()), |
| 20865 | &success); |
| 20866 | } else { |
| 20867 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 20867); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "type == MIRType::IntPtr" ")"); do { MOZ_CrashSequence (__null, 20867); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 20868 | masm.branchPtr(Assembler::LessThanOrEqual, input, Imm32(r->upper()), |
| 20869 | &success); |
| 20870 | } |
| 20871 | masm.assumeUnreachable( |
| 20872 | "Integer input should be lower or equal than Upperbound."); |
| 20873 | masm.bind(&success); |
| 20874 | } |
| 20875 | |
| 20876 | // For r->canHaveFractionalPart(), r->canBeNegativeZero(), and |
| 20877 | // r->exponent(), there's nothing to check, because if we ended up in the |
| 20878 | // integer range checking code, the value is already in an integer register |
| 20879 | // in the integer range. |
| 20880 | } |
| 20881 | |
| 20882 | void CodeGenerator::emitAssertRangeD(const Range* r, FloatRegister input, |
| 20883 | FloatRegister temp) { |
| 20884 | // Check the lower bound. |
| 20885 | if (r->hasInt32LowerBound()) { |
| 20886 | Label success; |
| 20887 | masm.loadConstantDouble(r->lower(), temp); |
| 20888 | if (r->canBeNaN()) { |
| 20889 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &success); |
| 20890 | } |
| 20891 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, |
| 20892 | &success); |
| 20893 | masm.assumeUnreachable( |
| 20894 | "Double input should be equal or higher than Lowerbound."); |
| 20895 | masm.bind(&success); |
| 20896 | } |
| 20897 | // Check the upper bound. |
| 20898 | if (r->hasInt32UpperBound()) { |
| 20899 | Label success; |
| 20900 | masm.loadConstantDouble(r->upper(), temp); |
| 20901 | if (r->canBeNaN()) { |
| 20902 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &success); |
| 20903 | } |
| 20904 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, &success); |
| 20905 | masm.assumeUnreachable( |
| 20906 | "Double input should be lower or equal than Upperbound."); |
| 20907 | masm.bind(&success); |
| 20908 | } |
| 20909 | |
| 20910 | // This code does not yet check r->canHaveFractionalPart(). This would require |
| 20911 | // new assembler interfaces to make rounding instructions available. |
| 20912 | |
| 20913 | if (!r->canBeNegativeZero()) { |
| 20914 | Label success; |
| 20915 | |
| 20916 | // First, test for being equal to 0.0, which also includes -0.0. |
| 20917 | masm.loadConstantDouble(0.0, temp); |
| 20918 | masm.branchDouble(Assembler::DoubleNotEqualOrUnordered, input, temp, |
| 20919 | &success); |
| 20920 | |
| 20921 | // The easiest way to distinguish -0.0 from 0.0 is that 1.0/-0.0 is |
| 20922 | // -Infinity instead of Infinity. |
| 20923 | masm.loadConstantDouble(1.0, temp); |
| 20924 | masm.divDouble(input, temp); |
| 20925 | masm.branchDouble(Assembler::DoubleGreaterThan, temp, input, &success); |
| 20926 | |
| 20927 | masm.assumeUnreachable("Input shouldn't be negative zero."); |
| 20928 | |
| 20929 | masm.bind(&success); |
| 20930 | } |
| 20931 | |
| 20932 | if (!r->hasInt32Bounds() && !r->canBeInfiniteOrNaN() && |
| 20933 | r->exponent() < FloatingPoint<double>::kExponentBias) { |
| 20934 | // Check the bounds implied by the maximum exponent. |
| 20935 | Label exponentLoOk; |
| 20936 | masm.loadConstantDouble(pow(2.0, r->exponent() + 1), temp); |
| 20937 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentLoOk); |
| 20938 | masm.branchDouble(Assembler::DoubleLessThanOrEqual, input, temp, |
| 20939 | &exponentLoOk); |
| 20940 | masm.assumeUnreachable("Check for exponent failed."); |
| 20941 | masm.bind(&exponentLoOk); |
| 20942 | |
| 20943 | Label exponentHiOk; |
| 20944 | masm.loadConstantDouble(-pow(2.0, r->exponent() + 1), temp); |
| 20945 | masm.branchDouble(Assembler::DoubleUnordered, input, input, &exponentHiOk); |
| 20946 | masm.branchDouble(Assembler::DoubleGreaterThanOrEqual, input, temp, |
| 20947 | &exponentHiOk); |
| 20948 | masm.assumeUnreachable("Check for exponent failed."); |
| 20949 | masm.bind(&exponentHiOk); |
| 20950 | } else if (!r->hasInt32Bounds() && !r->canBeNaN()) { |
| 20951 | // If we think the value can't be NaN, check that it isn't. |
| 20952 | Label notnan; |
| 20953 | masm.branchDouble(Assembler::DoubleOrdered, input, input, ¬nan); |
| 20954 | masm.assumeUnreachable("Input shouldn't be NaN."); |
| 20955 | masm.bind(¬nan); |
| 20956 | |
| 20957 | // If we think the value also can't be an infinity, check that it isn't. |
| 20958 | if (!r->canBeInfiniteOrNaN()) { |
| 20959 | Label notposinf; |
| 20960 | masm.loadConstantDouble(PositiveInfinity<double>(), temp); |
| 20961 | masm.branchDouble(Assembler::DoubleLessThan, input, temp, ¬posinf); |
| 20962 | masm.assumeUnreachable("Input shouldn't be +Inf."); |
| 20963 | masm.bind(¬posinf); |
| 20964 | |
| 20965 | Label notneginf; |
| 20966 | masm.loadConstantDouble(NegativeInfinity<double>(), temp); |
| 20967 | masm.branchDouble(Assembler::DoubleGreaterThan, input, temp, ¬neginf); |
| 20968 | masm.assumeUnreachable("Input shouldn't be -Inf."); |
| 20969 | masm.bind(¬neginf); |
| 20970 | } |
| 20971 | } |
| 20972 | } |
| 20973 | |
| 20974 | void CodeGenerator::visitAssertClass(LAssertClass* ins) { |
| 20975 | Register obj = ToRegister(ins->input()); |
| 20976 | Register temp = ToRegister(ins->temp0()); |
| 20977 | |
| 20978 | Label success; |
| 20979 | if (ins->mir()->getClass() == &FunctionClass) { |
| 20980 | // Allow both possible function classes here. |
| 20981 | masm.branchTestObjIsFunctionNoSpectreMitigations(Assembler::Equal, obj, |
| 20982 | temp, &success); |
| 20983 | } else { |
| 20984 | masm.branchTestObjClassNoSpectreMitigations( |
| 20985 | Assembler::Equal, obj, ins->mir()->getClass(), temp, &success); |
| 20986 | } |
| 20987 | masm.assumeUnreachable("Wrong KnownClass during run-time"); |
| 20988 | masm.bind(&success); |
| 20989 | } |
| 20990 | |
| 20991 | void CodeGenerator::visitAssertShape(LAssertShape* ins) { |
| 20992 | Register obj = ToRegister(ins->object()); |
| 20993 | |
| 20994 | Label success; |
| 20995 | masm.branchTestObjShapeNoSpectreMitigations(Assembler::Equal, obj, |
| 20996 | ins->mir()->shape(), &success); |
| 20997 | masm.assumeUnreachable("Wrong Shape during run-time"); |
| 20998 | masm.bind(&success); |
| 20999 | } |
| 21000 | |
| 21001 | void CodeGenerator::visitAssertRangeI(LAssertRangeI* ins) { |
| 21002 | Register input = ToRegister(ins->input()); |
| 21003 | const Range* r = ins->mir()->assertedRange(); |
| 21004 | |
| 21005 | emitAssertRangeI(ins->mir()->input()->type(), r, input); |
| 21006 | } |
| 21007 | |
| 21008 | void CodeGenerator::visitAssertRangeD(LAssertRangeD* ins) { |
| 21009 | FloatRegister input = ToFloatRegister(ins->input()); |
| 21010 | FloatRegister temp = ToFloatRegister(ins->temp0()); |
| 21011 | const Range* r = ins->mir()->assertedRange(); |
| 21012 | |
| 21013 | emitAssertRangeD(r, input, temp); |
| 21014 | } |
| 21015 | |
| 21016 | void CodeGenerator::visitAssertRangeF(LAssertRangeF* ins) { |
| 21017 | FloatRegister input = ToFloatRegister(ins->input()); |
| 21018 | FloatRegister temp = ToFloatRegister(ins->temp0()); |
| 21019 | FloatRegister temp2 = ToFloatRegister(ins->temp1()); |
| 21020 | |
| 21021 | const Range* r = ins->mir()->assertedRange(); |
| 21022 | |
| 21023 | masm.convertFloat32ToDouble(input, temp); |
| 21024 | emitAssertRangeD(r, temp, temp2); |
| 21025 | } |
| 21026 | |
| 21027 | void CodeGenerator::visitAssertRangeV(LAssertRangeV* ins) { |
| 21028 | const Range* r = ins->mir()->assertedRange(); |
| 21029 | ValueOperand value = ToValue(ins->input()); |
| 21030 | Label done; |
| 21031 | |
| 21032 | { |
| 21033 | ScratchTagScope tag(masm, value); |
| 21034 | masm.splitTagForTest(value, tag); |
| 21035 | |
| 21036 | { |
| 21037 | Label isNotInt32; |
| 21038 | masm.branchTestInt32(Assembler::NotEqual, tag, &isNotInt32); |
| 21039 | { |
| 21040 | ScratchTagScopeRelease _(&tag); |
| 21041 | Register unboxInt32 = ToTempUnboxRegister(ins->temp0()); |
| 21042 | Register input = masm.extractInt32(value, unboxInt32); |
| 21043 | emitAssertRangeI(MIRType::Int32, r, input); |
| 21044 | masm.jump(&done); |
| 21045 | } |
| 21046 | masm.bind(&isNotInt32); |
| 21047 | } |
| 21048 | |
| 21049 | { |
| 21050 | Label isNotDouble; |
| 21051 | masm.branchTestDouble(Assembler::NotEqual, tag, &isNotDouble); |
| 21052 | { |
| 21053 | ScratchTagScopeRelease _(&tag); |
| 21054 | FloatRegister input = ToFloatRegister(ins->temp1()); |
| 21055 | FloatRegister temp = ToFloatRegister(ins->temp2()); |
| 21056 | masm.unboxDouble(value, input); |
| 21057 | emitAssertRangeD(r, input, temp); |
| 21058 | masm.jump(&done); |
| 21059 | } |
| 21060 | masm.bind(&isNotDouble); |
| 21061 | } |
| 21062 | } |
| 21063 | |
| 21064 | masm.assumeUnreachable("Incorrect range for Value."); |
| 21065 | masm.bind(&done); |
| 21066 | } |
| 21067 | |
| 21068 | void CodeGenerator::visitInterruptCheck(LInterruptCheck* lir) { |
| 21069 | using Fn = bool (*)(JSContext*); |
| 21070 | OutOfLineCode* ool = |
| 21071 | oolCallVM<Fn, InterruptCheck>(lir, ArgList(), StoreNothing()); |
| 21072 | |
| 21073 | const void* interruptAddr = gen->runtime->addressOfInterruptBits(); |
| 21074 | masm.branch32(Assembler::NotEqual, AbsoluteAddress(interruptAddr), Imm32(0), |
| 21075 | ool->entry()); |
| 21076 | masm.bind(ool->rejoin()); |
| 21077 | } |
| 21078 | |
| 21079 | void CodeGenerator::visitWasmInterruptCheck(LWasmInterruptCheck* lir) { |
| 21080 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21080); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21080); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21081 | |
| 21082 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21083 | emitResumableWasmTrapOOL(lir, masm.framePushed(), |
| 21084 | lir->mir()->trapSiteDesc(), |
| 21085 | wasm::Trap::CheckInterrupt); |
| 21086 | masm.jump(ool.rejoin()); |
| 21087 | }); |
| 21088 | addOutOfLineCode(ool, lir->mir()); |
| 21089 | masm.branch32( |
| 21090 | Assembler::NotEqual, |
| 21091 | Address(ToRegister(lir->instance()), wasm::Instance::offsetOfInterrupt()), |
| 21092 | Imm32(0), ool->entry()); |
| 21093 | masm.bind(ool->rejoin()); |
| 21094 | } |
| 21095 | |
| 21096 | void CodeGenerator::visitWasmTrap(LWasmTrap* lir) { |
| 21097 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21097); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21097); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21098 | const MWasmTrap* mir = lir->mir(); |
| 21099 | |
| 21100 | masm.wasmTrap(mir->trap(), mir->trapSiteDesc()); |
| 21101 | } |
| 21102 | |
| 21103 | void CodeGenerator::visitWasmRefAsNonNull(LWasmRefAsNonNull* lir) { |
| 21104 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21104); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21104); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21105 | const MWasmRefAsNonNull* mir = lir->mir(); |
| 21106 | Label nonNull; |
| 21107 | Register ref = ToRegister(lir->ref()); |
| 21108 | |
| 21109 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21110 | masm.wasmTrap(wasm::Trap::NullPointerDereference, mir->trapSiteDesc()); |
| 21111 | }); |
| 21112 | addOutOfLineCode(ool, mir); |
| 21113 | masm.branchWasmAnyRefIsNull(true, ref, ool->entry()); |
| 21114 | } |
| 21115 | |
| 21116 | void CodeGenerator::visitWasmRefTestAbstract(LWasmRefTestAbstract* ins) { |
| 21117 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21117); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21117); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21118 | |
| 21119 | const MWasmRefTestAbstract* mir = ins->mir(); |
| 21120 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 21120); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()" ")"); do { MOZ_CrashSequence(__null, 21120); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21121 | |
| 21122 | Register ref = ToRegister(ins->ref()); |
| 21123 | Register superSTV = Register::Invalid(); |
| 21124 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
| 21125 | Register scratch2 = Register::Invalid(); |
| 21126 | Register result = ToRegister(ins->output()); |
| 21127 | Label onSuccess; |
| 21128 | Label onFail; |
| 21129 | Label join; |
| 21130 | masm.branchWasmRefIsSubtype(ref, mir->ref()->wasmRefType(), mir->destType(), |
| 21131 | &onSuccess, |
| 21132 | /*onSuccess=*/true, /*signalNullChecks=*/false, |
| 21133 | superSTV, scratch1, scratch2); |
| 21134 | masm.bind(&onFail); |
| 21135 | masm.xor32(result, result); |
| 21136 | masm.jump(&join); |
| 21137 | masm.bind(&onSuccess); |
| 21138 | masm.move32(Imm32(1), result); |
| 21139 | masm.bind(&join); |
| 21140 | } |
| 21141 | |
| 21142 | void CodeGenerator::visitWasmRefTestConcrete(LWasmRefTestConcrete* ins) { |
| 21143 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21143); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21143); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21144 | |
| 21145 | const MWasmRefTestConcrete* mir = ins->mir(); |
| 21146 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 21146); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()" ")"); do { MOZ_CrashSequence(__null, 21146); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21147 | |
| 21148 | Register ref = ToRegister(ins->ref()); |
| 21149 | Register superSTV = ToRegister(ins->superSTV()); |
| 21150 | Register scratch1 = ToRegister(ins->temp0()); |
| 21151 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
| 21152 | Register result = ToRegister(ins->output()); |
| 21153 | Label onSuccess; |
| 21154 | Label join; |
| 21155 | masm.branchWasmRefIsSubtype(ref, mir->ref()->wasmRefType(), mir->destType(), |
| 21156 | &onSuccess, |
| 21157 | /*onSuccess=*/true, /*signalNullChecks=*/false, |
| 21158 | superSTV, scratch1, scratch2); |
| 21159 | masm.move32(Imm32(0), result); |
| 21160 | masm.jump(&join); |
| 21161 | masm.bind(&onSuccess); |
| 21162 | masm.move32(Imm32(1), result); |
| 21163 | masm.bind(&join); |
| 21164 | } |
| 21165 | |
| 21166 | void CodeGenerator::visitWasmRefTestAbstractAndBranch( |
| 21167 | LWasmRefTestAbstractAndBranch* ins) { |
| 21168 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21168); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21168); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21169 | Register ref = ToRegister(ins->ref()); |
| 21170 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
| 21171 | Label* onSuccess = getJumpLabelForBranch(ins->ifTrue()); |
| 21172 | Label* onFail = getJumpLabelForBranch(ins->ifFalse()); |
| 21173 | masm.branchWasmRefIsSubtype(ref, ins->sourceType(), ins->destType(), |
| 21174 | onSuccess, /*onSuccess=*/true, |
| 21175 | /*signalNullChecks=*/false, Register::Invalid(), |
| 21176 | scratch1, Register::Invalid()); |
| 21177 | masm.jump(onFail); |
| 21178 | } |
| 21179 | |
| 21180 | void CodeGenerator::visitWasmRefTestConcreteAndBranch( |
| 21181 | LWasmRefTestConcreteAndBranch* ins) { |
| 21182 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21182); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21182); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21183 | Register ref = ToRegister(ins->ref()); |
| 21184 | Register superSTV = ToRegister(ins->superSTV()); |
| 21185 | Register scratch1 = ToRegister(ins->temp0()); |
| 21186 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
| 21187 | Label* onSuccess = getJumpLabelForBranch(ins->ifTrue()); |
| 21188 | Label* onFail = getJumpLabelForBranch(ins->ifFalse()); |
| 21189 | masm.branchWasmRefIsSubtype( |
| 21190 | ref, ins->sourceType(), ins->destType(), onSuccess, /*onSuccess=*/true, |
| 21191 | /*signalNullChecks=*/false, superSTV, scratch1, scratch2); |
| 21192 | masm.jump(onFail); |
| 21193 | } |
| 21194 | |
| 21195 | void CodeGenerator::visitWasmRefCastAbstract(LWasmRefCastAbstract* ins) { |
| 21196 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21196); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21196); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21197 | |
| 21198 | const MWasmRefCastAbstract* mir = ins->mir(); |
| 21199 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 21199); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mir->destType().isTypeRef()" ")"); do { MOZ_CrashSequence(__null, 21199); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21200 | |
| 21201 | Register ref = ToRegister(ins->ref()); |
| 21202 | Register superSTV = Register::Invalid(); |
| 21203 | Register scratch1 = ToTempRegisterOrInvalid(ins->temp0()); |
| 21204 | Register scratch2 = Register::Invalid(); |
| 21205 | MOZ_ASSERT(ref == ToRegister(ins->output()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(ref == ToRegister(ins->output()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ref == ToRegister(ins->output ())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ref == ToRegister(ins->output())", "./../../../../js/src/jit/CodeGenerator.cpp" , 21205); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref == ToRegister(ins->output())" ")"); do { MOZ_CrashSequence(__null, 21205); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21206 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21207 | masm.wasmTrap(wasm::Trap::BadCast, mir->trapSiteDesc()); |
| 21208 | }); |
| 21209 | addOutOfLineCode(ool, ins->mir()); |
| 21210 | FaultingCodeRange fcr = masm.branchWasmRefIsSubtype( |
| 21211 | ref, mir->ref()->wasmRefType(), mir->destType(), ool->entry(), |
| 21212 | /*onSuccess=*/false, /*signalNullChecks=*/true, superSTV, scratch1, |
| 21213 | scratch2); |
| 21214 | if (fcr.isValid()) { |
| 21215 | masm.appendAndVerify(wasm::Trap::BadCast, |
| 21216 | wasm::TrapMachineInsnForLoadWord(), fcr, |
| 21217 | mir->trapSiteDesc()); |
| 21218 | } |
| 21219 | } |
| 21220 | |
| 21221 | void CodeGenerator::visitWasmRefCastConcrete(LWasmRefCastConcrete* ins) { |
| 21222 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21222); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21222); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21223 | |
| 21224 | const MWasmRefCastConcrete* mir = ins->mir(); |
| 21225 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 21225); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mir->destType().isTypeRef()" ")"); do { MOZ_CrashSequence(__null, 21225); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21226 | |
| 21227 | Register ref = ToRegister(ins->ref()); |
| 21228 | Register superSTV = ToRegister(ins->superSTV()); |
| 21229 | Register scratch1 = ToRegister(ins->temp0()); |
| 21230 | Register scratch2 = ToTempRegisterOrInvalid(ins->temp1()); |
| 21231 | MOZ_ASSERT(ref == ToRegister(ins->output()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(ref == ToRegister(ins->output()))>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(ref == ToRegister(ins->output ())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure ("ref == ToRegister(ins->output())", "./../../../../js/src/jit/CodeGenerator.cpp" , 21231); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ref == ToRegister(ins->output())" ")"); do { MOZ_CrashSequence(__null, 21231); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21232 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21233 | masm.wasmTrap(wasm::Trap::BadCast, mir->trapSiteDesc()); |
| 21234 | }); |
| 21235 | addOutOfLineCode(ool, ins->mir()); |
| 21236 | FaultingCodeRange fcr = masm.branchWasmRefIsSubtype( |
| 21237 | ref, mir->ref()->wasmRefType(), mir->destType(), ool->entry(), |
| 21238 | /*onSuccess=*/false, /*signalNullChecks=*/true, superSTV, scratch1, |
| 21239 | scratch2); |
| 21240 | if (fcr.isValid()) { |
| 21241 | masm.appendAndVerify(wasm::Trap::BadCast, |
| 21242 | wasm::TrapMachineInsnForLoadWord(), fcr, |
| 21243 | mir->trapSiteDesc()); |
| 21244 | } |
| 21245 | } |
| 21246 | |
| 21247 | void CodeGenerator::callWasmStructAllocFun( |
| 21248 | LInstruction* lir, wasm::SymbolicAddress fun, Register typeDefIndex, |
| 21249 | Register allocSite, Register output, |
| 21250 | const wasm::TrapSiteDesc& trapSiteDesc) { |
| 21251 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21254); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { MOZ_CrashSequence(__null, 21254); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21252 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21254); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { MOZ_CrashSequence(__null, 21254); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21253 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21254); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { MOZ_CrashSequence(__null, 21254); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21254 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21254); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::StructNewIL_true || fun == wasm::SymbolicAddress::StructNewIL_false || fun == wasm::SymbolicAddress::StructNewOOL_true || fun == wasm::SymbolicAddress::StructNewOOL_false" ")"); do { MOZ_CrashSequence(__null, 21254); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21255 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21256); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21256); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21256 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21256); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21256); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21257 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21258); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21258); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21258 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21258); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigStructNewIL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21258); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21259 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21260); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21260); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21260 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21260); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21260); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21261 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21262); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21262); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21262 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21262); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigStructNewOOL_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21262); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21263 | |
| 21264 | masm.Push(InstanceReg); |
| 21265 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 21266 | saveLive(lir); |
| 21267 | |
| 21268 | masm.setupWasmABICall(fun); |
| 21269 | masm.passABIArg(InstanceReg); |
| 21270 | masm.passABIArg(typeDefIndex); |
| 21271 | masm.passABIArg(allocSite); |
| 21272 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 21273 | CodeOffset offset = |
| 21274 | masm.callWithABI(trapSiteDesc.bytecodeOffset, fun, |
| 21275 | mozilla::Some(instanceOffset), ABIType::General); |
| 21276 | masm.storeCallPointerResult(output); |
| 21277 | |
| 21278 | markSafepointAt(offset.offset(), lir); |
| 21279 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance); |
| 21280 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall); |
| 21281 | |
| 21282 | restoreLive(lir); |
| 21283 | masm.Pop(InstanceReg); |
| 21284 | #if JS_CODEGEN_ARM64 |
| 21285 | masm.syncStackPtr(); |
| 21286 | #endif |
| 21287 | |
| 21288 | masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr, |
| 21289 | wasm::Trap::ThrowReported, trapSiteDesc); |
| 21290 | } |
| 21291 | |
| 21292 | void CodeGenerator::visitWasmNewStructObject(LWasmNewStructObject* lir) { |
| 21293 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21293); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21293); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21294 | |
| 21295 | MWasmNewStructObject* mir = lir->mir(); |
| 21296 | uint32_t typeDefIndex = wasmCodeMeta()->types->indexOf(mir->typeDef()); |
| 21297 | |
| 21298 | Register allocSite = ToRegister(lir->allocSite()); |
| 21299 | Register output = ToRegister(lir->output()); |
| 21300 | Register temp = ToRegister(lir->temp0()); |
| 21301 | |
| 21302 | if (mir->isOutline()) { |
| 21303 | wasm::SymbolicAddress fun = mir->zeroFields() |
| 21304 | ? wasm::SymbolicAddress::StructNewOOL_true |
| 21305 | : wasm::SymbolicAddress::StructNewOOL_false; |
| 21306 | |
| 21307 | masm.move32(Imm32(typeDefIndex), temp); |
| 21308 | callWasmStructAllocFun(lir, fun, temp, allocSite, output, |
| 21309 | mir->trapSiteDesc()); |
| 21310 | } else { |
| 21311 | wasm::SymbolicAddress fun = mir->zeroFields() |
| 21312 | ? wasm::SymbolicAddress::StructNewIL_true |
| 21313 | : wasm::SymbolicAddress::StructNewIL_false; |
| 21314 | |
| 21315 | Register instance = ToRegister(lir->instance()); |
| 21316 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21316); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence (__null, 21316); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21317 | |
| 21318 | auto* ool = |
| 21319 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21320 | masm.move32(Imm32(typeDefIndex), temp); |
| 21321 | callWasmStructAllocFun(lir, fun, temp, allocSite, output, |
| 21322 | mir->trapSiteDesc()); |
| 21323 | masm.jump(ool.rejoin()); |
| 21324 | }); |
| 21325 | addOutOfLineCode(ool, lir->mir()); |
| 21326 | |
| 21327 | size_t offsetOfTypeDefData = wasm::Instance::offsetInData( |
| 21328 | wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex)); |
| 21329 | masm.wasmNewStructObject(instance, output, allocSite, temp, |
| 21330 | offsetOfTypeDefData, ool->entry(), |
| 21331 | mir->allocKind(), mir->zeroFields()); |
| 21332 | |
| 21333 | masm.bind(ool->rejoin()); |
| 21334 | } |
| 21335 | } |
| 21336 | |
| 21337 | void CodeGenerator::callWasmArrayAllocFun( |
| 21338 | LInstruction* lir, wasm::SymbolicAddress fun, Register numElements, |
| 21339 | Register typeDefIndex, Register allocSite, Register output, |
| 21340 | const wasm::TrapSiteDesc& trapSiteDesc) { |
| 21341 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21342); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" ")"); do { MOZ_CrashSequence(__null, 21342); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21342 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21342); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "fun == wasm::SymbolicAddress::ArrayNew_true || fun == wasm::SymbolicAddress::ArrayNew_false" ")"); do { MOZ_CrashSequence(__null, 21342); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21343 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21344); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21344); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21344 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21344); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigArrayNew_true.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21344); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21345 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21346); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21346); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 21346 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21346); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "wasm::SASigArrayNew_false.failureMode == wasm::FailureMode::FailOnNullPtr" ")"); do { MOZ_CrashSequence(__null, 21346); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21347 | |
| 21348 | masm.Push(InstanceReg); |
| 21349 | int32_t framePushedAfterInstance = masm.framePushed(); |
| 21350 | saveLive(lir); |
| 21351 | |
| 21352 | masm.setupWasmABICall(fun); |
| 21353 | masm.passABIArg(InstanceReg); |
| 21354 | masm.passABIArg(numElements); |
| 21355 | masm.passABIArg(typeDefIndex); |
| 21356 | masm.passABIArg(allocSite); |
| 21357 | int32_t instanceOffset = masm.framePushed() - framePushedAfterInstance; |
| 21358 | CodeOffset offset = |
| 21359 | masm.callWithABI(trapSiteDesc.bytecodeOffset, fun, |
| 21360 | mozilla::Some(instanceOffset), ABIType::General); |
| 21361 | masm.storeCallPointerResult(output); |
| 21362 | |
| 21363 | markSafepointAt(offset.offset(), lir); |
| 21364 | lir->safepoint()->setFramePushedAtStackMapBase(framePushedAfterInstance); |
| 21365 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::CodegenCall); |
| 21366 | |
| 21367 | restoreLive(lir); |
| 21368 | masm.Pop(InstanceReg); |
| 21369 | #if JS_CODEGEN_ARM64 |
| 21370 | masm.syncStackPtr(); |
| 21371 | #endif |
| 21372 | |
| 21373 | masm.wasmTrapOnFailedInstanceCall(output, wasm::FailureMode::FailOnNullPtr, |
| 21374 | wasm::Trap::ThrowReported, trapSiteDesc); |
| 21375 | } |
| 21376 | |
| 21377 | void CodeGenerator::visitWasmNewArrayObject(LWasmNewArrayObject* lir) { |
| 21378 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 21378); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 21378); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21379 | |
| 21380 | MWasmNewArrayObject* mir = lir->mir(); |
| 21381 | uint32_t typeDefIndex = wasmCodeMeta()->types->indexOf(mir->typeDef()); |
| 21382 | |
| 21383 | Register allocSite = ToRegister(lir->allocSite()); |
| 21384 | Register output = ToRegister(lir->output()); |
| 21385 | Register temp0 = ToRegister(lir->temp0()); |
| 21386 | Register temp1 = ToRegister(lir->temp1()); |
| 21387 | |
| 21388 | wasm::SymbolicAddress fun = mir->zeroFields() |
| 21389 | ? wasm::SymbolicAddress::ArrayNew_true |
| 21390 | : wasm::SymbolicAddress::ArrayNew_false; |
| 21391 | |
| 21392 | if (lir->numElements()->isConstant()) { |
| 21393 | // numElements is constant, so we can do optimized code generation. |
| 21394 | uint32_t numElements = lir->numElements()->toConstant()->toInt32(); |
| 21395 | CheckedUint32 arrayDataBytes = WasmArrayObject::calcArrayDataBytesChecked( |
| 21396 | mir->elemSize(), numElements); |
| 21397 | if (!arrayDataBytes.isValid() || |
| 21398 | arrayDataBytes.value() > WasmArrayObject_MaxInlineBytes) { |
| 21399 | // Too much array data to store inline. Immediately perform an instance |
| 21400 | // call to handle the out-of-line storage (or the trap). |
| 21401 | masm.move32(Imm32(typeDefIndex), temp0); |
| 21402 | masm.move32(Imm32(numElements), temp1); |
| 21403 | callWasmArrayAllocFun(lir, fun, temp1, temp0, allocSite, output, |
| 21404 | mir->trapSiteDesc()); |
| 21405 | } else { |
| 21406 | // arrayDataBytes is small enough to be stored inline in WasmArrayObject. |
| 21407 | // Attempt a nursery allocation and fall back to an instance call if it |
| 21408 | // fails. |
| 21409 | Register instance = ToRegister(lir->instance()); |
| 21410 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21410); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence (__null, 21410); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21411 | |
| 21412 | auto* ool = |
| 21413 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21414 | masm.move32(Imm32(typeDefIndex), temp0); |
| 21415 | masm.move32(Imm32(numElements), temp1); |
| 21416 | callWasmArrayAllocFun(lir, fun, temp1, temp0, allocSite, output, |
| 21417 | mir->trapSiteDesc()); |
| 21418 | masm.jump(ool.rejoin()); |
| 21419 | }); |
| 21420 | addOutOfLineCode(ool, lir->mir()); |
| 21421 | |
| 21422 | size_t offsetOfTypeDefData = wasm::Instance::offsetInData( |
| 21423 | wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex)); |
| 21424 | masm.wasmNewArrayObjectFixed( |
| 21425 | instance, output, allocSite, temp0, temp1, offsetOfTypeDefData, |
| 21426 | ool->entry(), numElements, arrayDataBytes.value(), mir->zeroFields()); |
| 21427 | |
| 21428 | masm.bind(ool->rejoin()); |
| 21429 | } |
| 21430 | } else { |
| 21431 | // numElements is dynamic. Attempt a dynamic inline-storage nursery |
| 21432 | // allocation and fall back to an instance call if it fails. |
| 21433 | Register instance = ToRegister(lir->instance()); |
| 21434 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21434); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "instance == InstanceReg" ")"); do { MOZ_CrashSequence (__null, 21434); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 21435 | Register numElements = ToRegister(lir->numElements()); |
| 21436 | |
| 21437 | auto* ool = |
| 21438 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21439 | masm.move32(Imm32(typeDefIndex), temp0); |
| 21440 | callWasmArrayAllocFun(lir, fun, numElements, temp0, allocSite, output, |
| 21441 | mir->trapSiteDesc()); |
| 21442 | masm.jump(ool.rejoin()); |
| 21443 | }); |
| 21444 | addOutOfLineCode(ool, lir->mir()); |
| 21445 | |
| 21446 | size_t offsetOfTypeDefData = wasm::Instance::offsetInData( |
| 21447 | wasmCodeMeta()->offsetOfTypeDefInstanceData(typeDefIndex)); |
| 21448 | masm.wasmNewArrayObject(instance, output, numElements, allocSite, temp1, |
| 21449 | offsetOfTypeDefData, ool->entry(), mir->elemSize(), |
| 21450 | mir->zeroFields()); |
| 21451 | |
| 21452 | masm.bind(ool->rejoin()); |
| 21453 | } |
| 21454 | } |
| 21455 | |
| 21456 | void CodeGenerator::visitWasmHeapReg(LWasmHeapReg* ins) { |
| 21457 | #ifdef WASM_HAS_HEAPREG1 |
| 21458 | masm.movePtr(HeapReg, ToRegister(ins->output())); |
| 21459 | #else |
| 21460 | MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "./../../../../js/src/jit/CodeGenerator.cpp" , 21460); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { MOZ_CrashSequence (__null, 21460); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 21461 | #endif |
| 21462 | } |
| 21463 | |
| 21464 | void CodeGenerator::emitResumableWasmTrapOOL( |
| 21465 | LInstruction* lir, size_t framePushed, |
| 21466 | const wasm::TrapSiteDesc& trapSiteDesc, wasm::Trap trap) { |
| 21467 | masm.wasmTrap(trap, trapSiteDesc); |
| 21468 | |
| 21469 | markSafepointAt(masm.currentOffset(), lir); |
| 21470 | |
| 21471 | // Note that masm.framePushed() doesn't include the register dump area. |
| 21472 | // That will be taken into account when the StackMap is created from the |
| 21473 | // LSafepoint. |
| 21474 | lir->safepoint()->setFramePushedAtStackMapBase(framePushed); |
| 21475 | lir->safepoint()->setWasmSafepointKind(WasmSafepointKind::Trap); |
| 21476 | } |
| 21477 | |
| 21478 | void CodeGenerator::visitWasmBoundsCheck(LWasmBoundsCheck* ins) { |
| 21479 | const MWasmBoundsCheck* mir = ins->mir(); |
| 21480 | |
| 21481 | Register ptr = ToRegister(ins->ptr()); |
| 21482 | if (ins->boundsCheckLimit()->isConstant()) { |
| 21483 | auto* ool = |
| 21484 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21485 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21486 | }); |
| 21487 | addOutOfLineCode(ool, mir); |
| 21488 | masm.branch32(Assembler::AboveOrEqual, ptr, |
| 21489 | Imm32(ins->boundsCheckLimit()->toConstant()->toInt32()), |
| 21490 | ool->entry()); |
| 21491 | return; |
| 21492 | } |
| 21493 | |
| 21494 | Register boundsCheckLimit = ToRegister(ins->boundsCheckLimit()); |
| 21495 | // When there are no spectre mitigations in place, branching out-of-line to |
| 21496 | // the trap is a big performance win, but with mitigations it's trickier. See |
| 21497 | // bug 1680243. |
| 21498 | if (JitOptions.spectreIndexMasking) { |
| 21499 | Label ok; |
| 21500 | masm.wasmBoundsCheck32(Assembler::Below, ptr, boundsCheckLimit, &ok); |
| 21501 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21502 | masm.bind(&ok); |
| 21503 | } else { |
| 21504 | auto* ool = |
| 21505 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21506 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21507 | }); |
| 21508 | addOutOfLineCode(ool, mir); |
| 21509 | masm.wasmBoundsCheck32(Assembler::AboveOrEqual, ptr, boundsCheckLimit, |
| 21510 | ool->entry()); |
| 21511 | } |
| 21512 | } |
| 21513 | |
| 21514 | void CodeGenerator::visitWasmBoundsCheck64(LWasmBoundsCheck64* ins) { |
| 21515 | const MWasmBoundsCheck* mir = ins->mir(); |
| 21516 | |
| 21517 | Register64 ptr = ToRegister64(ins->ptr()); |
| 21518 | if (IsConstant(ins->boundsCheckLimit())) { |
| 21519 | auto* ool = |
| 21520 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21521 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21522 | }); |
| 21523 | addOutOfLineCode(ool, mir); |
| 21524 | masm.branch64(Assembler::AboveOrEqual, ptr, |
| 21525 | Imm64(ToInt64(ins->boundsCheckLimit())), ool->entry()); |
| 21526 | return; |
| 21527 | } |
| 21528 | |
| 21529 | Register64 boundsCheckLimit = ToRegister64(ins->boundsCheckLimit()); |
| 21530 | // See above. |
| 21531 | if (JitOptions.spectreIndexMasking) { |
| 21532 | Label ok; |
| 21533 | masm.wasmBoundsCheck64(Assembler::Below, ptr, boundsCheckLimit, &ok); |
| 21534 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21535 | masm.bind(&ok); |
| 21536 | } else { |
| 21537 | auto* ool = |
| 21538 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21539 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21540 | }); |
| 21541 | addOutOfLineCode(ool, mir); |
| 21542 | masm.wasmBoundsCheck64(Assembler::AboveOrEqual, ptr, boundsCheckLimit, |
| 21543 | ool->entry()); |
| 21544 | } |
| 21545 | } |
| 21546 | |
| 21547 | void CodeGenerator::visitWasmBoundsCheckInstanceField( |
| 21548 | LWasmBoundsCheckInstanceField* ins) { |
| 21549 | const MWasmBoundsCheck* mir = ins->mir(); |
| 21550 | Register ptr = ToRegister(ins->ptr()); |
| 21551 | Register instance = ToRegister(ins->instance()); |
| 21552 | // See above. |
| 21553 | if (JitOptions.spectreIndexMasking) { |
| 21554 | Label ok; |
| 21555 | masm.wasmBoundsCheck32(Assembler::Condition::Below, ptr, |
| 21556 | Address(instance, ins->offset()), &ok); |
| 21557 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21558 | masm.bind(&ok); |
| 21559 | } else { |
| 21560 | auto* ool = |
| 21561 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21562 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21563 | }); |
| 21564 | addOutOfLineCode(ool, mir); |
| 21565 | masm.wasmBoundsCheck32(Assembler::Condition::AboveOrEqual, ptr, |
| 21566 | Address(instance, ins->offset()), ool->entry()); |
| 21567 | } |
| 21568 | } |
| 21569 | |
| 21570 | void CodeGenerator::visitWasmBoundsCheckInstanceField64( |
| 21571 | LWasmBoundsCheckInstanceField64* ins) { |
| 21572 | const MWasmBoundsCheck* mir = ins->mir(); |
| 21573 | Register64 ptr = ToRegister64(ins->ptr()); |
| 21574 | Register instance = ToRegister(ins->instance()); |
| 21575 | // See above. |
| 21576 | if (JitOptions.spectreIndexMasking) { |
| 21577 | Label ok; |
| 21578 | masm.wasmBoundsCheck64(Assembler::Condition::Below, ptr, |
| 21579 | Address(instance, ins->offset()), &ok); |
| 21580 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21581 | masm.bind(&ok); |
| 21582 | } else { |
| 21583 | auto* ool = |
| 21584 | new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21585 | masm.wasmTrap(wasm::Trap::OutOfBounds, mir->trapSiteDesc()); |
| 21586 | }); |
| 21587 | addOutOfLineCode(ool, mir); |
| 21588 | masm.wasmBoundsCheck64(Assembler::Condition::AboveOrEqual, ptr, |
| 21589 | Address(instance, ins->offset()), ool->entry()); |
| 21590 | } |
| 21591 | } |
| 21592 | |
| 21593 | void CodeGenerator::visitWasmBoundsCheckRange32(LWasmBoundsCheckRange32* ins) { |
| 21594 | const MWasmBoundsCheckRange32* mir = ins->mir(); |
| 21595 | Register index = ToRegister(ins->index()); |
| 21596 | Register length = ToRegister(ins->length()); |
| 21597 | Register limit = ToRegister(ins->limit()); |
| 21598 | Register tmp = ToRegister(ins->temp0()); |
| 21599 | |
| 21600 | masm.wasmBoundsCheckRange32(index, length, limit, tmp, mir->trapSiteDesc()); |
| 21601 | } |
| 21602 | |
| 21603 | void CodeGenerator::visitWasmAlignmentCheck(LWasmAlignmentCheck* ins) { |
| 21604 | const MWasmAlignmentCheck* mir = ins->mir(); |
| 21605 | Register ptr = ToRegister(ins->ptr()); |
| 21606 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21607 | masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->trapSiteDesc()); |
| 21608 | }); |
| 21609 | addOutOfLineCode(ool, mir); |
| 21610 | masm.branchTest32(Assembler::NonZero, ptr, Imm32(mir->byteSize() - 1), |
| 21611 | ool->entry()); |
| 21612 | } |
| 21613 | |
| 21614 | void CodeGenerator::visitWasmAlignmentCheck64(LWasmAlignmentCheck64* ins) { |
| 21615 | const MWasmAlignmentCheck* mir = ins->mir(); |
| 21616 | Register64 ptr = ToRegister64(ins->ptr()); |
| 21617 | #ifdef JS_64BIT1 |
| 21618 | Register r = ptr.reg; |
| 21619 | #else |
| 21620 | Register r = ptr.low; |
| 21621 | #endif |
| 21622 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 21623 | masm.wasmTrap(wasm::Trap::UnalignedAccess, mir->trapSiteDesc()); |
| 21624 | }); |
| 21625 | addOutOfLineCode(ool, mir); |
| 21626 | masm.branchTestPtr(Assembler::NonZero, r, Imm32(mir->byteSize() - 1), |
| 21627 | ool->entry()); |
| 21628 | } |
| 21629 | |
| 21630 | void CodeGenerator::visitWasmLoadInstance(LWasmLoadInstance* ins) { |
| 21631 | switch (ins->mir()->type()) { |
| 21632 | case MIRType::WasmAnyRef: |
| 21633 | case MIRType::Pointer: |
| 21634 | masm.loadPtr(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
| 21635 | ToRegister(ins->output())); |
| 21636 | break; |
| 21637 | case MIRType::Int32: |
| 21638 | masm.load32(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
| 21639 | ToRegister(ins->output())); |
| 21640 | break; |
| 21641 | default: |
| 21642 | MOZ_CRASH("MIRType not supported in WasmLoadInstance")do { do { } while (false); MOZ_ReportCrash("" "MIRType not supported in WasmLoadInstance" , "./../../../../js/src/jit/CodeGenerator.cpp", 21642); AnnotateMozCrashReason ("MOZ_CRASH(" "MIRType not supported in WasmLoadInstance" ")" ); do { MOZ_CrashSequence(__null, 21642); __attribute__((nomerge )) ::abort(); } while (false); } while (false); |
| 21643 | } |
| 21644 | } |
| 21645 | |
| 21646 | void CodeGenerator::visitWasmLoadInstance64(LWasmLoadInstance64* ins) { |
| 21647 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 21647); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ins->mir()->type() == MIRType::Int64" ")"); do { MOZ_CrashSequence(__null, 21647); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 21648 | masm.load64(Address(ToRegister(ins->instance()), ins->mir()->offset()), |
| 21649 | ToOutRegister64(ins)); |
| 21650 | } |
| 21651 | |
| 21652 | void CodeGenerator::incrementWarmUpCounter(AbsoluteAddress warmUpCount, |
| 21653 | JSScript* script, Register tmp) { |
| 21654 | // The code depends on the JitScript* not being discarded without also |
| 21655 | // invalidating Ion code. Assert this. |
| 21656 | #ifdef DEBUG1 |
| 21657 | Label ok; |
| 21658 | masm.movePtr(ImmGCPtr(script), tmp); |
| 21659 | masm.loadJitScript(tmp, tmp); |
| 21660 | masm.branchPtr(Assembler::Equal, tmp, ImmPtr(script->jitScript()), &ok); |
| 21661 | masm.assumeUnreachable("Didn't find JitScript?"); |
| 21662 | masm.bind(&ok); |
| 21663 | #endif |
| 21664 | |
| 21665 | masm.load32(warmUpCount, tmp); |
| 21666 | masm.add32(Imm32(1), tmp); |
| 21667 | masm.store32(tmp, warmUpCount); |
| 21668 | } |
| 21669 | |
| 21670 | void CodeGenerator::visitIncrementWarmUpCounter(LIncrementWarmUpCounter* ins) { |
| 21671 | Register tmp = ToRegister(ins->temp0()); |
| 21672 | |
| 21673 | AbsoluteAddress warmUpCount = |
| 21674 | AbsoluteAddress(ins->mir()->script()->jitScript()) |
| 21675 | .offset(JitScript::offsetOfWarmUpCount()); |
| 21676 | incrementWarmUpCounter(warmUpCount, ins->mir()->script(), tmp); |
| 21677 | } |
| 21678 | |
| 21679 | void CodeGenerator::visitLexicalCheck(LLexicalCheck* ins) { |
| 21680 | ValueOperand inputValue = ToValue(ins->input()); |
| 21681 | Label bail; |
| 21682 | masm.branchTestMagicValue(Assembler::Equal, inputValue, |
| 21683 | JS_UNINITIALIZED_LEXICAL, &bail); |
| 21684 | bailoutFrom(&bail, ins->snapshot()); |
| 21685 | } |
| 21686 | |
| 21687 | void CodeGenerator::visitThrowRuntimeLexicalError( |
| 21688 | LThrowRuntimeLexicalError* ins) { |
| 21689 | pushArg(Imm32(ins->mir()->errorNumber())); |
| 21690 | |
| 21691 | using Fn = bool (*)(JSContext*, unsigned); |
| 21692 | callVM<Fn, jit::ThrowRuntimeLexicalError>(ins); |
| 21693 | } |
| 21694 | |
| 21695 | void CodeGenerator::visitThrowMsg(LThrowMsg* ins) { |
| 21696 | pushArg(Imm32(static_cast<int32_t>(ins->mir()->throwMsgKind()))); |
| 21697 | |
| 21698 | using Fn = bool (*)(JSContext*, unsigned); |
| 21699 | callVM<Fn, js::ThrowMsgOperation>(ins); |
| 21700 | } |
| 21701 | |
| 21702 | void CodeGenerator::visitGlobalDeclInstantiation( |
| 21703 | LGlobalDeclInstantiation* ins) { |
| 21704 | pushArg(ImmPtr(ins->mir()->resumePoint()->pc())); |
| 21705 | pushArg(ImmGCPtr(ins->mir()->block()->info().script())); |
| 21706 | |
| 21707 | using Fn = bool (*)(JSContext*, HandleScript, const jsbytecode*); |
| 21708 | callVM<Fn, GlobalDeclInstantiationFromIon>(ins); |
| 21709 | } |
| 21710 | |
| 21711 | void CodeGenerator::visitDebugger(LDebugger* ins) { |
| 21712 | Register cx = ToRegister(ins->temp0()); |
| 21713 | |
| 21714 | masm.loadJSContext(cx); |
| 21715 | using Fn = bool (*)(JSContext* cx); |
| 21716 | masm.setupAlignedABICall(); |
| 21717 | masm.passABIArg(cx); |
| 21718 | masm.callWithABI<Fn, GlobalHasLiveOnDebuggerStatement>(); |
| 21719 | |
| 21720 | Label bail; |
| 21721 | masm.branchIfTrueBool(ReturnReg, &bail); |
| 21722 | bailoutFrom(&bail, ins->snapshot()); |
| 21723 | } |
| 21724 | |
| 21725 | void CodeGenerator::visitNewTarget(LNewTarget* ins) { |
| 21726 | ValueOperand output = ToOutValue(ins); |
| 21727 | |
| 21728 | // if (isConstructing) output = argv[Max(numActualArgs, numFormalArgs)] |
| 21729 | Label notConstructing, done; |
| 21730 | Address calleeToken(FramePointer, JitFrameLayout::offsetOfCalleeToken()); |
| 21731 | masm.branchTestPtr(Assembler::Zero, calleeToken, |
| 21732 | Imm32(CalleeToken_FunctionConstructing), ¬Constructing); |
| 21733 | |
| 21734 | Register argvLen = output.scratchReg(); |
| 21735 | masm.loadNumActualArgs(FramePointer, argvLen); |
| 21736 | |
| 21737 | Label useNFormals; |
| 21738 | |
| 21739 | size_t numFormalArgs = ins->mir()->block()->info().nargs(); |
| 21740 | masm.branchPtr(Assembler::Below, argvLen, Imm32(numFormalArgs), &useNFormals); |
| 21741 | |
| 21742 | size_t argsOffset = JitFrameLayout::offsetOfActualArgs(); |
| 21743 | { |
| 21744 | BaseValueIndex newTarget(FramePointer, argvLen, argsOffset); |
| 21745 | masm.loadValue(newTarget, output); |
| 21746 | masm.jump(&done); |
| 21747 | } |
| 21748 | |
| 21749 | masm.bind(&useNFormals); |
| 21750 | |
| 21751 | { |
| 21752 | Address newTarget(FramePointer, |
| 21753 | argsOffset + (numFormalArgs * sizeof(Value))); |
| 21754 | masm.loadValue(newTarget, output); |
| 21755 | masm.jump(&done); |
| 21756 | } |
| 21757 | |
| 21758 | // else output = undefined |
| 21759 | masm.bind(¬Constructing); |
| 21760 | masm.moveValue(UndefinedValue(), output); |
| 21761 | masm.bind(&done); |
| 21762 | } |
| 21763 | |
| 21764 | void CodeGenerator::visitCheckReturn(LCheckReturn* ins) { |
| 21765 | ValueOperand returnValue = ToValue(ins->returnValue()); |
| 21766 | ValueOperand thisValue = ToValue(ins->thisValue()); |
| 21767 | ValueOperand output = ToOutValue(ins); |
| 21768 | |
| 21769 | using Fn = bool (*)(JSContext*, HandleValue); |
| 21770 | OutOfLineCode* ool = oolCallVM<Fn, ThrowBadDerivedReturnOrUninitializedThis>( |
| 21771 | ins, ArgList(returnValue), StoreNothing()); |
| 21772 | |
| 21773 | Label noChecks; |
| 21774 | masm.branchTestObject(Assembler::Equal, returnValue, &noChecks); |
| 21775 | masm.branchTestUndefined(Assembler::NotEqual, returnValue, ool->entry()); |
| 21776 | masm.branchTestMagicValue(Assembler::Equal, thisValue, |
| 21777 | JS_UNINITIALIZED_LEXICAL, ool->entry()); |
| 21778 | masm.moveValue(thisValue, output); |
| 21779 | masm.jump(ool->rejoin()); |
| 21780 | masm.bind(&noChecks); |
| 21781 | masm.moveValue(returnValue, output); |
| 21782 | masm.bind(ool->rejoin()); |
| 21783 | } |
| 21784 | |
| 21785 | void CodeGenerator::visitCheckIsObj(LCheckIsObj* ins) { |
| 21786 | ValueOperand value = ToValue(ins->value()); |
| 21787 | Register output = ToRegister(ins->output()); |
| 21788 | |
| 21789 | using Fn = bool (*)(JSContext*, CheckIsObjectKind); |
| 21790 | OutOfLineCode* ool = oolCallVM<Fn, ThrowCheckIsObject>( |
| 21791 | ins, ArgList(Imm32(ins->mir()->checkKind())), StoreNothing()); |
| 21792 | |
| 21793 | masm.fallibleUnboxObject(value, output, ool->entry()); |
| 21794 | masm.bind(ool->rejoin()); |
| 21795 | } |
| 21796 | |
| 21797 | void CodeGenerator::visitCheckObjCoercible(LCheckObjCoercible* ins) { |
| 21798 | ValueOperand checkValue = ToValue(ins->checkValue()); |
| 21799 | |
| 21800 | using Fn = bool (*)(JSContext*, HandleValue); |
| 21801 | OutOfLineCode* ool = oolCallVM<Fn, ThrowObjectCoercible>( |
| 21802 | ins, ArgList(checkValue), StoreNothing()); |
| 21803 | masm.branchTestNull(Assembler::Equal, checkValue, ool->entry()); |
| 21804 | masm.branchTestUndefined(Assembler::Equal, checkValue, ool->entry()); |
| 21805 | masm.bind(ool->rejoin()); |
| 21806 | } |
| 21807 | |
| 21808 | void CodeGenerator::visitCheckClassHeritage(LCheckClassHeritage* ins) { |
| 21809 | ValueOperand heritage = ToValue(ins->heritage()); |
| 21810 | Register temp0 = ToRegister(ins->temp0()); |
| 21811 | Register temp1 = ToRegister(ins->temp1()); |
| 21812 | |
| 21813 | using Fn = bool (*)(JSContext*, HandleValue); |
| 21814 | OutOfLineCode* ool = oolCallVM<Fn, CheckClassHeritageOperation>( |
| 21815 | ins, ArgList(heritage), StoreNothing()); |
| 21816 | |
| 21817 | masm.branchTestNull(Assembler::Equal, heritage, ool->rejoin()); |
| 21818 | masm.fallibleUnboxObject(heritage, temp0, ool->entry()); |
| 21819 | |
| 21820 | masm.isConstructor(temp0, temp1, ool->entry()); |
| 21821 | masm.branchTest32(Assembler::Zero, temp1, temp1, ool->entry()); |
| 21822 | |
| 21823 | masm.bind(ool->rejoin()); |
| 21824 | } |
| 21825 | |
| 21826 | void CodeGenerator::visitCheckThis(LCheckThis* ins) { |
| 21827 | ValueOperand thisValue = ToValue(ins->thisValue()); |
| 21828 | |
| 21829 | using Fn = bool (*)(JSContext*); |
| 21830 | OutOfLineCode* ool = |
| 21831 | oolCallVM<Fn, ThrowUninitializedThis>(ins, ArgList(), StoreNothing()); |
| 21832 | masm.branchTestMagicValue(Assembler::Equal, thisValue, |
| 21833 | JS_UNINITIALIZED_LEXICAL, ool->entry()); |
| 21834 | masm.bind(ool->rejoin()); |
| 21835 | } |
| 21836 | |
| 21837 | void CodeGenerator::visitCheckThisReinit(LCheckThisReinit* ins) { |
| 21838 | ValueOperand thisValue = ToValue(ins->thisValue()); |
| 21839 | |
| 21840 | using Fn = bool (*)(JSContext*); |
| 21841 | OutOfLineCode* ool = |
| 21842 | oolCallVM<Fn, ThrowInitializedThis>(ins, ArgList(), StoreNothing()); |
| 21843 | masm.branchTestMagicValue(Assembler::NotEqual, thisValue, |
| 21844 | JS_UNINITIALIZED_LEXICAL, ool->entry()); |
| 21845 | masm.bind(ool->rejoin()); |
| 21846 | } |
| 21847 | |
| 21848 | void CodeGenerator::visitGenerator(LGenerator* lir) { |
| 21849 | Register callee = ToRegister(lir->callee()); |
| 21850 | Register environmentChain = ToRegister(lir->environmentChain()); |
| 21851 | Register argsObject = ToRegister(lir->argsObject()); |
| 21852 | |
| 21853 | pushArg(argsObject); |
| 21854 | pushArg(environmentChain); |
| 21855 | pushArg(ImmGCPtr(current->mir()->info().script())); |
| 21856 | pushArg(callee); |
| 21857 | |
| 21858 | using Fn = JSObject* (*)(JSContext * cx, HandleFunction, HandleScript, |
| 21859 | HandleObject, HandleObject); |
| 21860 | callVM<Fn, CreateGenerator>(lir); |
| 21861 | } |
| 21862 | |
| 21863 | void CodeGenerator::visitAsyncResolve(LAsyncResolve* lir) { |
| 21864 | Register generator = ToRegister(lir->generator()); |
| 21865 | ValueOperand value = ToValue(lir->value()); |
| 21866 | |
| 21867 | pushArg(value); |
| 21868 | pushArg(generator); |
| 21869 | |
| 21870 | using Fn = JSObject* (*)(JSContext*, Handle<AsyncFunctionGeneratorObject*>, |
| 21871 | HandleValue); |
| 21872 | callVM<Fn, js::AsyncFunctionResolve>(lir); |
| 21873 | } |
| 21874 | |
| 21875 | void CodeGenerator::visitAsyncAwait(LAsyncAwait* lir) { |
| 21876 | ValueOperand value = ToValue(lir->value()); |
| 21877 | Register generator = ToRegister(lir->generator()); |
| 21878 | |
| 21879 | pushArg(value); |
| 21880 | pushArg(generator); |
| 21881 | |
| 21882 | using Fn = JSObject* (*)(JSContext * cx, |
| 21883 | Handle<AsyncFunctionGeneratorObject*> genObj, |
| 21884 | HandleValue value); |
| 21885 | callVM<Fn, js::AsyncFunctionAwait>(lir); |
| 21886 | } |
| 21887 | |
| 21888 | void CodeGenerator::visitGeneratorResume(LGeneratorResume* lir) { |
| 21889 | Register genObj = ToRegister(lir->generator()); |
| 21890 | ValueOperand value = ToValue(lir->value()); |
| 21891 | int32_t resumeKind = lir->mir()->resumeKind(); |
| 21892 | |
| 21893 | Register callee = ToRegister(lir->temp0()); |
| 21894 | Register scratch = ToRegister(lir->temp1()); |
| 21895 | |
| 21896 | // Load callee. Note that JSOp::Resume is never used for modules. |
| 21897 | masm.unboxObject( |
| 21898 | Address(genObj, AbstractGeneratorObject::offsetOfCalleeOrModuleSlot()), |
| 21899 | callee); |
| 21900 | |
| 21901 | // The pushed argument Values are the formals (all |undefined|) and the resume |
| 21902 | // args stored after the formals. |
| 21903 | masm.loadFunctionArgCount(callee, scratch); |
| 21904 | masm.alignJitStackBasedOnNArgs(scratch, /* countIncludesThis = */ false, |
| 21905 | /* extraArgs = */ ResumeFrameArgs::NumSlots); |
| 21906 | |
| 21907 | // Unlike the Baseline version, we don't have to zero the alignment padding |
| 21908 | // here: Ion frames are traced with the safepoint's slot map, not as a range. |
| 21909 | |
| 21910 | // Push the resume args and the |undefined| formals. |
| 21911 | Address resumeIndexSlot(genObj, |
| 21912 | AbstractGeneratorObject::offsetOfResumeIndexSlot()); |
| 21913 | masm.pushGeneratorResumeArgsAndFormals( |
| 21914 | resumeIndexSlot, Int32Value(resumeKind), genObj, value, scratch); |
| 21915 | |
| 21916 | masm.PushCalleeToken(callee, /* constructing = */ false); |
| 21917 | masm.push(FrameDescriptor(FrameType::IonJS, /* argc = */ 0, |
| 21918 | /* hasInlined = */ false, |
| 21919 | /* isResumingGenerator = */ true)); |
| 21920 | |
| 21921 | // Load the code to call. Throw currently always resumes in Baseline. |
| 21922 | // See MaybeEnterJit. |
| 21923 | Register code = callee; |
| 21924 | if (resumeKind != int32_t(GeneratorResumeKind::Throw)) { |
| 21925 | masm.loadJitCodeRaw(callee, code); |
| 21926 | } else { |
| 21927 | masm.loadJitCodeRawNoIon(callee, code, scratch); |
| 21928 | } |
| 21929 | |
| 21930 | masm.switchToObjectRealm(genObj, scratch); |
| 21931 | |
| 21932 | // Mark the generator as running. |
| 21933 | masm.storeValue(Int32Value(AbstractGeneratorObject::RESUME_INDEX_RUNNING), |
| 21934 | resumeIndexSlot); |
| 21935 | |
| 21936 | // Call the callee's JIT code. Its prologue sees the descriptor bit and |
| 21937 | // dispatches to the resume point. |
| 21938 | ensureOsiSpace(); |
| 21939 | uint32_t callOffset = masm.callJit(code); |
| 21940 | markSafepointAt(callOffset, lir); |
| 21941 | |
| 21942 | static_assert(!JSReturnOperand.aliases(ReturnReg), |
| 21943 | "ReturnReg must not alias return value in JSReturnOperand"); |
| 21944 | masm.switchToRealm(gen->realm->realmPtr(), ReturnReg); |
| 21945 | |
| 21946 | masm.setFramePushed(frameSize()); |
| 21947 | emitRestoreStackPointerFromFP(); |
| 21948 | } |
| 21949 | |
| 21950 | // The offset of a ResumeFrameArgs slot from the frame pointer. |
| 21951 | static size_t OffsetOfResumeFrameArg(MIRGenerator* gen, uint32_t slot) { |
| 21952 | JSScript* script = gen->outerInfo().script(); |
| 21953 | size_t base = |
| 21954 | script->isFunction() |
| 21955 | ? JitFrameLayout::offsetOfActualArg(script->function()->nargs()) |
| 21956 | : JitFrameLayout::offsetOfModuleResumeArgs(); |
| 21957 | return base + ResumeFrameArgs::offsetOfSlot(slot); |
| 21958 | } |
| 21959 | |
| 21960 | static Address AddressOfFrameDescriptor() { |
| 21961 | return Address(FramePointer, CommonFrameLayout::offsetOfDescriptor()); |
| 21962 | } |
| 21963 | |
| 21964 | void CodeGenerator::visitResumeFrameArg(LResumeFrameArg* lir) { |
| 21965 | #ifdef DEBUG1 |
| 21966 | // The slots are only valid and traced on GC while this frame is mid-resume. |
| 21967 | Label ok; |
| 21968 | masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(), |
| 21969 | Imm32(FrameDescriptor::IsResumingGenerator), &ok); |
| 21970 | masm.assumeUnreachable("ResumeFrameArgs read outside a generator resume"); |
| 21971 | masm.bind(&ok); |
| 21972 | #endif |
| 21973 | ValueOperand output = ToOutValue(lir); |
| 21974 | masm.loadValue( |
| 21975 | Address(FramePointer, OffsetOfResumeFrameArg(gen, lir->mir()->slot())), |
| 21976 | output); |
| 21977 | } |
| 21978 | |
| 21979 | void CodeGenerator::visitIsResumingGenerator(LIsResumingGenerator* lir) { |
| 21980 | Register output = ToRegister(lir->output()); |
| 21981 | Label isResuming, done; |
| 21982 | masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(), |
| 21983 | Imm32(FrameDescriptor::IsResumingGenerator), &isResuming); |
| 21984 | masm.move32(Imm32(0), output); |
| 21985 | masm.jump(&done); |
| 21986 | masm.bind(&isResuming); |
| 21987 | masm.move32(Imm32(1), output); |
| 21988 | masm.bind(&done); |
| 21989 | } |
| 21990 | |
| 21991 | void CodeGenerator::visitIsResumingGeneratorAndBranch( |
| 21992 | LIsResumingGeneratorAndBranch* lir) { |
| 21993 | Label* ifTrue = getJumpLabelForBranch(lir->ifTrue()); |
| 21994 | Label* ifFalse = getJumpLabelForBranch(lir->ifFalse()); |
| 21995 | masm.branchTest32(Assembler::NonZero, AddressOfFrameDescriptor(), |
| 21996 | Imm32(FrameDescriptor::IsResumingGenerator), ifTrue); |
| 21997 | if (!isNextBlock(lir->ifFalse()->lir())) { |
| 21998 | masm.jump(ifFalse); |
| 21999 | } |
| 22000 | } |
| 22001 | |
| 22002 | void CodeGenerator::visitClearResumingGeneratorFlag( |
| 22003 | LClearResumingGeneratorFlag* lir) { |
| 22004 | masm.andPtr(Imm32(~int32_t(FrameDescriptor::IsResumingGenerator)), |
| 22005 | AddressOfFrameDescriptor()); |
| 22006 | } |
| 22007 | |
| 22008 | void CodeGenerator::visitCanSkipAwait(LCanSkipAwait* lir) { |
| 22009 | ValueOperand value = ToValue(lir->value()); |
| 22010 | Register scratch = ToRegister(lir->temp0()); |
| 22011 | |
| 22012 | // The await can only be skipped when this is the first frame of its |
| 22013 | // activation. See js::CanSkipAwait. |
| 22014 | Label notEntryFrame, done; |
| 22015 | masm.branchIfNotActivationEntryFrame(scratch, ¬EntryFrame); |
| 22016 | |
| 22017 | pushArg(value); |
| 22018 | |
| 22019 | using Fn = bool (*)(JSContext*, HandleValue, bool* canSkip); |
| 22020 | callVM<Fn, js::CanSkipAwait>(lir); |
| 22021 | masm.jump(&done); |
| 22022 | |
| 22023 | masm.bind(¬EntryFrame); |
| 22024 | masm.move32(Imm32(0), ReturnReg); |
| 22025 | |
| 22026 | masm.bind(&done); |
| 22027 | } |
| 22028 | |
| 22029 | void CodeGenerator::visitMaybeExtractAwaitValue(LMaybeExtractAwaitValue* lir) { |
| 22030 | ValueOperand value = ToValue(lir->value()); |
| 22031 | ValueOperand output = ToOutValue(lir); |
| 22032 | Register canSkip = ToRegister(lir->canSkip()); |
| 22033 | |
| 22034 | Label cantExtract, finished; |
| 22035 | masm.branchIfFalseBool(canSkip, &cantExtract); |
| 22036 | |
| 22037 | pushArg(value); |
| 22038 | |
| 22039 | using Fn = bool (*)(JSContext*, HandleValue, MutableHandleValue); |
| 22040 | callVM<Fn, js::ExtractAwaitValue>(lir); |
| 22041 | masm.jump(&finished); |
| 22042 | masm.bind(&cantExtract); |
| 22043 | |
| 22044 | masm.moveValue(value, output); |
| 22045 | |
| 22046 | masm.bind(&finished); |
| 22047 | } |
| 22048 | |
| 22049 | void CodeGenerator::visitDebugCheckSelfHosted(LDebugCheckSelfHosted* ins) { |
| 22050 | ValueOperand checkValue = ToValue(ins->checkValue()); |
| 22051 | pushArg(checkValue); |
| 22052 | using Fn = bool (*)(JSContext*, HandleValue); |
| 22053 | callVM<Fn, js::Debug_CheckSelfHosted>(ins); |
| 22054 | } |
| 22055 | |
| 22056 | void CodeGenerator::visitRandom(LRandom* ins) { |
| 22057 | using mozilla::non_crypto::XorShift128PlusRNG; |
| 22058 | |
| 22059 | FloatRegister output = ToFloatRegister(ins->output()); |
| 22060 | Register rngReg = ToRegister(ins->temp0()); |
| 22061 | |
| 22062 | Register64 temp1 = ToRegister64(ins->temp1()); |
| 22063 | Register64 temp2 = ToRegister64(ins->temp2()); |
| 22064 | |
| 22065 | const XorShift128PlusRNG* rng = gen->realm->addressOfRandomNumberGenerator(); |
| 22066 | masm.movePtr(ImmPtr(rng), rngReg); |
| 22067 | |
| 22068 | masm.randomDouble(rngReg, output, temp1, temp2); |
| 22069 | if (js::SupportDifferentialTesting()) { |
| 22070 | masm.loadConstantDouble(0.0, output); |
| 22071 | } |
| 22072 | } |
| 22073 | |
| 22074 | void CodeGenerator::visitSignExtendInt32(LSignExtendInt32* ins) { |
| 22075 | Register input = ToRegister(ins->input()); |
| 22076 | Register output = ToRegister(ins->output()); |
| 22077 | |
| 22078 | switch (ins->mir()->mode()) { |
| 22079 | case MSignExtendInt32::Byte: |
| 22080 | masm.move8SignExtend(input, output); |
| 22081 | break; |
| 22082 | case MSignExtendInt32::Half: |
| 22083 | masm.move16SignExtend(input, output); |
| 22084 | break; |
| 22085 | } |
| 22086 | } |
| 22087 | |
| 22088 | void CodeGenerator::visitSignExtendIntPtr(LSignExtendIntPtr* ins) { |
| 22089 | Register input = ToRegister(ins->input()); |
| 22090 | Register output = ToRegister(ins->output()); |
| 22091 | |
| 22092 | switch (ins->mir()->mode()) { |
| 22093 | case MSignExtendIntPtr::Byte: |
| 22094 | masm.move8SignExtendToPtr(input, output); |
| 22095 | break; |
| 22096 | case MSignExtendIntPtr::Half: |
| 22097 | masm.move16SignExtendToPtr(input, output); |
| 22098 | break; |
| 22099 | case MSignExtendIntPtr::Word: |
| 22100 | masm.move32SignExtendToPtr(input, output); |
| 22101 | break; |
| 22102 | } |
| 22103 | } |
| 22104 | |
| 22105 | void CodeGenerator::visitRotate(LRotate* ins) { |
| 22106 | MRotate* mir = ins->mir(); |
| 22107 | Register input = ToRegister(ins->input()); |
| 22108 | Register dest = ToRegister(ins->output()); |
| 22109 | |
| 22110 | const LAllocation* count = ins->count(); |
| 22111 | if (count->isConstant()) { |
| 22112 | int32_t c = ToInt32(count) & 0x1F; |
| 22113 | if (mir->isLeftRotate()) { |
| 22114 | masm.rotateLeft(Imm32(c), input, dest); |
| 22115 | } else { |
| 22116 | masm.rotateRight(Imm32(c), input, dest); |
| 22117 | } |
| 22118 | } else { |
| 22119 | Register creg = ToRegister(count); |
| 22120 | if (mir->isLeftRotate()) { |
| 22121 | masm.rotateLeft(creg, input, dest); |
| 22122 | } else { |
| 22123 | masm.rotateRight(creg, input, dest); |
| 22124 | } |
| 22125 | } |
| 22126 | } |
| 22127 | |
| 22128 | void CodeGenerator::visitRotateI64(LRotateI64* lir) { |
| 22129 | MRotate* mir = lir->mir(); |
| 22130 | const LAllocation* count = lir->count(); |
| 22131 | |
| 22132 | Register64 input = ToRegister64(lir->input()); |
| 22133 | Register64 output = ToOutRegister64(lir); |
| 22134 | Register temp = ToTempRegisterOrInvalid(lir->temp0()); |
| 22135 | |
| 22136 | if (count->isConstant()) { |
| 22137 | int32_t c = int32_t(count->toConstant()->toInt64() & 0x3F); |
| 22138 | if (!c) { |
| 22139 | if (input != output) { |
| 22140 | masm.move64(input, output); |
| 22141 | } |
| 22142 | return; |
| 22143 | } |
| 22144 | if (mir->isLeftRotate()) { |
| 22145 | masm.rotateLeft64(Imm32(c), input, output, temp); |
| 22146 | } else { |
| 22147 | masm.rotateRight64(Imm32(c), input, output, temp); |
| 22148 | } |
| 22149 | } else { |
| 22150 | if (mir->isLeftRotate()) { |
| 22151 | masm.rotateLeft64(ToRegister(count), input, output, temp); |
| 22152 | } else { |
| 22153 | masm.rotateRight64(ToRegister(count), input, output, temp); |
| 22154 | } |
| 22155 | } |
| 22156 | } |
| 22157 | |
| 22158 | void CodeGenerator::visitReinterpretCast(LReinterpretCast* lir) { |
| 22159 | MReinterpretCast* ins = lir->mir(); |
| 22160 | |
| 22161 | MIRType to = ins->type(); |
| 22162 | mozilla::DebugOnly<MIRType> from = ins->input()->type(); |
| 22163 | |
| 22164 | switch (to) { |
| 22165 | case MIRType::Int32: |
| 22166 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 22166); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "from == MIRType::Float32" ")"); do { MOZ_CrashSequence (__null, 22166); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 22167 | masm.moveFloat32ToGPR(ToFloatRegister(lir->input()), |
| 22168 | ToRegister(lir->output())); |
| 22169 | break; |
| 22170 | case MIRType::Float32: |
| 22171 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 22171); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "from == MIRType::Int32" ")"); do { MOZ_CrashSequence (__null, 22171); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 22172 | masm.moveGPRToFloat32(ToRegister(lir->input()), |
| 22173 | ToFloatRegister(lir->output())); |
| 22174 | break; |
| 22175 | case MIRType::Double: |
| 22176 | case MIRType::Int64: |
| 22177 | MOZ_CRASH("not handled by this LIR opcode")do { do { } while (false); MOZ_ReportCrash("" "not handled by this LIR opcode" , "./../../../../js/src/jit/CodeGenerator.cpp", 22177); AnnotateMozCrashReason ("MOZ_CRASH(" "not handled by this LIR opcode" ")"); do { MOZ_CrashSequence (__null, 22177); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 22178 | default: |
| 22179 | MOZ_CRASH("unexpected ReinterpretCast")do { do { } while (false); MOZ_ReportCrash("" "unexpected ReinterpretCast" , "./../../../../js/src/jit/CodeGenerator.cpp", 22179); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected ReinterpretCast" ")"); do { MOZ_CrashSequence (__null, 22179); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 22180 | } |
| 22181 | } |
| 22182 | |
| 22183 | void CodeGenerator::visitReinterpretCastFromI64(LReinterpretCastFromI64* lir) { |
| 22184 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 22184); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double" ")"); do { MOZ_CrashSequence(__null, 22184); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22185 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 22185); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Int64" ")"); do { MOZ_CrashSequence(__null, 22185); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22186 | masm.moveGPR64ToDouble(ToRegister64(lir->input()), |
| 22187 | ToFloatRegister(lir->output())); |
| 22188 | } |
| 22189 | |
| 22190 | void CodeGenerator::visitReinterpretCastToI64(LReinterpretCastToI64* lir) { |
| 22191 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 22191); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64" ")"); do { MOZ_CrashSequence(__null, 22191); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22192 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 22192); AnnotateMozCrashReason("MOZ_ASSERT" "(" "lir->mir()->input()->type() == MIRType::Double" ")"); do { MOZ_CrashSequence(__null, 22192); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 22193 | masm.moveDoubleToGPR64(ToFloatRegister(lir->input()), ToOutRegister64(lir)); |
| 22194 | } |
| 22195 | |
| 22196 | void CodeGenerator::visitNaNToZero(LNaNToZero* lir) { |
| 22197 | FloatRegister input = ToFloatRegister(lir->input()); |
| 22198 | |
| 22199 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 22200 | FloatRegister output = ToFloatRegister(lir->output()); |
| 22201 | masm.loadConstantDouble(0.0, output); |
| 22202 | masm.jump(ool.rejoin()); |
| 22203 | }); |
| 22204 | addOutOfLineCode(ool, lir->mir()); |
| 22205 | |
| 22206 | if (lir->mir()->operandIsNeverNegativeZero()) { |
| 22207 | masm.branchDouble(Assembler::DoubleUnordered, input, input, ool->entry()); |
| 22208 | } else { |
| 22209 | FloatRegister scratch = ToFloatRegister(lir->temp0()); |
| 22210 | masm.loadConstantDouble(0.0, scratch); |
| 22211 | masm.branchDouble(Assembler::DoubleEqualOrUnordered, input, scratch, |
| 22212 | ool->entry()); |
| 22213 | } |
| 22214 | masm.bind(ool->rejoin()); |
| 22215 | } |
| 22216 | |
| 22217 | void CodeGenerator::visitIsPackedArray(LIsPackedArray* lir) { |
| 22218 | Register obj = ToRegister(lir->object()); |
| 22219 | Register output = ToRegister(lir->output()); |
| 22220 | Register temp = ToRegister(lir->temp0()); |
| 22221 | |
| 22222 | masm.setIsPackedArray(obj, output, temp); |
| 22223 | } |
| 22224 | |
| 22225 | void CodeGenerator::visitGuardArrayIsPacked(LGuardArrayIsPacked* lir) { |
| 22226 | Register array = ToRegister(lir->array()); |
| 22227 | Register temp0 = ToRegister(lir->temp0()); |
| 22228 | Register temp1 = ToRegister(lir->temp1()); |
| 22229 | |
| 22230 | Label bail; |
| 22231 | masm.branchArrayIsNotPacked(array, temp0, temp1, &bail); |
| 22232 | bailoutFrom(&bail, lir->snapshot()); |
| 22233 | } |
| 22234 | |
| 22235 | void CodeGenerator::visitGuardElementsArePacked(LGuardElementsArePacked* lir) { |
| 22236 | Register elements = ToRegister(lir->elements()); |
| 22237 | |
| 22238 | Label bail; |
| 22239 | Address flags(elements, ObjectElements::offsetOfFlags()); |
| 22240 | masm.branchTest32(Assembler::NonZero, flags, |
| 22241 | Imm32(ObjectElements::NON_PACKED), &bail); |
| 22242 | bailoutFrom(&bail, lir->snapshot()); |
| 22243 | } |
| 22244 | |
| 22245 | void CodeGenerator::visitGetPrototypeOf(LGetPrototypeOf* lir) { |
| 22246 | Register target = ToRegister(lir->target()); |
| 22247 | ValueOperand out = ToOutValue(lir); |
| 22248 | Register scratch = out.scratchReg(); |
| 22249 | |
| 22250 | using Fn = bool (*)(JSContext*, HandleObject, MutableHandleValue); |
| 22251 | OutOfLineCode* ool = oolCallVM<Fn, jit::GetPrototypeOf>(lir, ArgList(target), |
| 22252 | StoreValueTo(out)); |
| 22253 | |
| 22254 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 22254); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")" ); do { MOZ_CrashSequence(__null, 22254); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 22255 | |
| 22256 | masm.loadObjProto(target, scratch); |
| 22257 | |
| 22258 | Label hasProto; |
| 22259 | masm.branchPtr(Assembler::Above, scratch, ImmWord(1), &hasProto); |
| 22260 | |
| 22261 | // Call into the VM for lazy prototypes. |
| 22262 | masm.branchPtr(Assembler::Equal, scratch, ImmWord(1), ool->entry()); |
| 22263 | |
| 22264 | masm.moveValue(NullValue(), out); |
| 22265 | masm.jump(ool->rejoin()); |
| 22266 | |
| 22267 | masm.bind(&hasProto); |
| 22268 | masm.tagValue(JSVAL_TYPE_OBJECT, scratch, out); |
| 22269 | |
| 22270 | masm.bind(ool->rejoin()); |
| 22271 | } |
| 22272 | |
| 22273 | void CodeGenerator::visitObjectWithProto(LObjectWithProto* lir) { |
| 22274 | pushArg(ToValue(lir->prototype())); |
| 22275 | |
| 22276 | using Fn = PlainObject* (*)(JSContext*, HandleValue); |
| 22277 | callVM<Fn, js::ObjectWithProtoOperation>(lir); |
| 22278 | } |
| 22279 | |
| 22280 | void CodeGenerator::visitObjectStaticProto(LObjectStaticProto* lir) { |
| 22281 | Register obj = ToRegister(lir->object()); |
| 22282 | Register output = ToRegister(lir->output()); |
| 22283 | |
| 22284 | masm.loadObjProto(obj, output); |
| 22285 | |
| 22286 | #ifdef DEBUG1 |
| 22287 | // We shouldn't encounter a null or lazy proto. |
| 22288 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 22288); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")" ); do { MOZ_CrashSequence(__null, 22288); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 22289 | |
| 22290 | Label done; |
| 22291 | masm.branchPtr(Assembler::Above, output, ImmWord(1), &done); |
| 22292 | masm.assumeUnreachable("Unexpected null or lazy proto in MObjectStaticProto"); |
| 22293 | masm.bind(&done); |
| 22294 | #endif |
| 22295 | } |
| 22296 | |
| 22297 | void CodeGenerator::visitBuiltinObject(LBuiltinObject* lir) { |
| 22298 | pushArg(Imm32(static_cast<int32_t>(lir->mir()->builtinObjectKind()))); |
| 22299 | |
| 22300 | using Fn = JSObject* (*)(JSContext*, BuiltinObjectKind); |
| 22301 | callVM<Fn, js::BuiltinObjectOperation>(lir); |
| 22302 | } |
| 22303 | |
| 22304 | static void EmitLoadSuperFunction(MacroAssembler& masm, Register callee, |
| 22305 | Register dest) { |
| 22306 | #ifdef DEBUG1 |
| 22307 | Label classCheckDone; |
| 22308 | masm.branchTestObjIsFunction(Assembler::Equal, callee, dest, callee, |
| 22309 | &classCheckDone); |
| 22310 | masm.assumeUnreachable("Unexpected non-JSFunction callee in JSOp::SuperFun"); |
| 22311 | masm.bind(&classCheckDone); |
| 22312 | #endif |
| 22313 | |
| 22314 | // Load prototype of callee |
| 22315 | masm.loadObjProto(callee, dest); |
| 22316 | |
| 22317 | #ifdef DEBUG1 |
| 22318 | // We won't encounter a lazy proto, because |callee| is guaranteed to be a |
| 22319 | // JSFunction and only proxy objects can have a lazy proto. |
| 22320 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 22320); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "uintptr_t(TaggedProto::LazyProto) == 1" ")" ); do { MOZ_CrashSequence(__null, 22320); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 22321 | |
| 22322 | Label proxyCheckDone; |
| 22323 | masm.branchPtr(Assembler::NotEqual, dest, ImmWord(1), &proxyCheckDone); |
| 22324 | masm.assumeUnreachable("Unexpected lazy proto in JSOp::SuperFun"); |
| 22325 | masm.bind(&proxyCheckDone); |
| 22326 | #endif |
| 22327 | } |
| 22328 | |
| 22329 | void CodeGenerator::visitSuperFunction(LSuperFunction* lir) { |
| 22330 | Register callee = ToRegister(lir->callee()); |
| 22331 | ValueOperand out = ToOutValue(lir); |
| 22332 | Register temp = out.scratchReg(); |
| 22333 | |
| 22334 | EmitLoadSuperFunction(masm, callee, temp); |
| 22335 | |
| 22336 | Label nullProto, done; |
| 22337 | masm.branchPtr(Assembler::Equal, temp, ImmWord(0), &nullProto); |
| 22338 | |
| 22339 | // Box prototype and return |
| 22340 | masm.tagValue(JSVAL_TYPE_OBJECT, temp, out); |
| 22341 | masm.jump(&done); |
| 22342 | |
| 22343 | masm.bind(&nullProto); |
| 22344 | masm.moveValue(NullValue(), out); |
| 22345 | |
| 22346 | masm.bind(&done); |
| 22347 | } |
| 22348 | |
| 22349 | void CodeGenerator::visitSuperFunctionAndUnbox(LSuperFunctionAndUnbox* lir) { |
| 22350 | Register callee = ToRegister(lir->callee()); |
| 22351 | Register output = ToRegister(lir->output()); |
| 22352 | |
| 22353 | EmitLoadSuperFunction(masm, callee, output); |
| 22354 | |
| 22355 | bailoutCmpPtr(Assembler::Equal, output, ImmWord(0), lir->snapshot()); |
| 22356 | } |
| 22357 | |
| 22358 | void CodeGenerator::visitInitHomeObject(LInitHomeObject* lir) { |
| 22359 | Register func = ToRegister(lir->function()); |
| 22360 | ValueOperand homeObject = ToValue(lir->homeObject()); |
| 22361 | |
| 22362 | masm.assertFunctionIsExtended(func); |
| 22363 | |
| 22364 | Address addr(func, FunctionExtended::offsetOfMethodHomeObjectSlot()); |
| 22365 | |
| 22366 | emitPreBarrier(addr); |
| 22367 | masm.storeValue(homeObject, addr); |
| 22368 | } |
| 22369 | |
| 22370 | void CodeGenerator::visitIsTypedArrayConstructor( |
| 22371 | LIsTypedArrayConstructor* lir) { |
| 22372 | Register object = ToRegister(lir->object()); |
| 22373 | Register output = ToRegister(lir->output()); |
| 22374 | |
| 22375 | masm.setIsDefinitelyTypedArrayConstructor(object, output); |
| 22376 | } |
| 22377 | |
| 22378 | void CodeGenerator::visitLoadValueTag(LLoadValueTag* lir) { |
| 22379 | ValueOperand value = ToValue(lir->value()); |
| 22380 | Register output = ToRegister(lir->output()); |
| 22381 | |
| 22382 | Register tag = masm.extractTag(value, output); |
| 22383 | if (tag != output) { |
| 22384 | masm.mov(tag, output); |
| 22385 | } |
| 22386 | } |
| 22387 | |
| 22388 | void CodeGenerator::visitGuardTagNotEqual(LGuardTagNotEqual* lir) { |
| 22389 | Register lhs = ToRegister(lir->lhs()); |
| 22390 | Register rhs = ToRegister(lir->rhs()); |
| 22391 | |
| 22392 | bailoutCmp32(Assembler::Equal, lhs, rhs, lir->snapshot()); |
| 22393 | |
| 22394 | // If both lhs and rhs are numbers, can't use tag comparison to do inequality |
| 22395 | // comparison |
| 22396 | Label done; |
| 22397 | masm.branchTestNumber(Assembler::NotEqual, lhs, &done); |
| 22398 | masm.branchTestNumber(Assembler::NotEqual, rhs, &done); |
| 22399 | bailout(lir->snapshot()); |
| 22400 | |
| 22401 | masm.bind(&done); |
| 22402 | } |
| 22403 | |
| 22404 | void CodeGenerator::visitLoadWrapperTarget(LLoadWrapperTarget* lir) { |
| 22405 | Register object = ToRegister(lir->object()); |
| 22406 | Register output = ToRegister(lir->output()); |
| 22407 | |
| 22408 | // Bail for revoked proxies. |
| 22409 | Label bail; |
| 22410 | Address targetAddr(object, ProxyObject::offsetOfPrivateSlot()); |
| 22411 | if (lir->mir()->fallible()) { |
| 22412 | masm.fallibleUnboxObject(targetAddr, output, &bail); |
| 22413 | bailoutFrom(&bail, lir->snapshot()); |
| 22414 | } else { |
| 22415 | masm.unboxObject(targetAddr, output); |
| 22416 | } |
| 22417 | } |
| 22418 | |
| 22419 | void CodeGenerator::visitLoadGetterSetterFunction( |
| 22420 | LLoadGetterSetterFunction* lir) { |
| 22421 | ValueOperand getterSetter = ToValue(lir->getterSetter()); |
| 22422 | Register output = ToRegister(lir->output()); |
| 22423 | |
| 22424 | masm.unboxNonDouble(getterSetter, output, JSVAL_TYPE_PRIVATE_GCTHING); |
| 22425 | |
| 22426 | size_t offset = lir->mir()->isGetter() ? GetterSetter::offsetOfGetter() |
| 22427 | : GetterSetter::offsetOfSetter(); |
| 22428 | masm.loadPtr(Address(output, offset), output); |
| 22429 | |
| 22430 | Label bail; |
| 22431 | masm.branchTestPtr(Assembler::Zero, output, output, &bail); |
| 22432 | if (lir->mir()->needsClassGuard()) { |
| 22433 | Register temp = ToRegister(lir->temp0()); |
| 22434 | masm.branchTestObjIsFunction(Assembler::NotEqual, output, temp, output, |
| 22435 | &bail); |
| 22436 | } |
| 22437 | |
| 22438 | bailoutFrom(&bail, lir->snapshot()); |
| 22439 | } |
| 22440 | |
| 22441 | void CodeGenerator::visitGuardHasGetterSetter(LGuardHasGetterSetter* lir) { |
| 22442 | Register object = ToRegister(lir->object()); |
| 22443 | Register temp0 = ToRegister(lir->temp0()); |
| 22444 | Register temp1 = ToRegister(lir->temp1()); |
| 22445 | Register temp2 = ToRegister(lir->temp2()); |
| 22446 | |
| 22447 | masm.movePropertyKey(lir->mir()->propId(), temp1); |
| 22448 | |
| 22449 | auto getterSetterVal = lir->mir()->getterSetterValue(); |
| 22450 | if (getterSetterVal.isValue()) { |
| 22451 | auto* gs = getterSetterVal.toValue().toGCThing()->as<GetterSetter>(); |
| 22452 | masm.movePtr(ImmGCPtr(gs), temp2); |
| 22453 | } else { |
| 22454 | // Load the GetterSetter* from the Value stored in the IonScript. |
| 22455 | Address valueAddr = getNurseryValueAddress(getterSetterVal, temp2); |
| 22456 | masm.unboxNonDouble(valueAddr, temp2, JSVAL_TYPE_PRIVATE_GCTHING); |
| 22457 | } |
| 22458 | |
| 22459 | using Fn = bool (*)(JSContext* cx, JSObject* obj, jsid id, |
| 22460 | GetterSetter* getterSetter); |
| 22461 | masm.setupAlignedABICall(); |
| 22462 | masm.loadJSContext(temp0); |
| 22463 | masm.passABIArg(temp0); |
| 22464 | masm.passABIArg(object); |
| 22465 | masm.passABIArg(temp1); |
| 22466 | masm.passABIArg(temp2); |
| 22467 | masm.callWithABI<Fn, ObjectHasGetterSetterPure>(); |
| 22468 | |
| 22469 | bailoutIfFalseBool(ReturnReg, lir->snapshot()); |
| 22470 | } |
| 22471 | |
| 22472 | void CodeGenerator::visitGuardIsExtensible(LGuardIsExtensible* lir) { |
| 22473 | Register object = ToRegister(lir->object()); |
| 22474 | Register temp = ToRegister(lir->temp0()); |
| 22475 | |
| 22476 | Label bail; |
| 22477 | masm.branchIfObjectNotExtensible(object, temp, &bail); |
| 22478 | bailoutFrom(&bail, lir->snapshot()); |
| 22479 | } |
| 22480 | |
| 22481 | void CodeGenerator::visitGuardInt32IsNonNegative( |
| 22482 | LGuardInt32IsNonNegative* lir) { |
| 22483 | Register index = ToRegister(lir->index()); |
| 22484 | |
| 22485 | bailoutCmp32(Assembler::LessThan, index, Imm32(0), lir->snapshot()); |
| 22486 | } |
| 22487 | |
| 22488 | void CodeGenerator::visitGuardIntPtrIsNonNegative( |
| 22489 | LGuardIntPtrIsNonNegative* lir) { |
| 22490 | Register index = ToRegister(lir->index()); |
| 22491 | |
| 22492 | bailoutCmpPtr(Assembler::LessThan, index, ImmWord(0), lir->snapshot()); |
| 22493 | } |
| 22494 | |
| 22495 | void CodeGenerator::visitGuardInt32Range(LGuardInt32Range* lir) { |
| 22496 | Register input = ToRegister(lir->input()); |
| 22497 | |
| 22498 | bailoutCmp32(Assembler::LessThan, input, Imm32(lir->mir()->minimum()), |
| 22499 | lir->snapshot()); |
| 22500 | bailoutCmp32(Assembler::GreaterThan, input, Imm32(lir->mir()->maximum()), |
| 22501 | lir->snapshot()); |
| 22502 | } |
| 22503 | |
| 22504 | void CodeGenerator::visitGuardIndexIsNotDenseElement( |
| 22505 | LGuardIndexIsNotDenseElement* lir) { |
| 22506 | Register object = ToRegister(lir->object()); |
| 22507 | Register index = ToRegister(lir->index()); |
| 22508 | Register temp = ToRegister(lir->temp0()); |
| 22509 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
| 22510 | |
| 22511 | // Load obj->elements. |
| 22512 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
| 22513 | |
| 22514 | // Ensure index >= initLength or the element is a hole. |
| 22515 | Label notDense; |
| 22516 | Address capacity(temp, ObjectElements::offsetOfInitializedLength()); |
| 22517 | masm.spectreBoundsCheck32(index, capacity, spectreTemp, ¬Dense); |
| 22518 | |
| 22519 | BaseObjectElementIndex element(temp, index); |
| 22520 | masm.branchTestMagic(Assembler::Equal, element, JS_ELEMENTS_HOLE, ¬Dense); |
| 22521 | |
| 22522 | bailout(lir->snapshot()); |
| 22523 | |
| 22524 | masm.bind(¬Dense); |
| 22525 | } |
| 22526 | |
| 22527 | void CodeGenerator::visitGuardIndexIsValidUpdateOrAdd( |
| 22528 | LGuardIndexIsValidUpdateOrAdd* lir) { |
| 22529 | Register object = ToRegister(lir->object()); |
| 22530 | Register index = ToRegister(lir->index()); |
| 22531 | Register temp = ToRegister(lir->temp0()); |
| 22532 | Register spectreTemp = ToTempRegisterOrInvalid(lir->temp1()); |
| 22533 | |
| 22534 | // Load obj->elements. |
| 22535 | masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp); |
| 22536 | |
| 22537 | Label success; |
| 22538 | |
| 22539 | // If length is writable, branch to &success. All indices are writable. |
| 22540 | Address flags(temp, ObjectElements::offsetOfFlags()); |
| 22541 | masm.branchTest32(Assembler::Zero, flags, |
| 22542 | Imm32(ObjectElements::Flags::NONWRITABLE_ARRAY_LENGTH), |
| 22543 | &success); |
| 22544 | |
| 22545 | // Otherwise, ensure index is in bounds. |
| 22546 | Label bail; |
| 22547 | Address length(temp, ObjectElements::offsetOfLength()); |
| 22548 | masm.spectreBoundsCheck32(index, length, spectreTemp, &bail); |
| 22549 | masm.bind(&success); |
| 22550 | |
| 22551 | bailoutFrom(&bail, lir->snapshot()); |
| 22552 | } |
| 22553 | |
| 22554 | void CodeGenerator::visitCallAddOrUpdateSparseElement( |
| 22555 | LCallAddOrUpdateSparseElement* lir) { |
| 22556 | Register object = ToRegister(lir->object()); |
| 22557 | Register index = ToRegister(lir->index()); |
| 22558 | ValueOperand value = ToValue(lir->value()); |
| 22559 | |
| 22560 | pushArg(Imm32(lir->mir()->strict())); |
| 22561 | pushArg(value); |
| 22562 | pushArg(index); |
| 22563 | pushArg(object); |
| 22564 | |
| 22565 | using Fn = |
| 22566 | bool (*)(JSContext*, Handle<NativeObject*>, int32_t, HandleValue, bool); |
| 22567 | callVM<Fn, js::AddOrUpdateSparseElementHelper>(lir); |
| 22568 | } |
| 22569 | |
| 22570 | void CodeGenerator::visitCallGetSparseElement(LCallGetSparseElement* lir) { |
| 22571 | Register object = ToRegister(lir->object()); |
| 22572 | Register index = ToRegister(lir->index()); |
| 22573 | |
| 22574 | pushArg(index); |
| 22575 | pushArg(object); |
| 22576 | |
| 22577 | using Fn = |
| 22578 | bool (*)(JSContext*, Handle<NativeObject*>, int32_t, MutableHandleValue); |
| 22579 | callVM<Fn, js::GetSparseElementHelper>(lir); |
| 22580 | } |
| 22581 | |
| 22582 | void CodeGenerator::visitCallNativeGetElement(LCallNativeGetElement* lir) { |
| 22583 | Register object = ToRegister(lir->object()); |
| 22584 | Register index = ToRegister(lir->index()); |
| 22585 | |
| 22586 | pushArg(index); |
| 22587 | pushArg(TypedOrValueRegister(MIRType::Object, AnyRegister(object))); |
| 22588 | pushArg(object); |
| 22589 | |
| 22590 | using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t, |
| 22591 | MutableHandleValue); |
| 22592 | callVM<Fn, js::NativeGetElement>(lir); |
| 22593 | } |
| 22594 | |
| 22595 | void CodeGenerator::visitCallNativeGetElementSuper( |
| 22596 | LCallNativeGetElementSuper* lir) { |
| 22597 | Register object = ToRegister(lir->object()); |
| 22598 | Register index = ToRegister(lir->index()); |
| 22599 | ValueOperand receiver = ToValue(lir->receiver()); |
| 22600 | |
| 22601 | pushArg(index); |
| 22602 | pushArg(receiver); |
| 22603 | pushArg(object); |
| 22604 | |
| 22605 | using Fn = bool (*)(JSContext*, Handle<NativeObject*>, HandleValue, int32_t, |
| 22606 | MutableHandleValue); |
| 22607 | callVM<Fn, js::NativeGetElement>(lir); |
| 22608 | } |
| 22609 | |
| 22610 | void CodeGenerator::visitCallObjectHasSparseElement( |
| 22611 | LCallObjectHasSparseElement* lir) { |
| 22612 | Register object = ToRegister(lir->object()); |
| 22613 | Register index = ToRegister(lir->index()); |
| 22614 | Register temp0 = ToRegister(lir->temp0()); |
| 22615 | Register temp1 = ToRegister(lir->temp1()); |
| 22616 | Register output = ToRegister(lir->output()); |
| 22617 | |
| 22618 | masm.reserveStack(sizeof(Value)); |
| 22619 | masm.moveStackPtrTo(temp1); |
| 22620 | |
| 22621 | using Fn = bool (*)(JSContext*, NativeObject*, int32_t, Value*); |
| 22622 | masm.setupAlignedABICall(); |
| 22623 | masm.loadJSContext(temp0); |
| 22624 | masm.passABIArg(temp0); |
| 22625 | masm.passABIArg(object); |
| 22626 | masm.passABIArg(index); |
| 22627 | masm.passABIArg(temp1); |
| 22628 | masm.callWithABI<Fn, HasNativeElementPure>(); |
| 22629 | masm.storeCallPointerResult(temp0); |
| 22630 | |
| 22631 | Label bail, ok; |
| 22632 | uint32_t framePushed = masm.framePushed(); |
| 22633 | masm.branchIfTrueBool(temp0, &ok); |
| 22634 | masm.adjustStack(sizeof(Value)); |
| 22635 | masm.jump(&bail); |
| 22636 | |
| 22637 | masm.bind(&ok); |
| 22638 | masm.setFramePushed(framePushed); |
| 22639 | masm.unboxBoolean(Address(masm.getStackPointer(), 0), output); |
| 22640 | masm.adjustStack(sizeof(Value)); |
| 22641 | |
| 22642 | bailoutFrom(&bail, lir->snapshot()); |
| 22643 | } |
| 22644 | |
| 22645 | void CodeGenerator::visitBigIntAsIntN(LBigIntAsIntN* ins) { |
| 22646 | Register bits = ToRegister(ins->bits()); |
| 22647 | Register input = ToRegister(ins->input()); |
| 22648 | |
| 22649 | pushArg(bits); |
| 22650 | pushArg(input); |
| 22651 | |
| 22652 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t); |
| 22653 | callVM<Fn, jit::BigIntAsIntN>(ins); |
| 22654 | } |
| 22655 | |
| 22656 | void CodeGenerator::visitBigIntAsUintN(LBigIntAsUintN* ins) { |
| 22657 | Register bits = ToRegister(ins->bits()); |
| 22658 | Register input = ToRegister(ins->input()); |
| 22659 | |
| 22660 | pushArg(bits); |
| 22661 | pushArg(input); |
| 22662 | |
| 22663 | using Fn = BigInt* (*)(JSContext*, HandleBigInt, int32_t); |
| 22664 | callVM<Fn, jit::BigIntAsUintN>(ins); |
| 22665 | } |
| 22666 | |
| 22667 | void CodeGenerator::visitGuardNonGCThing(LGuardNonGCThing* ins) { |
| 22668 | ValueOperand input = ToValue(ins->input()); |
| 22669 | |
| 22670 | Label bail; |
| 22671 | masm.branchTestGCThing(Assembler::Equal, input, &bail); |
| 22672 | bailoutFrom(&bail, ins->snapshot()); |
| 22673 | } |
| 22674 | |
| 22675 | void CodeGenerator::visitToHashableNonGCThing(LToHashableNonGCThing* ins) { |
| 22676 | ValueOperand input = ToValue(ins->input()); |
| 22677 | FloatRegister tempFloat = ToFloatRegister(ins->temp0()); |
| 22678 | ValueOperand output = ToOutValue(ins); |
| 22679 | |
| 22680 | masm.toHashableNonGCThing(input, output, tempFloat); |
| 22681 | } |
| 22682 | |
| 22683 | void CodeGenerator::visitToHashableString(LToHashableString* ins) { |
| 22684 | Register input = ToRegister(ins->input()); |
| 22685 | Register output = ToRegister(ins->output()); |
| 22686 | |
| 22687 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
| 22688 | auto* ool = oolCallVM<Fn, js::AtomizeString>(ins, ArgList(input), |
| 22689 | StoreRegisterTo(output)); |
| 22690 | |
| 22691 | Label isAtom; |
| 22692 | masm.branchTest32(Assembler::NonZero, |
| 22693 | Address(input, JSString::offsetOfFlags()), |
| 22694 | Imm32(StringFlags::ATOM_BIT), &isAtom); |
| 22695 | |
| 22696 | masm.tryFastAtomize(input, output, output, ool->entry()); |
| 22697 | masm.jump(ool->rejoin()); |
| 22698 | masm.bind(&isAtom); |
| 22699 | masm.movePtr(input, output); |
| 22700 | masm.bind(ool->rejoin()); |
| 22701 | } |
| 22702 | |
| 22703 | void CodeGenerator::visitToHashableValue(LToHashableValue* ins) { |
| 22704 | ValueOperand input = ToValue(ins->input()); |
| 22705 | FloatRegister tempFloat = ToFloatRegister(ins->temp0()); |
| 22706 | ValueOperand output = ToOutValue(ins); |
| 22707 | |
| 22708 | Register str = output.scratchReg(); |
| 22709 | |
| 22710 | using Fn = JSAtom* (*)(JSContext*, JSString*); |
| 22711 | auto* ool = |
| 22712 | oolCallVM<Fn, js::AtomizeString>(ins, ArgList(str), StoreRegisterTo(str)); |
| 22713 | |
| 22714 | masm.toHashableValue(input, output, tempFloat, ool->entry(), ool->rejoin()); |
| 22715 | } |
| 22716 | |
| 22717 | void CodeGenerator::visitHashNonGCThing(LHashNonGCThing* ins) { |
| 22718 | ValueOperand input = ToValue(ins->input()); |
| 22719 | Register temp = ToRegister(ins->temp0()); |
| 22720 | Register output = ToRegister(ins->output()); |
| 22721 | |
| 22722 | masm.prepareHashNonGCThing(input, output, temp); |
| 22723 | } |
| 22724 | |
| 22725 | void CodeGenerator::visitHashString(LHashString* ins) { |
| 22726 | Register input = ToRegister(ins->input()); |
| 22727 | Register temp = ToRegister(ins->temp0()); |
| 22728 | Register output = ToRegister(ins->output()); |
| 22729 | |
| 22730 | masm.prepareHashString(input, output, temp); |
| 22731 | } |
| 22732 | |
| 22733 | void CodeGenerator::visitHashSymbol(LHashSymbol* ins) { |
| 22734 | Register input = ToRegister(ins->input()); |
| 22735 | Register output = ToRegister(ins->output()); |
| 22736 | |
| 22737 | masm.prepareHashSymbol(input, output); |
| 22738 | } |
| 22739 | |
| 22740 | void CodeGenerator::visitHashBigInt(LHashBigInt* ins) { |
| 22741 | Register input = ToRegister(ins->input()); |
| 22742 | Register temp0 = ToRegister(ins->temp0()); |
| 22743 | Register temp1 = ToRegister(ins->temp1()); |
| 22744 | Register temp2 = ToRegister(ins->temp2()); |
| 22745 | Register output = ToRegister(ins->output()); |
| 22746 | |
| 22747 | masm.prepareHashBigInt(input, output, temp0, temp1, temp2); |
| 22748 | } |
| 22749 | |
| 22750 | void CodeGenerator::visitHashObject(LHashObject* ins) { |
| 22751 | Register setObj = ToRegister(ins->setObject()); |
| 22752 | ValueOperand input = ToValue(ins->input()); |
| 22753 | Register temp0 = ToRegister(ins->temp0()); |
| 22754 | Register temp1 = ToRegister(ins->temp1()); |
| 22755 | Register temp2 = ToRegister(ins->temp2()); |
| 22756 | Register temp3 = ToRegister(ins->temp3()); |
| 22757 | Register output = ToRegister(ins->output()); |
| 22758 | |
| 22759 | masm.prepareHashObject(setObj, input, output, temp0, temp1, temp2, temp3); |
| 22760 | } |
| 22761 | |
| 22762 | void CodeGenerator::visitHashValue(LHashValue* ins) { |
| 22763 | Register setObj = ToRegister(ins->setObject()); |
| 22764 | ValueOperand input = ToValue(ins->input()); |
| 22765 | Register temp0 = ToRegister(ins->temp0()); |
| 22766 | Register temp1 = ToRegister(ins->temp1()); |
| 22767 | Register temp2 = ToRegister(ins->temp2()); |
| 22768 | Register temp3 = ToRegister(ins->temp3()); |
| 22769 | Register output = ToRegister(ins->output()); |
| 22770 | |
| 22771 | masm.prepareHashValue(setObj, input, output, temp0, temp1, temp2, temp3); |
| 22772 | } |
| 22773 | |
| 22774 | void CodeGenerator::visitSetObjectHasNonBigInt(LSetObjectHasNonBigInt* ins) { |
| 22775 | Register setObj = ToRegister(ins->setObject()); |
| 22776 | ValueOperand input = ToValue(ins->value()); |
| 22777 | Register hash = ToRegister(ins->hash()); |
| 22778 | Register temp0 = ToRegister(ins->temp0()); |
| 22779 | Register temp1 = ToRegister(ins->temp1()); |
| 22780 | Register output = ToRegister(ins->output()); |
| 22781 | |
| 22782 | masm.setObjectHasNonBigInt(setObj, input, hash, output, temp0, temp1); |
| 22783 | } |
| 22784 | |
| 22785 | void CodeGenerator::visitSetObjectHasBigInt(LSetObjectHasBigInt* ins) { |
| 22786 | Register setObj = ToRegister(ins->setObject()); |
| 22787 | ValueOperand input = ToValue(ins->value()); |
| 22788 | Register hash = ToRegister(ins->hash()); |
| 22789 | Register temp0 = ToRegister(ins->temp0()); |
| 22790 | Register temp1 = ToRegister(ins->temp1()); |
| 22791 | Register temp2 = ToRegister(ins->temp2()); |
| 22792 | Register temp3 = ToRegister(ins->temp3()); |
| 22793 | Register output = ToRegister(ins->output()); |
| 22794 | |
| 22795 | masm.setObjectHasBigInt(setObj, input, hash, output, temp0, temp1, temp2, |
| 22796 | temp3); |
| 22797 | } |
| 22798 | |
| 22799 | void CodeGenerator::visitSetObjectHasValue(LSetObjectHasValue* ins) { |
| 22800 | Register setObj = ToRegister(ins->setObject()); |
| 22801 | ValueOperand input = ToValue(ins->value()); |
| 22802 | Register hash = ToRegister(ins->hash()); |
| 22803 | Register temp0 = ToRegister(ins->temp0()); |
| 22804 | Register temp1 = ToRegister(ins->temp1()); |
| 22805 | Register temp2 = ToRegister(ins->temp2()); |
| 22806 | Register temp3 = ToRegister(ins->temp3()); |
| 22807 | Register output = ToRegister(ins->output()); |
| 22808 | |
| 22809 | masm.setObjectHasValue(setObj, input, hash, output, temp0, temp1, temp2, |
| 22810 | temp3); |
| 22811 | } |
| 22812 | |
| 22813 | void CodeGenerator::visitSetObjectHasValueVMCall( |
| 22814 | LSetObjectHasValueVMCall* ins) { |
| 22815 | pushArg(ToValue(ins->value())); |
| 22816 | pushArg(ToRegister(ins->setObject())); |
| 22817 | |
| 22818 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*); |
| 22819 | callVM<Fn, jit::SetObjectHas>(ins); |
| 22820 | } |
| 22821 | |
| 22822 | void CodeGenerator::visitSetObjectDelete(LSetObjectDelete* ins) { |
| 22823 | pushArg(ToValue(ins->key())); |
| 22824 | pushArg(ToRegister(ins->setObject())); |
| 22825 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue, bool*); |
| 22826 | callVM<Fn, jit::SetObjectDelete>(ins); |
| 22827 | } |
| 22828 | |
| 22829 | void CodeGenerator::visitSetObjectAdd(LSetObjectAdd* ins) { |
| 22830 | pushArg(ToValue(ins->key())); |
| 22831 | pushArg(ToRegister(ins->setObject())); |
| 22832 | using Fn = bool (*)(JSContext*, Handle<SetObject*>, HandleValue); |
| 22833 | callVM<Fn, jit::SetObjectAdd>(ins); |
| 22834 | } |
| 22835 | |
| 22836 | void CodeGenerator::visitSetObjectSize(LSetObjectSize* ins) { |
| 22837 | Register setObj = ToRegister(ins->setObject()); |
| 22838 | Register output = ToRegister(ins->output()); |
| 22839 | |
| 22840 | masm.loadSetObjectSize(setObj, output); |
| 22841 | } |
| 22842 | |
| 22843 | void CodeGenerator::visitMapObjectHasNonBigInt(LMapObjectHasNonBigInt* ins) { |
| 22844 | Register mapObj = ToRegister(ins->mapObject()); |
| 22845 | ValueOperand input = ToValue(ins->value()); |
| 22846 | Register hash = ToRegister(ins->hash()); |
| 22847 | Register temp0 = ToRegister(ins->temp0()); |
| 22848 | Register temp1 = ToRegister(ins->temp1()); |
| 22849 | Register output = ToRegister(ins->output()); |
| 22850 | |
| 22851 | masm.mapObjectHasNonBigInt(mapObj, input, hash, output, temp0, temp1); |
| 22852 | } |
| 22853 | |
| 22854 | void CodeGenerator::visitMapObjectHasBigInt(LMapObjectHasBigInt* ins) { |
| 22855 | Register mapObj = ToRegister(ins->mapObject()); |
| 22856 | ValueOperand input = ToValue(ins->value()); |
| 22857 | Register hash = ToRegister(ins->hash()); |
| 22858 | Register temp0 = ToRegister(ins->temp0()); |
| 22859 | Register temp1 = ToRegister(ins->temp1()); |
| 22860 | Register temp2 = ToRegister(ins->temp2()); |
| 22861 | Register temp3 = ToRegister(ins->temp3()); |
| 22862 | Register output = ToRegister(ins->output()); |
| 22863 | |
| 22864 | masm.mapObjectHasBigInt(mapObj, input, hash, output, temp0, temp1, temp2, |
| 22865 | temp3); |
| 22866 | } |
| 22867 | |
| 22868 | void CodeGenerator::visitMapObjectHasValue(LMapObjectHasValue* ins) { |
| 22869 | Register mapObj = ToRegister(ins->mapObject()); |
| 22870 | ValueOperand input = ToValue(ins->value()); |
| 22871 | Register hash = ToRegister(ins->hash()); |
| 22872 | Register temp0 = ToRegister(ins->temp0()); |
| 22873 | Register temp1 = ToRegister(ins->temp1()); |
| 22874 | Register temp2 = ToRegister(ins->temp2()); |
| 22875 | Register temp3 = ToRegister(ins->temp3()); |
| 22876 | Register output = ToRegister(ins->output()); |
| 22877 | |
| 22878 | masm.mapObjectHasValue(mapObj, input, hash, output, temp0, temp1, temp2, |
| 22879 | temp3); |
| 22880 | } |
| 22881 | |
| 22882 | void CodeGenerator::visitMapObjectHasValueVMCall( |
| 22883 | LMapObjectHasValueVMCall* ins) { |
| 22884 | pushArg(ToValue(ins->value())); |
| 22885 | pushArg(ToRegister(ins->mapObject())); |
| 22886 | |
| 22887 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*); |
| 22888 | callVM<Fn, jit::MapObjectHas>(ins); |
| 22889 | } |
| 22890 | |
| 22891 | void CodeGenerator::visitMapObjectGetNonBigInt(LMapObjectGetNonBigInt* ins) { |
| 22892 | Register mapObj = ToRegister(ins->mapObject()); |
| 22893 | ValueOperand input = ToValue(ins->value()); |
| 22894 | Register hash = ToRegister(ins->hash()); |
| 22895 | Register temp0 = ToRegister(ins->temp0()); |
| 22896 | Register temp1 = ToRegister(ins->temp1()); |
| 22897 | ValueOperand output = ToOutValue(ins); |
| 22898 | |
| 22899 | masm.mapObjectGetNonBigInt(mapObj, input, hash, output, temp0, temp1, |
| 22900 | output.scratchReg()); |
| 22901 | } |
| 22902 | |
| 22903 | void CodeGenerator::visitMapObjectGetBigInt(LMapObjectGetBigInt* ins) { |
| 22904 | Register mapObj = ToRegister(ins->mapObject()); |
| 22905 | ValueOperand input = ToValue(ins->value()); |
| 22906 | Register hash = ToRegister(ins->hash()); |
| 22907 | Register temp0 = ToRegister(ins->temp0()); |
| 22908 | Register temp1 = ToRegister(ins->temp1()); |
| 22909 | Register temp2 = ToRegister(ins->temp2()); |
| 22910 | Register temp3 = ToRegister(ins->temp3()); |
| 22911 | ValueOperand output = ToOutValue(ins); |
| 22912 | |
| 22913 | masm.mapObjectGetBigInt(mapObj, input, hash, output, temp0, temp1, temp2, |
| 22914 | temp3, output.scratchReg()); |
| 22915 | } |
| 22916 | |
| 22917 | void CodeGenerator::visitMapObjectGetValue(LMapObjectGetValue* ins) { |
| 22918 | Register mapObj = ToRegister(ins->mapObject()); |
| 22919 | ValueOperand input = ToValue(ins->value()); |
| 22920 | Register hash = ToRegister(ins->hash()); |
| 22921 | Register temp0 = ToRegister(ins->temp0()); |
| 22922 | Register temp1 = ToRegister(ins->temp1()); |
| 22923 | Register temp2 = ToRegister(ins->temp2()); |
| 22924 | Register temp3 = ToRegister(ins->temp3()); |
| 22925 | ValueOperand output = ToOutValue(ins); |
| 22926 | |
| 22927 | masm.mapObjectGetValue(mapObj, input, hash, output, temp0, temp1, temp2, |
| 22928 | temp3, output.scratchReg()); |
| 22929 | } |
| 22930 | |
| 22931 | void CodeGenerator::visitMapObjectGetValueVMCall( |
| 22932 | LMapObjectGetValueVMCall* ins) { |
| 22933 | pushArg(ToValue(ins->value())); |
| 22934 | pushArg(ToRegister(ins->mapObject())); |
| 22935 | |
| 22936 | using Fn = |
| 22937 | bool (*)(JSContext*, Handle<MapObject*>, HandleValue, MutableHandleValue); |
| 22938 | callVM<Fn, jit::MapObjectGet>(ins); |
| 22939 | } |
| 22940 | |
| 22941 | void CodeGenerator::visitMapObjectDelete(LMapObjectDelete* ins) { |
| 22942 | pushArg(ToValue(ins->key())); |
| 22943 | pushArg(ToRegister(ins->mapObject())); |
| 22944 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, bool*); |
| 22945 | callVM<Fn, jit::MapObjectDelete>(ins); |
| 22946 | } |
| 22947 | |
| 22948 | void CodeGenerator::visitMapObjectSet(LMapObjectSet* ins) { |
| 22949 | pushArg(ToValue(ins->value())); |
| 22950 | pushArg(ToValue(ins->key())); |
| 22951 | pushArg(ToRegister(ins->mapObject())); |
| 22952 | using Fn = bool (*)(JSContext*, Handle<MapObject*>, HandleValue, HandleValue); |
| 22953 | callVM<Fn, jit::MapObjectSet>(ins); |
| 22954 | } |
| 22955 | |
| 22956 | void CodeGenerator::visitMapObjectSize(LMapObjectSize* ins) { |
| 22957 | Register mapObj = ToRegister(ins->mapObject()); |
| 22958 | Register output = ToRegister(ins->output()); |
| 22959 | |
| 22960 | masm.loadMapObjectSize(mapObj, output); |
| 22961 | } |
| 22962 | |
| 22963 | void CodeGenerator::emitWeakMapLookupObject( |
| 22964 | Register weakMap, Register obj, Register hashTable, Register hashCode, |
| 22965 | Register scratch, Register scratch2, Register scratch3, Register scratch4, |
| 22966 | Register scratch5, Label* found, Label* missing) { |
| 22967 | // Load hash map if it exists. If not, jump to missing. |
| 22968 | Address mapAddr(weakMap, |
| 22969 | NativeObject::getFixedSlotOffset(WeakMapObject::DataSlot)); |
| 22970 | masm.branchTestUndefined(Assembler::Equal, mapAddr, missing); |
| 22971 | masm.loadPrivate(mapAddr, hashTable); |
| 22972 | |
| 22973 | // Hash and scramble address of object. |
| 22974 | #ifdef JS_PUNBOX641 |
| 22975 | ValueOperand boxedObj(scratch); |
| 22976 | #else |
| 22977 | ValueOperand boxedObj(scratch, obj); |
| 22978 | #endif |
| 22979 | masm.tagValue(JSVAL_TYPE_OBJECT, obj, boxedObj); |
| 22980 | masm.hashAndScrambleValue(boxedObj, hashCode, scratch2); |
| 22981 | masm.prepareHashMFBT(hashCode, /*alreadyScrambled*/ true); |
| 22982 | |
| 22983 | using Entry = WeakMapObject::Map::Entry; |
| 22984 | auto matchEntry = [&]() { |
| 22985 | Register entry = scratch; |
| 22986 | Label noMatch; |
| 22987 | masm.fallibleUnboxObject(Address(entry, Entry::offsetOfKey()), scratch2, |
| 22988 | &noMatch); |
| 22989 | masm.branchPtr(Assembler::Equal, obj, scratch2, found); |
| 22990 | masm.bind(&noMatch); |
| 22991 | }; |
| 22992 | masm.lookupMFBT<WeakMapObject::Map>(hashTable, hashCode, scratch, scratch2, |
| 22993 | scratch3, scratch4, scratch5, missing, |
| 22994 | matchEntry); |
| 22995 | } |
| 22996 | |
| 22997 | void CodeGenerator::visitWeakMapGetObject(LWeakMapGetObject* ins) { |
| 22998 | #ifndef JS_CODEGEN_X86 |
| 22999 | Register weakMap = ToRegister(ins->weakMap()); |
| 23000 | Register obj = ToRegister(ins->object()); |
| 23001 | Register hashTable = ToRegister(ins->temp0()); |
| 23002 | Register hashCode = ToRegister(ins->temp1()); |
| 23003 | Register scratch = ToRegister(ins->temp2()); |
| 23004 | Register scratch2 = ToRegister(ins->temp3()); |
| 23005 | Register scratch3 = ToRegister(ins->temp4()); |
| 23006 | Register scratch4 = ToRegister(ins->temp5()); |
| 23007 | Register scratch5 = ToRegister(ins->temp6()); |
| 23008 | ValueOperand output = ToOutValue(ins); |
| 23009 | |
| 23010 | Label found, missing; |
| 23011 | |
| 23012 | emitWeakMapLookupObject(weakMap, obj, hashTable, hashCode, scratch, scratch2, |
| 23013 | scratch3, scratch4, scratch5, &found, &missing); |
| 23014 | |
| 23015 | masm.bind(&found); |
| 23016 | |
| 23017 | using Entry = WeakMapObject::Map::Entry; |
| 23018 | masm.loadValue(Address(scratch, Entry::offsetOfValue()), output); |
| 23019 | |
| 23020 | auto* ool = new (alloc()) LambdaOutOfLineCode([=, this](OutOfLineCode& ool) { |
| 23021 | // Unboxed, tenured GC cell that needs to be barriered is in scratch. |
| 23022 | |
| 23023 | LiveRegisterSet regsToSave(RegisterSet::Volatile()); |
| 23024 | regsToSave.takeUnchecked(hashTable); |
| 23025 | regsToSave.takeUnchecked(hashCode); |
| 23026 | regsToSave.takeUnchecked(scratch); |
| 23027 | regsToSave.takeUnchecked(scratch2); |
| 23028 | regsToSave.takeUnchecked(scratch3); |
| 23029 | regsToSave.takeUnchecked(scratch4); |
| 23030 | regsToSave.takeUnchecked(scratch5); |
| 23031 | masm.PushRegsInMask(regsToSave); |
| 23032 | |
| 23033 | masm.movePtr(ImmPtr(mirGen().realm->zone()->addressOfZone()), scratch2); |
| 23034 | |
| 23035 | using Fn = void (*)(js::gc::TenuredCell*, Zone*); |
| 23036 | masm.setupAlignedABICall(); |
| 23037 | masm.passABIArg(scratch); |
| 23038 | masm.passABIArg(scratch2); |
| 23039 | masm.callWithABI<Fn, js::jit::WeakMapValueReadBarrier>(); |
| 23040 | |
| 23041 | masm.PopRegsInMask(regsToSave); |
| 23042 | |
| 23043 | masm.jump(ool.rejoin()); |
| 23044 | }); |
| 23045 | addOutOfLineCode(ool, ins->mir()); |
| 23046 | |
| 23047 | masm.emitWeakMapBarrierFastPath(output, scratch, scratch2, scratch3, scratch4, |
| 23048 | scratch5, ool->entry()); |
| 23049 | masm.jump(ool->rejoin()); |
| 23050 | |
| 23051 | masm.bind(&missing); |
| 23052 | masm.moveValue(UndefinedValue(), output); |
| 23053 | |
| 23054 | masm.bind(ool->rejoin()); |
| 23055 | #else |
| 23056 | // x86 doesn't have enough registers, so we call into the VM. |
| 23057 | Register weakMap = ToRegister(ins->weakMap()); |
| 23058 | Register obj = ToRegister(ins->object()); |
| 23059 | Register temp = ToRegister(ins->temp0()); |
| 23060 | ValueOperand output = ToOutValue(ins); |
| 23061 | |
| 23062 | // The result Value will be stored on the stack. |
| 23063 | masm.reserveStack(sizeof(Value)); |
| 23064 | masm.moveStackPtrTo(temp); |
| 23065 | |
| 23066 | using Fn = void (*)(WeakMapObject*, JSObject*, Value*); |
| 23067 | masm.setupAlignedABICall(); |
| 23068 | masm.passABIArg(weakMap); |
| 23069 | masm.passABIArg(obj); |
| 23070 | masm.passABIArg(temp); |
| 23071 | masm.callWithABI<Fn, js::WeakMapObject::getObject>(); |
| 23072 | |
| 23073 | masm.Pop(output); |
| 23074 | #endif |
| 23075 | } |
| 23076 | |
| 23077 | void CodeGenerator::visitWeakMapHasObject(LWeakMapHasObject* ins) { |
| 23078 | #ifndef JS_CODEGEN_X86 |
| 23079 | Register weakMap = ToRegister(ins->weakMap()); |
| 23080 | Register obj = ToRegister(ins->object()); |
| 23081 | Register hashTable = ToRegister(ins->temp0()); |
| 23082 | Register hashCode = ToRegister(ins->temp1()); |
| 23083 | Register scratch = ToRegister(ins->temp2()); |
| 23084 | Register scratch2 = ToRegister(ins->temp3()); |
| 23085 | Register scratch3 = ToRegister(ins->temp4()); |
| 23086 | Register scratch4 = ToRegister(ins->temp5()); |
| 23087 | Register scratch5 = ToRegister(ins->temp6()); |
| 23088 | Register output = ToRegister(ins->output()); |
| 23089 | |
| 23090 | Label found, missing, done; |
| 23091 | |
| 23092 | emitWeakMapLookupObject(weakMap, obj, hashTable, hashCode, scratch, scratch2, |
| 23093 | scratch3, scratch4, scratch5, &found, &missing); |
| 23094 | |
| 23095 | masm.bind(&found); |
| 23096 | masm.move32(Imm32(1), output); |
| 23097 | masm.jump(&done); |
| 23098 | |
| 23099 | masm.bind(&missing); |
| 23100 | masm.move32(Imm32(0), output); |
| 23101 | masm.bind(&done); |
| 23102 | #else |
| 23103 | // x86 doesn't have enough registers, so we call into the VM. |
| 23104 | Register weakMap = ToRegister(ins->weakMap()); |
| 23105 | Register obj = ToRegister(ins->object()); |
| 23106 | Register output = ToRegister(ins->output()); |
| 23107 | |
| 23108 | using Fn = bool (*)(WeakMapObject*, JSObject*); |
| 23109 | masm.setupAlignedABICall(); |
| 23110 | masm.passABIArg(weakMap); |
| 23111 | masm.passABIArg(obj); |
| 23112 | masm.callWithABI<Fn, js::WeakMapObject::hasObject>(); |
| 23113 | masm.storeCallBoolResult(output); |
| 23114 | #endif |
| 23115 | } |
| 23116 | |
| 23117 | void CodeGenerator::visitWeakSetHasObject(LWeakSetHasObject* ins) { |
| 23118 | Register weakSet = ToRegister(ins->weakSet()); |
| 23119 | Register obj = ToRegister(ins->object()); |
| 23120 | Register output = ToRegister(ins->output()); |
| 23121 | |
| 23122 | using Fn = bool (*)(WeakSetObject*, JSObject*); |
| 23123 | masm.setupAlignedABICall(); |
| 23124 | masm.passABIArg(weakSet); |
| 23125 | masm.passABIArg(obj); |
| 23126 | masm.callWithABI<Fn, js::WeakSetObject::hasObject>(); |
| 23127 | masm.storeCallBoolResult(output); |
| 23128 | } |
| 23129 | |
| 23130 | void CodeGenerator::visitDateFillLocalTimeSlots(LDateFillLocalTimeSlots* ins) { |
| 23131 | Register date = ToRegister(ins->date()); |
| 23132 | Register temp = ToRegister(ins->temp0()); |
| 23133 | |
| 23134 | masm.dateFillLocalTimeSlots(date, temp, liveVolatileRegs(ins)); |
| 23135 | } |
| 23136 | |
| 23137 | void CodeGenerator::visitDateHoursFromSecondsIntoYear( |
| 23138 | LDateHoursFromSecondsIntoYear* ins) { |
| 23139 | auto secondsIntoYear = ToValue(ins->secondsIntoYear()); |
| 23140 | auto output = ToOutValue(ins); |
| 23141 | Register temp0 = ToRegister(ins->temp0()); |
| 23142 | Register temp1 = ToRegister(ins->temp1()); |
| 23143 | |
| 23144 | masm.dateHoursFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
| 23145 | } |
| 23146 | |
| 23147 | void CodeGenerator::visitDateMinutesFromSecondsIntoYear( |
| 23148 | LDateMinutesFromSecondsIntoYear* ins) { |
| 23149 | auto secondsIntoYear = ToValue(ins->secondsIntoYear()); |
| 23150 | auto output = ToOutValue(ins); |
| 23151 | Register temp0 = ToRegister(ins->temp0()); |
| 23152 | Register temp1 = ToRegister(ins->temp1()); |
| 23153 | |
| 23154 | masm.dateMinutesFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
| 23155 | } |
| 23156 | |
| 23157 | void CodeGenerator::visitDateSecondsFromSecondsIntoYear( |
| 23158 | LDateSecondsFromSecondsIntoYear* ins) { |
| 23159 | auto secondsIntoYear = ToValue(ins->secondsIntoYear()); |
| 23160 | auto output = ToOutValue(ins); |
| 23161 | Register temp0 = ToRegister(ins->temp0()); |
| 23162 | Register temp1 = ToRegister(ins->temp1()); |
| 23163 | |
| 23164 | masm.dateSecondsFromSecondsIntoYear(secondsIntoYear, output, temp0, temp1); |
| 23165 | } |
| 23166 | |
| 23167 | void CodeGenerator::visitDateNow(LDateNow* ins) { |
| 23168 | Register temp0 = ToRegister(ins->temp0()); |
| 23169 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 23169); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 23169); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23170 | |
| 23171 | using Fn = double (*)(JSContext*); |
| 23172 | masm.setupAlignedABICall(); |
| 23173 | masm.loadJSContext(temp0); |
| 23174 | masm.passABIArg(temp0); |
| 23175 | masm.callWithABI<Fn, jit::DateNow>(ABIType::Float64); |
| 23176 | } |
| 23177 | |
| 23178 | void CodeGenerator::visitDateParse(LDateParse* ins) { |
| 23179 | Register string = ToRegister(ins->string()); |
| 23180 | Register temp0 = ToRegister(ins->temp0()); |
| 23181 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 23181); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 23181); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23182 | |
| 23183 | using Fn = double (*)(JSContext*, const JSString*); |
| 23184 | masm.setupAlignedABICall(); |
| 23185 | masm.loadJSContext(temp0); |
| 23186 | masm.passABIArg(temp0); |
| 23187 | masm.passABIArg(string); |
| 23188 | masm.callWithABI<Fn, jit::DateParse>(ABIType::Float64); |
| 23189 | } |
| 23190 | |
| 23191 | void CodeGenerator::visitTimeClip(LTimeClip* ins) { |
| 23192 | auto time = ToFloatRegister(ins->time()); |
| 23193 | auto output = ToFloatRegister(ins->output()); |
| 23194 | |
| 23195 | masm.timeClip(time, output); |
| 23196 | } |
| 23197 | |
| 23198 | void CodeGenerator::visitTimeClipCall(LTimeClipCall* ins) { |
| 23199 | auto time = ToFloatRegister(ins->time()); |
| 23200 | auto output = ToFloatRegister(ins->output()); |
| 23201 | auto temp = ToRegister(ins->temp0()); |
| 23202 | |
| 23203 | masm.timeClip(time, output, temp, liveVolatileRegs(ins)); |
| 23204 | } |
| 23205 | |
| 23206 | void CodeGenerator::visitLocalTimeToUTC(LLocalTimeToUTC* ins) { |
| 23207 | Register64 localTime = ToRegister64(ins->localTime()); |
| 23208 | Register temp0 = ToRegister(ins->temp0()); |
| 23209 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 23209); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(ins->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 23209); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23210 | |
| 23211 | using Fn = double (*)(JSContext*, int64_t); |
| 23212 | masm.setupAlignedABICall(); |
| 23213 | masm.loadJSContext(temp0); |
| 23214 | masm.passABIArg(temp0); |
| 23215 | masm.passABIArg(localTime); |
| 23216 | masm.callWithABI<Fn, jit::DateLocalTimeToUTC>(ABIType::Float64); |
| 23217 | } |
| 23218 | |
| 23219 | void CodeGenerator::visitYearFromTime(LYearFromTime* ins) { |
| 23220 | FloatRegister utcTime = ToFloatRegister(ins->utcTime()); |
| 23221 | Register temp0 = ToRegister(ins->temp0()); |
| 23222 | Register temp1 = ToRegister(ins->temp1()); |
| 23223 | ValueOperand output = ToOutValue(ins); |
| 23224 | |
| 23225 | masm.reserveStack(sizeof(JS::Value)); |
| 23226 | masm.moveStackPtrTo(temp1); |
| 23227 | |
| 23228 | using Fn = void (*)(JSContext*, double, JS::Value*); |
| 23229 | masm.setupAlignedABICall(); |
| 23230 | masm.loadJSContext(temp0); |
| 23231 | masm.passABIArg(temp0); |
| 23232 | masm.passABIArg(utcTime, ABIType::Float64); |
| 23233 | masm.passABIArg(temp1); |
| 23234 | masm.callWithABI<Fn, jit::DateYearFromTime>(); |
| 23235 | |
| 23236 | masm.Pop(output); |
| 23237 | } |
| 23238 | |
| 23239 | void CodeGenerator::visitMonthFromTime(LMonthFromTime* ins) { |
| 23240 | FloatRegister utcTime = ToFloatRegister(ins->utcTime()); |
| 23241 | Register temp0 = ToRegister(ins->temp0()); |
| 23242 | Register temp1 = ToRegister(ins->temp1()); |
| 23243 | ValueOperand output = ToOutValue(ins); |
| 23244 | |
| 23245 | masm.reserveStack(sizeof(JS::Value)); |
| 23246 | masm.moveStackPtrTo(temp1); |
| 23247 | |
| 23248 | using Fn = void (*)(JSContext*, double, JS::Value*); |
| 23249 | masm.setupAlignedABICall(); |
| 23250 | masm.loadJSContext(temp0); |
| 23251 | masm.passABIArg(temp0); |
| 23252 | masm.passABIArg(utcTime, ABIType::Float64); |
| 23253 | masm.passABIArg(temp1); |
| 23254 | masm.callWithABI<Fn, jit::DateMonthFromTime>(); |
| 23255 | |
| 23256 | masm.Pop(output); |
| 23257 | } |
| 23258 | |
| 23259 | void CodeGenerator::visitDateFromTime(LDateFromTime* ins) { |
| 23260 | FloatRegister utcTime = ToFloatRegister(ins->utcTime()); |
| 23261 | Register temp0 = ToRegister(ins->temp0()); |
| 23262 | Register temp1 = ToRegister(ins->temp1()); |
| 23263 | ValueOperand output = ToOutValue(ins); |
| 23264 | |
| 23265 | masm.reserveStack(sizeof(JS::Value)); |
| 23266 | masm.moveStackPtrTo(temp1); |
| 23267 | |
| 23268 | using Fn = void (*)(JSContext*, double, JS::Value*); |
| 23269 | masm.setupAlignedABICall(); |
| 23270 | masm.loadJSContext(temp0); |
| 23271 | masm.passABIArg(temp0); |
| 23272 | masm.passABIArg(utcTime, ABIType::Float64); |
| 23273 | masm.passABIArg(temp1); |
| 23274 | masm.callWithABI<Fn, jit::DateDateFromTime>(); |
| 23275 | |
| 23276 | masm.Pop(output); |
| 23277 | } |
| 23278 | |
| 23279 | void CodeGenerator::visitNewDateObject(LNewDateObject* lir) { |
| 23280 | FloatRegister utcTime = ToFloatRegister(lir->utcTime()); |
| 23281 | Register output = ToRegister(lir->output()); |
| 23282 | Register temp = ToRegister(lir->temp0()); |
| 23283 | |
| 23284 | JSObject* templateObj = lir->mir()->templateObject(); |
| 23285 | |
| 23286 | using Fn = JSObject* (*)(JSContext*, double); |
| 23287 | auto* ool = oolCallVM<Fn, jit::NewDateObject>(lir, ArgList(utcTime), |
| 23288 | StoreRegisterTo(output)); |
| 23289 | |
| 23290 | TemplateObject templateObject(templateObj); |
| 23291 | masm.createGCObject(output, temp, templateObject, gc::Heap::Default, |
| 23292 | ool->entry()); |
| 23293 | masm.boxDouble(utcTime, Address(output, DateObject::offsetOfUTCTimeSlot())); |
| 23294 | |
| 23295 | masm.bind(ool->rejoin()); |
| 23296 | } |
| 23297 | |
| 23298 | void CodeGenerator::visitCanonicalizeNaND(LCanonicalizeNaND* ins) { |
| 23299 | auto output = ToFloatRegister(ins->output()); |
| 23300 | MOZ_ASSERT(output == ToFloatRegister(ins->input()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(output == ToFloatRegister(ins->input()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output == ToFloatRegister(ins->input())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output == ToFloatRegister(ins->input())" , "./../../../../js/src/jit/CodeGenerator.cpp", 23300); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output == ToFloatRegister(ins->input())" ")"); do { MOZ_CrashSequence(__null, 23300); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23301 | |
| 23302 | masm.canonicalizeDoubleNaN(output); |
| 23303 | } |
| 23304 | |
| 23305 | void CodeGenerator::visitCanonicalizeNaNF(LCanonicalizeNaNF* ins) { |
| 23306 | auto output = ToFloatRegister(ins->output()); |
| 23307 | MOZ_ASSERT(output == ToFloatRegister(ins->input()))do { static_assert( mozilla::detail::AssertionConditionType< decltype(output == ToFloatRegister(ins->input()))>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(output == ToFloatRegister(ins->input())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("output == ToFloatRegister(ins->input())" , "./../../../../js/src/jit/CodeGenerator.cpp", 23307); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output == ToFloatRegister(ins->input())" ")"); do { MOZ_CrashSequence(__null, 23307); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23308 | |
| 23309 | masm.canonicalizeFloatNaN(output); |
| 23310 | } |
| 23311 | |
| 23312 | template <size_t NumDefs> |
| 23313 | void CodeGenerator::emitIonToWasmCallBase(LIonToWasmCallBase<NumDefs>* lir) { |
| 23314 | wasm::JitCallStackArgVector stackArgs; |
| 23315 | masm.propagateOOM(stackArgs.reserve(lir->numOperands())); |
| 23316 | if (masm.oom()) { |
| 23317 | return; |
| 23318 | } |
| 23319 | |
| 23320 | MIonToWasmCall* mir = lir->mir(); |
| 23321 | const wasm::FuncExport& funcExport = mir->funcExport(); |
| 23322 | const wasm::FuncType& sig = |
| 23323 | mir->instance()->code().codeMeta().getFuncType(funcExport.funcIndex()); |
| 23324 | |
| 23325 | ABIArgGenerator abi(ABIKind::Wasm); |
| 23326 | for (size_t i = 0; i < lir->numOperands(); i++) { |
| 23327 | MIRType argMir; |
| 23328 | switch (sig.args()[i].kind()) { |
| 23329 | case wasm::ValType::I32: |
| 23330 | case wasm::ValType::I64: |
| 23331 | case wasm::ValType::F32: |
| 23332 | case wasm::ValType::F64: |
| 23333 | argMir = sig.args()[i].toMIRType(); |
| 23334 | break; |
| 23335 | case wasm::ValType::V128: |
| 23336 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23336); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected argument type when calling from ion to wasm" ")"); do { MOZ_CrashSequence(__null, 23336); __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 23337 | case wasm::ValType::Ref: |
| 23338 | // temporarilyUnsupportedReftypeForEntry() restricts args to externref |
| 23339 | 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()", "./../../../../js/src/jit/CodeGenerator.cpp" , 23339); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "sig.args()[i].refType().isExtern()" ")"); do { MOZ_CrashSequence(__null, 23339); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23340 | // Argument is boxed on the JS side to an anyref, so passed as a |
| 23341 | // pointer here. |
| 23342 | argMir = sig.args()[i].toMIRType(); |
| 23343 | break; |
| 23344 | } |
| 23345 | |
| 23346 | ABIArg arg = abi.next(argMir); |
| 23347 | switch (arg.kind()) { |
| 23348 | case ABIArg::GPR: |
| 23349 | case ABIArg::FPU: { |
| 23350 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 23350); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToAnyRegister(lir->getOperand(i)) == arg.reg()" ")"); do { MOZ_CrashSequence(__null, 23350); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23351 | stackArgs.infallibleEmplaceBack(wasm::JitCallStackArg()); |
| 23352 | break; |
| 23353 | } |
| 23354 | case ABIArg::Stack: { |
| 23355 | const LAllocation* larg = lir->getOperand(i); |
| 23356 | if (larg->isConstant()) { |
| 23357 | stackArgs.infallibleEmplaceBack(ToInt32(larg)); |
| 23358 | } else if (larg->isGeneralReg()) { |
| 23359 | stackArgs.infallibleEmplaceBack(ToRegister(larg)); |
| 23360 | } else if (larg->isFloatReg()) { |
| 23361 | stackArgs.infallibleEmplaceBack(ToFloatRegister(larg)); |
| 23362 | } else { |
| 23363 | // Always use the stack pointer here because GenerateDirectCallFromJit |
| 23364 | // depends on this. |
| 23365 | Address addr = ToAddress<BaseRegForAddress::SP>(larg); |
| 23366 | stackArgs.infallibleEmplaceBack(addr); |
| 23367 | } |
| 23368 | break; |
| 23369 | } |
| 23370 | #ifdef JS_CODEGEN_REGISTER_PAIR |
| 23371 | case ABIArg::GPR_PAIR: { |
| 23372 | MOZ_CRASH(do { do { } while (false); MOZ_ReportCrash("" "no way to pass i64, and wasm uses hardfp for function calls" , "./../../../../js/src/jit/CodeGenerator.cpp", 23373); AnnotateMozCrashReason ("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls" ")"); do { MOZ_CrashSequence(__null, 23373); __attribute__(( nomerge)) ::abort(); } while (false); } while (false) |
| 23373 | "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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23373); AnnotateMozCrashReason ("MOZ_CRASH(" "no way to pass i64, and wasm uses hardfp for function calls" ")"); do { MOZ_CrashSequence(__null, 23373); __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 23374 | } |
| 23375 | #endif |
| 23376 | case ABIArg::Uninitialized: { |
| 23377 | MOZ_CRASH("Uninitialized ABIArg kind")do { do { } while (false); MOZ_ReportCrash("" "Uninitialized ABIArg kind" , "./../../../../js/src/jit/CodeGenerator.cpp", 23377); AnnotateMozCrashReason ("MOZ_CRASH(" "Uninitialized ABIArg kind" ")"); do { MOZ_CrashSequence (__null, 23377); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 23378 | } |
| 23379 | } |
| 23380 | } |
| 23381 | |
| 23382 | const wasm::ValTypeVector& results = sig.results(); |
| 23383 | if (results.length() == 0) { |
| 23384 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23384); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value" ")"); do { MOZ_CrashSequence(__null, 23384); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23385 | } else { |
| 23386 | 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" ")", "./../../../../js/src/jit/CodeGenerator.cpp" , 23386); AnnotateMozCrashReason("MOZ_ASSERT" "(" "results.length() == 1" ") (" "multi-value return unimplemented" ")"); do { MOZ_CrashSequence (__null, 23386); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 23387 | switch (results[0].kind()) { |
| 23388 | case wasm::ValType::I32: |
| 23389 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23389); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int32" ")"); do { MOZ_CrashSequence(__null, 23389); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23390 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23390); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "ToRegister(lir->output()) == ReturnReg" ")"); do { MOZ_CrashSequence(__null, 23390); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23391 | break; |
| 23392 | case wasm::ValType::I64: |
| 23393 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23393); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Int64" ")"); do { MOZ_CrashSequence(__null, 23393); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23394 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 23394); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToOutRegister64(lir) == ReturnReg64" ")"); do { MOZ_CrashSequence(__null, 23394); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23395 | break; |
| 23396 | case wasm::ValType::F32: |
| 23397 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23397); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Float32" ")"); do { MOZ_CrashSequence(__null, 23397); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23398 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 23398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnFloat32Reg" ")"); do { MOZ_CrashSequence(__null, 23398); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23399 | break; |
| 23400 | case wasm::ValType::F64: |
| 23401 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23401); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Double" ")"); do { MOZ_CrashSequence(__null, 23401); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23402 | 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", "./../../../../js/src/jit/CodeGenerator.cpp" , 23402); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ToFloatRegister(lir->output()) == ReturnDoubleReg" ")"); do { MOZ_CrashSequence(__null, 23402); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23403 | break; |
| 23404 | case wasm::ValType::V128: |
| 23405 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23405); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected return type when calling from ion to wasm" ")"); do { MOZ_CrashSequence(__null, 23405); __attribute__(( nomerge)) ::abort(); } while (false); } while (false); |
| 23406 | case wasm::ValType::Ref: |
| 23407 | // The wasm stubs layer unboxes anything that needs to be unboxed |
| 23408 | // and leaves it in a Value. A FuncRef/EqRef we could in principle |
| 23409 | // leave it as a raw object pointer but for now it complicates the |
| 23410 | // API to do so. |
| 23411 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23411); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "lir->mir()->type() == MIRType::Value" ")"); do { MOZ_CrashSequence(__null, 23411); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23412 | break; |
| 23413 | } |
| 23414 | } |
| 23415 | |
| 23416 | WasmInstanceObject* instObj = lir->mir()->instanceObject(); |
| 23417 | |
| 23418 | Register scratch = ToRegister(lir->temp()); |
| 23419 | |
| 23420 | uint32_t callOffset; |
| 23421 | ensureOsiSpace(); |
| 23422 | GenerateDirectCallFromJit(masm, funcExport, instObj->instance(), stackArgs, |
| 23423 | scratch, &callOffset); |
| 23424 | |
| 23425 | // Add the instance object to the constant pool, so it is transferred to |
| 23426 | // the owning IonScript and so that it gets traced as long as the IonScript |
| 23427 | // lives. |
| 23428 | |
| 23429 | uint32_t unused; |
| 23430 | masm.propagateOOM(graph.addConstantToPool(ObjectValue(*instObj), &unused)); |
| 23431 | |
| 23432 | markSafepointAt(callOffset, lir); |
| 23433 | } |
| 23434 | |
| 23435 | void CodeGenerator::visitIonToWasmCall(LIonToWasmCall* lir) { |
| 23436 | emitIonToWasmCallBase(lir); |
| 23437 | } |
| 23438 | void CodeGenerator::visitIonToWasmCallV(LIonToWasmCallV* lir) { |
| 23439 | emitIonToWasmCallBase(lir); |
| 23440 | } |
| 23441 | void CodeGenerator::visitIonToWasmCallI64(LIonToWasmCallI64* lir) { |
| 23442 | emitIonToWasmCallBase(lir); |
| 23443 | } |
| 23444 | |
| 23445 | void CodeGenerator::visitWasmNullConstant(LWasmNullConstant* lir) { |
| 23446 | masm.xorPtr(ToRegister(lir->output()), ToRegister(lir->output())); |
| 23447 | } |
| 23448 | |
| 23449 | void CodeGenerator::visitWasmFence(LWasmFence* lir) { |
| 23450 | 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()" , "./../../../../js/src/jit/CodeGenerator.cpp", 23450); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "gen->compilingWasm()" ")"); do { MOZ_CrashSequence (__null, 23450); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 23451 | masm.memoryBarrier(MemoryBarrier::Full()); |
| 23452 | } |
| 23453 | |
| 23454 | void CodeGenerator::visitWasmAnyRefFromJSValue(LWasmAnyRefFromJSValue* lir) { |
| 23455 | ValueOperand input = ToValue(lir->def()); |
| 23456 | ValueOperand temp = ToValue(lir->temp1()); |
| 23457 | Register output = ToRegister(lir->output()); |
| 23458 | FloatRegister tempFloat = ToFloatRegister(lir->temp0()); |
| 23459 | |
| 23460 | using Fn = JSObject* (*)(JSContext * cx, HandleValue value); |
| 23461 | OutOfLineCode* oolBoxValue = oolCallVM<Fn, wasm::AnyRef::boxValue>( |
| 23462 | lir, ArgList(temp), StoreRegisterTo(output)); |
| 23463 | |
| 23464 | // If the value is a double that is a negative denormal and denormals |
| 23465 | // are disabled, then `convertValueToWasmAnyRef` will view |
| 23466 | // it as '-0' (which must be boxed in the OOL path). However, the |
| 23467 | // AnyRef boxing code uses `mozilla::NumberIsInt32` which does not |
| 23468 | // properly handle the CPU DAZ/FTZ flags and asserts that the value doesn't |
| 23469 | // actually need to be boxed. |
| 23470 | // |
| 23471 | // Making `mozilla::NumberIsInt32` handle the CPU DAZ/FTZ flags would |
| 23472 | // add a significant cost to many hot-paths. We instead just |
| 23473 | // eagerly canonicalize denormals to +-0.0 here to avoid inconsistent |
| 23474 | // results (see Bug 1971519). |
| 23475 | masm.moveValue(input, temp); |
| 23476 | masm.canonicalizeValueZero(temp, tempFloat); |
| 23477 | |
| 23478 | masm.convertValueToWasmAnyRef(temp, output, tempFloat, oolBoxValue->entry()); |
| 23479 | masm.bind(oolBoxValue->rejoin()); |
| 23480 | } |
| 23481 | |
| 23482 | void CodeGenerator::visitWasmAnyRefFromJSObject(LWasmAnyRefFromJSObject* lir) { |
| 23483 | Register input = ToRegister(lir->def()); |
| 23484 | Register output = ToRegister(lir->output()); |
| 23485 | masm.convertObjectToWasmAnyRef(input, output); |
| 23486 | } |
| 23487 | |
| 23488 | void CodeGenerator::visitWasmAnyRefFromJSString(LWasmAnyRefFromJSString* lir) { |
| 23489 | Register input = ToRegister(lir->def()); |
| 23490 | Register output = ToRegister(lir->output()); |
| 23491 | masm.convertStringToWasmAnyRef(input, output); |
| 23492 | } |
| 23493 | |
| 23494 | void CodeGenerator::visitWasmAnyRefIsJSString(LWasmAnyRefIsJSString* lir) { |
| 23495 | Register input = ToRegister(lir->input()); |
| 23496 | Register output = ToRegister(lir->output()); |
| 23497 | Register temp = ToRegister(lir->temp0()); |
| 23498 | Label fallthrough; |
| 23499 | Label isJSString; |
| 23500 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
| 23501 | masm.move32(Imm32(0), output); |
| 23502 | masm.jump(&fallthrough); |
| 23503 | masm.bind(&isJSString); |
| 23504 | masm.move32(Imm32(1), output); |
| 23505 | masm.bind(&fallthrough); |
| 23506 | } |
| 23507 | |
| 23508 | void CodeGenerator::visitWasmTrapIfAnyRefIsNotJSString( |
| 23509 | LWasmTrapIfAnyRefIsNotJSString* lir) { |
| 23510 | Register input = ToRegister(lir->input()); |
| 23511 | Register temp = ToRegister(lir->temp0()); |
| 23512 | Label isJSString; |
| 23513 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
| 23514 | masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc()); |
| 23515 | masm.bind(&isJSString); |
| 23516 | } |
| 23517 | |
| 23518 | void CodeGenerator::visitWasmAnyRefJSStringLength( |
| 23519 | LWasmAnyRefJSStringLength* lir) { |
| 23520 | Register input = ToRegister(lir->input()); |
| 23521 | Register output = ToRegister(lir->output()); |
| 23522 | Register temp = ToRegister(lir->temp0()); |
| 23523 | Label isJSString; |
| 23524 | masm.branchWasmAnyRefIsJSString(true, input, temp, &isJSString); |
| 23525 | masm.wasmTrap(lir->mir()->trap(), lir->mir()->trapSiteDesc()); |
| 23526 | masm.bind(&isJSString); |
| 23527 | masm.untagWasmAnyRef(input, temp, wasm::AnyRefTag::String); |
| 23528 | masm.loadStringLength(temp, output); |
| 23529 | } |
| 23530 | |
| 23531 | void CodeGenerator::visitWasmNewI31Ref(LWasmNewI31Ref* lir) { |
| 23532 | if (lir->value()->isConstant()) { |
| 23533 | // i31ref are often created with constants. If that's the case we will |
| 23534 | // do the operation statically here. This is similar to what is done |
| 23535 | // in masm.truncate32ToWasmI31Ref. |
| 23536 | Register output = ToRegister(lir->output()); |
| 23537 | uint32_t value = |
| 23538 | static_cast<uint32_t>(lir->value()->toConstant()->toInt32()); |
| 23539 | uintptr_t ptr = wasm::AnyRef::fromUint32Truncate(value).rawValue(); |
| 23540 | masm.movePtr(ImmWord(ptr), output); |
| 23541 | } else { |
| 23542 | Register value = ToRegister(lir->value()); |
| 23543 | Register output = ToRegister(lir->output()); |
| 23544 | masm.truncate32ToWasmI31Ref(value, output); |
| 23545 | } |
| 23546 | } |
| 23547 | |
| 23548 | void CodeGenerator::visitWasmI31RefGet(LWasmI31RefGet* lir) { |
| 23549 | Register value = ToRegister(lir->input()); |
| 23550 | Register output = ToRegister(lir->output()); |
| 23551 | if (lir->mir()->wideningOp() == wasm::FieldWideningOp::Signed) { |
| 23552 | masm.convertWasmI31RefTo32Signed(value, output); |
| 23553 | } else { |
| 23554 | masm.convertWasmI31RefTo32Unsigned(value, output); |
| 23555 | } |
| 23556 | } |
| 23557 | |
| 23558 | #ifdef JS_64BIT1 |
| 23559 | void CodeGenerator::visitWasmAddSubI128HI64(LWasmAddSubI128HI64* lir) { |
| 23560 | Register lhsLo = ToRegister(lir->lhsLo()); |
| 23561 | Register lhsHi = ToRegister(lir->lhsHi()); |
| 23562 | Register rhsLo = ToRegister(lir->rhsLo()); |
| 23563 | Register rhsHi = ToRegister(lir->rhsHi()); |
| 23564 | Register output = ToRegister(lir->output()); |
| 23565 | // This holds because all 4 operands are non-AtStart variants. |
| 23566 | MOZ_ASSERT(output != lhsLo && output != lhsHi && output != rhsLo &&do { static_assert( mozilla::detail::AssertionConditionType< decltype(output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi" , "./../../../../js/src/jit/CodeGenerator.cpp", 23567); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi" ")"); do { MOZ_CrashSequence(__null, 23567); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false) |
| 23567 | output != rhsHi)do { static_assert( mozilla::detail::AssertionConditionType< decltype(output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi ))), 0))) { do { } while (false); MOZ_ReportAssertionFailure( "output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi" , "./../../../../js/src/jit/CodeGenerator.cpp", 23567); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "output != lhsLo && output != lhsHi && output != rhsLo && output != rhsHi" ")"); do { MOZ_CrashSequence(__null, 23567); __attribute__(( nomerge)) ::abort(); } while (false); } } while (false); |
| 23568 | masm.wasmAddSubI128HI64(lhsLo, lhsHi, rhsLo, rhsHi, output, lir->isAdd()); |
| 23569 | } |
| 23570 | #endif |
| 23571 | |
| 23572 | #ifndef JS_64BIT1 |
| 23573 | void CodeGenerator::visitWasmLoadInstanceScratch2xI32( |
| 23574 | LWasmLoadInstanceScratch2xI32* lir) { |
| 23575 | Register64 output = ToOutRegister64(lir); |
| 23576 | Register instance = ToRegister(lir->instance()); |
| 23577 | uint32_t offset = |
| 23578 | wasm::Instance::offsetofBaselineScratchWords() + lir->byteOffset(); |
| 23579 | masm.loadPtr(Address(instance, offset + 0), output.low); |
| 23580 | masm.loadPtr(Address(instance, offset + 4), output.high); |
| 23581 | } |
| 23582 | |
| 23583 | void CodeGenerator::visitWasmStoreInstanceScratch2xI32( |
| 23584 | LWasmStoreInstanceScratch2xI32* lir) { |
| 23585 | Register64 value = ToRegister64(lir->value()); |
| 23586 | Register instance = ToRegister(lir->instance()); |
| 23587 | uint32_t offset = |
| 23588 | wasm::Instance::offsetofBaselineScratchWords() + lir->byteOffset(); |
| 23589 | masm.storePtr(value.low, Address(instance, offset + 0)); |
| 23590 | masm.storePtr(value.high, Address(instance, offset + 4)); |
| 23591 | } |
| 23592 | #endif |
| 23593 | |
| 23594 | void CodeGenerator::visitAddDisposableResource(LAddDisposableResource* lir) { |
| 23595 | Register environment = ToRegister(lir->environment()); |
| 23596 | ValueOperand resource = ToValue(lir->resource()); |
| 23597 | ValueOperand method = ToValue(lir->method()); |
| 23598 | Register needsClosure = ToRegister(lir->needsClosure()); |
| 23599 | uint8_t hint = lir->mir()->hint(); |
| 23600 | |
| 23601 | pushArg(Imm32(hint)); |
| 23602 | pushArg(needsClosure); |
| 23603 | pushArg(method); |
| 23604 | pushArg(resource); |
| 23605 | pushArg(environment); |
| 23606 | |
| 23607 | using Fn = bool (*)(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, |
| 23608 | JS::Handle<JS::Value>, bool, UsingHint); |
| 23609 | callVM<Fn, js::AddDisposableResourceToCapability>(lir); |
| 23610 | } |
| 23611 | |
| 23612 | void CodeGenerator::visitTakeDisposeCapability(LTakeDisposeCapability* lir) { |
| 23613 | Register environment = ToRegister(lir->environment()); |
| 23614 | ValueOperand output = ToOutValue(lir); |
| 23615 | |
| 23616 | Address capabilityAddr( |
| 23617 | environment, DisposableEnvironmentObject::offsetOfDisposeCapability()); |
| 23618 | emitPreBarrier(capabilityAddr); |
| 23619 | masm.loadValue(capabilityAddr, output); |
| 23620 | masm.storeValue(JS::UndefinedValue(), capabilityAddr); |
| 23621 | } |
| 23622 | |
| 23623 | #ifdef FUZZING_JS_FUZZILLI |
| 23624 | void CodeGenerator::emitFuzzilliHashObject(LInstruction* lir, Register obj, |
| 23625 | Register output) { |
| 23626 | using Fn = void (*)(JSContext* cx, JSObject* obj, uint32_t* out); |
| 23627 | OutOfLineCode* ool = oolCallVM<Fn, FuzzilliHashObjectInl>( |
| 23628 | lir, ArgList(obj), StoreRegisterTo(output)); |
| 23629 | |
| 23630 | masm.jump(ool->entry()); |
| 23631 | masm.bind(ool->rejoin()); |
| 23632 | } |
| 23633 | |
| 23634 | void CodeGenerator::emitFuzzilliHashBigInt(LInstruction* lir, Register bigInt, |
| 23635 | Register output) { |
| 23636 | LiveRegisterSet volatileRegs = liveVolatileRegs(lir); |
| 23637 | volatileRegs.takeUnchecked(output); |
| 23638 | |
| 23639 | masm.PushRegsInMask(volatileRegs); |
| 23640 | |
| 23641 | using Fn = uint32_t (*)(BigInt* bigInt); |
| 23642 | masm.setupUnalignedABICall(output); |
| 23643 | masm.passABIArg(bigInt); |
| 23644 | masm.callWithABI<Fn, js::FuzzilliHashBigInt>(); |
| 23645 | masm.storeCallInt32Result(output); |
| 23646 | |
| 23647 | masm.PopRegsInMask(volatileRegs); |
| 23648 | } |
| 23649 | |
| 23650 | void CodeGenerator::visitFuzzilliHashV(LFuzzilliHashV* ins) { |
| 23651 | ValueOperand value = ToValue(ins->value()); |
| 23652 | |
| 23653 | FloatRegister scratchFloat = ToFloatRegister(ins->temp1()); |
| 23654 | Register scratch = ToRegister(ins->temp0()); |
| 23655 | Register output = ToRegister(ins->output()); |
| 23656 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23656); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "scratch != output" ")"); do { MOZ_CrashSequence (__null, 23656); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 23657 | |
| 23658 | Label hashDouble, done; |
| 23659 | |
| 23660 | Label isInt32, isDouble, isNull, isUndefined, isBoolean, isBigInt, isObject; |
| 23661 | { |
| 23662 | ScratchTagScope tag(masm, value); |
| 23663 | masm.splitTagForTest(value, tag); |
| 23664 | |
| 23665 | masm.branchTestInt32(Assembler::Equal, tag, &isInt32); |
| 23666 | masm.branchTestDouble(Assembler::Equal, tag, &isDouble); |
| 23667 | masm.branchTestNull(Assembler::Equal, tag, &isNull); |
| 23668 | masm.branchTestUndefined(Assembler::Equal, tag, &isUndefined); |
| 23669 | masm.branchTestBoolean(Assembler::Equal, tag, &isBoolean); |
| 23670 | masm.branchTestBigInt(Assembler::Equal, tag, &isBigInt); |
| 23671 | masm.branchTestObject(Assembler::Equal, tag, &isObject); |
| 23672 | |
| 23673 | // Symbol or String. |
| 23674 | masm.move32(Imm32(0), output); |
| 23675 | masm.jump(&done); |
| 23676 | } |
| 23677 | |
| 23678 | masm.bind(&isInt32); |
| 23679 | { |
| 23680 | masm.unboxInt32(value, scratch); |
| 23681 | masm.convertInt32ToDouble(scratch, scratchFloat); |
| 23682 | masm.jump(&hashDouble); |
| 23683 | } |
| 23684 | |
| 23685 | masm.bind(&isDouble); |
| 23686 | { |
| 23687 | masm.unboxDouble(value, scratchFloat); |
| 23688 | masm.jump(&hashDouble); |
| 23689 | } |
| 23690 | |
| 23691 | masm.bind(&isNull); |
| 23692 | { |
| 23693 | masm.loadConstantDouble(1.0, scratchFloat); |
| 23694 | masm.jump(&hashDouble); |
| 23695 | } |
| 23696 | |
| 23697 | masm.bind(&isUndefined); |
| 23698 | { |
| 23699 | masm.loadConstantDouble(2.0, scratchFloat); |
| 23700 | masm.jump(&hashDouble); |
| 23701 | } |
| 23702 | |
| 23703 | masm.bind(&isBoolean); |
| 23704 | { |
| 23705 | masm.unboxBoolean(value, scratch); |
| 23706 | masm.add32(Imm32(3), scratch); |
| 23707 | masm.convertInt32ToDouble(scratch, scratchFloat); |
| 23708 | masm.jump(&hashDouble); |
| 23709 | } |
| 23710 | |
| 23711 | masm.bind(&isBigInt); |
| 23712 | { |
| 23713 | masm.unboxBigInt(value, scratch); |
| 23714 | emitFuzzilliHashBigInt(ins, scratch, output); |
| 23715 | masm.jump(&done); |
| 23716 | } |
| 23717 | |
| 23718 | masm.bind(&isObject); |
| 23719 | { |
| 23720 | masm.unboxObject(value, scratch); |
| 23721 | emitFuzzilliHashObject(ins, scratch, output); |
| 23722 | masm.jump(&done); |
| 23723 | } |
| 23724 | |
| 23725 | masm.bind(&hashDouble); |
| 23726 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 23727 | |
| 23728 | masm.bind(&done); |
| 23729 | } |
| 23730 | |
| 23731 | void CodeGenerator::visitFuzzilliHashT(LFuzzilliHashT* ins) { |
| 23732 | const LAllocation* value = ins->value(); |
| 23733 | MIRType mirType = ins->mir()->getOperand(0)->type(); |
| 23734 | |
| 23735 | Register scratch = ToTempRegisterOrInvalid(ins->temp0()); |
| 23736 | FloatRegister scratchFloat = ToTempFloatRegisterOrInvalid(ins->temp1()); |
| 23737 | |
| 23738 | Register output = ToRegister(ins->output()); |
| 23739 | 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" , "./../../../../js/src/jit/CodeGenerator.cpp", 23739); AnnotateMozCrashReason ("MOZ_ASSERT" "(" "scratch != output" ")"); do { MOZ_CrashSequence (__null, 23739); __attribute__((nomerge)) ::abort(); } while ( false); } } while (false); |
| 23740 | |
| 23741 | switch (mirType) { |
| 23742 | case MIRType::Undefined: { |
| 23743 | masm.loadConstantDouble(2.0, scratchFloat); |
| 23744 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 23745 | break; |
| 23746 | } |
| 23747 | |
| 23748 | case MIRType::Null: { |
| 23749 | masm.loadConstantDouble(1.0, scratchFloat); |
| 23750 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 23751 | break; |
| 23752 | } |
| 23753 | |
| 23754 | case MIRType::Int32: { |
| 23755 | masm.move32(ToRegister(value), scratch); |
| 23756 | masm.convertInt32ToDouble(scratch, scratchFloat); |
| 23757 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 23758 | break; |
| 23759 | } |
| 23760 | |
| 23761 | case MIRType::Double: { |
| 23762 | masm.moveDouble(ToFloatRegister(value), scratchFloat); |
| 23763 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 23764 | break; |
| 23765 | } |
| 23766 | |
| 23767 | case MIRType::Float32: { |
| 23768 | masm.convertFloat32ToDouble(ToFloatRegister(value), scratchFloat); |
| 23769 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 23770 | break; |
| 23771 | } |
| 23772 | |
| 23773 | case MIRType::Boolean: { |
| 23774 | masm.add32(Imm32(3), ToRegister(value), scratch); |
| 23775 | masm.convertInt32ToDouble(scratch, scratchFloat); |
| 23776 | masm.fuzzilliHashDouble(scratchFloat, output, scratch); |
| 23777 | break; |
| 23778 | } |
| 23779 | |
| 23780 | case MIRType::BigInt: { |
| 23781 | emitFuzzilliHashBigInt(ins, ToRegister(value), output); |
| 23782 | break; |
| 23783 | } |
| 23784 | |
| 23785 | case MIRType::Object: { |
| 23786 | emitFuzzilliHashObject(ins, ToRegister(value), output); |
| 23787 | break; |
| 23788 | } |
| 23789 | |
| 23790 | default: |
| 23791 | MOZ_CRASH("unexpected type")do { do { } while (false); MOZ_ReportCrash("" "unexpected type" , "./../../../../js/src/jit/CodeGenerator.cpp", 23791); AnnotateMozCrashReason ("MOZ_CRASH(" "unexpected type" ")"); do { MOZ_CrashSequence( __null, 23791); __attribute__((nomerge)) ::abort(); } while ( false); } while (false); |
| 23792 | } |
| 23793 | } |
| 23794 | |
| 23795 | void CodeGenerator::visitFuzzilliHashStore(LFuzzilliHashStore* ins) { |
| 23796 | Register value = ToRegister(ins->value()); |
| 23797 | Register temp0 = ToRegister(ins->temp0()); |
| 23798 | Register temp1 = ToRegister(ins->temp1()); |
| 23799 | |
| 23800 | masm.fuzzilliStoreHash(value, temp0, temp1); |
| 23801 | } |
| 23802 | #endif |
| 23803 | |
| 23804 | static_assert(!std::is_polymorphic_v<CodeGenerator>, |
| 23805 | "CodeGenerator should not have any virtual methods"); |
| 23806 | |
| 23807 | } // namespace jit |
| 23808 | } // namespace js |