[JSC] Pass VM& parameter as much as possible
[WebKit.git] / Source / JavaScriptCore / bytecode / ObjectAllocationProfileInlines.h
1 /*
2  * Copyright (C) 2017 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 ALWAYS_INLINE void ObjectAllocationProfile::initializeProfile(VM& vm, JSGlobalObject* globalObject, JSCell* owner, JSObject* prototype, unsigned inferredInlineCapacity, JSFunction* constructor, FunctionRareData* functionRareData)
35 {
36     ASSERT(!m_allocator);
37     ASSERT(!m_structure);
38     ASSERT(!m_prototype);
39     ASSERT(!m_inlineCapacity);
40
41     // FIXME: Teach create_this's fast path how to allocate poly
42     // proto objects: https://bugs.webkit.org/show_bug.cgi?id=177517
43
44     bool isPolyProto = false;
45     FunctionExecutable* executable = nullptr;
46     if (constructor) {
47         // FIXME: A JSFunction should watch the poly proto watchpoint if it is not invalidated.
48         // That way it can clear this object allocation profile to ensure it stops allocating
49         // mono proto |this| values when it knows that it should be allocating poly proto
50         // |this| values:
51         // https://bugs.webkit.org/show_bug.cgi?id=177792
52
53         executable = constructor->jsExecutable();
54
55         if (Structure* structure = executable->cachedPolyProtoStructure()) {
56             RELEASE_ASSERT(structure->typeInfo().type() == FinalObjectType);
57             m_allocator = Allocator();
58             m_structure.set(vm, owner, structure);
59             m_prototype.set(vm, owner, prototype);
60             m_inlineCapacity = structure->inlineCapacity();
61             return;
62         }
63
64         isPolyProto = false;
65         if (Options::forcePolyProto())
66             isPolyProto = true;
67         else
68             isPolyProto = executable->ensurePolyProtoWatchpoint().hasBeenInvalidated() && executable->singletonFunction()->hasBeenInvalidated();
69     }
70
71     unsigned inlineCapacity = 0;
72     if (inferredInlineCapacity < JSFinalObject::defaultInlineCapacity()) {
73         // Try to shrink the object based on static analysis.
74         inferredInlineCapacity += possibleDefaultPropertyCount(vm, prototype);
75
76         if (!inferredInlineCapacity) {
77             // Empty objects are rare, so most likely the static analyzer just didn't
78             // see the real initializer function. This can happen with helper functions.
79             inferredInlineCapacity = JSFinalObject::defaultInlineCapacity();
80         } else if (inferredInlineCapacity > JSFinalObject::defaultInlineCapacity()) {
81             // Default properties are weak guesses, so don't allow them to turn a small
82             // object into a large object.
83             inferredInlineCapacity = JSFinalObject::defaultInlineCapacity();
84         }
85
86         inlineCapacity = inferredInlineCapacity;
87         ASSERT(inlineCapacity < JSFinalObject::maxInlineCapacity());
88     } else {
89         // Normal or large object.
90         inlineCapacity = inferredInlineCapacity;
91         if (inlineCapacity > JSFinalObject::maxInlineCapacity())
92             inlineCapacity = JSFinalObject::maxInlineCapacity();
93     }
94
95     if (isPolyProto) {
96         ++inlineCapacity;
97         inlineCapacity = std::min(inlineCapacity, JSFinalObject::maxInlineCapacity());
98     }
99
100     ASSERT(inlineCapacity > 0);
101     ASSERT(inlineCapacity <= JSFinalObject::maxInlineCapacity());
102
103     size_t allocationSize = JSFinalObject::allocationSize(inlineCapacity);
104     Allocator allocator = subspaceFor<JSFinalObject>(vm)->allocatorForNonVirtual(allocationSize, AllocatorForMode::EnsureAllocator);
105
106     // Take advantage of extra inline capacity available in the size class.
107     if (allocator) {
108         size_t slop = (allocator.cellSize() - allocationSize) / sizeof(WriteBarrier<Unknown>);
109         inlineCapacity += slop;
110         if (inlineCapacity > JSFinalObject::maxInlineCapacity())
111             inlineCapacity = JSFinalObject::maxInlineCapacity();
112     }
113
114     Structure* structure = vm.structureCache.emptyObjectStructureForPrototype(globalObject, prototype, inlineCapacity, isPolyProto, executable);
115
116     if (isPolyProto) {
117         ASSERT(structure->hasPolyProto());
118         m_allocator = Allocator();
119         executable->setCachedPolyProtoStructure(vm, structure);
120     } else {
121         if (executable) {
122             ASSERT(constructor);
123             ASSERT(functionRareData);
124             InlineWatchpointSet& polyProtoWatchpointSet = executable->ensurePolyProtoWatchpoint();
125             structure->ensureRareData(vm)->setSharedPolyProtoWatchpoint(executable->sharedPolyProtoWatchpoint());
126             if (polyProtoWatchpointSet.isStillValid() && !functionRareData->hasAllocationProfileClearingWatchpoint()) {
127                 // If we happen to go poly proto in the future, we want to clear this particular
128                 // object allocation profile so we can transition to allocating poly proto objects.
129                 Watchpoint* watchpoint = functionRareData->createAllocationProfileClearingWatchpoint();
130                 polyProtoWatchpointSet.add(watchpoint);
131             }
132         }
133
134         m_allocator = allocator;
135     }
136
137     // Ensure that if another thread sees the structure and prototype, it will see it properly created.
138     WTF::storeStoreFence();
139
140     m_structure.set(vm, owner, structure);
141     m_prototype.set(vm, owner, prototype);
142     m_inlineCapacity = inlineCapacity;
143 }
144
145 ALWAYS_INLINE unsigned ObjectAllocationProfile::possibleDefaultPropertyCount(VM& vm, JSObject* prototype)
146 {
147     if (prototype == prototype->globalObject(vm)->objectPrototype())
148         return 0;
149
150     size_t count = 0;
151     PropertyNameArray propertyNameArray(&vm, PropertyNameMode::StringsAndSymbols, PrivateSymbolMode::Include);
152     prototype->structure(vm)->getPropertyNamesFromStructure(vm, propertyNameArray, EnumerationMode());
153     PropertyNameArrayData::PropertyNameVector& propertyNameVector = propertyNameArray.data()->propertyNameVector();
154     for (size_t i = 0; i < propertyNameVector.size(); ++i) {
155         JSValue value = prototype->getDirect(vm, propertyNameVector[i]);
156
157         // Functions are common, and are usually class-level objects that are not overridden.
158         if (jsDynamicCast<JSFunction*>(vm, value))
159             continue;
160
161         ++count;
162
163     }
164     return count;
165 }
166
167 } // namespace JSC