1/*
2 * Copyright (C) 2008-2018 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 "CodeBlock.h"
29#include "CodeOrigin.h"
30#include "Instruction.h"
31#include "JITStubRoutine.h"
32#include "MacroAssembler.h"
33#include "Options.h"
34#include "RegisterSet.h"
35#include "Structure.h"
36#include "StructureSet.h"
37#include "StructureStubClearingWatchpoint.h"
38#include "StubInfoSummary.h"
39
40namespace JSC {
41
42#if ENABLE(JIT)
43
44class AccessCase;
45class AccessGenerationResult;
46class PolymorphicAccess;
47
48enum class AccessType : int8_t {
49 Get,
50 GetWithThis,
51 GetDirect,
52 TryGet,
53 Put,
54 In,
55 InstanceOf
56};
57
58enum class CacheType : int8_t {
59 Unset,
60 GetByIdSelf,
61 PutByIdReplace,
62 InByIdSelf,
63 Stub,
64 ArrayLength,
65 StringLength
66};
67
68class StructureStubInfo {
69 WTF_MAKE_NONCOPYABLE(StructureStubInfo);
70 WTF_MAKE_FAST_ALLOCATED;
71public:
72 StructureStubInfo(AccessType);
73 ~StructureStubInfo();
74
75 void initGetByIdSelf(CodeBlock*, Structure* baseObjectStructure, PropertyOffset);
76 void initArrayLength();
77 void initStringLength();
78 void initPutByIdReplace(CodeBlock*, Structure* baseObjectStructure, PropertyOffset);
79 void initInByIdSelf(CodeBlock*, Structure* baseObjectStructure, PropertyOffset);
80
81 AccessGenerationResult addAccessCase(const GCSafeConcurrentJSLocker&, CodeBlock*, const Identifier&, std::unique_ptr<AccessCase>);
82
83 void reset(CodeBlock*);
84
85 void deref();
86 void aboutToDie();
87
88 // Check if the stub has weak references that are dead. If it does, then it resets itself,
89 // either entirely or just enough to ensure that those dead pointers don't get used anymore.
90 void visitWeakReferences(CodeBlock*);
91
92 // This returns true if it has marked everything that it will ever mark.
93 bool propagateTransitions(SlotVisitor&);
94
95 ALWAYS_INLINE bool considerCaching(CodeBlock* codeBlock, Structure* structure)
96 {
97 // We never cache non-cells.
98 if (!structure) {
99 sawNonCell = true;
100 return false;
101 }
102
103 // This method is called from the Optimize variants of IC slow paths. The first part of this
104 // method tries to determine if the Optimize variant should really behave like the
105 // non-Optimize variant and leave the IC untouched.
106 //
107 // If we determine that we should do something to the IC then the next order of business is
108 // to determine if this Structure would impact the IC at all. We know that it won't, if we
109 // have already buffered something on its behalf. That's what the bufferedStructures set is
110 // for.
111
112 everConsidered = true;
113 if (!countdown) {
114 // Check if we have been doing repatching too frequently. If so, then we should cool off
115 // for a while.
116 WTF::incrementWithSaturation(repatchCount);
117 if (repatchCount > Options::repatchCountForCoolDown()) {
118 // We've been repatching too much, so don't do it now.
119 repatchCount = 0;
120 // The amount of time we require for cool-down depends on the number of times we've
121 // had to cool down in the past. The relationship is exponential. The max value we
122 // allow here is 2^256 - 2, since the slow paths may increment the count to indicate
123 // that they'd like to temporarily skip patching just this once.
124 countdown = WTF::leftShiftWithSaturation(
125 static_cast<uint8_t>(Options::initialCoolDownCount()),
126 numberOfCoolDowns,
127 static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - 1));
128 WTF::incrementWithSaturation(numberOfCoolDowns);
129
130 // We may still have had something buffered. Trigger generation now.
131 bufferingCountdown = 0;
132 return true;
133 }
134
135 // We don't want to return false due to buffering indefinitely.
136 if (!bufferingCountdown) {
137 // Note that when this returns true, it's possible that we will not even get an
138 // AccessCase because this may cause Repatch.cpp to simply do an in-place
139 // repatching.
140 return true;
141 }
142
143 bufferingCountdown--;
144
145 // Now protect the IC buffering. We want to proceed only if this is a structure that
146 // we don't already have a case buffered for. Note that if this returns true but the
147 // bufferingCountdown is not zero then we will buffer the access case for later without
148 // immediately generating code for it.
149 //
150 // NOTE: This will behave oddly for InstanceOf if the user varies the prototype but not
151 // the base's structure. That seems unlikely for the canonical use of instanceof, where
152 // the prototype is fixed.
153 bool isNewlyAdded = bufferedStructures.add(structure);
154 if (isNewlyAdded) {
155 VM& vm = *codeBlock->vm();
156 vm.heap.writeBarrier(codeBlock);
157 }
158 return isNewlyAdded;
159 }
160 countdown--;
161 return false;
162 }
163
164 StubInfoSummary summary() const;
165
166 static StubInfoSummary summary(const StructureStubInfo*);
167
168 bool containsPC(void* pc) const;
169
170 CodeOrigin codeOrigin;
171 CallSiteIndex callSiteIndex;
172
173 union {
174 struct {
175 WriteBarrierBase<Structure> baseObjectStructure;
176 PropertyOffset offset;
177 } byIdSelf;
178 PolymorphicAccess* stub;
179 } u;
180
181 // Represents those structures that already have buffered AccessCases in the PolymorphicAccess.
182 // Note that it's always safe to clear this. If we clear it prematurely, then if we see the same
183 // structure again during this buffering countdown, we will create an AccessCase object for it.
184 // That's not so bad - we'll get rid of the redundant ones once we regenerate.
185 StructureSet bufferedStructures;
186
187 struct {
188 CodeLocationLabel<JITStubRoutinePtrTag> start; // This is either the start of the inline IC for *byId caches. or the location of patchable jump for 'instanceof' caches.
189 CodeLocationLabel<JSInternalPtrTag> doneLocation;
190 CodeLocationCall<JSInternalPtrTag> slowPathCallLocation;
191 CodeLocationLabel<JITStubRoutinePtrTag> slowPathStartLocation;
192
193 RegisterSet usedRegisters;
194
195 uint32_t inlineSize() const
196 {
197 int32_t inlineSize = MacroAssembler::differenceBetweenCodePtr(start, doneLocation);
198 ASSERT(inlineSize >= 0);
199 return inlineSize;
200 }
201
202 GPRReg baseGPR;
203 GPRReg valueGPR;
204 GPRReg thisGPR;
205#if USE(JSVALUE32_64)
206 GPRReg valueTagGPR;
207 GPRReg baseTagGPR;
208 GPRReg thisTagGPR;
209#endif
210 } patch;
211
212 GPRReg baseGPR() const
213 {
214 return patch.baseGPR;
215 }
216
217 CodeLocationCall<JSInternalPtrTag> slowPathCallLocation() { return patch.slowPathCallLocation; }
218 CodeLocationLabel<JSInternalPtrTag> doneLocation() { return patch.doneLocation; }
219 CodeLocationLabel<JITStubRoutinePtrTag> slowPathStartLocation() { return patch.slowPathStartLocation; }
220
221 CodeLocationJump<JSInternalPtrTag> patchableJump()
222 {
223 ASSERT(accessType == AccessType::InstanceOf);
224 return patch.start.jumpAtOffset<JSInternalPtrTag>(0);
225 }
226
227 JSValueRegs valueRegs() const
228 {
229 return JSValueRegs(
230#if USE(JSVALUE32_64)
231 patch.valueTagGPR,
232#endif
233 patch.valueGPR);
234 }
235
236
237 AccessType accessType;
238 CacheType cacheType;
239 uint8_t countdown; // We repatch only when this is zero. If not zero, we decrement.
240 uint8_t repatchCount;
241 uint8_t numberOfCoolDowns;
242 uint8_t bufferingCountdown;
243 bool resetByGC : 1;
244 bool tookSlowPath : 1;
245 bool everConsidered : 1;
246 bool prototypeIsKnownObject : 1; // Only relevant for InstanceOf.
247 bool sawNonCell : 1;
248};
249
250inline CodeOrigin getStructureStubInfoCodeOrigin(StructureStubInfo& structureStubInfo)
251{
252 return structureStubInfo.codeOrigin;
253}
254
255inline J_JITOperation_ESsiJI appropriateOptimizingGetByIdFunction(AccessType type)
256{
257 switch (type) {
258 case AccessType::Get:
259 return operationGetByIdOptimize;
260 case AccessType::TryGet:
261 return operationTryGetByIdOptimize;
262 case AccessType::GetDirect:
263 return operationGetByIdDirectOptimize;
264 case AccessType::GetWithThis:
265 default:
266 ASSERT_NOT_REACHED();
267 return nullptr;
268 }
269}
270
271inline J_JITOperation_EJI appropriateGenericGetByIdFunction(AccessType type)
272{
273 switch (type) {
274 case AccessType::Get:
275 return operationGetByIdGeneric;
276 case AccessType::TryGet:
277 return operationTryGetByIdGeneric;
278 case AccessType::GetDirect:
279 return operationGetByIdDirectGeneric;
280 case AccessType::GetWithThis:
281 default:
282 ASSERT_NOT_REACHED();
283 return nullptr;
284 }
285}
286
287#else
288
289class StructureStubInfo;
290
291#endif // ENABLE(JIT)
292
293typedef HashMap<CodeOrigin, StructureStubInfo*, CodeOriginApproximateHash> StubInfoMap;
294
295} // namespace JSC
296