/ bytecode / ObjectAllocationProfileInlines.h
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