Ignore:
Timestamp:
Sep 15, 2013, 11:53:23 AM (12 years ago)
Author:
[email protected]
Message:

Deoptimize deoptimization: make DFGOSRExitCompiler64.cpp more hackable
https://bugs.webkit.org/show_bug.cgi?id=121374

Reviewed by Geoffrey Garen.

This reduces the size of DFGOSRExitCompiler64.cpp by almost 50%, and makes it
super easy to add new recovery kinds. For recoveries that involve reboxing, it
allows you to keep most of the code common between the on-stack and in-reg
cases: they all get funneled through the "load from scratch buffer, convert,
and then store to stack" logic.

This opens up a bunch of possibilities. It'll make adding Int48 much easier,
and it probably will come in handy as we do various DFG stack layout changes in
support of the FTL.

  • bytecode/ValueRecovery.h:

(JSC::ValueRecovery::dumpInContext):
(JSC::ValueRecovery::dump):

  • dfg/DFGOSRExitCompiler.cpp:

(JSC::DFG::shortOperandsDump):

  • dfg/DFGOSRExitCompiler64.cpp:

(JSC::DFG::OSRExitCompiler::compileExit):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/dfg/DFGOSRExitCompiler64.cpp

    r155711 r155820  
    7070    //    starts mutating state before verifying the speculation it has already made.
    7171   
    72     GPRReg alreadyBoxed = InvalidGPRReg;
    73    
    7472    if (recovery) {
    7573        switch (recovery->type()) {
     
    7775            m_jit.sub32(recovery->src(), recovery->dest());
    7876            m_jit.or64(GPRInfo::tagTypeNumberRegister, recovery->dest());
    79             alreadyBoxed = recovery->dest();
    8077            break;
    8178           
     
    150147        }
    151148    }
    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;
    177190   
    178191    for (size_t index = 0; index < operands.size(); ++index) {
    179192        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       
    340194        switch (recovery.technique()) {
    341195        case InGPR:
    342196        case UnboxedInt32InGPR:
    343197        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           
    351201        default:
    352202            break;
     
    354204    }
    355205   
    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);
    396282            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().
    596355   
    597356    handleExitCounts(m_jit, exit);
    598357   
    599     // 14) Reify inlined call frames.
     358    // 8) Reify inlined call frames.
    600359   
    601360    reifyInlinedCallFrames(m_jit, exit);
    602361   
    603     // 15) Create arguments if necessary and place them into the appropriate aliased
    604     //     registers.
     362    // 9) Create arguments if necessary and place them into the appropriate aliased
     363    //    registers.
    605364   
    606365    if (haveArguments) {
     
    652411    }
    653412   
    654     // 16) Load the result of the last bytecode operation into regT0.
     413    // 10) Load the result of the last bytecode operation into regT0.
    655414   
    656415    if (exit.m_lastSetOperand != std::numeric_limits<int>::max())
    657416        m_jit.load64(AssemblyHelpers::addressFor(exit.m_lastSetOperand), GPRInfo::cachedResultRegister);
    658417   
    659     // 17) And finish.
     418    // 11) And finish.
    660419   
    661420    adjustAndJumpToTarget(m_jit, exit);
Note: See TracChangeset for help on using the changeset viewer.