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
44namespace JSC {
45
46class AbstractSamplingCounter;
47class CodeBlock;
48class VM;
49
50namespace DFG {
51
52class JITCodeGenerator;
53class NodeToRegisterMap;
54class OSRExitJumpPlaceholder;
55class SlowPathGenerator;
56class SpeculativeJIT;
57class SpeculationRecovery;
58
59struct EntryLocation;
60struct 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.
67struct 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
78class CallBeginToken {
79public:
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
129private:
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.
142struct 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
161struct 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).
244class JITCompiler : public CCallHelpers {
245public:
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
426private:
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
479