]>
Commit | Line | Data |
---|---|---|
14957cd0 A |
1 | /* |
2 | * Copyright (C) 2011 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 | #ifndef DFGJITCompiler_h | |
27 | #define DFGJITCompiler_h | |
28 | ||
29 | #if ENABLE(DFG_JIT) | |
30 | ||
6fe7ccc8 | 31 | #include <assembler/LinkBuffer.h> |
14957cd0 A |
32 | #include <assembler/MacroAssembler.h> |
33 | #include <bytecode/CodeBlock.h> | |
6fe7ccc8 A |
34 | #include <dfg/DFGCCallHelpers.h> |
35 | #include <dfg/DFGFPRInfo.h> | |
36 | #include <dfg/DFGGPRInfo.h> | |
14957cd0 A |
37 | #include <dfg/DFGGraph.h> |
38 | #include <dfg/DFGRegisterBank.h> | |
39 | #include <jit/JITCode.h> | |
40 | ||
14957cd0 A |
41 | namespace JSC { |
42 | ||
43 | class AbstractSamplingCounter; | |
44 | class CodeBlock; | |
45 | class JSGlobalData; | |
46 | ||
47 | namespace DFG { | |
48 | ||
49 | class JITCodeGenerator; | |
6fe7ccc8 | 50 | class NodeToRegisterMap; |
14957cd0 A |
51 | class SpeculativeJIT; |
52 | class SpeculationRecovery; | |
53 | ||
54 | struct EntryLocation; | |
6fe7ccc8 | 55 | struct OSRExit; |
14957cd0 | 56 | |
6fe7ccc8 | 57 | // === CallLinkRecord === |
14957cd0 | 58 | // |
6fe7ccc8 A |
59 | // A record of a call out from JIT code that needs linking to a helper function. |
60 | // Every CallLinkRecord contains a reference to the call instruction & the function | |
61 | // that it needs to be linked to. | |
62 | struct CallLinkRecord { | |
63 | CallLinkRecord(MacroAssembler::Call call, FunctionPtr function) | |
14957cd0 A |
64 | : m_call(call) |
65 | , m_function(function) | |
66 | { | |
67 | } | |
68 | ||
6fe7ccc8 A |
69 | MacroAssembler::Call m_call; |
70 | FunctionPtr m_function; | |
71 | }; | |
72 | ||
73 | class CallBeginToken { | |
74 | public: | |
75 | CallBeginToken() | |
76 | #if !ASSERT_DISABLED | |
77 | : m_codeOriginIndex(UINT_MAX) | |
78 | #endif | |
79 | { | |
80 | } | |
81 | ||
82 | explicit CallBeginToken(unsigned codeOriginIndex) | |
83 | #if !ASSERT_DISABLED | |
84 | : m_codeOriginIndex(codeOriginIndex) | |
85 | #endif | |
86 | { | |
87 | UNUSED_PARAM(codeOriginIndex); | |
88 | } | |
89 | ||
90 | void assertCodeOriginIndex(unsigned codeOriginIndex) const | |
91 | { | |
92 | ASSERT_UNUSED(codeOriginIndex, codeOriginIndex < UINT_MAX); | |
93 | ASSERT_UNUSED(codeOriginIndex, codeOriginIndex == m_codeOriginIndex); | |
94 | } | |
95 | ||
96 | private: | |
97 | #if !ASSERT_DISABLED | |
98 | unsigned m_codeOriginIndex; | |
99 | #endif | |
100 | }; | |
101 | ||
102 | // === CallExceptionRecord === | |
103 | // | |
104 | // A record of a call out from JIT code that might throw an exception. | |
105 | // Calls that might throw an exception also record the Jump taken on exception | |
106 | // (unset if not present) and code origin used to recover handler/source info. | |
107 | struct CallExceptionRecord { | |
108 | CallExceptionRecord(MacroAssembler::Call call, CodeOrigin codeOrigin, CallBeginToken token) | |
109 | : m_call(call) | |
110 | , m_codeOrigin(codeOrigin) | |
111 | , m_token(token) | |
112 | { | |
113 | } | |
114 | ||
115 | CallExceptionRecord(MacroAssembler::Call call, MacroAssembler::Jump exceptionCheck, CodeOrigin codeOrigin, CallBeginToken token) | |
14957cd0 | 116 | : m_call(call) |
14957cd0 | 117 | , m_exceptionCheck(exceptionCheck) |
6fe7ccc8 A |
118 | , m_codeOrigin(codeOrigin) |
119 | , m_token(token) | |
14957cd0 A |
120 | { |
121 | } | |
122 | ||
123 | MacroAssembler::Call m_call; | |
14957cd0 | 124 | MacroAssembler::Jump m_exceptionCheck; |
6fe7ccc8 A |
125 | CodeOrigin m_codeOrigin; |
126 | CallBeginToken m_token; | |
127 | }; | |
128 | ||
129 | struct PropertyAccessRecord { | |
130 | enum RegisterMode { RegistersFlushed, RegistersInUse }; | |
131 | ||
132 | #if USE(JSVALUE64) | |
133 | PropertyAccessRecord(CodeOrigin codeOrigin, MacroAssembler::DataLabelPtr deltaCheckImmToCall, MacroAssembler::Call functionCall, MacroAssembler::PatchableJump deltaCallToStructCheck, MacroAssembler::DataLabelCompact deltaCallToLoadOrStore, MacroAssembler::Label deltaCallToSlowCase, MacroAssembler::Label deltaCallToDone, int8_t baseGPR, int8_t valueGPR, int8_t scratchGPR, RegisterMode registerMode = RegistersInUse) | |
134 | #elif USE(JSVALUE32_64) | |
135 | PropertyAccessRecord(CodeOrigin codeOrigin, MacroAssembler::DataLabelPtr deltaCheckImmToCall, MacroAssembler::Call functionCall, MacroAssembler::PatchableJump deltaCallToStructCheck, MacroAssembler::DataLabelCompact deltaCallToTagLoadOrStore, MacroAssembler::DataLabelCompact deltaCallToPayloadLoadOrStore, MacroAssembler::Label deltaCallToSlowCase, MacroAssembler::Label deltaCallToDone, int8_t baseGPR, int8_t valueTagGPR, int8_t valueGPR, int8_t scratchGPR, RegisterMode registerMode = RegistersInUse) | |
136 | #endif | |
137 | : m_codeOrigin(codeOrigin) | |
138 | , m_deltaCheckImmToCall(deltaCheckImmToCall) | |
139 | , m_functionCall(functionCall) | |
140 | , m_deltaCallToStructCheck(deltaCallToStructCheck) | |
141 | #if USE(JSVALUE64) | |
142 | , m_deltaCallToLoadOrStore(deltaCallToLoadOrStore) | |
143 | #elif USE(JSVALUE32_64) | |
144 | , m_deltaCallToTagLoadOrStore(deltaCallToTagLoadOrStore) | |
145 | , m_deltaCallToPayloadLoadOrStore(deltaCallToPayloadLoadOrStore) | |
146 | #endif | |
147 | , m_deltaCallToSlowCase(deltaCallToSlowCase) | |
148 | , m_deltaCallToDone(deltaCallToDone) | |
149 | , m_baseGPR(baseGPR) | |
150 | #if USE(JSVALUE32_64) | |
151 | , m_valueTagGPR(valueTagGPR) | |
152 | #endif | |
153 | , m_valueGPR(valueGPR) | |
154 | , m_scratchGPR(scratchGPR) | |
155 | , m_registerMode(registerMode) | |
156 | { | |
157 | } | |
158 | ||
159 | CodeOrigin m_codeOrigin; | |
160 | MacroAssembler::DataLabelPtr m_deltaCheckImmToCall; | |
161 | MacroAssembler::Call m_functionCall; | |
162 | MacroAssembler::PatchableJump m_deltaCallToStructCheck; | |
163 | #if USE(JSVALUE64) | |
164 | MacroAssembler::DataLabelCompact m_deltaCallToLoadOrStore; | |
165 | #elif USE(JSVALUE32_64) | |
166 | MacroAssembler::DataLabelCompact m_deltaCallToTagLoadOrStore; | |
167 | MacroAssembler::DataLabelCompact m_deltaCallToPayloadLoadOrStore; | |
168 | #endif | |
169 | MacroAssembler::Label m_deltaCallToSlowCase; | |
170 | MacroAssembler::Label m_deltaCallToDone; | |
171 | int8_t m_baseGPR; | |
172 | #if USE(JSVALUE32_64) | |
173 | int8_t m_valueTagGPR; | |
174 | #endif | |
175 | int8_t m_valueGPR; | |
176 | int8_t m_scratchGPR; | |
177 | RegisterMode m_registerMode; | |
14957cd0 A |
178 | }; |
179 | ||
180 | // === JITCompiler === | |
181 | // | |
182 | // DFG::JITCompiler is responsible for generating JIT code from the dataflow graph. | |
183 | // It does so by delegating to the speculative & non-speculative JITs, which | |
184 | // generate to a MacroAssembler (which the JITCompiler owns through an inheritance | |
185 | // relationship). The JITCompiler holds references to information required during | |
186 | // compilation, and also records information used in linking (e.g. a list of all | |
187 | // call to be linked). | |
6fe7ccc8 | 188 | class JITCompiler : public CCallHelpers { |
14957cd0 | 189 | public: |
6fe7ccc8 A |
190 | JITCompiler(Graph& dfg) |
191 | : CCallHelpers(&dfg.m_globalData, dfg.m_codeBlock) | |
14957cd0 | 192 | , m_graph(dfg) |
6fe7ccc8 | 193 | , m_currentCodeOriginIndex(0) |
14957cd0 A |
194 | { |
195 | } | |
196 | ||
6fe7ccc8 A |
197 | bool compile(JITCode& entry); |
198 | bool compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck); | |
14957cd0 A |
199 | |
200 | // Accessors for properties. | |
201 | Graph& graph() { return m_graph; } | |
6fe7ccc8 A |
202 | |
203 | // Get a token for beginning a call, and set the current code origin index in | |
204 | // the call frame. | |
205 | CallBeginToken beginCall() | |
14957cd0 | 206 | { |
6fe7ccc8 A |
207 | unsigned codeOriginIndex = m_currentCodeOriginIndex++; |
208 | store32(TrustedImm32(codeOriginIndex), tagFor(static_cast<VirtualRegister>(RegisterFile::ArgumentCount))); | |
209 | return CallBeginToken(codeOriginIndex); | |
14957cd0 A |
210 | } |
211 | ||
6fe7ccc8 A |
212 | // Notify the JIT of a call that does not require linking. |
213 | void notifyCall(Call functionCall, CodeOrigin codeOrigin, CallBeginToken token) | |
14957cd0 | 214 | { |
6fe7ccc8 | 215 | m_exceptionChecks.append(CallExceptionRecord(functionCall, codeOrigin, token)); |
14957cd0 A |
216 | } |
217 | ||
6fe7ccc8 A |
218 | // Add a call out from JIT code, without an exception check. |
219 | Call appendCall(const FunctionPtr& function) | |
14957cd0 | 220 | { |
6fe7ccc8 A |
221 | Call functionCall = call(); |
222 | m_calls.append(CallLinkRecord(functionCall, function)); | |
223 | return functionCall; | |
14957cd0 A |
224 | } |
225 | ||
6fe7ccc8 A |
226 | // Add a call out from JIT code, with an exception check. |
227 | void addExceptionCheck(Call functionCall, CodeOrigin codeOrigin, CallBeginToken token) | |
14957cd0 | 228 | { |
6fe7ccc8 A |
229 | move(TrustedImm32(m_exceptionChecks.size()), GPRInfo::nonPreservedNonReturnGPR); |
230 | m_exceptionChecks.append(CallExceptionRecord(functionCall, emitExceptionCheck(), codeOrigin, token)); | |
14957cd0 | 231 | } |
6fe7ccc8 A |
232 | |
233 | // Add a call out from JIT code, with a fast exception check that tests if the return value is zero. | |
234 | void addFastExceptionCheck(Call functionCall, CodeOrigin codeOrigin, CallBeginToken token) | |
14957cd0 | 235 | { |
6fe7ccc8 A |
236 | move(TrustedImm32(m_exceptionChecks.size()), GPRInfo::nonPreservedNonReturnGPR); |
237 | Jump exceptionCheck = branchTestPtr(Zero, GPRInfo::returnValueGPR); | |
238 | m_exceptionChecks.append(CallExceptionRecord(functionCall, exceptionCheck, codeOrigin, token)); | |
14957cd0 | 239 | } |
6fe7ccc8 A |
240 | |
241 | // Helper methods to get predictions | |
242 | PredictedType getPrediction(Node& node) { return node.prediction(); } | |
243 | PredictedType getPrediction(NodeIndex nodeIndex) { return getPrediction(graph()[nodeIndex]); } | |
244 | PredictedType getPrediction(Edge nodeUse) { return getPrediction(nodeUse.index()); } | |
245 | ||
246 | #if USE(JSVALUE32_64) | |
247 | void* addressOfDoubleConstant(NodeIndex nodeIndex) | |
14957cd0 | 248 | { |
6fe7ccc8 A |
249 | ASSERT(m_graph.isNumberConstant(nodeIndex)); |
250 | unsigned constantIndex = graph()[nodeIndex].constantNumber(); | |
251 | return &(codeBlock()->constantRegister(FirstConstantRegisterIndex + constantIndex)); | |
14957cd0 A |
252 | } |
253 | #endif | |
254 | ||
6fe7ccc8 | 255 | void addPropertyAccess(const PropertyAccessRecord& record) |
14957cd0 | 256 | { |
6fe7ccc8 | 257 | m_propertyAccesses.append(record); |
14957cd0 A |
258 | } |
259 | ||
6fe7ccc8 | 260 | void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin) |
14957cd0 | 261 | { |
6fe7ccc8 | 262 | m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, codeOrigin)); |
14957cd0 | 263 | } |
6fe7ccc8 A |
264 | |
265 | void addWeakReference(JSCell* target) | |
14957cd0 | 266 | { |
6fe7ccc8 | 267 | m_codeBlock->appendWeakReference(target); |
14957cd0 | 268 | } |
6fe7ccc8 A |
269 | |
270 | void addWeakReferenceTransition(JSCell* codeOrigin, JSCell* from, JSCell* to) | |
14957cd0 | 271 | { |
6fe7ccc8 | 272 | m_codeBlock->appendWeakReferenceTransition(codeOrigin, from, to); |
14957cd0 | 273 | } |
6fe7ccc8 A |
274 | |
275 | template<typename T> | |
276 | Jump branchWeakPtr(RelationalCondition cond, T left, JSCell* weakPtr) | |
14957cd0 | 277 | { |
6fe7ccc8 A |
278 | Jump result = branchPtr(cond, left, TrustedImmPtr(weakPtr)); |
279 | addWeakReference(weakPtr); | |
280 | return result; | |
14957cd0 | 281 | } |
6fe7ccc8 A |
282 | |
283 | void noticeOSREntry(BasicBlock& basicBlock, JITCompiler::Label blockHead, LinkBuffer& linkBuffer) | |
14957cd0 | 284 | { |
6fe7ccc8 A |
285 | #if DFG_ENABLE(OSR_ENTRY) |
286 | OSREntryData* entry = codeBlock()->appendDFGOSREntryData(basicBlock.bytecodeBegin, linkBuffer.offsetOf(blockHead)); | |
287 | ||
288 | entry->m_expectedValues = basicBlock.valuesAtHead; | |
289 | ||
290 | // Fix the expected values: in our protocol, a dead variable will have an expected | |
291 | // value of (None, []). But the old JIT may stash some values there. So we really | |
292 | // need (Top, TOP). | |
293 | for (size_t argument = 0; argument < basicBlock.variablesAtHead.numberOfArguments(); ++argument) { | |
294 | NodeIndex nodeIndex = basicBlock.variablesAtHead.argument(argument); | |
295 | if (nodeIndex == NoNode || !m_graph[nodeIndex].shouldGenerate()) | |
296 | entry->m_expectedValues.argument(argument).makeTop(); | |
297 | } | |
298 | for (size_t local = 0; local < basicBlock.variablesAtHead.numberOfLocals(); ++local) { | |
299 | NodeIndex nodeIndex = basicBlock.variablesAtHead.local(local); | |
300 | if (nodeIndex == NoNode || !m_graph[nodeIndex].shouldGenerate()) | |
301 | entry->m_expectedValues.local(local).makeTop(); | |
302 | else if (m_graph[nodeIndex].variableAccessData()->shouldUseDoubleFormat()) | |
303 | entry->m_localsForcedDouble.set(local); | |
304 | } | |
14957cd0 | 305 | #else |
6fe7ccc8 A |
306 | UNUSED_PARAM(basicBlock); |
307 | UNUSED_PARAM(blockHead); | |
308 | UNUSED_PARAM(linkBuffer); | |
14957cd0 | 309 | #endif |
6fe7ccc8 | 310 | } |
14957cd0 A |
311 | |
312 | private: | |
6fe7ccc8 A |
313 | // Internal implementation to compile. |
314 | void compileEntry(); | |
315 | void compileBody(SpeculativeJIT&); | |
316 | void link(LinkBuffer&); | |
317 | ||
318 | void exitSpeculativeWithOSR(const OSRExit&, SpeculationRecovery*); | |
319 | void linkOSRExits(); | |
320 | ||
14957cd0 A |
321 | // The dataflow graph currently being generated. |
322 | Graph& m_graph; | |
323 | ||
14957cd0 | 324 | // Vector of calls out from JIT code, including exception handler information. |
6fe7ccc8 A |
325 | // Count of the number of CallRecords with exception handlers. |
326 | Vector<CallLinkRecord> m_calls; | |
327 | Vector<CallExceptionRecord> m_exceptionChecks; | |
328 | ||
329 | struct JSCallRecord { | |
330 | JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, CodeOrigin codeOrigin) | |
331 | : m_fastCall(fastCall) | |
332 | , m_slowCall(slowCall) | |
333 | , m_targetToCheck(targetToCheck) | |
334 | , m_callType(callType) | |
335 | , m_codeOrigin(codeOrigin) | |
336 | { | |
337 | } | |
338 | ||
339 | Call m_fastCall; | |
340 | Call m_slowCall; | |
341 | DataLabelPtr m_targetToCheck; | |
342 | CallLinkInfo::CallType m_callType; | |
343 | CodeOrigin m_codeOrigin; | |
344 | }; | |
345 | ||
346 | Vector<PropertyAccessRecord, 4> m_propertyAccesses; | |
347 | Vector<JSCallRecord, 4> m_jsCalls; | |
348 | unsigned m_currentCodeOriginIndex; | |
14957cd0 A |
349 | }; |
350 | ||
351 | } } // namespace JSC::DFG | |
352 | ||
353 | #endif | |
354 | #endif | |
355 |