1/*
2 * Copyright (C) 2014-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#if ENABLE(JIT)
29
30#include "AccessCase.h"
31#include "JITStubRoutine.h"
32#include "JSFunctionInlines.h"
33#include "MacroAssembler.h"
34#include "ScratchRegisterAllocator.h"
35#include <wtf/Vector.h>
36
37namespace JSC {
38namespace DOMJIT {
39class GetterSetter;
40}
41
42class CodeBlock;
43class PolymorphicAccess;
44class StructureStubInfo;
45class WatchpointsOnStructureStubInfo;
46class ScratchRegisterAllocator;
47
48class AccessGenerationResult {
49public:
50 enum Kind {
51 MadeNoChanges,
52 GaveUp,
53 Buffered,
54 GeneratedNewCode,
55 GeneratedFinalCode, // Generated so much code that we never want to generate code again.
56 ResetStubAndFireWatchpoints // We found out some data that makes us want to start over fresh with this stub. Currently, this happens when we detect poly proto.
57 };
58
59
60 AccessGenerationResult() = default;
61 AccessGenerationResult(AccessGenerationResult&&) = default;
62 AccessGenerationResult& operator=(AccessGenerationResult&&) = default;
63
64 AccessGenerationResult(Kind kind)
65 : m_kind(kind)
66 {
67 RELEASE_ASSERT(kind != GeneratedNewCode);
68 RELEASE_ASSERT(kind != GeneratedFinalCode);
69 }
70
71 AccessGenerationResult(Kind kind, MacroAssemblerCodePtr<JITStubRoutinePtrTag> code)
72 : m_kind(kind)
73 , m_code(code)
74 {
75 RELEASE_ASSERT(kind == GeneratedNewCode || kind == GeneratedFinalCode);
76 RELEASE_ASSERT(code);
77 }
78
79 bool operator==(const AccessGenerationResult& other) const
80 {
81 return m_kind == other.m_kind && m_code == other.m_code;
82 }
83
84 bool operator!=(const AccessGenerationResult& other) const
85 {
86 return !(*this == other);
87 }
88
89 explicit operator bool() const
90 {
91 return *this != AccessGenerationResult();
92 }
93
94 Kind kind() const { return m_kind; }
95
96 const MacroAssemblerCodePtr<JITStubRoutinePtrTag>& code() const { return m_code; }
97
98 bool madeNoChanges() const { return m_kind == MadeNoChanges; }
99 bool gaveUp() const { return m_kind == GaveUp; }
100 bool buffered() const { return m_kind == Buffered; }
101 bool generatedNewCode() const { return m_kind == GeneratedNewCode; }
102 bool generatedFinalCode() const { return m_kind == GeneratedFinalCode; }
103 bool shouldResetStubAndFireWatchpoints() const { return m_kind == ResetStubAndFireWatchpoints; }
104
105 // If we gave up on this attempt to generate code, or if we generated the "final" code, then we
106 // should give up after this.
107 bool shouldGiveUpNow() const { return gaveUp() || generatedFinalCode(); }
108
109 bool generatedSomeCode() const { return generatedNewCode() || generatedFinalCode(); }
110
111 void dump(PrintStream&) const;
112
113 void addWatchpointToFire(InlineWatchpointSet& set, StringFireDetail detail)
114 {
115 m_watchpointsToFire.append(std::pair<InlineWatchpointSet&, StringFireDetail>(set, detail));
116 }
117 void fireWatchpoints(VM& vm)
118 {
119 ASSERT(m_kind == ResetStubAndFireWatchpoints);
120 for (auto& pair : m_watchpointsToFire)
121 pair.first.invalidate(vm, pair.second);
122 }
123
124private:
125 Kind m_kind;
126 MacroAssemblerCodePtr<JITStubRoutinePtrTag> m_code;
127 Vector<std::pair<InlineWatchpointSet&, StringFireDetail>> m_watchpointsToFire;
128};
129
130class PolymorphicAccess {
131 WTF_MAKE_NONCOPYABLE(PolymorphicAccess);
132 WTF_MAKE_FAST_ALLOCATED;
133public:
134 PolymorphicAccess();
135 ~PolymorphicAccess();
136
137 // When this fails (returns GaveUp), this will leave the old stub intact but you should not try
138 // to call this method again for that PolymorphicAccess instance.
139 AccessGenerationResult addCases(
140 const GCSafeConcurrentJSLocker&, VM&, CodeBlock*, StructureStubInfo&, Vector<std::unique_ptr<AccessCase>, 2>);
141
142 AccessGenerationResult addCase(
143 const GCSafeConcurrentJSLocker&, VM&, CodeBlock*, StructureStubInfo&, std::unique_ptr<AccessCase>);
144
145 AccessGenerationResult regenerate(const GCSafeConcurrentJSLocker&, VM&, CodeBlock*, StructureStubInfo&);
146
147 bool isEmpty() const { return m_list.isEmpty(); }
148 unsigned size() const { return m_list.size(); }
149 const AccessCase& at(unsigned i) const { return *m_list[i]; }
150 const AccessCase& operator[](unsigned i) const { return *m_list[i]; }
151
152 // If this returns false then we are requesting a reset of the owning StructureStubInfo.
153 bool visitWeak(VM&) const;
154
155 // This returns true if it has marked everything it will ever marked. This can be used as an
156 // optimization to then avoid calling this method again during the fixpoint.
157 bool propagateTransitions(SlotVisitor&) const;
158
159 void aboutToDie();
160
161 void dump(PrintStream& out) const;
162 bool containsPC(void* pc) const
163 {
164 if (!m_stubRoutine)
165 return false;
166
167 uintptr_t pcAsInt = bitwise_cast<uintptr_t>(pc);
168 return m_stubRoutine->startAddress() <= pcAsInt && pcAsInt <= m_stubRoutine->endAddress();
169 }
170
171private:
172 friend class AccessCase;
173 friend class CodeBlock;
174 friend struct AccessGenerationState;
175
176 typedef Vector<std::unique_ptr<AccessCase>, 2> ListType;
177
178 void commit(
179 const GCSafeConcurrentJSLocker&, VM&, std::unique_ptr<WatchpointsOnStructureStubInfo>&, CodeBlock*, StructureStubInfo&,
180 AccessCase&);
181
182 ListType m_list;
183 RefPtr<JITStubRoutine> m_stubRoutine;
184 std::unique_ptr<WatchpointsOnStructureStubInfo> m_watchpoints;
185 std::unique_ptr<Vector<WriteBarrier<JSCell>>> m_weakReferences;
186};
187
188struct AccessGenerationState {
189 AccessGenerationState(VM& vm, JSGlobalObject* globalObject)
190 : m_vm(vm)
191 , m_globalObject(globalObject)
192 , m_calculatedRegistersForCallAndExceptionHandling(false)
193 , m_needsToRestoreRegistersIfException(false)
194 , m_calculatedCallSiteIndex(false)
195 {
196 u.thisGPR = InvalidGPRReg;
197 }
198 VM& m_vm;
199 JSGlobalObject* m_globalObject;
200 CCallHelpers* jit { nullptr };
201 ScratchRegisterAllocator* allocator;
202 ScratchRegisterAllocator::PreservedState preservedReusedRegisterState;
203 PolymorphicAccess* access { nullptr };
204 StructureStubInfo* stubInfo { nullptr };
205 MacroAssembler::JumpList success;
206 MacroAssembler::JumpList failAndRepatch;
207 MacroAssembler::JumpList failAndIgnore;
208 GPRReg baseGPR { InvalidGPRReg };
209 union {
210 GPRReg thisGPR;
211 GPRReg prototypeGPR;
212 GPRReg propertyGPR;
213 } u;
214 JSValueRegs valueRegs;
215 GPRReg scratchGPR { InvalidGPRReg };
216 FPRReg scratchFPR { InvalidFPRReg };
217 std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
218 Vector<WriteBarrier<JSCell>> weakReferences;
219
220 void installWatchpoint(const ObjectPropertyCondition&);
221
222 void restoreScratch();
223 void succeed();
224
225 struct SpillState {
226 SpillState() = default;
227 SpillState(RegisterSet&& regs, unsigned usedStackBytes)
228 : spilledRegisters(WTFMove(regs))
229 , numberOfStackBytesUsedForRegisterPreservation(usedStackBytes)
230 {
231 }
232
233 RegisterSet spilledRegisters { };
234 unsigned numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
235
236 bool isEmpty() const { return numberOfStackBytesUsedForRegisterPreservation == std::numeric_limits<unsigned>::max(); }
237 };
238
239 const RegisterSet& calculateLiveRegistersForCallAndExceptionHandling();
240
241 SpillState preserveLiveRegistersToStackForCall(const RegisterSet& extra = { });
242
243 void restoreLiveRegistersFromStackForCallWithThrownException(const SpillState&);
244 void restoreLiveRegistersFromStackForCall(const SpillState&, const RegisterSet& dontRestore = { });
245
246 const RegisterSet& liveRegistersForCall();
247
248 CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
249 DisposableCallSiteIndex callSiteIndexForExceptionHandling();
250
251 const HandlerInfo& originalExceptionHandler();
252
253 bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
254 CallSiteIndex originalCallSiteIndex() const;
255
256 void emitExplicitExceptionHandler();
257
258 void setSpillStateForJSGetterSetter(SpillState& spillState)
259 {
260 if (!m_spillStateForJSGetterSetter.isEmpty()) {
261 ASSERT(m_spillStateForJSGetterSetter.numberOfStackBytesUsedForRegisterPreservation == spillState.numberOfStackBytesUsedForRegisterPreservation);
262 ASSERT(m_spillStateForJSGetterSetter.spilledRegisters == spillState.spilledRegisters);
263 }
264 m_spillStateForJSGetterSetter = spillState;
265 }
266 SpillState spillStateForJSGetterSetter() const { return m_spillStateForJSGetterSetter; }
267
268private:
269 const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite();
270
271 RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
272 RegisterSet m_liveRegistersForCall;
273 CallSiteIndex m_callSiteIndex;
274 SpillState m_spillStateForJSGetterSetter;
275 bool m_calculatedRegistersForCallAndExceptionHandling : 1;
276 bool m_needsToRestoreRegistersIfException : 1;
277 bool m_calculatedCallSiteIndex : 1;
278};
279
280} // namespace JSC
281
282namespace WTF {
283
284void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind);
285void printInternal(PrintStream&, JSC::AccessCase::AccessType);
286void printInternal(PrintStream&, JSC::AccessCase::State);
287
288} // namespace WTF
289
290#endif // ENABLE(JIT)
291