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 | |
37 | namespace JSC { |
38 | namespace DOMJIT { |
39 | class GetterSetter; |
40 | } |
41 | |
42 | class CodeBlock; |
43 | class PolymorphicAccess; |
44 | class StructureStubInfo; |
45 | class WatchpointsOnStructureStubInfo; |
46 | class ScratchRegisterAllocator; |
47 | |
48 | class AccessGenerationResult { |
49 | public: |
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 | |
124 | private: |
125 | Kind m_kind; |
126 | MacroAssemblerCodePtr<JITStubRoutinePtrTag> m_code; |
127 | Vector<std::pair<InlineWatchpointSet&, StringFireDetail>> m_watchpointsToFire; |
128 | }; |
129 | |
130 | class PolymorphicAccess { |
131 | WTF_MAKE_NONCOPYABLE(PolymorphicAccess); |
132 | WTF_MAKE_FAST_ALLOCATED; |
133 | public: |
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 | |
171 | private: |
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 | |
188 | struct 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& = { }); |
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 | |
268 | private: |
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 | |
282 | namespace WTF { |
283 | |
284 | void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind); |
285 | void printInternal(PrintStream&, JSC::AccessCase::AccessType); |
286 | void printInternal(PrintStream&, JSC::AccessCase::State); |
287 | |
288 | } // namespace WTF |
289 | |
290 | #endif // ENABLE(JIT) |
291 | |