Changeset 203368 in webkit
- Timestamp:
- Jul 18, 2016, 1:12:45 PM (10 years ago)
- Location:
- trunk
- Files:
-
- 9 added
- 6 edited
-
LayoutTests/ChangeLog (modified) (1 diff)
-
LayoutTests/js/regress/freeze-and-do-work-expected.txt (added)
-
LayoutTests/js/regress/freeze-and-do-work.html (added)
-
LayoutTests/js/regress/prevent-extensions-and-do-work-expected.txt (added)
-
LayoutTests/js/regress/prevent-extensions-and-do-work.html (added)
-
LayoutTests/js/regress/script-tests/freeze-and-do-work.js (added)
-
LayoutTests/js/regress/script-tests/prevent-extensions-and-do-work.js (added)
-
LayoutTests/js/regress/script-tests/seal-and-do-work.js (added)
-
LayoutTests/js/regress/seal-and-do-work-expected.txt (added)
-
LayoutTests/js/regress/seal-and-do-work.html (added)
-
Source/JavaScriptCore/ChangeLog (modified) (1 diff)
-
Source/JavaScriptCore/runtime/JSObject.cpp (modified) (12 diffs)
-
Source/JavaScriptCore/runtime/Structure.cpp (modified) (6 diffs)
-
Source/JavaScriptCore/runtime/Structure.h (modified) (1 diff)
-
Source/JavaScriptCore/runtime/StructureTransitionTable.h (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r203367 r203368 1 2016-07-17 Filip Pizlo <fpizlo@apple.com> 2 3 Object.preventExtensions/seal/freeze makes code much slower 4 https://bugs.webkit.org/show_bug.cgi?id=143247 5 6 Reviewed by Michael Saboff. 7 8 These tests now run ~25x faster. 9 10 * js/regress/freeze-and-do-work-expected.txt: Added. 11 * js/regress/freeze-and-do-work.html: Added. 12 * js/regress/prevent-extensions-and-do-work-expected.txt: Added. 13 * js/regress/prevent-extensions-and-do-work.html: Added. 14 * js/regress/script-tests/freeze-and-do-work.js: Added. 15 (Foo): 16 * js/regress/script-tests/prevent-extensions-and-do-work.js: Added. 17 (Foo): 18 * js/regress/script-tests/seal-and-do-work.js: Added. 19 (Foo): 20 * js/regress/seal-and-do-work-expected.txt: Added. 21 * js/regress/seal-and-do-work.html: Added. 22 1 23 2016-07-18 Ryan Haddad <ryanhaddad@apple.com> 2 24 -
trunk/Source/JavaScriptCore/ChangeLog
r203365 r203368 1 2016-07-17 Filip Pizlo <fpizlo@apple.com> 2 3 Object.preventExtensions/seal/freeze makes code much slower 4 https://bugs.webkit.org/show_bug.cgi?id=143247 5 6 Reviewed by Michael Saboff. 7 8 This has been a huge pet peeve of mine for a long time, but I was always afraid of fixing 9 it because I thought that it would be hard. Well, it looks like it's not hard at all. 10 11 The problem is that you cannot mutate a structure that participates in transition caching. 12 You can only clone the structure and mutate that one. But if you do this, you have to make 13 a hard choice: 14 15 1) Clone the structure without caching the transition. This is what the code did before 16 this change. It's the most obvious choice, but it introduces an uncacheable transition 17 that leads to an explosion of structures, which then breaks all inline caches. 18 19 2) Perform one of the existing cacheable transitions. Cacheable transitions can either add 20 properties or they can do one of the NonPropertyTransitions, which until now have been 21 restricted to just IndexingType transitions. So, only adding transitions or making 22 certain prescribed changes to the indexing type count as cacheable transitions. 23 24 This change decouples NonPropertyTransition from IndexingType and adds three new kinds of 25 transitions: PreventExtensions, Seal, and Freeze. We have to give any cacheable transition 26 a name that fully disambiguates this transition from any other, so that the transition can 27 be cached. Since we're already giving them names in an enum, I figured that the most 28 pragmatic way to implement them is to have Structure::nonPropertyTransition() case on the 29 NonPropertyTransition and implement all of the mutations associated with that transition. 30 The alternative would have been to allow callers of nonPropertyTransition() to supply 31 something like a lambda that describes the mutation, but this seemed awkward since each 32 set of mutations has to anyway be tied to one of the NonPropertyTransition members. 33 34 This is an enormous speed-up on microbenchmarks that use Object.preventExtensions(), 35 Object.seal(), or Object.freeze(). I don't know if "real" benchmarks use these features 36 and I don't really care. This should be fast. 37 38 * runtime/JSObject.cpp: 39 (JSC::JSObject::notifyPresenceOfIndexedAccessors): 40 (JSC::JSObject::createInitialUndecided): 41 (JSC::JSObject::createInitialInt32): 42 (JSC::JSObject::createInitialDouble): 43 (JSC::JSObject::createInitialContiguous): 44 (JSC::JSObject::convertUndecidedToInt32): 45 (JSC::JSObject::convertUndecidedToDouble): 46 (JSC::JSObject::convertUndecidedToContiguous): 47 (JSC::JSObject::convertInt32ToDouble): 48 (JSC::JSObject::convertInt32ToContiguous): 49 (JSC::JSObject::convertDoubleToContiguous): 50 (JSC::JSObject::switchToSlowPutArrayStorage): 51 * runtime/Structure.cpp: 52 (JSC::Structure::suggestedArrayStorageTransition): 53 (JSC::Structure::addPropertyTransition): 54 (JSC::Structure::toUncacheableDictionaryTransition): 55 (JSC::Structure::sealTransition): 56 (JSC::Structure::freezeTransition): 57 (JSC::Structure::preventExtensionsTransition): 58 (JSC::Structure::takePropertyTableOrCloneIfPinned): 59 (JSC::Structure::nonPropertyTransition): 60 (JSC::Structure::pin): 61 (JSC::Structure::pinForCaching): 62 (JSC::Structure::allocateRareData): 63 * runtime/Structure.h: 64 * runtime/StructureTransitionTable.h: 65 (JSC::toAttributes): 66 (JSC::changesIndexingType): 67 (JSC::newIndexingType): 68 (JSC::preventsExtensions): 69 (JSC::setsDontDeleteOnAllProperties): 70 (JSC::setsReadOnlyOnAllProperties): 71 1 72 2016-07-17 Filip Pizlo <fpizlo@apple.com> 2 73 -
trunk/Source/JavaScriptCore/runtime/JSObject.cpp
r203004 r203368 771 771 return; 772 772 773 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AddIndexedAccessors));773 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AddIndexedAccessors)); 774 774 775 775 if (!vm.prototypeMap.isPrototype(this)) … … 799 799 DeferGC deferGC(vm.heap); 800 800 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); 801 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateUndecided);801 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateUndecided); 802 802 setStructureAndButterfly(vm, newStructure, newButterfly); 803 803 return newButterfly; … … 808 808 DeferGC deferGC(vm.heap); 809 809 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); 810 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32);810 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32); 811 811 setStructureAndButterfly(vm, newStructure, newButterfly); 812 812 return newButterfly->contiguousInt32(); … … 819 819 for (unsigned i = newButterfly->vectorLength(); i--;) 820 820 newButterfly->contiguousDouble()[i] = PNaN; 821 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble);821 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble); 822 822 setStructureAndButterfly(vm, newStructure, newButterfly); 823 823 return newButterfly->contiguousDouble(); … … 828 828 DeferGC deferGC(vm.heap); 829 829 Butterfly* newButterfly = createInitialIndexedStorage(vm, length, sizeof(EncodedJSValue)); 830 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous);830 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous); 831 831 setStructureAndButterfly(vm, newStructure, newButterfly); 832 832 return newButterfly->contiguous(); … … 863 863 { 864 864 ASSERT(hasUndecided(indexingType())); 865 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateInt32));865 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateInt32)); 866 866 return m_butterfly.get()->contiguousInt32(); 867 867 } … … 875 875 butterfly->contiguousDouble()[i] = PNaN; 876 876 877 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));877 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble)); 878 878 return m_butterfly.get()->contiguousDouble(); 879 879 } … … 882 882 { 883 883 ASSERT(hasUndecided(indexingType())); 884 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));884 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous)); 885 885 return m_butterfly.get()->contiguous(); 886 886 } … … 947 947 } 948 948 949 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateDouble));949 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateDouble)); 950 950 return m_butterfly.get()->contiguousDouble(); 951 951 } … … 955 955 ASSERT(hasInt32(indexingType())); 956 956 957 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));957 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous)); 958 958 return m_butterfly.get()->contiguous(); 959 959 } … … 1003 1003 } 1004 1004 1005 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), AllocateContiguous));1005 setStructure(vm, Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::AllocateContiguous)); 1006 1006 return m_butterfly.get()->contiguous(); 1007 1007 } … … 1291 1291 switch (indexingType()) { 1292 1292 case ALL_UNDECIDED_INDEXING_TYPES: 1293 convertUndecidedToArrayStorage(vm, AllocateSlowPutArrayStorage);1293 convertUndecidedToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage); 1294 1294 break; 1295 1295 1296 1296 case ALL_INT32_INDEXING_TYPES: 1297 convertInt32ToArrayStorage(vm, AllocateSlowPutArrayStorage);1297 convertInt32ToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage); 1298 1298 break; 1299 1299 1300 1300 case ALL_DOUBLE_INDEXING_TYPES: 1301 convertDoubleToArrayStorage(vm, AllocateSlowPutArrayStorage);1301 convertDoubleToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage); 1302 1302 break; 1303 1303 1304 1304 case ALL_CONTIGUOUS_INDEXING_TYPES: 1305 convertContiguousToArrayStorage(vm, AllocateSlowPutArrayStorage);1305 convertContiguousToArrayStorage(vm, NonPropertyTransition::AllocateSlowPutArrayStorage); 1306 1306 break; 1307 1307 1308 1308 case NonArrayWithArrayStorage: 1309 1309 case ArrayWithArrayStorage: { 1310 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), SwitchToSlowPutArrayStorage);1310 Structure* newStructure = Structure::nonPropertyTransition(vm, structure(vm), NonPropertyTransition::SwitchToSlowPutArrayStorage); 1311 1311 setStructure(vm, newStructure); 1312 1312 break; -
trunk/Source/JavaScriptCore/runtime/Structure.cpp
r202588 r203368 436 436 { 437 437 if (needsSlowPutIndexing()) 438 return AllocateSlowPutArrayStorage;439 440 return AllocateArrayStorage;438 return NonPropertyTransition::AllocateSlowPutArrayStorage; 439 440 return NonPropertyTransition::AllocateArrayStorage; 441 441 } 442 442 … … 588 588 } 589 589 590 // In future we may want to cache this transition.591 590 Structure* Structure::sealTransition(VM& vm, Structure* structure) 592 591 { 593 Structure* transition = preventExtensionsTransition(vm, structure); 594 595 if (transition->propertyTable()) { 596 PropertyTable::iterator end = transition->propertyTable()->end(); 597 for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter) 598 iter->attributes |= DontDelete; 599 } 600 601 transition->checkOffsetConsistency(); 602 return transition; 603 } 604 605 // In future we may want to cache this transition. 592 return nonPropertyTransition(vm, structure, NonPropertyTransition::Seal); 593 } 594 606 595 Structure* Structure::freezeTransition(VM& vm, Structure* structure) 607 596 { 608 Structure* transition = preventExtensionsTransition(vm, structure); 609 610 if (transition->propertyTable()) { 611 PropertyTable::iterator iter = transition->propertyTable()->begin(); 612 PropertyTable::iterator end = transition->propertyTable()->end(); 613 if (iter != end) 614 transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); 615 for (; iter != end; ++iter) 616 iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); 617 } 618 619 ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties()); 620 ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties()); 621 transition->checkOffsetConsistency(); 622 return transition; 623 } 624 625 // In future we may want to cache this transition. 597 return nonPropertyTransition(vm, structure, NonPropertyTransition::Freeze); 598 } 599 626 600 Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure) 627 601 { 628 Structure* transition = create(vm, structure); 629 630 // Don't set m_offset, as one cannot transition to this. 631 632 DeferGC deferGC(vm.heap); 633 structure->materializePropertyMapIfNecessary(vm, deferGC); 634 transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); 635 transition->m_offset = structure->m_offset; 636 transition->setDidPreventExtensions(true); 637 transition->pin(); 638 639 transition->checkOffsetConsistency(); 640 return transition; 602 return nonPropertyTransition(vm, structure, NonPropertyTransition::PreventExtensions); 641 603 } 642 604 … … 663 625 IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); 664 626 665 if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { 666 if (globalObject->isOriginalArrayStructure(structure)) { 667 Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); 668 if (result->indexingTypeIncludingHistory() == indexingType) { 669 structure->didTransitionFromThisStructure(); 670 return result; 627 if (changesIndexingType(transitionKind)) { 628 if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { 629 if (globalObject->isOriginalArrayStructure(structure)) { 630 Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); 631 if (result->indexingTypeIncludingHistory() == indexingType) { 632 structure->didTransitionFromThisStructure(); 633 return result; 634 } 671 635 } 672 636 } … … 680 644 } 681 645 646 DeferGC deferGC(vm.heap); 647 682 648 Structure* transition = create(vm, structure); 683 649 transition->setAttributesInPrevious(attributes); 684 650 transition->m_blob.setIndexingType(indexingType); 685 transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); 686 transition->m_offset = structure->m_offset; 687 checkOffset(transition->m_offset, transition->inlineCapacity()); 651 652 if (preventsExtensions(transitionKind)) 653 transition->setDidPreventExtensions(true); 654 655 unsigned additionalPropertyAttributes = 0; 656 if (setsDontDeleteOnAllProperties(transitionKind)) 657 additionalPropertyAttributes |= DontDelete; 658 if (setsReadOnlyOnAllProperties(transitionKind)) 659 additionalPropertyAttributes |= ReadOnly; 660 if (additionalPropertyAttributes) { 661 // We pin the property table on transitions that do wholesale editing of the property 662 // table, since our logic for walking the property transition chain to rematerialize the 663 // table doesn't know how to take into account such wholesale edits. 664 665 structure->materializePropertyMapIfNecessary(vm, deferGC); 666 transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); 667 transition->m_offset = structure->m_offset; 668 transition->pinForCaching(); 669 670 if (transition->propertyTable()) { 671 for (auto& entry : *transition->propertyTable().get()) 672 entry.attributes |= additionalPropertyAttributes; 673 } 674 } else { 675 transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); 676 transition->m_offset = structure->m_offset; 677 checkOffset(transition->m_offset, transition->inlineCapacity()); 678 } 679 680 if (setsReadOnlyOnAllProperties(transitionKind) 681 && transition->propertyTable() 682 && !transition->propertyTable()->isEmpty()) 683 transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); 688 684 689 685 if (structure->isDictionary()) … … 693 689 structure->m_transitionTable.add(vm, transition); 694 690 } 691 695 692 transition->checkOffsetConsistency(); 696 693 return transition; … … 815 812 setIsPinnedPropertyTable(true); 816 813 clearPreviousID(); 814 m_nameInPrevious = nullptr; 815 } 816 817 void Structure::pinForCaching() 818 { 819 ASSERT(propertyTable()); 820 setIsPinnedPropertyTable(true); 817 821 m_nameInPrevious = nullptr; 818 822 } -
trunk/Source/JavaScriptCore/runtime/Structure.h
r201853 r203368 701 701 702 702 void pin(); 703 void pinForCaching(); 703 704 704 705 bool isRareData(JSCell* cell) const -
trunk/Source/JavaScriptCore/runtime/StructureTransitionTable.h
r188978 r203368 41 41 // Support for attributes used to indicate transitions not related to properties. 42 42 // If any of these are used, the string portion of the key should be 0. 43 enum NonPropertyTransition{43 enum class NonPropertyTransition : unsigned { 44 44 AllocateUndecided, 45 45 AllocateInt32, … … 49 49 AllocateSlowPutArrayStorage, 50 50 SwitchToSlowPutArrayStorage, 51 AddIndexedAccessors 51 AddIndexedAccessors, 52 PreventExtensions, 53 Seal, 54 Freeze 52 55 }; 53 56 54 57 inline unsigned toAttributes(NonPropertyTransition transition) 55 58 { 56 return transition + FirstInternalAttribute; 59 return static_cast<unsigned>(transition) + FirstInternalAttribute; 60 } 61 62 inline bool changesIndexingType(NonPropertyTransition transition) 63 { 64 switch (transition) { 65 case NonPropertyTransition::AllocateUndecided: 66 case NonPropertyTransition::AllocateInt32: 67 case NonPropertyTransition::AllocateDouble: 68 case NonPropertyTransition::AllocateContiguous: 69 case NonPropertyTransition::AllocateArrayStorage: 70 case NonPropertyTransition::AllocateSlowPutArrayStorage: 71 case NonPropertyTransition::SwitchToSlowPutArrayStorage: 72 case NonPropertyTransition::AddIndexedAccessors: 73 return true; 74 default: 75 return false; 76 } 57 77 } 58 78 … … 60 80 { 61 81 switch (transition) { 62 case AllocateUndecided:82 case NonPropertyTransition::AllocateUndecided: 63 83 ASSERT(!hasIndexedProperties(oldType)); 64 84 return oldType | UndecidedShape; 65 case AllocateInt32:85 case NonPropertyTransition::AllocateInt32: 66 86 ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType)); 67 87 return (oldType & ~IndexingShapeMask) | Int32Shape; 68 case AllocateDouble:88 case NonPropertyTransition::AllocateDouble: 69 89 ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType)); 70 90 return (oldType & ~IndexingShapeMask) | DoubleShape; 71 case AllocateContiguous:91 case NonPropertyTransition::AllocateContiguous: 72 92 ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType)); 73 93 return (oldType & ~IndexingShapeMask) | ContiguousShape; 74 case AllocateArrayStorage:94 case NonPropertyTransition::AllocateArrayStorage: 75 95 ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType)); 76 96 return (oldType & ~IndexingShapeMask) | ArrayStorageShape; 77 case AllocateSlowPutArrayStorage:97 case NonPropertyTransition::AllocateSlowPutArrayStorage: 78 98 ASSERT(!hasIndexedProperties(oldType) || hasUndecided(oldType) || hasInt32(oldType) || hasDouble(oldType) || hasContiguous(oldType) || hasContiguous(oldType)); 79 99 return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape; 80 case SwitchToSlowPutArrayStorage:100 case NonPropertyTransition::SwitchToSlowPutArrayStorage: 81 101 ASSERT(hasArrayStorage(oldType)); 82 102 return (oldType & ~IndexingShapeMask) | SlowPutArrayStorageShape; 83 case AddIndexedAccessors:103 case NonPropertyTransition::AddIndexedAccessors: 84 104 return oldType | MayHaveIndexedAccessors; 85 105 default: 86 RELEASE_ASSERT_NOT_REACHED();87 106 return oldType; 107 } 108 } 109 110 inline bool preventsExtensions(NonPropertyTransition transition) 111 { 112 switch (transition) { 113 case NonPropertyTransition::PreventExtensions: 114 case NonPropertyTransition::Seal: 115 case NonPropertyTransition::Freeze: 116 return true; 117 default: 118 return false; 119 } 120 } 121 122 inline bool setsDontDeleteOnAllProperties(NonPropertyTransition transition) 123 { 124 switch (transition) { 125 case NonPropertyTransition::Seal: 126 case NonPropertyTransition::Freeze: 127 return true; 128 default: 129 return false; 130 } 131 } 132 133 inline bool setsReadOnlyOnAllProperties(NonPropertyTransition transition) 134 { 135 switch (transition) { 136 case NonPropertyTransition::Freeze: 137 return true; 138 default: 139 return false; 88 140 } 89 141 }
Note:
See TracChangeset
for help on using the changeset viewer.