Changeset 155820 in webkit for trunk/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
- Timestamp:
- Sep 15, 2013, 11:53:23 AM (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp
r155711 r155820 70 70 // starts mutating state before verifying the speculation it has already made. 71 71 72 GPRReg alreadyBoxed = InvalidGPRReg;73 74 72 if (recovery) { 75 73 switch (recovery->type()) { … … 77 75 m_jit.sub32(recovery->src(), recovery->dest()); 78 76 m_jit.or64(GPRInfo::tagTypeNumberRegister, recovery->dest()); 79 alreadyBoxed = recovery->dest();80 77 break; 81 78 … … 150 147 } 151 148 } 152 153 // 4) Figure out how many scratch slots we'll need. We need one for every GPR/FPR 154 // whose destination is now occupied by a DFG virtual register, and we need 155 // one for every displaced virtual register if there are more than 156 // GPRInfo::numberOfRegisters of them. Also see if there are any constants, 157 // any undefined slots, any FPR slots, and any unboxed ints. 158 159 Vector<bool> poisonedVirtualRegisters(operands.numberOfLocals()); 160 for (unsigned i = 0; i < poisonedVirtualRegisters.size(); ++i) 161 poisonedVirtualRegisters[i] = false; 162 163 unsigned numberOfPoisonedVirtualRegisters = 0; 164 unsigned numberOfDisplacedVirtualRegisters = 0; 165 166 // Booleans for fast checks. We expect that most OSR exits do not have to rebox 167 // Int32s, have no FPRs, and have no constants. If there are constants, we 168 // expect most of them to be jsUndefined(); if that's true then we handle that 169 // specially to minimize code size and execution time. 170 bool haveUnboxedInt32s = false; 171 bool haveUnboxedDoubles = false; 172 bool haveFPRs = false; 173 bool haveConstants = false; 174 bool haveUndefined = false; 175 bool haveUInt32s = false; 176 bool haveArguments = false; 149 150 // What follows is an intentionally simple OSR exit implementation that generates 151 // fairly poor code but is very easy to hack. In particular, it dumps all state that 152 // needs conversion into a scratch buffer so that in step 6, where we actually do the 153 // conversions, we know that all temp registers are free to use and the variable is 154 // definitely in a well-known spot in the scratch buffer regardless of whether it had 155 // originally been in a register or spilled. This allows us to decouple "where was 156 // the variable" from "how was it represented". Consider that the 157 // AlreadyInJSStackAsUnboxedInt32 recovery: it tells us that the value is in a 158 // particular place and that that place holds an unboxed int32. We have three different 159 // places that a value could be (stack, displaced, register) and a bunch of different 160 // ways of representing a value. The number of recoveries is three * a bunch. The code 161 // below means that we have to have three + a bunch cases rather than three * a bunch. 162 // Once we have loaded the value from wherever it was, the reboxing is the same 163 // regardless of its location. Likewise, before we do the reboxing, the way we get to 164 // the value (i.e. where we load it from) is the same regardless of its type. Because 165 // the code below always dumps everything into a scratch buffer first, the two 166 // questions become orthogonal, which simplifies adding new types and adding new 167 // locations. 168 // 169 // This raises the question: does using such a suboptimal implementation of OSR exit, 170 // where we always emit code to dump all state into a scratch buffer only to then 171 // dump it right back into the stack, hurt us in any way? The asnwer is that OSR exits 172 // are rare. Our tiering strategy ensures this. This is because if an OSR exit is 173 // taken more than ~100 times, we jettison the DFG code block along with all of its 174 // exits. It is impossible for an OSR exit - i.e. the code we compile below - to 175 // execute frequently enough for the codegen to matter that much. It probably matters 176 // enough that we don't want to turn this into some super-slow function call, but so 177 // long as we're generating straight-line code, that code can be pretty bad. Also 178 // because we tend to exit only along one OSR exit from any DFG code block - that's an 179 // empirical result that we're extremely confident about - the code size of this 180 // doesn't matter much. Hence any attempt to optimize the codegen here is just purely 181 // harmful to the system: it probably won't reduce either net memory usage or net 182 // execution time. It will only prevent us from cleanly decoupling "where was the 183 // variable" from "how was it represented", which will make it more difficult to add 184 // features in the future and it will make it harder to reason about bugs. 185 186 // 4) Save all state from GPRs into the scratch buffer. 187 188 ScratchBuffer* scratchBuffer = m_jit.vm()->scratchBufferForSize(sizeof(EncodedJSValue) * operands.size()); 189 EncodedJSValue* scratch = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0; 177 190 178 191 for (size_t index = 0; index < operands.size(); ++index) { 179 192 const ValueRecovery& recovery = operands[index]; 180 switch (recovery.technique()) { 181 case Int32DisplacedInJSStack: 182 case DoubleDisplacedInJSStack: 183 case DisplacedInJSStack: { 184 numberOfDisplacedVirtualRegisters++; 185 ASSERT(operandIsLocal(recovery.virtualRegister())); 186 187 // See if we might like to store to this virtual register before doing 188 // virtual register shuffling. If so, we say that the virtual register 189 // is poisoned: it cannot be stored to until after displaced virtual 190 // registers are handled. We track poisoned virtual register carefully 191 // to ensure this happens efficiently. Note that we expect this case 192 // to be rare, so the handling of it is optimized for the cases in 193 // which it does not happen. 194 int local = operandToLocal(recovery.virtualRegister()); 195 if (local < (int)operands.numberOfLocals()) { 196 switch (operands.local(local).technique()) { 197 case InGPR: 198 case UnboxedInt32InGPR: 199 case UInt32InGPR: 200 case InFPR: 201 if (!poisonedVirtualRegisters[local]) { 202 poisonedVirtualRegisters[local] = true; 203 numberOfPoisonedVirtualRegisters++; 204 } 205 break; 206 default: 207 break; 208 } 209 } 210 break; 211 } 212 case UnboxedInt32InGPR: 213 case AlreadyInJSStackAsUnboxedInt32: 214 haveUnboxedInt32s = true; 215 break; 216 217 case AlreadyInJSStackAsUnboxedDouble: 218 haveUnboxedDoubles = true; 219 break; 220 221 case UInt32InGPR: 222 haveUInt32s = true; 223 break; 224 225 case InFPR: 226 haveFPRs = true; 227 break; 228 229 case Constant: 230 haveConstants = true; 231 if (recovery.constant().isUndefined()) 232 haveUndefined = true; 233 break; 234 235 case ArgumentsThatWereNotCreated: 236 haveArguments = true; 237 break; 238 239 default: 240 break; 241 } 242 } 243 244 #if DFG_ENABLE(DEBUG_VERBOSE) 245 dataLogF(" "); 246 if (numberOfPoisonedVirtualRegisters) 247 dataLogF("Poisoned=%u ", numberOfPoisonedVirtualRegisters); 248 if (numberOfDisplacedVirtualRegisters) 249 dataLogF("Displaced=%u ", numberOfDisplacedVirtualRegisters); 250 if (haveUnboxedInt32s) 251 dataLogF("UnboxedInt32 "); 252 if (haveUnboxedDoubles) 253 dataLogF("UnboxedDoubles "); 254 if (haveUInt32s) 255 dataLogF("UInt32 "); 256 if (haveFPRs) 257 dataLogF("FPR "); 258 if (haveConstants) 259 dataLogF("Constants "); 260 if (haveUndefined) 261 dataLogF("Undefined "); 262 dataLogF(" "); 263 #endif 264 265 ScratchBuffer* scratchBuffer = m_jit.vm()->scratchBufferForSize(sizeof(EncodedJSValue) * std::max(haveUInt32s ? 2u : 0u, numberOfPoisonedVirtualRegisters + (numberOfDisplacedVirtualRegisters <= GPRInfo::numberOfRegisters ? 0 : numberOfDisplacedVirtualRegisters))); 266 EncodedJSValue* scratchDataBuffer = scratchBuffer ? static_cast<EncodedJSValue*>(scratchBuffer->dataBuffer()) : 0; 267 268 // From here on, the code assumes that it is profitable to maximize the distance 269 // between when something is computed and when it is stored. 270 271 // 5) Perform all reboxing of integers. 272 273 if (haveUnboxedInt32s || haveUInt32s) { 274 for (size_t index = 0; index < operands.size(); ++index) { 275 const ValueRecovery& recovery = operands[index]; 276 switch (recovery.technique()) { 277 case UnboxedInt32InGPR: 278 if (recovery.gpr() != alreadyBoxed) 279 m_jit.or64(GPRInfo::tagTypeNumberRegister, recovery.gpr()); 280 break; 281 282 case AlreadyInJSStackAsUnboxedInt32: 283 m_jit.store32(AssemblyHelpers::TrustedImm32(static_cast<uint32_t>(TagTypeNumber >> 32)), AssemblyHelpers::tagFor(static_cast<VirtualRegister>(operands.operandForIndex(index)))); 284 break; 285 286 case UInt32InGPR: { 287 // This occurs when the speculative JIT left an unsigned 32-bit integer 288 // in a GPR. If it's positive, we can just box the int. Otherwise we 289 // need to turn it into a boxed double. 290 291 // We don't try to be clever with register allocation here; we assume 292 // that the program is using FPRs and we don't try to figure out which 293 // ones it is using. Instead just temporarily save fpRegT0 and then 294 // restore it. This makes sense because this path is not cheap to begin 295 // with, and should happen very rarely. 296 297 GPRReg addressGPR = GPRInfo::regT0; 298 if (addressGPR == recovery.gpr()) 299 addressGPR = GPRInfo::regT1; 300 301 m_jit.store64(addressGPR, scratchDataBuffer); 302 m_jit.move(AssemblyHelpers::TrustedImmPtr(scratchDataBuffer + 1), addressGPR); 303 m_jit.storeDouble(FPRInfo::fpRegT0, addressGPR); 304 305 AssemblyHelpers::Jump positive = m_jit.branch32(AssemblyHelpers::GreaterThanOrEqual, recovery.gpr(), AssemblyHelpers::TrustedImm32(0)); 306 307 m_jit.convertInt32ToDouble(recovery.gpr(), FPRInfo::fpRegT0); 308 m_jit.addDouble(AssemblyHelpers::AbsoluteAddress(&AssemblyHelpers::twoToThe32), FPRInfo::fpRegT0); 309 m_jit.boxDouble(FPRInfo::fpRegT0, recovery.gpr()); 310 311 AssemblyHelpers::Jump done = m_jit.jump(); 312 313 positive.link(&m_jit); 314 315 m_jit.or64(GPRInfo::tagTypeNumberRegister, recovery.gpr()); 316 317 done.link(&m_jit); 318 319 m_jit.loadDouble(addressGPR, FPRInfo::fpRegT0); 320 m_jit.load64(scratchDataBuffer, addressGPR); 321 break; 322 } 323 324 default: 325 break; 326 } 327 } 328 } 329 330 // 6) Dump all non-poisoned GPRs. For poisoned GPRs, save them into the scratch storage. 331 // Note that GPRs do not have a fast change (like haveFPRs) because we expect that 332 // most OSR failure points will have at least one GPR that needs to be dumped. 333 334 initializePoisoned(operands.numberOfLocals()); 335 unsigned currentPoisonIndex = 0; 336 337 for (size_t index = 0; index < operands.size(); ++index) { 338 const ValueRecovery& recovery = operands[index]; 339 int operand = operands.operandForIndex(index); 193 340 194 switch (recovery.technique()) { 341 195 case InGPR: 342 196 case UnboxedInt32InGPR: 343 197 case UInt32InGPR: 344 if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { 345 m_jit.store64(recovery.gpr(), scratchDataBuffer + currentPoisonIndex); 346 m_poisonScratchIndices[operands.variableForIndex(index)] = currentPoisonIndex; 347 currentPoisonIndex++; 348 } else 349 m_jit.store64(recovery.gpr(), AssemblyHelpers::addressFor((VirtualRegister)operand)); 350 break; 198 m_jit.store64(recovery.gpr(), scratch + index); 199 break; 200 351 201 default: 352 202 break; … … 354 204 } 355 205 356 // At this point all GPRs are available for scratch use. 357 358 if (haveFPRs) { 359 // 7) Box all doubles (relies on there being more GPRs than FPRs) 360 361 for (size_t index = 0; index < operands.size(); ++index) { 362 const ValueRecovery& recovery = operands[index]; 363 if (recovery.technique() != InFPR) 364 continue; 365 FPRReg fpr = recovery.fpr(); 366 GPRReg gpr = GPRInfo::toRegister(FPRInfo::toIndex(fpr)); 367 m_jit.boxDouble(fpr, gpr); 368 } 369 370 // 8) Dump all doubles into the stack, or to the scratch storage if 371 // the destination virtual register is poisoned. 372 373 for (size_t index = 0; index < operands.size(); ++index) { 374 const ValueRecovery& recovery = operands[index]; 375 if (recovery.technique() != InFPR) 376 continue; 377 GPRReg gpr = GPRInfo::toRegister(FPRInfo::toIndex(recovery.fpr())); 378 if (operands.isVariable(index) && poisonedVirtualRegisters[operands.variableForIndex(index)]) { 379 m_jit.store64(gpr, scratchDataBuffer + currentPoisonIndex); 380 m_poisonScratchIndices[operands.variableForIndex(index)] = currentPoisonIndex; 381 currentPoisonIndex++; 382 } else 383 m_jit.store64(gpr, AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); 384 } 385 } 386 387 // At this point all GPRs and FPRs are available for scratch use. 388 389 // 9) Box all unboxed doubles in the stack. 390 if (haveUnboxedDoubles) { 391 for (size_t index = 0; index < operands.size(); ++index) { 392 const ValueRecovery& recovery = operands[index]; 393 if (recovery.technique() != AlreadyInJSStackAsUnboxedDouble) 394 continue; 395 m_jit.loadDouble(AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index)), FPRInfo::fpRegT0); 206 // And voila, all FPRs are free to reuse. 207 208 // 5) Save all state from FPRs into the scratch buffer. 209 210 for (size_t index = 0; index < operands.size(); ++index) { 211 const ValueRecovery& recovery = operands[index]; 212 213 switch (recovery.technique()) { 214 case InFPR: 215 m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0); 216 m_jit.storeDouble(recovery.fpr(), GPRInfo::regT0); 217 break; 218 219 default: 220 break; 221 } 222 } 223 224 // Now, all FPRs are also free. 225 226 // 5) Save all state from the stack into the scratch buffer. For simplicity we 227 // do this even for state that's already in the right place on the stack. 228 // It makes things simpler later. 229 230 for (size_t index = 0; index < operands.size(); ++index) { 231 const ValueRecovery& recovery = operands[index]; 232 int operand = operands.operandForIndex(index); 233 234 switch (recovery.technique()) { 235 case DisplacedInJSStack: 236 case Int32DisplacedInJSStack: 237 case DoubleDisplacedInJSStack: 238 m_jit.load64(AssemblyHelpers::addressFor(recovery.virtualRegister()), GPRInfo::regT0); 239 m_jit.store64(GPRInfo::regT0, scratch + index); 240 break; 241 242 case AlreadyInJSStackAsUnboxedInt32: 243 case AlreadyInJSStackAsUnboxedDouble: 244 m_jit.load64(AssemblyHelpers::addressFor(operand), GPRInfo::regT0); 245 m_jit.store64(GPRInfo::regT0, scratch + index); 246 break; 247 248 default: 249 break; 250 } 251 } 252 253 // 6) Do all data format conversions and store the results into the stack. 254 255 bool haveArguments = false; 256 257 for (size_t index = 0; index < operands.size(); ++index) { 258 const ValueRecovery& recovery = operands[index]; 259 int operand = operands.operandForIndex(index); 260 261 switch (recovery.technique()) { 262 case InGPR: 263 case DisplacedInJSStack: 264 m_jit.load64(scratch + index, GPRInfo::regT0); 265 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); 266 break; 267 268 case AlreadyInJSStackAsUnboxedInt32: 269 case UnboxedInt32InGPR: 270 case Int32DisplacedInJSStack: 271 m_jit.load64(scratch + index, GPRInfo::regT0); 272 m_jit.zeroExtend32ToPtr(GPRInfo::regT0, GPRInfo::regT0); 273 m_jit.or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0); 274 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); 275 break; 276 277 case AlreadyInJSStackAsUnboxedDouble: 278 case InFPR: 279 case DoubleDisplacedInJSStack: 280 m_jit.move(AssemblyHelpers::TrustedImmPtr(scratch + index), GPRInfo::regT0); 281 m_jit.loadDouble(GPRInfo::regT0, FPRInfo::fpRegT0); 396 282 m_jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0); 397 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); 398 } 399 } 400 401 ASSERT(currentPoisonIndex == numberOfPoisonedVirtualRegisters); 402 403 // 10) Reshuffle displaced virtual registers. Optimize for the case that 404 // the number of displaced virtual registers is not more than the number 405 // of available physical registers. 406 407 if (numberOfDisplacedVirtualRegisters) { 408 if (numberOfDisplacedVirtualRegisters <= GPRInfo::numberOfRegisters) { 409 // So far this appears to be the case that triggers all the time, but 410 // that is far from guaranteed. 411 412 unsigned displacementIndex = 0; 413 for (size_t index = 0; index < operands.size(); ++index) { 414 const ValueRecovery& recovery = operands[index]; 415 switch (recovery.technique()) { 416 case DisplacedInJSStack: 417 m_jit.load64(AssemblyHelpers::addressFor(recovery.virtualRegister()), GPRInfo::toRegister(displacementIndex++)); 418 break; 419 420 case Int32DisplacedInJSStack: { 421 GPRReg gpr = GPRInfo::toRegister(displacementIndex++); 422 m_jit.load32(AssemblyHelpers::addressFor(recovery.virtualRegister()), gpr); 423 m_jit.or64(GPRInfo::tagTypeNumberRegister, gpr); 424 break; 425 } 426 427 case DoubleDisplacedInJSStack: { 428 GPRReg gpr = GPRInfo::toRegister(displacementIndex++); 429 m_jit.load64(AssemblyHelpers::addressFor(recovery.virtualRegister()), gpr); 430 m_jit.sub64(GPRInfo::tagTypeNumberRegister, gpr); 431 break; 432 } 433 434 default: 435 break; 436 } 437 } 438 439 displacementIndex = 0; 440 for (size_t index = 0; index < operands.size(); ++index) { 441 const ValueRecovery& recovery = operands[index]; 442 switch (recovery.technique()) { 443 case DisplacedInJSStack: 444 case Int32DisplacedInJSStack: 445 case DoubleDisplacedInJSStack: 446 m_jit.store64(GPRInfo::toRegister(displacementIndex++), AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); 447 break; 448 449 default: 450 break; 451 } 452 } 453 } else { 454 // FIXME: This should use the shuffling algorithm that we use 455 // for speculative->non-speculative jumps, if we ever discover that 456 // some hot code with lots of live values that get displaced and 457 // spilled really enjoys frequently failing speculation. 458 459 // For now this code is engineered to be correct but probably not 460 // super. In particular, it correctly handles cases where for example 461 // the displacements are a permutation of the destination values, like 462 // 463 // 1 -> 2 464 // 2 -> 1 465 // 466 // It accomplishes this by simply lifting all of the virtual registers 467 // from their old (DFG JIT) locations and dropping them in a scratch 468 // location in memory, and then transferring from that scratch location 469 // to their new (old JIT) locations. 470 471 unsigned scratchIndex = numberOfPoisonedVirtualRegisters; 472 for (size_t index = 0; index < operands.size(); ++index) { 473 const ValueRecovery& recovery = operands[index]; 474 475 switch (recovery.technique()) { 476 case DisplacedInJSStack: 477 m_jit.load64(AssemblyHelpers::addressFor(recovery.virtualRegister()), GPRInfo::regT0); 478 m_jit.store64(GPRInfo::regT0, scratchDataBuffer + scratchIndex++); 479 break; 480 481 case Int32DisplacedInJSStack: { 482 m_jit.load32(AssemblyHelpers::addressFor(recovery.virtualRegister()), GPRInfo::regT0); 483 m_jit.or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0); 484 m_jit.store64(GPRInfo::regT0, scratchDataBuffer + scratchIndex++); 485 break; 486 } 487 488 case DoubleDisplacedInJSStack: { 489 m_jit.load64(AssemblyHelpers::addressFor(recovery.virtualRegister()), GPRInfo::regT0); 490 m_jit.sub64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0); 491 m_jit.store64(GPRInfo::regT0, scratchDataBuffer + scratchIndex++); 492 break; 493 } 494 495 default: 496 break; 497 } 498 } 499 500 scratchIndex = numberOfPoisonedVirtualRegisters; 501 for (size_t index = 0; index < operands.size(); ++index) { 502 const ValueRecovery& recovery = operands[index]; 503 switch (recovery.technique()) { 504 case DisplacedInJSStack: 505 case Int32DisplacedInJSStack: 506 case DoubleDisplacedInJSStack: 507 m_jit.load64(scratchDataBuffer + scratchIndex++, GPRInfo::regT0); 508 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); 509 break; 510 511 default: 512 break; 513 } 514 } 515 516 ASSERT(scratchIndex == numberOfPoisonedVirtualRegisters + numberOfDisplacedVirtualRegisters); 517 } 518 } 519 520 // 11) Dump all poisoned virtual registers. 521 522 if (numberOfPoisonedVirtualRegisters) { 523 for (int virtualRegister = 0; virtualRegister < (int)operands.numberOfLocals(); ++virtualRegister) { 524 if (!poisonedVirtualRegisters[virtualRegister]) 525 continue; 526 527 const ValueRecovery& recovery = operands.local(virtualRegister); 528 switch (recovery.technique()) { 529 case InGPR: 530 case UnboxedInt32InGPR: 531 case UInt32InGPR: 532 case InFPR: 533 m_jit.load64(scratchDataBuffer + poisonIndex(virtualRegister), GPRInfo::regT0); 534 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)localToOperand(virtualRegister))); 535 break; 536 537 default: 538 break; 539 } 540 } 541 } 542 543 // 12) Dump all constants. Optimize for Undefined, since that's a constant we see 544 // often. 545 546 if (haveConstants) { 547 if (haveUndefined) 548 m_jit.move(AssemblyHelpers::TrustedImm64(JSValue::encode(jsUndefined())), GPRInfo::regT0); 549 550 for (size_t index = 0; index < operands.size(); ++index) { 551 const ValueRecovery& recovery = operands[index]; 552 if (recovery.technique() != Constant) 553 continue; 554 if (recovery.constant().isUndefined()) 555 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); 556 else 557 m_jit.store64(AssemblyHelpers::TrustedImm64(JSValue::encode(recovery.constant())), AssemblyHelpers::addressFor((VirtualRegister)operands.operandForIndex(index))); 558 } 559 } 560 561 // 13) Adjust the old JIT's execute counter. Since we are exiting OSR, we know 562 // that all new calls into this code will go to the new JIT, so the execute 563 // counter only affects call frames that performed OSR exit and call frames 564 // that were still executing the old JIT at the time of another call frame's 565 // OSR exit. We want to ensure that the following is true: 566 // 567 // (a) Code the performs an OSR exit gets a chance to reenter optimized 568 // code eventually, since optimized code is faster. But we don't 569 // want to do such reentery too aggressively (see (c) below). 570 // 571 // (b) If there is code on the call stack that is still running the old 572 // JIT's code and has never OSR'd, then it should get a chance to 573 // perform OSR entry despite the fact that we've exited. 574 // 575 // (c) Code the performs an OSR exit should not immediately retry OSR 576 // entry, since both forms of OSR are expensive. OSR entry is 577 // particularly expensive. 578 // 579 // (d) Frequent OSR failures, even those that do not result in the code 580 // running in a hot loop, result in recompilation getting triggered. 581 // 582 // To ensure (c), we'd like to set the execute counter to 583 // counterValueForOptimizeAfterWarmUp(). This seems like it would endanger 584 // (a) and (b), since then every OSR exit would delay the opportunity for 585 // every call frame to perform OSR entry. Essentially, if OSR exit happens 586 // frequently and the function has few loops, then the counter will never 587 // become non-negative and OSR entry will never be triggered. OSR entry 588 // will only happen if a loop gets hot in the old JIT, which does a pretty 589 // good job of ensuring (a) and (b). But that doesn't take care of (d), 590 // since each speculation failure would reset the execute counter. 591 // So we check here if the number of speculation failures is significantly 592 // larger than the number of successes (we want 90% success rate), and if 593 // there have been a large enough number of failures. If so, we set the 594 // counter to 0; otherwise we set the counter to 595 // counterValueForOptimizeAfterWarmUp(). 283 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); 284 break; 285 286 case UInt32InGPR: { 287 m_jit.load64(scratch + index, GPRInfo::regT0); 288 m_jit.zeroExtend32ToPtr(GPRInfo::regT0, GPRInfo::regT0); 289 AssemblyHelpers::Jump positive = m_jit.branch32( 290 AssemblyHelpers::GreaterThanOrEqual, 291 GPRInfo::regT0, AssemblyHelpers::TrustedImm32(0)); 292 m_jit.convertInt32ToDouble(GPRInfo::regT0, FPRInfo::fpRegT0); 293 m_jit.addDouble( 294 AssemblyHelpers::AbsoluteAddress(&AssemblyHelpers::twoToThe32), 295 FPRInfo::fpRegT0); 296 m_jit.boxDouble(FPRInfo::fpRegT0, GPRInfo::regT0); 297 AssemblyHelpers::Jump done = m_jit.jump(); 298 positive.link(&m_jit); 299 m_jit.or64(GPRInfo::tagTypeNumberRegister, GPRInfo::regT0); 300 done.link(&m_jit); 301 m_jit.store64(GPRInfo::regT0, AssemblyHelpers::addressFor(operand)); 302 break; 303 } 304 305 case Constant: 306 m_jit.store64( 307 AssemblyHelpers::TrustedImm64(JSValue::encode(recovery.constant())), 308 AssemblyHelpers::addressFor(operand)); 309 break; 310 311 case ArgumentsThatWereNotCreated: 312 haveArguments = true; 313 break; 314 315 default: 316 break; 317 } 318 } 319 320 // 7) Adjust the old JIT's execute counter. Since we are exiting OSR, we know 321 // that all new calls into this code will go to the new JIT, so the execute 322 // counter only affects call frames that performed OSR exit and call frames 323 // that were still executing the old JIT at the time of another call frame's 324 // OSR exit. We want to ensure that the following is true: 325 // 326 // (a) Code the performs an OSR exit gets a chance to reenter optimized 327 // code eventually, since optimized code is faster. But we don't 328 // want to do such reentery too aggressively (see (c) below). 329 // 330 // (b) If there is code on the call stack that is still running the old 331 // JIT's code and has never OSR'd, then it should get a chance to 332 // perform OSR entry despite the fact that we've exited. 333 // 334 // (c) Code the performs an OSR exit should not immediately retry OSR 335 // entry, since both forms of OSR are expensive. OSR entry is 336 // particularly expensive. 337 // 338 // (d) Frequent OSR failures, even those that do not result in the code 339 // running in a hot loop, result in recompilation getting triggered. 340 // 341 // To ensure (c), we'd like to set the execute counter to 342 // counterValueForOptimizeAfterWarmUp(). This seems like it would endanger 343 // (a) and (b), since then every OSR exit would delay the opportunity for 344 // every call frame to perform OSR entry. Essentially, if OSR exit happens 345 // frequently and the function has few loops, then the counter will never 346 // become non-negative and OSR entry will never be triggered. OSR entry 347 // will only happen if a loop gets hot in the old JIT, which does a pretty 348 // good job of ensuring (a) and (b). But that doesn't take care of (d), 349 // since each speculation failure would reset the execute counter. 350 // So we check here if the number of speculation failures is significantly 351 // larger than the number of successes (we want 90% success rate), and if 352 // there have been a large enough number of failures. If so, we set the 353 // counter to 0; otherwise we set the counter to 354 // counterValueForOptimizeAfterWarmUp(). 596 355 597 356 handleExitCounts(m_jit, exit); 598 357 599 // 14) Reify inlined call frames.358 // 8) Reify inlined call frames. 600 359 601 360 reifyInlinedCallFrames(m_jit, exit); 602 361 603 // 15) Create arguments if necessary and place them into the appropriate aliased604 // 362 // 9) Create arguments if necessary and place them into the appropriate aliased 363 // registers. 605 364 606 365 if (haveArguments) { … … 652 411 } 653 412 654 // 1 6) Load the result of the last bytecode operation into regT0.413 // 10) Load the result of the last bytecode operation into regT0. 655 414 656 415 if (exit.m_lastSetOperand != std::numeric_limits<int>::max()) 657 416 m_jit.load64(AssemblyHelpers::addressFor(exit.m_lastSetOperand), GPRInfo::cachedResultRegister); 658 417 659 // 1 7) And finish.418 // 11) And finish. 660 419 661 420 adjustAndJumpToTarget(m_jit, exit);
Note:
See TracChangeset
for help on using the changeset viewer.