]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2011, 2013 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 | ||
31 | #include "CodeBlock.h" | |
32 | #include "DFGCCallHelpers.h" | |
33 | #include "DFGDisassembler.h" | |
34 | #include "DFGFPRInfo.h" | |
35 | #include "DFGGPRInfo.h" | |
36 | #include "DFGGraph.h" | |
37 | #include "DFGOSRExitCompilationInfo.h" | |
38 | #include "DFGRegisterBank.h" | |
39 | #include "DFGRegisterSet.h" | |
40 | #include "JITCode.h" | |
41 | #include "LinkBuffer.h" | |
42 | #include "MacroAssembler.h" | |
43 | ||
44 | namespace JSC { | |
45 | ||
46 | class AbstractSamplingCounter; | |
47 | class CodeBlock; | |
48 | class VM; | |
49 | ||
50 | namespace DFG { | |
51 | ||
52 | class JITCodeGenerator; | |
53 | class NodeToRegisterMap; | |
54 | class OSRExitJumpPlaceholder; | |
55 | class SlowPathGenerator; | |
56 | class SpeculativeJIT; | |
57 | class SpeculationRecovery; | |
58 | ||
59 | struct EntryLocation; | |
60 | struct OSRExit; | |
61 | ||
62 | // === CallLinkRecord === | |
63 | // | |
64 | // A record of a call out from JIT code that needs linking to a helper function. | |
65 | // Every CallLinkRecord contains a reference to the call instruction & the function | |
66 | // that it needs to be linked to. | |
67 | struct CallLinkRecord { | |
68 | CallLinkRecord(MacroAssembler::Call call, FunctionPtr function) | |
69 | : m_call(call) | |
70 | , m_function(function) | |
71 | { | |
72 | } | |
73 | ||
74 | MacroAssembler::Call m_call; | |
75 | FunctionPtr m_function; | |
76 | }; | |
77 | ||
78 | class CallBeginToken { | |
79 | public: | |
80 | CallBeginToken() | |
81 | #if !ASSERT_DISABLED | |
82 | : m_registered(false) | |
83 | , m_exceptionCheckIndex(std::numeric_limits<unsigned>::max()) | |
84 | #endif | |
85 | { | |
86 | } | |
87 | ||
88 | ~CallBeginToken() | |
89 | { | |
90 | ASSERT(m_registered || !m_codeOrigin.isSet()); | |
91 | ASSERT(m_codeOrigin.isSet() == (m_exceptionCheckIndex != std::numeric_limits<unsigned>::max())); | |
92 | } | |
93 | ||
94 | void set(CodeOrigin codeOrigin, unsigned index) | |
95 | { | |
96 | #if !ASSERT_DISABLED | |
97 | ASSERT(m_registered || !m_codeOrigin.isSet()); | |
98 | ASSERT(m_codeOrigin.isSet() == (m_exceptionCheckIndex != std::numeric_limits<unsigned>::max())); | |
99 | m_codeOrigin = codeOrigin; | |
100 | m_registered = false; | |
101 | m_exceptionCheckIndex = index; | |
102 | #else | |
103 | UNUSED_PARAM(codeOrigin); | |
104 | UNUSED_PARAM(index); | |
105 | #endif | |
106 | } | |
107 | ||
108 | void registerWithExceptionCheck(CodeOrigin codeOrigin, unsigned index) | |
109 | { | |
110 | #if !ASSERT_DISABLED | |
111 | ASSERT(m_codeOrigin == codeOrigin); | |
112 | if (m_registered) | |
113 | return; | |
114 | ASSERT(m_exceptionCheckIndex == index); | |
115 | m_registered = true; | |
116 | #else | |
117 | UNUSED_PARAM(codeOrigin); | |
118 | UNUSED_PARAM(index); | |
119 | #endif | |
120 | } | |
121 | ||
122 | #if !ASSERT_DISABLED | |
123 | const CodeOrigin& codeOrigin() const | |
124 | { | |
125 | return m_codeOrigin; | |
126 | } | |
127 | #endif | |
128 | ||
129 | private: | |
130 | #if !ASSERT_DISABLED | |
131 | CodeOrigin m_codeOrigin; | |
132 | bool m_registered; | |
133 | unsigned m_exceptionCheckIndex; | |
134 | #endif | |
135 | }; | |
136 | ||
137 | // === CallExceptionRecord === | |
138 | // | |
139 | // A record of a call out from JIT code that might throw an exception. | |
140 | // Calls that might throw an exception also record the Jump taken on exception | |
141 | // (unset if not present) and code origin used to recover handler/source info. | |
142 | struct CallExceptionRecord { | |
143 | CallExceptionRecord(MacroAssembler::Call call, CodeOrigin codeOrigin) | |
144 | : m_call(call) | |
145 | , m_codeOrigin(codeOrigin) | |
146 | { | |
147 | } | |
148 | ||
149 | CallExceptionRecord(MacroAssembler::Call call, MacroAssembler::Jump exceptionCheck, CodeOrigin codeOrigin) | |
150 | : m_call(call) | |
151 | , m_exceptionCheck(exceptionCheck) | |
152 | , m_codeOrigin(codeOrigin) | |
153 | { | |
154 | } | |
155 | ||
156 | MacroAssembler::Call m_call; | |
157 | MacroAssembler::Jump m_exceptionCheck; | |
158 | CodeOrigin m_codeOrigin; | |
159 | }; | |
160 | ||
161 | struct PropertyAccessRecord { | |
162 | enum RegisterMode { RegistersFlushed, RegistersInUse }; | |
163 | ||
164 | #if USE(JSVALUE64) | |
165 | PropertyAccessRecord( | |
166 | CodeOrigin codeOrigin, | |
167 | MacroAssembler::DataLabelPtr structureImm, | |
168 | MacroAssembler::PatchableJump structureCheck, | |
169 | MacroAssembler::ConvertibleLoadLabel propertyStorageLoad, | |
170 | MacroAssembler::DataLabelCompact loadOrStore, | |
171 | SlowPathGenerator* slowPathGenerator, | |
172 | MacroAssembler::Label done, | |
173 | int8_t baseGPR, | |
174 | int8_t valueGPR, | |
175 | const RegisterSet& usedRegisters, | |
176 | RegisterMode registerMode = RegistersInUse) | |
177 | #elif USE(JSVALUE32_64) | |
178 | PropertyAccessRecord( | |
179 | CodeOrigin codeOrigin, | |
180 | MacroAssembler::DataLabelPtr structureImm, | |
181 | MacroAssembler::PatchableJump structureCheck, | |
182 | MacroAssembler::ConvertibleLoadLabel propertyStorageLoad, | |
183 | MacroAssembler::DataLabelCompact tagLoadOrStore, | |
184 | MacroAssembler::DataLabelCompact payloadLoadOrStore, | |
185 | SlowPathGenerator* slowPathGenerator, | |
186 | MacroAssembler::Label done, | |
187 | int8_t baseGPR, | |
188 | int8_t valueTagGPR, | |
189 | int8_t valueGPR, | |
190 | const RegisterSet& usedRegisters, | |
191 | RegisterMode registerMode = RegistersInUse) | |
192 | #endif | |
193 | : m_codeOrigin(codeOrigin) | |
194 | , m_structureImm(structureImm) | |
195 | , m_structureCheck(structureCheck) | |
196 | , m_propertyStorageLoad(propertyStorageLoad) | |
197 | #if USE(JSVALUE64) | |
198 | , m_loadOrStore(loadOrStore) | |
199 | #elif USE(JSVALUE32_64) | |
200 | , m_tagLoadOrStore(tagLoadOrStore) | |
201 | , m_payloadLoadOrStore(payloadLoadOrStore) | |
202 | #endif | |
203 | , m_slowPathGenerator(slowPathGenerator) | |
204 | , m_done(done) | |
205 | , m_baseGPR(baseGPR) | |
206 | #if USE(JSVALUE32_64) | |
207 | , m_valueTagGPR(valueTagGPR) | |
208 | #endif | |
209 | , m_valueGPR(valueGPR) | |
210 | , m_usedRegisters(usedRegisters) | |
211 | , m_registerMode(registerMode) | |
212 | { | |
213 | } | |
214 | ||
215 | CodeOrigin m_codeOrigin; | |
216 | MacroAssembler::DataLabelPtr m_structureImm; | |
217 | MacroAssembler::PatchableJump m_structureCheck; | |
218 | MacroAssembler::ConvertibleLoadLabel m_propertyStorageLoad; | |
219 | #if USE(JSVALUE64) | |
220 | MacroAssembler::DataLabelCompact m_loadOrStore; | |
221 | #elif USE(JSVALUE32_64) | |
222 | MacroAssembler::DataLabelCompact m_tagLoadOrStore; | |
223 | MacroAssembler::DataLabelCompact m_payloadLoadOrStore; | |
224 | #endif | |
225 | SlowPathGenerator* m_slowPathGenerator; | |
226 | MacroAssembler::Label m_done; | |
227 | int8_t m_baseGPR; | |
228 | #if USE(JSVALUE32_64) | |
229 | int8_t m_valueTagGPR; | |
230 | #endif | |
231 | int8_t m_valueGPR; | |
232 | RegisterSet m_usedRegisters; | |
233 | RegisterMode m_registerMode; | |
234 | }; | |
235 | ||
236 | // === JITCompiler === | |
237 | // | |
238 | // DFG::JITCompiler is responsible for generating JIT code from the dataflow graph. | |
239 | // It does so by delegating to the speculative & non-speculative JITs, which | |
240 | // generate to a MacroAssembler (which the JITCompiler owns through an inheritance | |
241 | // relationship). The JITCompiler holds references to information required during | |
242 | // compilation, and also records information used in linking (e.g. a list of all | |
243 | // call to be linked). | |
244 | class JITCompiler : public CCallHelpers { | |
245 | public: | |
246 | JITCompiler(Graph& dfg); | |
247 | ||
248 | bool compile(JITCode& entry); | |
249 | bool compileFunction(JITCode& entry, MacroAssemblerCodePtr& entryWithArityCheck); | |
250 | ||
251 | // Accessors for properties. | |
252 | Graph& graph() { return m_graph; } | |
253 | ||
254 | // Methods to set labels for the disassembler. | |
255 | void setStartOfCode() | |
256 | { | |
257 | if (LIKELY(!m_disassembler)) | |
258 | return; | |
259 | m_disassembler->setStartOfCode(labelIgnoringWatchpoints()); | |
260 | } | |
261 | ||
262 | void setForBlock(BlockIndex blockIndex) | |
263 | { | |
264 | if (LIKELY(!m_disassembler)) | |
265 | return; | |
266 | m_disassembler->setForBlock(blockIndex, labelIgnoringWatchpoints()); | |
267 | } | |
268 | ||
269 | void setForNode(Node* node) | |
270 | { | |
271 | if (LIKELY(!m_disassembler)) | |
272 | return; | |
273 | m_disassembler->setForNode(node, labelIgnoringWatchpoints()); | |
274 | } | |
275 | ||
276 | void setEndOfMainPath() | |
277 | { | |
278 | if (LIKELY(!m_disassembler)) | |
279 | return; | |
280 | m_disassembler->setEndOfMainPath(labelIgnoringWatchpoints()); | |
281 | } | |
282 | ||
283 | void setEndOfCode() | |
284 | { | |
285 | if (LIKELY(!m_disassembler)) | |
286 | return; | |
287 | m_disassembler->setEndOfCode(labelIgnoringWatchpoints()); | |
288 | } | |
289 | ||
290 | unsigned currentCodeOriginIndex() const | |
291 | { | |
292 | return m_currentCodeOriginIndex; | |
293 | } | |
294 | ||
295 | // Get a token for beginning a call, and set the current code origin index in | |
296 | // the call frame. For each beginCall() there must be at least one exception | |
297 | // check, and all of the exception checks must have the same CodeOrigin as the | |
298 | // beginCall(). | |
299 | void beginCall(CodeOrigin codeOrigin, CallBeginToken& token) | |
300 | { | |
301 | unsigned index = m_exceptionChecks.size(); | |
302 | store32(TrustedImm32(index), tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount))); | |
303 | token.set(codeOrigin, index); | |
304 | } | |
305 | ||
306 | // Notify the JIT of a call that does not require linking. | |
307 | void notifyCall(Call functionCall, CodeOrigin codeOrigin, CallBeginToken& token) | |
308 | { | |
309 | token.registerWithExceptionCheck(codeOrigin, m_exceptionChecks.size()); | |
310 | m_exceptionChecks.append(CallExceptionRecord(functionCall, codeOrigin)); | |
311 | } | |
312 | ||
313 | // Add a call out from JIT code, without an exception check. | |
314 | Call appendCall(const FunctionPtr& function) | |
315 | { | |
316 | Call functionCall = call(); | |
317 | m_calls.append(CallLinkRecord(functionCall, function)); | |
318 | return functionCall; | |
319 | } | |
320 | ||
321 | void prepareForExceptionCheck() | |
322 | { | |
323 | move(TrustedImm32(m_exceptionChecks.size()), GPRInfo::nonPreservedNonReturnGPR); | |
324 | } | |
325 | ||
326 | // Add a call out from JIT code, with an exception check. | |
327 | void addExceptionCheck(Call functionCall, CodeOrigin codeOrigin, CallBeginToken& token) | |
328 | { | |
329 | prepareForExceptionCheck(); | |
330 | token.registerWithExceptionCheck(codeOrigin, m_exceptionChecks.size()); | |
331 | m_exceptionChecks.append(CallExceptionRecord(functionCall, emitExceptionCheck(), codeOrigin)); | |
332 | } | |
333 | ||
334 | // Add a call out from JIT code, with a fast exception check that tests if the return value is zero. | |
335 | void addFastExceptionCheck(Call functionCall, CodeOrigin codeOrigin, CallBeginToken& token) | |
336 | { | |
337 | prepareForExceptionCheck(); | |
338 | Jump exceptionCheck = branchTestPtr(Zero, GPRInfo::returnValueGPR); | |
339 | token.registerWithExceptionCheck(codeOrigin, m_exceptionChecks.size()); | |
340 | m_exceptionChecks.append(CallExceptionRecord(functionCall, exceptionCheck, codeOrigin)); | |
341 | } | |
342 | ||
343 | void appendExitInfo(MacroAssembler::JumpList jumpsToFail = MacroAssembler::JumpList()) | |
344 | { | |
345 | OSRExitCompilationInfo info; | |
346 | info.m_failureJumps = jumpsToFail; | |
347 | m_exitCompilationInfo.append(info); | |
348 | } | |
349 | ||
350 | #if USE(JSVALUE32_64) | |
351 | void* addressOfDoubleConstant(Node* node) | |
352 | { | |
353 | ASSERT(m_graph.isNumberConstant(node)); | |
354 | unsigned constantIndex = node->constantNumber(); | |
355 | return &(codeBlock()->constantRegister(FirstConstantRegisterIndex + constantIndex)); | |
356 | } | |
357 | #endif | |
358 | ||
359 | void addPropertyAccess(const PropertyAccessRecord& record) | |
360 | { | |
361 | m_propertyAccesses.append(record); | |
362 | } | |
363 | ||
364 | void addJSCall(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin) | |
365 | { | |
366 | m_jsCalls.append(JSCallRecord(fastCall, slowCall, targetToCheck, callType, callee, codeOrigin)); | |
367 | } | |
368 | ||
369 | void addWeakReference(JSCell* target) | |
370 | { | |
371 | m_codeBlock->appendWeakReference(target); | |
372 | } | |
373 | ||
374 | void addWeakReferences(const StructureSet& structureSet) | |
375 | { | |
376 | for (unsigned i = structureSet.size(); i--;) | |
377 | addWeakReference(structureSet[i]); | |
378 | } | |
379 | ||
380 | void addWeakReferenceTransition(JSCell* codeOrigin, JSCell* from, JSCell* to) | |
381 | { | |
382 | m_codeBlock->appendWeakReferenceTransition(codeOrigin, from, to); | |
383 | } | |
384 | ||
385 | template<typename T> | |
386 | Jump branchWeakPtr(RelationalCondition cond, T left, JSCell* weakPtr) | |
387 | { | |
388 | Jump result = branchPtr(cond, left, TrustedImmPtr(weakPtr)); | |
389 | addWeakReference(weakPtr); | |
390 | return result; | |
391 | } | |
392 | ||
393 | void noticeOSREntry(BasicBlock& basicBlock, JITCompiler::Label blockHead, LinkBuffer& linkBuffer) | |
394 | { | |
395 | #if DFG_ENABLE(OSR_ENTRY) | |
396 | // OSR entry is not allowed into blocks deemed unreachable by control flow analysis. | |
397 | if (!basicBlock.cfaHasVisited) | |
398 | return; | |
399 | ||
400 | OSREntryData* entry = codeBlock()->appendDFGOSREntryData(basicBlock.bytecodeBegin, linkBuffer.offsetOf(blockHead)); | |
401 | ||
402 | entry->m_expectedValues = basicBlock.valuesAtHead; | |
403 | ||
404 | // Fix the expected values: in our protocol, a dead variable will have an expected | |
405 | // value of (None, []). But the old JIT may stash some values there. So we really | |
406 | // need (Top, TOP). | |
407 | for (size_t argument = 0; argument < basicBlock.variablesAtHead.numberOfArguments(); ++argument) { | |
408 | Node* node = basicBlock.variablesAtHead.argument(argument); | |
409 | if (!node || !node->shouldGenerate()) | |
410 | entry->m_expectedValues.argument(argument).makeTop(); | |
411 | } | |
412 | for (size_t local = 0; local < basicBlock.variablesAtHead.numberOfLocals(); ++local) { | |
413 | Node* node = basicBlock.variablesAtHead.local(local); | |
414 | if (!node || !node->shouldGenerate()) | |
415 | entry->m_expectedValues.local(local).makeTop(); | |
416 | else if (node->variableAccessData()->shouldUseDoubleFormat()) | |
417 | entry->m_localsForcedDouble.set(local); | |
418 | } | |
419 | #else | |
420 | UNUSED_PARAM(basicBlock); | |
421 | UNUSED_PARAM(blockHead); | |
422 | UNUSED_PARAM(linkBuffer); | |
423 | #endif | |
424 | } | |
425 | ||
426 | private: | |
427 | friend class OSRExitJumpPlaceholder; | |
428 | ||
429 | // Internal implementation to compile. | |
430 | void compileEntry(); | |
431 | void compileBody(SpeculativeJIT&); | |
432 | void link(LinkBuffer&); | |
433 | ||
434 | void exitSpeculativeWithOSR(const OSRExit&, SpeculationRecovery*); | |
435 | void compileExceptionHandlers(); | |
436 | void linkOSRExits(); | |
437 | ||
438 | // The dataflow graph currently being generated. | |
439 | Graph& m_graph; | |
440 | ||
441 | OwnPtr<Disassembler> m_disassembler; | |
442 | ||
443 | // Vector of calls out from JIT code, including exception handler information. | |
444 | // Count of the number of CallRecords with exception handlers. | |
445 | Vector<CallLinkRecord> m_calls; | |
446 | Vector<CallExceptionRecord> m_exceptionChecks; | |
447 | ||
448 | struct JSCallRecord { | |
449 | JSCallRecord(Call fastCall, Call slowCall, DataLabelPtr targetToCheck, CallLinkInfo::CallType callType, GPRReg callee, CodeOrigin codeOrigin) | |
450 | : m_fastCall(fastCall) | |
451 | , m_slowCall(slowCall) | |
452 | , m_targetToCheck(targetToCheck) | |
453 | , m_callType(callType) | |
454 | , m_callee(callee) | |
455 | , m_codeOrigin(codeOrigin) | |
456 | { | |
457 | } | |
458 | ||
459 | Call m_fastCall; | |
460 | Call m_slowCall; | |
461 | DataLabelPtr m_targetToCheck; | |
462 | CallLinkInfo::CallType m_callType; | |
463 | GPRReg m_callee; | |
464 | CodeOrigin m_codeOrigin; | |
465 | }; | |
466 | ||
467 | Vector<PropertyAccessRecord, 4> m_propertyAccesses; | |
468 | Vector<JSCallRecord, 4> m_jsCalls; | |
469 | Vector<OSRExitCompilationInfo> m_exitCompilationInfo; | |
470 | Vector<Vector<Label> > m_exitSiteLabels; | |
471 | unsigned m_currentCodeOriginIndex; | |
472 | }; | |
473 | ||
474 | } } // namespace JSC::DFG | |
475 | ||
476 | #endif | |
477 | #endif | |
478 |