Ignore:
Timestamp:
Jan 14, 2015, 1:00:52 PM (10 years ago)
Author:
[email protected]
Message:

Fixes operationPutByIdOptimizes such that they check that the put didn't
change the structure of the object who's property access is being
cached. Also removes uses of the new base value from the cache generation code.
https://bugs.webkit.org/show_bug.cgi?id=139500

Reviewed by Filip Pizlo.

  • jit/JITOperations.cpp:

(JSC::operationPutByIdStrictOptimize): saved the structure before the put.
(JSC::operationPutByIdNonStrictOptimize): ditto.
(JSC::operationPutByIdDirectStrictOptimize): ditto.
(JSC::operationPutByIdDirectNonStrictOptimize): ditto.

  • jit/Repatch.cpp:

(JSC::generateByIdStub):
(JSC::tryCacheGetByID):
(JSC::tryBuildGetByIDList):
(JSC::emitPutReplaceStub):
(JSC::emitPutTransitionStubAndGetOldStructure): Added.
(JSC::tryCachePutByID):
(JSC::repatchPutByID):
(JSC::tryBuildPutByIdList):
(JSC::tryRepatchIn):
(JSC::emitPutTransitionStub): Deleted.

  • jit/Repatch.h:
  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL):

  • runtime/JSPropertyNameEnumerator.h:

(JSC::genericPropertyNameEnumerator):

  • runtime/Operations.h:

(JSC::normalizePrototypeChainForChainAccess): restructured to not use the base value.
(JSC::normalizePrototypeChain): restructured to not use the base value.

  • tests/mozilla/mozilla-tests.yaml:
  • tests/stress/proto-setter.js: Added.
  • tests/stress/put-by-id-build-list-order-recurse.js: Added.

Added test that fails without this patch.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/jit/Repatch.cpp

    r177401 r178441  
    298298    CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, RefPtr<JITStubRoutine>& stubRoutine)
    299299{
     300
    300301    VM* vm = &exec->vm();
    301302    GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
     
    349350        watchpointSet->add(stubInfo.addWatchpoint(codeBlock));
    350351
    351     Structure* currStructure = structure;
     352    Structure* currStructure = structure; 
    352353    JSObject* protoObject = 0;
    353354    if (chain) {
     
    748749
    749750    JSCell* baseCell = baseValue.asCell();
    750     Structure* structure = baseCell->structure();
     751    Structure* structure = baseCell->structure(*vm);
    751752
    752753    InlineCacheAction action = actionForCell(*vm, baseCell);
     
    833834
    834835        if (slot.isUnset())
    835             count = normalizePrototypeChain(exec, baseCell);
     836            count = normalizePrototypeChain(exec, structure);
    836837        else
    837838            count = normalizePrototypeChainForChainAccess(
    838                 exec, baseValue, slot.slotBase(), ident, offset);
     839                exec, structure, slot.slotBase(), ident, offset);
    839840        if (count == InvalidPrototypeChain)
    840841            return GiveUpOnCache;
     
    908909static void emitPutReplaceStub(
    909910    ExecState* exec,
    910     JSValue,
    911911    const Identifier&,
    912912    const PutPropertySlot& slot,
    913913    StructureStubInfo& stubInfo,
    914     PutKind,
    915914    Structure* structure,
    916915    CodeLocationLabel failureLabel,
     
    986985}
    987986
    988 static void emitPutTransitionStub(
    989     ExecState* exec,
    990     JSValue,
    991     const Identifier&,
    992     const PutPropertySlot& slot,
    993     StructureStubInfo& stubInfo,
    994     PutKind putKind,
    995     Structure* structure,
    996     Structure* oldStructure,
    997     StructureChain* prototypeChain,
    998     CodeLocationLabel failureLabel,
    999     RefPtr<JITStubRoutine>& stubRoutine)
    1000 {
    1001     VM* vm = &exec->vm();
     987static Structure* emitPutTransitionStubAndGetOldStructure(ExecState* exec, VM* vm, Structure*& structure, const Identifier& ident,
     988    const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
     989{
     990    PropertyName pname(ident);
     991    Structure* oldStructure = structure;
     992    if (!oldStructure->isObject() || oldStructure->isDictionary() || pname.asIndex() != PropertyName::NotAnIndex)
     993        return nullptr;
     994
     995    PropertyOffset propertyOffset;
     996    structure = Structure::addPropertyTransitionToExistingStructureConcurrently(oldStructure, ident.impl(), 0, propertyOffset);
     997
     998    if (!structure || !structure->isObject() || structure->isDictionary() || !structure->propertyAccessesAreCacheable())
     999        return nullptr;
     1000
     1001    // Skip optimizing the case where we need a realloc, if we don't have
     1002    // enough registers to make it happen.
     1003    if (GPRInfo::numberOfRegisters < 6
     1004        && oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()
     1005        && oldStructure->outOfLineCapacity()) {
     1006        return nullptr;
     1007    }
     1008
     1009    // Skip optimizing the case where we need realloc, and the structure has
     1010    // indexing storage.
     1011    // FIXME: We shouldn't skip this! Implement it!
     1012    // https://bugs.webkit.org/show_bug.cgi?id=130914
     1013    if (oldStructure->couldHaveIndexingHeader())
     1014        return nullptr;
     1015
     1016    if (normalizePrototypeChain(exec, structure) == InvalidPrototypeChain)
     1017        return nullptr;
     1018
     1019    StructureChain* prototypeChain = structure->prototypeChain(exec);
     1020
     1021    // emitPutTransitionStub
     1022
     1023    CodeLocationLabel failureLabel = stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase);
     1024    RefPtr<JITStubRoutine>& stubRoutine = stubInfo.stubRoutine;
    10021025
    10031026    GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
     
    12231246            structure->outOfLineCapacity() != oldStructure->outOfLineCapacity(),
    12241247            structure);
    1225 }
    1226 
    1227 static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
     1248
     1249    return oldStructure;
     1250}
     1251
     1252static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
    12281253{
    12291254    if (Options::forceICFailure())
     
    12351260    if (!baseValue.isCell())
    12361261        return GiveUpOnCache;
    1237     JSCell* baseCell = baseValue.asCell();
    1238     Structure* structure = baseCell->structure(*vm);
    1239     Structure* oldStructure = structure->previousID();
    12401262   
    12411263    if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
    12421264        return GiveUpOnCache;
     1265
    12431266    if (!structure->propertyAccessesAreCacheable())
    12441267        return GiveUpOnCache;
     
    12471270    if (slot.base() == baseValue && slot.isCacheablePut()) {
    12481271        if (slot.type() == PutPropertySlot::NewProperty) {
    1249             if (structure->isDictionary())
     1272
     1273            Structure* oldStructure = emitPutTransitionStubAndGetOldStructure(exec, vm, structure, ident, slot, stubInfo, putKind);
     1274            if (!oldStructure)
    12501275                return GiveUpOnCache;
    12511276           
    1252             // Skip optimizing the case where we need a realloc, if we don't have
    1253             // enough registers to make it happen.
    1254             if (GPRInfo::numberOfRegisters < 6
    1255                 && oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()
    1256                 && oldStructure->outOfLineCapacity())
    1257                 return GiveUpOnCache;
    1258            
    1259             // Skip optimizing the case where we need realloc, and the structure has
    1260             // indexing storage.
    1261             // FIXME: We shouldn't skip this!  Implement it!
    1262             // https://bugs.webkit.org/show_bug.cgi?id=130914
    1263             if (oldStructure->couldHaveIndexingHeader())
    1264                 return GiveUpOnCache;
    1265            
    1266             if (normalizePrototypeChain(exec, baseCell) == InvalidPrototypeChain)
    1267                 return GiveUpOnCache;
    1268            
    12691277            StructureChain* prototypeChain = structure->prototypeChain(exec);
    1270            
    1271             emitPutTransitionStub(
    1272                 exec, baseValue, ident, slot, stubInfo, putKind,
    1273                 structure, oldStructure, prototypeChain,
    1274                 stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
    1275                 stubInfo.stubRoutine);
    12761278           
    12771279            RepatchBuffer repatchBuffer(codeBlock);
     
    12951297        return RetryCacheLater;
    12961298    }
     1299
    12971300    if ((slot.isCacheableCustom() || slot.isCacheableSetter())
    12981301        && stubInfo.patch.spillMode == DontSpill) {
     
    13031306        size_t count = 0;
    13041307        if (baseValue != slot.base()) {
    1305             count = normalizePrototypeChainForChainAccess(exec, baseCell, slot.base(), ident, offset);
     1308            count = normalizePrototypeChainForChainAccess(exec, structure, slot.base(), ident, offset);
    13061309            if (count == InvalidPrototypeChain)
    13071310                return GiveUpOnCache;
    1308 
    13091311            prototypeChain = structure->prototypeChain(exec);
    13101312        }
     
    13341336}
    13351337
    1336 void repatchPutByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
     1338void repatchPutByID(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
    13371339{
    13381340    GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);
    13391341   
    1340     if (tryCachePutByID(exec, baseValue, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
     1342    if (tryCachePutByID(exec, baseValue, structure, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
    13411343        repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
    13421344}
     
    13471349    VM* vm = &exec->vm();
    13481350
    1349     if (!baseValue.isCell() || !structure)
     1351    if (!baseValue.isCell())
    13501352        return GiveUpOnCache;
    1351     JSCell* baseCell = baseValue.asCell();
    1352 
    1353     if (baseCell->structure(*vm)->id() != structure->id())
    1354         return GiveUpOnCache;
    1355 
    1356     Structure* oldStructure = structure->previousID();
    1357    
    1358    
     1353
    13591354    if (!slot.isCacheablePut() && !slot.isCacheableCustom() && !slot.isCacheableSetter())
    13601355        return GiveUpOnCache;
     
    13691364       
    13701365        if (slot.type() == PutPropertySlot::NewProperty) {
    1371             if (structure->isDictionary())
    1372                 return GiveUpOnCache;
    1373            
    1374             // Skip optimizing the case where we need a realloc, if we don't have
    1375             // enough registers to make it happen.
    1376             if (GPRInfo::numberOfRegisters < 6
    1377                 && oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()
    1378                 && oldStructure->outOfLineCapacity())
    1379                 return GiveUpOnCache;
    1380            
    1381             // Skip optimizing the case where we need realloc, and the structure has
    1382             // indexing storage.
    1383             if (oldStructure->couldHaveIndexingHeader())
    1384                 return GiveUpOnCache;
    1385            
    1386             if (normalizePrototypeChain(exec, baseCell) == InvalidPrototypeChain)
    1387                 return GiveUpOnCache;
    1388            
    1389             StructureChain* prototypeChain = structure->prototypeChain(exec);
    1390            
    13911366            list = PolymorphicPutByIdList::from(putKind, stubInfo);
    13921367            if (list->isFull())
    13931368                return GiveUpOnCache; // Will get here due to recursion.
    1394            
    1395             // We're now committed to creating the stub. Mogrify the meta-data accordingly.
    1396             emitPutTransitionStub(
    1397                 exec, baseValue, propertyName, slot, stubInfo, putKind,
    1398                 structure, oldStructure, prototypeChain,
    1399                 CodeLocationLabel(list->currentSlowPathTarget()),
    1400                 stubRoutine);
    1401            
     1369
     1370            Structure* oldStructure = emitPutTransitionStubAndGetOldStructure(exec, vm, structure, propertyName, slot, stubInfo, putKind);
     1371
     1372            if (!oldStructure)
     1373                return GiveUpOnCache;
     1374
     1375            StructureChain* prototypeChain = structure->prototypeChain(exec);
     1376            stubRoutine = stubInfo.stubRoutine;
    14021377            list->addAccess(
    14031378                PutByIdAccess::transition(
     
    14051380                    oldStructure, structure, prototypeChain,
    14061381                    stubRoutine));
     1382
    14071383        } else {
    14081384            list = PolymorphicPutByIdList::from(putKind, stubInfo);
     
    14141390            // We're now committed to creating the stub. Mogrify the meta-data accordingly.
    14151391            emitPutReplaceStub(
    1416                 exec, baseValue, propertyName, slot, stubInfo, putKind,
     1392                exec, propertyName, slot, stubInfo,
    14171393                structure, CodeLocationLabel(list->currentSlowPathTarget()), stubRoutine);
    1418            
     1394
    14191395            list->addAccess(
    14201396                PutByIdAccess::replace(
     
    14221398                    structure, stubRoutine));
    14231399        }
    1424        
    14251400        RepatchBuffer repatchBuffer(codeBlock);
    14261401        repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
    1427        
    14281402        if (list->isFull())
    14291403            repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateGenericPutByIdFunction(slot, putKind));
    1430        
     1404
    14311405        return RetryCacheLater;
    14321406    }
     
    14391413        size_t count = 0;
    14401414        if (baseValue != slot.base()) {
    1441             count = normalizePrototypeChainForChainAccess(exec, baseCell, slot.base(), propertyName, offset);
     1415            count = normalizePrototypeChainForChainAccess(exec, structure, slot.base(), propertyName, offset);
    14421416            if (count == InvalidPrototypeChain)
    14431417                return GiveUpOnCache;
    1444 
    14451418            prototypeChain = structure->prototypeChain(exec);
    14461419        }
     1420       
    14471421        PolymorphicPutByIdList* list;
    14481422        list = PolymorphicPutByIdList::from(putKind, stubInfo);
     
    14951469    CodeBlock* codeBlock = exec->codeBlock();
    14961470    VM* vm = &exec->vm();
    1497     Structure* structure = base->structure();
     1471    Structure* structure = base->structure(*vm);
    14981472   
    14991473    PropertyOffset offsetIgnored;
    1500     size_t count = normalizePrototypeChainForChainAccess(exec, base, wasFound ? slot.slotBase() : JSValue(), ident, offsetIgnored);
     1474    JSValue foundSlotBase = wasFound ? slot.slotBase() : JSValue();
     1475    size_t count = !foundSlotBase || foundSlotBase != base ?
     1476        normalizePrototypeChainForChainAccess(exec, structure, foundSlotBase, ident, offsetIgnored) : 0;
    15011477    if (count == InvalidPrototypeChain)
    15021478        return GiveUpOnCache;
Note: See TracChangeset for help on using the changeset viewer.