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&, const Identifier&, Vector<std::unique_ptr<AccessCase>, 2>);
141
142 AccessGenerationResult addCase(
143 const GCSafeConcurrentJSLocker&, VM&, CodeBlock*, StructureStubInfo&, const Identifier&, std::unique_ptr<AccessCase>);
144
145 AccessGenerationResult regenerate(const GCSafeConcurrentJSLocker&, VM&, CodeBlock*, StructureStubInfo&, const Identifier&);
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 const Identifier&, 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 }
197 VM& m_vm;
198 JSGlobalObject* m_globalObject;
199 CCallHelpers* jit { nullptr };
200 ScratchRegisterAllocator* allocator;
201 ScratchRegisterAllocator::PreservedState preservedReusedRegisterState;
202 PolymorphicAccess* access { nullptr };
203 StructureStubInfo* stubInfo { nullptr };
204 MacroAssembler::JumpList success;
205 MacroAssembler::JumpList failAndRepatch;
206 MacroAssembler::JumpList failAndIgnore;
207 GPRReg baseGPR { InvalidGPRReg };
208 GPRReg thisGPR { InvalidGPRReg };
209 JSValueRegs valueRegs;
210 GPRReg scratchGPR { InvalidGPRReg };
211 const Identifier* ident;
212 std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
213 Vector<WriteBarrier<JSCell>> weakReferences;
214
215 Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition());
216
217 void restoreScratch();
218 void succeed();
219
220 struct SpillState {
221 SpillState() = default;
222 SpillState(RegisterSet&& regs, unsigned usedStackBytes)
223 : spilledRegisters(WTFMove(regs))
224 , numberOfStackBytesUsedForRegisterPreservation(usedStackBytes)
225 {
226 }
227
228 RegisterSet spilledRegisters { };
229 unsigned numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
230
231 bool isEmpty() const { return numberOfStackBytesUsedForRegisterPreservation == std::numeric_limits<unsigned>::max(); }
232 };
233
234 const RegisterSet& calculateLiveRegistersForCallAndExceptionHandling();
235
236 SpillState preserveLiveRegistersToStackForCall(const RegisterSet& extra = { });
237
238 void restoreLiveRegistersFromStackForCallWithThrownException(const SpillState&);
239 void restoreLiveRegistersFromStackForCall(const SpillState&, const RegisterSet& dontRestore = { });
240
241 const RegisterSet& liveRegistersForCall();
242
243 CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
244 CallSiteIndex callSiteIndexForExceptionHandling()
245 {
246 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
247 RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
248 RELEASE_ASSERT(m_calculatedCallSiteIndex);
249 return m_callSiteIndex;
250 }
251
252 const HandlerInfo& originalExceptionHandler();
253
254 bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
255 CallSiteIndex originalCallSiteIndex() const;
256
257 void emitExplicitExceptionHandler();
258
259 void setSpillStateForJSGetterSetter(SpillState& spillState)
260 {
261 if (!m_spillStateForJSGetterSetter.isEmpty()) {
262 ASSERT(m_spillStateForJSGetterSetter.numberOfStackBytesUsedForRegisterPreservation == spillState.numberOfStackBytesUsedForRegisterPreservation);
263 ASSERT(m_spillStateForJSGetterSetter.spilledRegisters == spillState.spilledRegisters);
264 }
265 m_spillStateForJSGetterSetter = spillState;
266 }
267 SpillState spillStateForJSGetterSetter() const { return m_spillStateForJSGetterSetter; }
268
269private:
270 const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite();
271
272 RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
273 RegisterSet m_liveRegistersForCall;
274 CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits<unsigned>::max()) };
275 SpillState m_spillStateForJSGetterSetter;
276 bool m_calculatedRegistersForCallAndExceptionHandling : 1;
277 bool m_needsToRestoreRegistersIfException : 1;
278 bool m_calculatedCallSiteIndex : 1;
279};
280
281} // namespace JSC
282
283namespace WTF {
284
285void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind);
286void printInternal(PrintStream&, JSC::AccessCase::AccessType);
287void printInternal(PrintStream&, JSC::AccessCase::State);
288
289} // namespace WTF
290
291#endif // ENABLE(JIT)
292