ObjectAllocationProfileInlines.h
1 /* 2 * Copyright (C) 2017-2019 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #pragma once 27 28 #include "ObjectAllocationProfile.h" 29 30 #include "JSFunctionInlines.h" 31 32 namespace JSC { 33 34 template<typename Derived> 35 ALWAYS_INLINE void ObjectAllocationProfileBase<Derived>::initializeProfile(VM& vm, JSGlobalObject* globalObject, JSCell* owner, JSObject* prototype, unsigned inferredInlineCapacity, JSFunction* constructor, FunctionRareData* functionRareData) 36 { 37 ASSERT(!m_allocator); 38 ASSERT(!m_structure); 39 40 // FIXME: Teach create_this's fast path how to allocate poly 41 // proto objects: https://bugs.webkit.org/show_bug.cgi?id=177517 42 43 bool isPolyProto = false; 44 FunctionExecutable* executable = nullptr; 45 if (constructor) { 46 // FIXME: A JSFunction should watch the poly proto watchpoint if it is not invalidated. 47 // That way it can clear this object allocation profile to ensure it stops allocating 48 // mono proto |this| values when it knows that it should be allocating poly proto 49 // |this| values: 50 // https://bugs.webkit.org/show_bug.cgi?id=177792 51 52 executable = constructor->jsExecutable(); 53 54 if (Structure* structure = executable->cachedPolyProtoStructure()) { 55 RELEASE_ASSERT(structure->typeInfo().type() == FinalObjectType); 56 m_allocator = Allocator(); 57 m_structure.set(vm, owner, structure); 58 static_cast<Derived*>(this)->setPrototype(vm, owner, prototype); 59 return; 60 } 61 62 isPolyProto = false; 63 if (Options::forcePolyProto()) 64 isPolyProto = true; 65 else 66 isPolyProto = executable->ensurePolyProtoWatchpoint().hasBeenInvalidated() && executable->singleton().hasBeenInvalidated(); 67 } 68 69 unsigned inlineCapacity = 0; 70 if (inferredInlineCapacity < JSFinalObject::defaultInlineCapacity()) { 71 // Try to shrink the object based on static analysis. 72 inferredInlineCapacity += possibleDefaultPropertyCount(vm, prototype); 73 74 if (!inferredInlineCapacity) { 75 // Empty objects are rare, so most likely the static analyzer just didn't 76 // see the real initializer function. This can happen with helper functions. 77 inferredInlineCapacity = JSFinalObject::defaultInlineCapacity(); 78 } else if (inferredInlineCapacity > JSFinalObject::defaultInlineCapacity()) { 79 // Default properties are weak guesses, so don't allow them to turn a small 80 // object into a large object. 81 inferredInlineCapacity = JSFinalObject::defaultInlineCapacity(); 82 } 83 84 inlineCapacity = inferredInlineCapacity; 85 ASSERT(inlineCapacity < JSFinalObject::maxInlineCapacity()); 86 } else { 87 // Normal or large object. 88 inlineCapacity = inferredInlineCapacity; 89 if (inlineCapacity > JSFinalObject::maxInlineCapacity()) 90 inlineCapacity = JSFinalObject::maxInlineCapacity(); 91 } 92 93 if (isPolyProto) { 94 ++inlineCapacity; 95 inlineCapacity = std::min(inlineCapacity, JSFinalObject::maxInlineCapacity()); 96 } 97 98 ASSERT(inlineCapacity > 0); 99 ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity()); 100 101 size_t allocationSize = JSFinalObject::allocationSize(inlineCapacity); 102 Allocator allocator = subspaceFor<JSFinalObject>(vm)->allocatorForNonVirtual(allocationSize, AllocatorForMode::EnsureAllocator); 103 104 // Take advantage of extra inline capacity available in the size class. 105 if (allocator) { 106 size_t slop = (allocator.cellSize() - allocationSize) / sizeof(WriteBarrier<Unknown>); 107 inlineCapacity += slop; 108 if (inlineCapacity > JSFinalObject::maxInlineCapacity()) 109 inlineCapacity = JSFinalObject::maxInlineCapacity(); 110 } 111 112 Structure* structure = vm.structureCache.emptyObjectStructureForPrototype(globalObject, prototype, inlineCapacity, isPolyProto, executable); 113 114 if (isPolyProto) { 115 ASSERT(structure->hasPolyProto()); 116 m_allocator = Allocator(); 117 executable->setCachedPolyProtoStructure(vm, structure); 118 } else { 119 if (executable) { 120 ASSERT(constructor); 121 ASSERT(functionRareData); 122 InlineWatchpointSet& polyProtoWatchpointSet = executable->ensurePolyProtoWatchpoint(); 123 structure->ensureRareData(vm)->setSharedPolyProtoWatchpoint(executable->sharedPolyProtoWatchpoint()); 124 if (polyProtoWatchpointSet.isStillValid() && !functionRareData->hasAllocationProfileClearingWatchpoint()) { 125 // If we happen to go poly proto in the future, we want to clear this particular 126 // object allocation profile so we can transition to allocating poly proto objects. 127 Watchpoint* watchpoint = functionRareData->createAllocationProfileClearingWatchpoint(); 128 polyProtoWatchpointSet.add(watchpoint); 129 } 130 } 131 132 m_allocator = allocator; 133 } 134 135 // Ensure that if another thread sees the structure and prototype, it will see it properly created. 136 WTF::storeStoreFence(); 137 138 m_structure.set(vm, owner, structure); 139 static_cast<Derived*>(this)->setPrototype(vm, owner, prototype); 140 } 141 142 template<typename Derived> 143 ALWAYS_INLINE unsigned ObjectAllocationProfileBase<Derived>::possibleDefaultPropertyCount(VM& vm, JSObject* prototype) 144 { 145 if (prototype == prototype->globalObject(vm)->objectPrototype()) 146 return 0; 147 148 size_t count = 0; 149 PropertyNameArray propertyNameArray(vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Include); 150 prototype->structure(vm)->getPropertyNamesFromStructure(vm, propertyNameArray, DontEnumPropertiesMode::Include); 151 PropertyNameArrayData::PropertyNameVector& propertyNameVector = propertyNameArray.data()->propertyNameVector(); 152 for (size_t i = 0; i < propertyNameVector.size(); ++i) { 153 JSValue value = prototype->getDirect(vm, propertyNameVector[i]); 154 155 // Functions are common, and are usually class-level objects that are not overridden. 156 if (jsDynamicCast<JSFunction*>(vm, value)) 157 continue; 158 159 ++count; 160 161 } 162 return count; 163 } 164 165 } // namespace JSC